diff options
Diffstat (limited to 'source3/utils/smbpasswd.c')
-rw-r--r-- | source3/utils/smbpasswd.c | 367 |
1 files changed, 344 insertions, 23 deletions
diff --git a/source3/utils/smbpasswd.c b/source3/utils/smbpasswd.c index b4576a603c4..3442efc3a82 100644 --- a/source3/utils/smbpasswd.c +++ b/source3/utils/smbpasswd.c @@ -1,11 +1,14 @@ /* - * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright - * (C) Jeremy Allison 1995-1998 + * Unix SMB/Netbios implementation. + * Version 1.9. + * smbpasswd module. + * Copyright (C) Jeremy Allison 1995-1998 + * Copyright (C) Tim Potter 2001 * - * 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 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 @@ -14,8 +17,7 @@ * * 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. - */ + * Mass Ave, Cambridge, MA 02139, USA. */ #include "includes.h" @@ -136,6 +138,288 @@ unable to join domain.\n"); return (int)ret; } +/* Initialise client credentials for authenticated pipe access */ + +void init_rpcclient_creds(struct ntuser_creds *creds, char* username, + char* domain, char* password) +{ + ZERO_STRUCTP(creds); + + if (lp_encrypted_passwords()) { + pwd_make_lm_nt_16(&creds->pwd, password); + } else { + pwd_set_cleartext(&creds->pwd, password); + } + + fstrcpy(creds->user_name, username); + fstrcpy(creds->domain, domain); +} + +/********************************************************* +Join a domain using the administrator username and password +**********************************************************/ + +/* Macro for checking RPC error codes to make things more readable */ + +#define CHECK_RPC_ERR(rpc, msg) \ + if ((result = rpc) != NT_STATUS_NO_PROBLEMO) { \ + DEBUG(0, (msg ": %s\n", get_nt_error_msg(result))); \ + goto done; \ + } + +#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \ + if ((result = rpc) != NT_STATUS_NO_PROBLEMO) { \ + DEBUG(0, debug_args); \ + goto done; \ + } + +static int join_domain_byuser(char *domain, char *remote_machine, + char *username, char *password) +{ + /* libsmb variables */ + + struct nmb_name calling, called; + struct ntuser_creds creds; + struct cli_state cli; + fstring dest_host, acct_name; + struct in_addr dest_ip; + TALLOC_CTX *mem_ctx; + + /* rpc variables */ + + POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol; + DOM_SID domain_sid; + uint32 user_rid; + + /* Password stuff */ + + char *machine_pwd; + int plen = 0; + uchar pwbuf[516], ntpw[16], sess_key[16]; + SAM_USERINFO_CTR ctr; + SAM_USER_INFO_24 p24; + SAM_USER_INFO_10 p10; + + /* Misc */ + + uint32 result; + int retval = 1; + + /* Connect to remote machine */ + + ZERO_STRUCT(cli); + ZERO_STRUCT(creds); + + if (!(mem_ctx = talloc_init())) { + DEBUG(0, ("Could not initialise talloc context\n")); + goto done; + } + + if (!cli_initialise(&cli)) { + DEBUG(0, ("Could not initialise client structure\n")); + goto done; + } + + init_rpcclient_creds(&creds, username, domain, password); + cli_init_creds(&cli, &creds); + + if (!resolve_srv_name(remote_machine, dest_host, &dest_ip)) { + DEBUG(0, ("Could not resolve name %s\n", remote_machine)); + goto done; + } + + make_nmb_name(&called, dns_to_netbios_name(dest_host), 0x20); + make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0); + + if (!cli_establish_connection(&cli, dest_host, &dest_ip, &calling, + &called, "IPC$", "IPC", False, True)) { + DEBUG(0, ("Error connecting to %s\n", dest_host)); + goto done; + } + + /* Fetch domain sid */ + + if (!cli_nt_session_open(&cli, PIPE_LSARPC)) { + DEBUG(0, ("Error connecting to SAM pipe\n")); + goto done; + } + + + CHECK_RPC_ERR(cli_lsa_open_policy(&cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &lsa_pol), + "error opening lsa policy handle"); + + CHECK_RPC_ERR(cli_lsa_query_info_policy(&cli, mem_ctx, &lsa_pol, + 5, domain, &domain_sid), + "error querying info policy"); + + cli_lsa_close(&cli, mem_ctx, &lsa_pol); + + cli_nt_session_close(&cli); /* Done with this pipe */ + + /* Create domain user */ + + if (!cli_nt_session_open(&cli, PIPE_SAMR)) { + DEBUG(0, ("Error connecting to SAM pipe\n")); + goto done; + } + + CHECK_RPC_ERR(cli_samr_connect(&cli, mem_ctx, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &sam_pol), + "could not connect to SAM database"); + + + CHECK_RPC_ERR(cli_samr_open_domain(&cli, mem_ctx, &sam_pol, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &domain_sid, &domain_pol), + "could not open domain"); + + /* Create domain user */ + + fstrcpy(acct_name, global_myname); + fstrcat(acct_name, "$"); + + strlower(acct_name); + + { + uint32 unknown = 0xe005000b; + + result = cli_samr_create_dom_user(&cli, mem_ctx, &domain_pol, + acct_name, ACB_WSTRUST, + unknown, &user_pol, + &user_rid); + } + + if (result == NT_STATUS_USER_EXISTS) { + uint32 num_rids, *name_types, *user_rids; + uint32 flags = 0x3e8; + char *names; + + /* Look up existing rid */ + + names = (char *)&acct_name[0]; + + CHECK_RPC_ERR_DEBUG( + cli_samr_lookup_names(&cli, mem_ctx, + &domain_pol, flags, + 1, &names, &num_rids, + &user_rids, &name_types), + ("error looking up rid for user %s: %s\n", + acct_name, get_nt_error_msg(result))); + + if (name_types[0] != SID_NAME_USER) { + DEBUG(0, ("%s is not a user account\n", acct_name)); + goto done; + } + + user_rid = user_rids[0]; + + /* Open handle on user */ + + CHECK_RPC_ERR_DEBUG( + cli_samr_open_user(&cli, mem_ctx, &domain_pol, + SEC_RIGHTS_MAXIMUM_ALLOWED, + user_rid, &user_pol), + ("could not re-open existing user %s: %s\n", + acct_name, get_nt_error_msg(result))); + + } else if (result != NT_STATUS_NOPROBLEMO) { + DEBUG(0, ("error creating domain user: %s\n", + get_nt_error_msg(result))); + goto done; + } + + /* Create a random machine account password */ + + { + UNISTR2 upw; /* Unicode password */ + + upw.buffer = (uint16 *)talloc_zero(mem_ctx, 0xc * + sizeof(uint16)); + + upw.uni_str_len = 0xc; + upw.uni_max_len = 0xc; + + machine_pwd = (char *)upw.buffer; + plen = upw.uni_str_len * 2; + generate_random_buffer(machine_pwd, plen, True); + + encode_pw_buffer(pwbuf, machine_pwd, plen, False); + + nt_owf_genW(&upw, ntpw); + } + + /* Set password on machine account */ + + ZERO_STRUCT(ctr); + ZERO_STRUCT(p24); + + init_sam_user_info24(&p24, pwbuf); + + ctr.switch_value = 24; + ctr.info.id24 = &p24; + + /* I don't think this is quite the right place for this + calculation. It should be moved somewhere where the credentials + are calculated. )-: */ + + mdfour(sess_key, cli.pwd.smb_nt_pwd, 16); + + CHECK_RPC_ERR(cli_samr_set_userinfo(&cli, mem_ctx, &user_pol, 24, + sess_key, &ctr), + "error setting trust account password"); + + /* Why do we have to try to (re-)set the ACB to be the same as what + we passed in the samr_create_dom_user() call? When a NT + workstation is joined to a domain by an administrator the + acb_info is set to 0x80. For a normal user with "Add + workstations to the domain" rights the acb_info is 0x84. I'm + not sure whether it is supposed to make a difference or not. NT + seems to cope with either value so don't bomb out if the set + userinfo2 level 0x10 fails. -tpot */ + + ctr.switch_value = 0x10; + ctr.info.id10 = &p10; + + init_sam_user_info10(&p10, ACB_WSTRUST); + + /* Ignoring the return value is necessary for joining a domain + as a normal user with "Add workstation to domain" privilege. */ + + result = cli_samr_set_userinfo2(&cli, mem_ctx, &user_pol, 0x10, + sess_key, &ctr); + + /* Now store the secret in the secrets database */ + + strupper(domain); + + if (!secrets_store_domain_sid(domain, &domain_sid) || + !secrets_store_trust_account_password(domain, ntpw)) { + DEBUG(0, ("error storing domain secrets\n")); + goto done; + } + + retval = 0; /* Success! */ + + done: + /* Close down pipe - this will clean up open policy handles */ + + if (cli.nt_pipe_fnum) + cli_nt_session_close(&cli); + + /* Display success or failure */ + + if (retval != 0) { + trust_password_delete(domain); + fprintf(stderr,"Unable to join domain %s.\n",domain); + } else { + printf("Joined domain %s.\n",domain); + } + + return retval; +} static void set_line_buffering(FILE *f) { @@ -256,10 +540,10 @@ static int process_root(int argc, char *argv[]) { struct passwd *pwd; int result = 0, ch; - BOOL joining_domain = False; + BOOL joining_domain = False, got_pass = False; int local_flags = 0; BOOL stdin_passwd_get = False; - char *user_name = NULL; + fstring user_name, user_password; char *new_domain = NULL; char *new_passwd = NULL; char *old_passwd = NULL; @@ -275,17 +559,17 @@ static int process_root(int argc, char *argv[]) break; case 'x': local_flags |= LOCAL_DELETE_USER; - user_name = optarg; + fstrcpy(user_name, optarg); new_passwd = xstrdup("XXXXXX"); break; case 'd': local_flags |= LOCAL_DISABLE_USER; - user_name = optarg; + fstrcpy(user_name, optarg); new_passwd = xstrdup("XXXXXX"); break; case 'e': local_flags |= LOCAL_ENABLE_USER; - user_name = optarg; + fstrcpy(user_name, optarg); break; case 'm': local_flags |= LOCAL_TRUST_ACCOUNT; @@ -314,9 +598,21 @@ static int process_root(int argc, char *argv[]) case 'D': DEBUGLEVEL = atoi(optarg); break; - case 'U': - user_name = optarg; + case 'U': { + char *lp; + + fstrcpy(user_name, optarg); + + if ((lp = strchr(user_name, '%'))) { + *lp = 0; + fstrcpy(user_password, lp + 1); + got_pass = True; + memset(strchr(optarg, '%') + 1, 'X', + strlen(user_password)); + } + break; + } default: usage(); } @@ -342,10 +638,35 @@ static int process_root(int argc, char *argv[]) load_interfaces(); } - if(joining_domain) { + /* Join a domain */ + + if (joining_domain) { + if (argc != 0) usage(); - return join_domain(new_domain, remote_machine); + + /* Are we joining by specifing an admin username and + password? */ + + if (user_name[0]) { + + /* Get administrator password if not specified */ + + if (!got_pass) { + char *pass = getpass("Password: "); + + if (pass) + pstrcpy(user_password, pass); + } + + return join_domain_byuser(new_domain, remote_machine, + user_name, user_password); + } else { + + /* Or just with the server manager? */ + + return join_domain(new_domain, remote_machine); + } } /* @@ -356,21 +677,21 @@ static int process_root(int argc, char *argv[]) case 0: break; case 1: - user_name = argv[0]; + fstrcpy(user_name, argv[0]); break; case 2: - user_name = argv[0]; + fstrcpy(user_name, argv[0]); new_passwd = xstrdup(argv[1]); break; default: usage(); } - if (!user_name && (pwd = sys_getpwuid(0))) { - user_name = xstrdup(pwd->pw_name); + if (!user_name[0] && (pwd = sys_getpwuid(0))) { + fstrcpy(user_name, pwd->pw_name); } - if (!user_name) { + if (!user_name[0]) { fprintf(stderr,"You must specify a username\n"); exit(1); } @@ -400,7 +721,7 @@ static int process_root(int argc, char *argv[]) */ slprintf(buf, sizeof(buf)-1, "%s$", user_name); - user_name = buf; + fstrcpy(user_name, buf); } if (remote_machine != NULL) { |