From 3bc87626ae7894269535333aadb45ec786f3908d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 4 Dec 2001 05:03:03 +0000 Subject: Add 'net rpc join' to match the ADS equiv. This kills off the offending code in smbpasswd -j -Uab%c In the process we have changed from unsing compelatly random passwords to random, 15 char ascii strings. While this does produce a decrese in entropy, it is still vastly greater than we need, considering the application. In the meantime this allows us to actually *type* the machine account password duruign debugging. This code also adds a 'check' step to the join, confirming that the stored password does indeed do somthing of value :-) Andrew Bartlett (This used to be commit c0b7ee6ee547dc7ff798eaf8cb63fbe344073029) --- source3/Makefile.in | 5 +- source3/libsmb/pwd_cache.c | 2 + source3/libsmb/smbencrypt.c | 17 +-- source3/utils/net_rpc.c | 3 +- source3/utils/net_rpc_join.c | 311 +++++++++++++++++++++++++++++++++++++++++++ source3/utils/smbpasswd.c | 297 +---------------------------------------- 6 files changed, 327 insertions(+), 308 deletions(-) create mode 100644 source3/utils/net_rpc_join.c (limited to 'source3') diff --git a/source3/Makefile.in b/source3/Makefile.in index 8c121fea0d..37217c953a 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -320,7 +320,10 @@ CLIENT_OBJ = client/client.o client/clitar.o \ $(READLINE_OBJ) NET_OBJ = utils/net.o utils/net_ads.o utils/net_rap.o utils/net_rpc.o \ - $(LIBSMB_OBJ) $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_OBJ) + utils/net_rpc_join.o \ + $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_OBJ) \ + $(GROUPDB_OBJ) $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_OBJ) \ + $(NECESSARY_BECAUSE_SAMBA_DEPENDENCIES_ARE_SO_BROKEN_OBJ) CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_OBJ) diff --git a/source3/libsmb/pwd_cache.c b/source3/libsmb/pwd_cache.c index 3c3d5cb741..9404588223 100644 --- a/source3/libsmb/pwd_cache.c +++ b/source3/libsmb/pwd_cache.c @@ -135,6 +135,7 @@ void pwd_set_cleartext(struct pwd_info *pwd, char *clr) pwd->cleartext = True; pwd->null_pwd = False; pwd->crypted = False; + pwd_make_lm_nt_16(pwd, clr); } /**************************************************************************** @@ -147,6 +148,7 @@ void pwd_get_cleartext(struct pwd_info *pwd, char *clr) fstrcpy(clr, pwd->password); else clr[0] = 0; + } /**************************************************************************** diff --git a/source3/libsmb/smbencrypt.c b/source3/libsmb/smbencrypt.c index 2e27455362..b0fecd1c19 100644 --- a/source3/libsmb/smbencrypt.c +++ b/source3/libsmb/smbencrypt.c @@ -262,27 +262,20 @@ void SMBsesskeygen_ntv1(const uchar kr[16], } /*********************************************************** - encode a password buffer + encode a password buffer. The caller gets to figure out + what to put in it. ************************************************************/ -BOOL encode_pw_buffer(char buffer[516], const char *new_pass, - int new_pw_len, BOOL nt_pass_set) +BOOL encode_pw_buffer(char buffer[516], char *new_pw, int new_pw_length) { generate_random_buffer((unsigned char *)buffer, 516, True); - if (nt_pass_set) { - new_pw_len *= 2; - push_ucs2(NULL, &buffer[512 - new_pw_len], new_pass, - new_pw_len, 0); - } else { - push_ascii(&buffer[512 - new_pw_len], new_pass, - new_pw_len, 0); - } + memcpy(&buffer[512 - new_pw_length], new_pw, new_pw_length); /* * The length of the new password is in the last 4 bytes of * the data buffer. */ - SIVAL(buffer, 512, new_pw_len); + SIVAL(buffer, 512, new_pw_length); return True; } diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c index db4c0c881d..e3e1d4fc70 100644 --- a/source3/utils/net_rpc.c +++ b/source3/utils/net_rpc.c @@ -23,13 +23,14 @@ int net_rpc_usage(int argc, const char **argv) { - d_printf("\nNot implemented\n"); + d_printf(" net rpc join \tto join a domin \n"); return -1; } int net_rpc(int argc, const char **argv) { struct functable func[] = { + {"join", net_rpc_join}, {NULL, NULL} }; return net_run_function(argc, argv, func, net_rpc_usage); diff --git a/source3/utils/net_rpc_join.c b/source3/utils/net_rpc_join.c new file mode 100644 index 0000000000..11e878fdf4 --- /dev/null +++ b/source3/utils/net_rpc_join.c @@ -0,0 +1,311 @@ +/* + Samba Unix/Linux SMB client library + Version 3.0 + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + 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 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.h" + +/* Macro for checking RPC error codes to make things more readable */ + +#define CHECK_RPC_ERR(rpc, msg) \ + if (!NT_STATUS_IS_OK(result = rpc)) { \ + DEBUG(0, (msg ": %s\n", get_nt_error_msg(result))); \ + goto done; \ + } + +#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \ + if (!NT_STATUS_IS_OK(result = rpc)) { \ + DEBUG(0, debug_args); \ + goto done; \ + } + +/********************************************************* +Join a domain using the administrator username and password +**********************************************************/ + +int net_rpc_join(int argc, const char **argv) +{ + + extern pstring global_myname; + + /* libsmb variables */ + + struct cli_state *cli; + fstring acct_name; + TALLOC_CTX *mem_ctx; + uint32 acb_info; + + /* rpc variables */ + + POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol; + DOM_SID domain_sid; + uint32 user_rid; + + /* Password stuff */ + + char *clear_trust_password = NULL; + fstring ucs2_trust_password; + int ucs2_pw_len; + uchar stored_md4_trust_password[16]; + uchar pwbuf[516], sess_key[16]; + SAM_USERINFO_CTR ctr; + SAM_USER_INFO_24 p24; + SAM_USER_INFO_10 p10; + + /* Misc */ + + NTSTATUS result; + int retval = 1; + fstring domain; + + /* Connect to remote machine */ + + if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC))) + return 1; + + if (!(mem_ctx = talloc_init())) { + DEBUG(0, ("Could not initialise talloc context\n")); + 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); + + acb_info = (lp_server_role() == ROLE_DOMAIN_BDC) ? ACB_SVRTRUST : + ACB_WSTRUST; + + { + uint32 unknown = 0xe005000b; + + result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol, + acct_name, acb_info, + unknown, &user_pol, + &user_rid); + + /* We *must* do this.... don't ask... */ + + CHECK_RPC_ERR_DEBUG(cli_samr_close(cli, mem_ctx, &user_pol), ("error closing user policy")); + result = NT_STATUS_USER_EXISTS; + } + + if (NT_STATUS_EQUAL(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 (!NT_STATUS_IS_OK(result)) { + DEBUG(0, ("error creating domain user: %s\n", + get_nt_error_msg(result))); + goto done; + } + + /* Create a random machine account password */ + + clear_trust_password = generate_random_str(15); + clear_trust_password = strdup("samba2"); + + ucs2_pw_len = push_ucs2(NULL, ucs2_trust_password, + clear_trust_password, + sizeof(ucs2_trust_password), 0); + +#if DEBUG_PASSWORD + DEBUG(100, ("machine password is being set to:\n")); + dump_data(100, clear_trust_password, 6); + + DEBUG(100, ("machine password unicode is (len %d):\n", ucs2_pw_len)); + dump_data(100, ucs2_trust_password, ucs2_pw_len); + +#endif + + encode_pw_buffer((char *)pwbuf, ucs2_trust_password, + ucs2_pw_len); + + /* Set password on machine account */ + + ZERO_STRUCT(ctr); + ZERO_STRUCT(p24); + + init_sam_user_info24(&p24, (char *)pwbuf,24); + + 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 */ + + ZERO_STRUCT(ctr); + ctr.switch_value = 0x10; + ctr.info.id10 = &p10; + + init_sam_user_info10(&p10, acb_info); + + /* 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); + + secrets_init(); + + if (!secrets_store_domain_sid(domain, &domain_sid)) { + DEBUG(0, ("error storing domain sid for %s\n", domain)); + goto done; + } + + if (!secrets_store_machine_password(clear_trust_password)) { + DEBUG(0, ("error storing plaintext domain secrets for %s\n", domain)); + } + + /* Now check the whole process from top-to-bottom */ + + cli_samr_close(cli, mem_ctx, &user_pol); + + cli_nt_session_close(cli); /* Done with this pipe */ + + if (!cli_nt_session_open(cli, PIPE_NETLOGON)) { + DEBUG(0, ("Error connecting to NETLOGON pipe\n")); + goto done; + } + + if (!secrets_fetch_trust_account_password(domain, + stored_md4_trust_password, NULL)) { + DEBUG(0, ("Could not reterive secrets we just stored!")); + goto done; + } + + CHECK_RPC_ERR(cli_nt_setup_creds(cli, stored_md4_trust_password), + "error in domain join verification"); + + 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); + } + + cli_shutdown(cli); + + SAFE_FREE(clear_trust_password); + + return retval; +} + + diff --git a/source3/utils/smbpasswd.c b/source3/utils/smbpasswd.c index 8d50b94a0a..92136bea7a 100644 --- a/source3/utils/smbpasswd.c +++ b/source3/utils/smbpasswd.c @@ -121,298 +121,6 @@ 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 (!NT_STATUS_IS_OK(result = rpc)) { \ - DEBUG(0, (msg ": %s\n", get_nt_error_msg(result))); \ - goto done; \ - } - -#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \ - if (!NT_STATUS_IS_OK(result = rpc)) { \ - 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; - uint32 acb_info; - - /* 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 */ - - NTSTATUS 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); - - acb_info = (lp_server_role() == ROLE_DOMAIN_BDC) ? ACB_SVRTRUST : - ACB_WSTRUST; - - { - uint32 unknown = 0xe005000b; - - result = cli_samr_create_dom_user(&cli, mem_ctx, &domain_pol, - acct_name, acb_info, - unknown, &user_pol, - &user_rid); - - /* We *must* do this.... don't ask... */ - - CHECK_RPC_ERR_DEBUG(cli_samr_close(&cli, mem_ctx, &user_pol), ("error closing user policy")); - result = NT_STATUS_USER_EXISTS; - } - - if (NT_STATUS_V(result) == NT_STATUS_V(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 (!NT_STATUS_IS_OK(result)) { - 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((unsigned char *)machine_pwd, plen, True); - - encode_pw_buffer((char *)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, (char *)pwbuf,24); - - 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 */ - - ZERO_STRUCT(ctr); - ctr.switch_value = 0x10; - ctr.info.id10 = &p10; - - init_sam_user_info10(&p10, acb_info); - - /* 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) { @@ -657,8 +365,9 @@ static int process_root(int argc, char *argv[]) pstrcpy(user_password, pass); } - return join_domain_byuser(new_domain, remote_machine, - user_name, user_password); + d_printf("use net rpc join to do this now.\n"); + return 1; + } else { /* Or just with the server manager? */ -- cgit