From 335a277662d28b935c9d84a3d7a98276afdffd3e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 13 Jan 2005 07:50:09 +0000 Subject: r4722: Start to add 'net join' to Samba4. Andrew Bartlett (This used to be commit a9b960609142e15ba5950eb1b22944eb6df18d9c) --- source4/libnet/config.mk | 3 +- source4/libnet/libnet.h | 1 + source4/libnet/libnet_join.c | 279 +++++++++++++++++++++++++++++++++++++++++ source4/libnet/libnet_join.h | 51 ++++++++ source4/libnet/libnet_passwd.c | 46 +------ source4/utils/net/config.mk | 3 +- source4/utils/net/net.c | 2 +- source4/utils/net/net_join.c | 108 ++++++++++++++++ 8 files changed, 445 insertions(+), 48 deletions(-) create mode 100644 source4/libnet/libnet_join.c create mode 100644 source4/libnet/libnet_join.h create mode 100644 source4/utils/net/net_join.c diff --git a/source4/libnet/config.mk b/source4/libnet/config.mk index 969a2bbd34..8d95cdd8e2 100644 --- a/source4/libnet/config.mk +++ b/source4/libnet/config.mk @@ -6,7 +6,8 @@ INIT_OBJ_FILES = \ ADD_OBJ_FILES = \ libnet/libnet_passwd.o \ libnet/libnet_time.o \ - libnet/libnet_rpc.o + libnet/libnet_rpc.o \ + libnet/libnet_join.o REQUIRED_SUBSYSTEMS = RPC_NDR_SAMR RPC_NDR_SRVSVC # End SUBSYSTEM LIBNET ################################# diff --git a/source4/libnet/libnet.h b/source4/libnet/libnet.h index 5568113747..0939c20a9f 100644 --- a/source4/libnet/libnet.h +++ b/source4/libnet/libnet.h @@ -35,3 +35,4 @@ struct libnet_context { #include "libnet/libnet_passwd.h" #include "libnet/libnet_time.h" #include "libnet/libnet_rpc.h" +#include "libnet/libnet_join.h" diff --git a/source4/libnet/libnet_join.c b/source4/libnet/libnet_join.c new file mode 100644 index 0000000000..871e5c5e24 --- /dev/null +++ b/source4/libnet/libnet_join.c @@ -0,0 +1,279 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Andrew Bartlett 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr.h" +#include "lib/crypto/crypto.h" + +/* + * do a domain join using DCERPC/SAMR calls + * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) + * is it correct to contact the the pdc of the domain of the user who's password should be set? + * 2. do a samr_Connect to get a policy handle + * 3. do a samr_LookupDomain to get the domain sid + * 4. do a samr_OpenDomain to get a domain handle + * 5. do a samr_CreateAccount to try and get a new account + * + * If that fails, do: + * 5.1. do a samr_LookupNames to get the users rid + * 5.2. do a samr_OpenUser to get a user handle + * + * 6. call libnet_SetPassword_samr_handle to set the password + * + * 7. do a samrSetUserInfo to set the account flags + */ +static NTSTATUS libnet_JoinDomain_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r) +{ + NTSTATUS status; + union libnet_rpc_connect c; + struct samr_Connect sc; + struct policy_handle p_handle; + struct samr_LookupDomain ld; + struct samr_String d_name; + struct samr_OpenDomain od; + struct policy_handle d_handle; + struct samr_LookupNames ln; + struct samr_OpenUser ou; + struct samr_CreateUser2 cu; + struct policy_handle u_handle; + struct samr_SetUserInfo sui; + union samr_UserInfo u_info; + union libnet_SetPassword r2; + struct samr_GetUserPwInfo pwp; + struct samr_String samr_account_name; + + uint32 rid, access_granted; + int policy_min_pw_len = 0; + + /* prepare connect to the SAMR pipe of users domain PDC */ + c.pdc.level = LIBNET_RPC_CONNECT_PDC; + c.pdc.in.domain_name = r->samr.in.domain_name; + c.pdc.in.dcerpc_iface_name = DCERPC_SAMR_NAME; + c.pdc.in.dcerpc_iface_uuid = DCERPC_SAMR_UUID; + c.pdc.in.dcerpc_iface_version = DCERPC_SAMR_VERSION; + + /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */ + status = libnet_rpc_connect(ctx, mem_ctx, &c); + if (!NT_STATUS_IS_OK(status)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "Connection to SAMR pipe of PDC of domain '%s' failed: %s\n", + r->samr.in.domain_name, nt_errstr(status)); + return status; + } + + /* prepare samr_Connect */ + ZERO_STRUCT(p_handle); + sc.in.system_name = NULL; + sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + sc.out.connect_handle = &p_handle; + + /* 2. do a samr_Connect to get a policy handle */ + status = dcerpc_samr_Connect(c.pdc.out.dcerpc_pipe, mem_ctx, &sc); + if (!NT_STATUS_IS_OK(status)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_Connect failed: %s\n", + nt_errstr(status)); + goto disconnect; + } + + /* check result of samr_Connect */ + if (!NT_STATUS_IS_OK(sc.out.result)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_Connect failed: %s\n", + nt_errstr(sc.out.result)); + status = sc.out.result; + goto disconnect; + } + + /* prepare samr_LookupDomain */ + d_name.string = r->samr.in.domain_name; + ld.in.connect_handle = &p_handle; + ld.in.domain = &d_name; + + /* 3. do a samr_LookupDomain to get the domain sid */ + status = dcerpc_samr_LookupDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &ld); + if (!NT_STATUS_IS_OK(status)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_LookupDomain for [%s] failed: %s\n", + r->samr.in.domain_name, nt_errstr(status)); + goto disconnect; + } + + /* check result of samr_LookupDomain */ + if (!NT_STATUS_IS_OK(ld.out.result)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_LookupDomain for [%s] failed: %s\n", + r->samr.in.domain_name, nt_errstr(ld.out.result)); + status = ld.out.result; + goto disconnect; + } + + /* prepare samr_OpenDomain */ + ZERO_STRUCT(d_handle); + od.in.connect_handle = &p_handle; + od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + od.in.sid = ld.out.sid; + od.out.domain_handle = &d_handle; + + /* 4. do a samr_OpenDomain to get a domain handle */ + status = dcerpc_samr_OpenDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &od); + if (!NT_STATUS_IS_OK(status)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_OpenDomain for [%s] failed: %s\n", + r->samr.in.domain_name, nt_errstr(status)); + goto disconnect; + } + + /* prepare samr_CreateUser2 */ + ZERO_STRUCT(u_handle); + cu.in.domain_handle = &d_handle; + cu.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + samr_account_name.string = r->samr.in.account_name; + cu.in.account_name = &samr_account_name; + cu.in.acct_flags = r->samr.in.acct_type; + cu.out.user_handle = &u_handle; + cu.out.rid = &rid; + cu.out.access_granted = &access_granted; + + /* 4. do a samr_CreateUser2 to get an account handle, or an error */ + status = dcerpc_samr_CreateUser2(c.pdc.out.dcerpc_pipe, mem_ctx, &cu); + if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_CreateUser2 for [%s] failed: %s\n", + r->samr.in.domain_name, nt_errstr(status)); + goto disconnect; + + } else if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { + /* prepare samr_LookupNames */ + ln.in.domain_handle = &d_handle; + ln.in.num_names = 1; + ln.in.names = talloc_array_p(mem_ctx, struct samr_String, 1); + if (!ln.in.names) { + r->samr.out.error_string = "Out of Memory"; + return NT_STATUS_NO_MEMORY; + } + ln.in.names[0].string = r->samr.in.account_name; + + /* 5. do a samr_LookupNames to get the users rid */ + status = dcerpc_samr_LookupNames(c.pdc.out.dcerpc_pipe, mem_ctx, &ln); + if (!NT_STATUS_IS_OK(status)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_LookupNames for [%s] failed: %s\n", + r->samr.in.account_name, nt_errstr(status)); + goto disconnect; + } + + + /* check if we got one RID for the user */ + if (ln.out.rids.count != 1) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_LookupNames for [%s] returns %d RIDs\n", + r->samr.in.account_name, ln.out.rids.count); + status = NT_STATUS_INVALID_PARAMETER; + goto disconnect; + } + + /* prepare samr_OpenUser */ + ZERO_STRUCT(u_handle); + ou.in.domain_handle = &d_handle; + ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + ou.in.rid = ln.out.rids.ids[0]; + ou.out.user_handle = &u_handle; + + /* 6. do a samr_OpenUser to get a user handle */ + status = dcerpc_samr_OpenUser(c.pdc.out.dcerpc_pipe, mem_ctx, &ou); + if (!NT_STATUS_IS_OK(status)) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_OpenUser for [%s] failed: %s\n", + r->samr.in.account_name, nt_errstr(status)); + goto disconnect; + } + } + + pwp.in.user_handle = &u_handle; + + status = dcerpc_samr_GetUserPwInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info.min_password_length; + } + + r->samr.out.join_password = generate_random_str(mem_ctx, MAX(8, policy_min_pw_len)); + + r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE; + r2.samr_handle.in.account_name = r->samr.in.account_name; + r2.samr_handle.in.newpassword = r->samr.out.join_password; + r2.samr_handle.in.user_handle = &u_handle; + r2.samr_handle.in.dcerpc_pipe = c.pdc.out.dcerpc_pipe; + + status = libnet_SetPassword(ctx, mem_ctx, &r2); + + r->samr.out.error_string = r2.samr_handle.out.error_string; + + if (!NT_STATUS_IS_OK(status)) { + goto disconnect; + } + + /* prepare samr_SetUserInfo level 23 */ + ZERO_STRUCT(u_info); + u_info.info16.acct_flags = r->samr.in.acct_type; + + sui.in.user_handle = &u_handle; + sui.in.info = &u_info; + sui.in.level = 16; + + dcerpc_samr_SetUserInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &sui); + +disconnect: + /* close connection */ + dcerpc_pipe_close(c.pdc.out.dcerpc_pipe); + + return status; +} + +static NTSTATUS libnet_JoinDomain_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r) +{ + NTSTATUS status; + union libnet_JoinDomain r2; + + r2.samr.level = LIBNET_JOIN_DOMAIN_SAMR; + r2.samr.in.account_name = r->generic.in.account_name; + r2.samr.in.domain_name = r->generic.in.domain_name; + r2.samr.in.acct_type = r->generic.in.acct_type; + + status = libnet_JoinDomain(ctx, mem_ctx, &r2); + + r->generic.out.error_string = r2.samr.out.error_string; + + return status; +} + +NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r) +{ + switch (r->generic.level) { + case LIBNET_JOIN_DOMAIN_GENERIC: + return libnet_JoinDomain_generic(ctx, mem_ctx, r); + case LIBNET_JOIN_DOMAIN_SAMR: + return libnet_JoinDomain_samr(ctx, mem_ctx, r); + } + + return NT_STATUS_INVALID_LEVEL; +} diff --git a/source4/libnet/libnet_join.h b/source4/libnet/libnet_join.h new file mode 100644 index 0000000000..8788016e8e --- /dev/null +++ b/source4/libnet/libnet_join.h @@ -0,0 +1,51 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Andrew Bartlett 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* struct and enum for doing a remote domain join */ +enum libnet_JoinDomain_level { + LIBNET_JOIN_DOMAIN_GENERIC, + LIBNET_JOIN_DOMAIN_SAMR, +}; + +union libnet_JoinDomain { + struct { + enum libnet_JoinDomain_level level; + + struct _libnet_JoinDomain_in { + const char *domain_name; + const char *account_name; + uint32 acct_type; + } in; + + struct _libnet_JoinDomain_out { + const char *error_string; + const char *join_password; + } out; + } generic; + + struct { + enum libnet_JoinDomain_level level; + struct _libnet_JoinDomain_in in; + struct _libnet_JoinDomain_out out; + } samr; + +}; + diff --git a/source4/libnet/libnet_passwd.c b/source4/libnet/libnet_passwd.c index c36c478733..20be3a9dce 100644 --- a/source4/libnet/libnet_passwd.c +++ b/source4/libnet/libnet_passwd.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Andrew Bartlett 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -544,15 +545,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX * goto disconnect; } - /* check result of samr_Connect */ - if (!NT_STATUS_IS_OK(sc.out.result)) { - r->samr.out.error_string = talloc_asprintf(mem_ctx, - "samr_Connect failed: %s\n", - nt_errstr(sc.out.result)); - status = sc.out.result; - goto disconnect; - } - /* prepare samr_LookupDomain */ d_name.string = r->samr.in.domain_name; ld.in.connect_handle = &p_handle; @@ -567,15 +559,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX * goto disconnect; } - /* check result of samr_LookupDomain */ - if (!NT_STATUS_IS_OK(ld.out.result)) { - r->samr.out.error_string = talloc_asprintf(mem_ctx, - "samr_LookupDomain for [%s] failed: %s\n", - r->samr.in.domain_name, nt_errstr(ld.out.result)); - status = ld.out.result; - goto disconnect; - } - /* prepare samr_OpenDomain */ ZERO_STRUCT(d_handle); od.in.connect_handle = &p_handle; @@ -592,15 +575,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX * goto disconnect; } - /* check result of samr_OpenDomain */ - if (!NT_STATUS_IS_OK(od.out.result)) { - r->samr.out.error_string = talloc_asprintf(mem_ctx, - "samr_OpenDomain for [%s] failed: %s\n", - r->samr.in.domain_name, nt_errstr(od.out.result)); - status = od.out.result; - goto disconnect; - } - /* prepare samr_LookupNames */ ln.in.domain_handle = &d_handle; ln.in.num_names = 1; @@ -620,15 +594,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX * goto disconnect; } - /* check result of samr_LookupNames */ - if (!NT_STATUS_IS_OK(ln.out.result)) { - r->samr.out.error_string = talloc_asprintf(mem_ctx, - "samr_LookupNames for [%s] failed: %s\n", - r->samr.in.account_name, nt_errstr(ln.out.result)); - status = ln.out.result; - goto disconnect; -} - /* check if we got one RID for the user */ if (ln.out.rids.count != 1) { r->samr.out.error_string = talloc_asprintf(mem_ctx, @@ -654,15 +619,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX * goto disconnect; } - /* check result of samr_OpenUser */ - if (!NT_STATUS_IS_OK(ou.out.result)) { - r->samr.out.error_string = talloc_asprintf(mem_ctx, - "samr_OpenUser for [%s] failed: %s\n", - r->samr.in.account_name, nt_errstr(ou.out.result)); - status = ou.out.result; - goto disconnect; - } - r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE; r2.samr_handle.in.account_name = r->samr.in.account_name; r2.samr_handle.in.newpassword = r->samr.in.newpassword; diff --git a/source4/utils/net/config.mk b/source4/utils/net/config.mk index f1f6646e9c..e1bc813bec 100644 --- a/source4/utils/net/config.mk +++ b/source4/utils/net/config.mk @@ -6,7 +6,8 @@ OBJ_FILES = \ utils/net/net.o \ utils/net/net_password.o \ - utils/net/net_time.o + utils/net/net_time.o \ + utils/net/net_join.o REQUIRED_SUBSYSTEMS = \ CONFIG \ LIBCMDLINE \ diff --git a/source4/utils/net/net.c b/source4/utils/net/net.c index 022bb9225e..350ec251d9 100644 --- a/source4/utils/net/net.c +++ b/source4/utils/net/net.c @@ -139,7 +139,7 @@ static int net_help_usage(struct net_context *ctx, int argc, const char **argv) static const struct net_functable net_functable[] = { {"password", net_password, net_password_usage, net_password_help}, {"time", net_time, net_time_usage, net_time_help}, - + {"join", net_join, net_join_usage, net_join_help}, {"help", net_help, net_help_usage, net_help}, {NULL, NULL} }; diff --git a/source4/utils/net/net_join.c b/source4/utils/net/net_join.c new file mode 100644 index 0000000000..81e795a3ce --- /dev/null +++ b/source4/utils/net/net_join.c @@ -0,0 +1,108 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + + Copyright (C) 2004 Stefan Metzmacher (metze@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "utils/net/net.h" +#include "libnet/libnet.h" +#include "librpc/gen_ndr/ndr_samr.h" + +static int net_join_domain(struct net_context *ctx, int argc, const char **argv) +{ + NTSTATUS status; + struct libnet_context *libnetctx; + union libnet_JoinDomain r; + char *tmp; + const char *domain_name; + + switch (argc) { + case 0: /* no args -> fail */ + DEBUG(0,("net_join_domain: no args\n")); + return -1; + case 1: /* only DOMAIN */ + tmp = talloc_strdup(ctx->mem_ctx, argv[0]); + break; + default: /* too mayn args -> fail */ + DEBUG(0,("net_join_domain: too many args [%d]\n",argc)); + return -1; + } + + domain_name = tmp; + + libnetctx = libnet_context_init(); + if (!libnetctx) { + return -1; + } + libnetctx->user.account_name = ctx->user.account_name; + libnetctx->user.domain_name = ctx->user.domain_name; + libnetctx->user.password = ctx->user.password; + + /* prepare password change */ + r.generic.level = LIBNET_JOIN_DOMAIN_GENERIC; + r.generic.in.domain_name = domain_name; + r.generic.in.account_name = talloc_asprintf(ctx->mem_ctx, "%s$", lp_netbios_name()); + r.generic.in.acct_type = ACB_SVRTRUST; + + /* do the domain join */ + status = libnet_JoinDomain(libnetctx, ctx->mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("net_join_domain: %s\n",r.generic.out.error_string)); + return -1; + } + + libnet_context_destroy(&libnetctx); + + return 0; +} + +static int net_join_domain_usage(struct net_context *ctx, int argc, const char **argv) +{ + d_printf("net_join_domain_usage: TODO\n"); + return 0; +} + +static int net_join_domain_help(struct net_context *ctx, int argc, const char **argv) +{ + d_printf("net_join_domain_help: TODO\n"); + return 0; +} + +static const struct net_functable net_password_functable[] = { + {"domain", net_join_domain, net_join_domain_usage, net_join_domain_help}, + {NULL, NULL} +}; + +int net_join(struct net_context *ctx, int argc, const char **argv) +{ + + return net_run_function(ctx, argc, argv, net_password_functable, net_password_usage); +} + +int net_join_usage(struct net_context *ctx, int argc, const char **argv) +{ + d_printf("net_password_usage: TODO\n"); + return 0; +} + +int net_join_help(struct net_context *ctx, int argc, const char **argv) +{ + d_printf("net_password_help: TODO\n"); + return 0; +} -- cgit