/**************************************************************************** check for existance of a trans2 call ****************************************************************************/ static NTSTATUS try_trans2(struct cli_state *cli, int op, char *param, char *data, int param_len, int data_len, unsigned int *rparam_len, unsigned int *rdata_len) { uint16 setup = op; char *rparam=NULL, *rdata=NULL; if (!cli_send_trans(cli, SMBtrans2, NULL, /* name */ -1, 0, /* fid, flags */ &setup, 1, 0, /* setup, length, max */ param, param_len, 2, /* param, length, max */ data, data_len, cli->max_xmit /* data, length, max */ )) { return cli_nt_error(cli); } cli_receive_trans(cli, SMBtrans2, &rparam, rparam_len, &rdata, rdata_len); SAFE_FREE(rdata); SAFE_FREE(rparam); return cli_nt_error(cli); }
int cli_errno(struct cli_state *cli) { NTSTATUS status; if (cli_is_nt_error(cli)) { status = cli_nt_error(cli); return map_errno_from_nt_status(status); } if (cli_is_dos_error(cli)) { uint8 eclass; uint32 ecode; cli_dos_error(cli, &eclass, &ecode); status = dos_to_ntstatus(eclass, ecode); return map_errno_from_nt_status(status); } /* * Yuck! A special case for this Vista error. Since its high-order * byte isn't 0xc0, it doesn't match cli_is_nt_error() above. */ status = cli_nt_error(cli); if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT)) { return EACCES; } /* for other cases */ return EINVAL; }
/**************************************************************************** check for existance of a nttrans call ****************************************************************************/ static NTSTATUS try_nttrans(struct cli_state *cli, int op, char *param, char *data, int param_len, int data_len, unsigned int *rparam_len, unsigned int *rdata_len) { char *rparam=NULL, *rdata=NULL; if (!cli_send_nt_trans(cli, op, 0, NULL, 0, 0, param, param_len, 2, /* param, length, max */ data, data_len, cli->max_xmit /* data, length, max */ )) { return cli_nt_error(cli); } cli_receive_nt_trans(cli, &rparam, rparam_len, &rdata, rdata_len); SAFE_FREE(rdata); SAFE_FREE(rparam); return cli_nt_error(cli); }
/* * Convert an SMB error into a UNIX error ... */ int SMBC_errno(SMBCCTX *context, struct cli_state *c) { int ret = cli_errno(c); if (cli_is_dos_error(c)) { uint8 eclass; uint32 ecode; cli_dos_error(c, &eclass, &ecode); DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", (int)eclass, (int)ecode, (int)ecode, ret)); } else { NTSTATUS status; status = cli_nt_error(c); DEBUG(3,("smbc errno %s -> %d\n", nt_errstr(status), ret)); } return ret; }
/* send a raw ioctl - used by the torture code */ NTSTATUS cli_raw_ioctl(struct cli_state *cli, int fnum, uint32 code, DATA_BLOB *blob) { memset(cli->outbuf,'\0',smb_size); memset(cli->inbuf,'\0',smb_size); set_message(cli->outbuf, 3, 0, True); SCVAL(cli->outbuf,smb_com,SMBioctl); cli_setup_packet(cli); SSVAL(cli->outbuf, smb_vwv0, fnum); SSVAL(cli->outbuf, smb_vwv1, code>>16); SSVAL(cli->outbuf, smb_vwv2, (code&0xFFFF)); cli_send_smb(cli); if (!cli_receive_smb(cli)) { return NT_STATUS_UNEXPECTED_NETWORK_ERROR; } if (cli_is_error(cli)) { return cli_nt_error(cli); } *blob = data_blob(NULL, 0); return NT_STATUS_OK; }
static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out) { uint16 setup; char param[4]; char *rparam=NULL, *rdata=NULL; unsigned int rparam_count=0, rdata_count=0; NTSTATUS status = NT_STATUS_OK; setup = TRANSACT2_SETFSINFO; SSVAL(param,0,0); SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION); if (!cli_send_trans(cli, SMBtrans2, NULL, 0, 0, &setup, 1, 0, param, 4, 0, (char *)in->data, in->length, CLI_BUFFER_SIZE)) { status = cli_nt_error(cli); goto out; } if (!cli_receive_trans(cli, SMBtrans2, &rparam, &rparam_count, &rdata, &rdata_count)) { status = cli_nt_error(cli); goto out; } if (cli_is_error(cli)) { status = cli_nt_error(cli); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { goto out; } } *out = data_blob(rdata, rdata_count); *param_out = data_blob(rparam, rparam_count); out: SAFE_FREE(rparam); SAFE_FREE(rdata); return status; }
NTSTATUS cli_get_nt_error(struct cli_state *cli) { if (cli_is_nt_error(cli)) { return cli_nt_error(cli); } else if (cli_is_dos_error(cli)) { uint32 ecode; uint8 eclass; cli_dos_error(cli, &eclass, &ecode); return dos_to_ntstatus(eclass, ecode); } else { /* Something went wrong, we don't know what. */ return NT_STATUS_UNSUCCESSFUL; } }
/**************************************************************************** send a lock with a specified locktype this is used for testing LOCKING_ANDX_CANCEL_LOCK ****************************************************************************/ NTSTATUS cli_locktype(struct cli_state *cli, int fnum, uint32 offset, uint32 len, int timeout, unsigned char locktype) { char *p; int saved_timeout = cli->timeout; memset(cli->outbuf,'\0',smb_size); memset(cli->inbuf,'\0', smb_size); set_message(cli->outbuf,8,0,True); SCVAL(cli->outbuf,smb_com,SMBlockingX); SSVAL(cli->outbuf,smb_tid,cli->cnum); cli_setup_packet(cli); SCVAL(cli->outbuf,smb_vwv0,0xFF); SSVAL(cli->outbuf,smb_vwv2,fnum); SCVAL(cli->outbuf,smb_vwv3,locktype); SIVALS(cli->outbuf, smb_vwv4, timeout); SSVAL(cli->outbuf,smb_vwv6,0); SSVAL(cli->outbuf,smb_vwv7,1); p = smb_buf(cli->outbuf); SSVAL(p, 0, cli->pid); SIVAL(p, 2, offset); SIVAL(p, 6, len); p += 10; cli_setup_bcc(cli, p); cli_send_smb(cli); if (timeout != 0) { cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 2*1000); } if (!cli_receive_smb(cli)) { cli->timeout = saved_timeout; return NT_STATUS_UNSUCCESSFUL; } cli->timeout = saved_timeout; return cli_nt_error(cli); }
NTSTATUS cli_full_connection(struct cli_state **output_cli, const char *my_name, const char *dest_host, struct in_addr *dest_ip, int port, const char *service, const char *service_type, const char *user, const char *domain, const char *password, int pass_len) { struct ntuser_creds creds; NTSTATUS nt_status; struct nmb_name calling; struct nmb_name called; struct cli_state *cli; struct in_addr ip; if (!output_cli) DEBUG(0, ("output_cli is NULL!?!")); *output_cli = NULL; make_nmb_name(&calling, my_name, 0x0); make_nmb_name(&called , dest_host, 0x20); again: if (!(cli = cli_initialise(NULL))) return NT_STATUS_NO_MEMORY; if (cli_set_port(cli, port) != port) { cli_shutdown(cli); return NT_STATUS_UNSUCCESSFUL; } ip = *dest_ip; DEBUG(3,("Connecting to host=%s share=%s\n", dest_host, service)); if (!cli_connect(cli, dest_host, &ip)) { DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n", nmb_namestr(&called), inet_ntoa(*dest_ip))); cli_shutdown(cli); return NT_STATUS_UNSUCCESSFUL; } if (!cli_session_request(cli, &calling, &called)) { char *p; DEBUG(1,("session request to %s failed (%s)\n", called.name, cli_errstr(cli))); cli_shutdown(cli); if ((p=strchr(called.name, '.')) && !is_ipaddress(called.name)) { *p = 0; goto again; } if (strcmp(called.name, "*SMBSERVER")) { make_nmb_name(&called , "*SMBSERVER", 0x20); goto again; } return NT_STATUS_UNSUCCESSFUL; } if (!cli_negprot(cli)) { DEBUG(1,("failed negprot\n")); nt_status = NT_STATUS_UNSUCCESSFUL; cli_shutdown(cli); return nt_status; } if (!cli_session_setup(cli, user, password, pass_len, password, pass_len, domain)) { DEBUG(1,("failed session setup\n")); nt_status = cli_nt_error(cli); cli_shutdown(cli); if (NT_STATUS_IS_OK(nt_status)) nt_status = NT_STATUS_UNSUCCESSFUL; return nt_status; } if (service) { if (!cli_send_tconX(cli, service, service_type, password, pass_len)) { DEBUG(1,("failed tcon_X\n")); nt_status = cli_nt_error(cli); cli_shutdown(cli); if (NT_STATUS_IS_OK(nt_status)) nt_status = NT_STATUS_UNSUCCESSFUL; return nt_status; } } init_creds(&creds, user, domain, password, pass_len); cli_init_creds(cli, &creds); *output_cli = cli; return NT_STATUS_OK; }
NTSTATUS remote_password_change(const char *remote_machine, const char *user_name, const char *old_passwd, const char *new_passwd, char **err_str) { struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_hnd = NULL; char *user, *domain, *p; NTSTATUS result; bool pass_must_change = False; user = talloc_strdup(talloc_tos(), user_name); SMB_ASSERT(user != NULL); domain = talloc_strdup(talloc_tos(), ""); SMB_ASSERT(domain != NULL); /* allow usernames of the form domain\\user or domain/user */ if ((p = strchr_m(user,'\\')) || (p = strchr_m(user,'/')) || (p = strchr_m(user,*lp_winbind_separator()))) { *p = 0; domain = user; user = p+1; } *err_str = NULL; result = cli_connect_nb(remote_machine, NULL, 0, 0x20, NULL, SMB_SIGNING_DEFAULT, 0, &cli); if (!NT_STATUS_IS_OK(result)) { if (asprintf(err_str, "Unable to connect to SMB server on " "machine %s. Error was : %s.\n", remote_machine, nt_errstr(result))==-1) { *err_str = NULL; } return result; } result = cli_negprot(cli, PROTOCOL_NT1); if (!NT_STATUS_IS_OK(result)) { if (asprintf(err_str, "machine %s rejected the negotiate " "protocol. Error was : %s.\n", remote_machine, nt_errstr(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } /* Given things like SMB signing, restrict anonymous and the like, try an authenticated connection first */ result = cli_session_setup(cli, user_name, old_passwd, strlen(old_passwd)+1, old_passwd, strlen(old_passwd)+1, ""); if (!NT_STATUS_IS_OK(result)) { /* Password must change or Password expired are the only valid * error conditions here from where we can proceed, the rest like * account locked out or logon failure will lead to errors later * anyway */ if (!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) && !NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED)) { if (asprintf(err_str, "Could not connect to machine %s: " "%s\n", remote_machine, nt_errstr(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } pass_must_change = True; /* * We should connect as the anonymous user here, in case * the server has "must change password" checked... * Thanks to <*****@*****.**> for this fix. */ result = cli_session_setup(cli, "", "", 0, "", 0, ""); if (!NT_STATUS_IS_OK(result)) { if (asprintf(err_str, "machine %s rejected the session " "setup. Error was : %s.\n", remote_machine, nt_errstr(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } result = cli_init_creds(cli, "", "", NULL); if (!NT_STATUS_IS_OK(result)) { cli_shutdown(cli); return result; } } else { result = cli_init_creds(cli, user, domain, old_passwd); if (!NT_STATUS_IS_OK(result)) { cli_shutdown(cli); return result; } } result = cli_tree_connect(cli, "IPC$", "IPC", "", 1); if (!NT_STATUS_IS_OK(result)) { if (asprintf(err_str, "machine %s rejected the tconX on the " "IPC$ share. Error was : %s.\n", remote_machine, nt_errstr(result))) { *err_str = NULL; } cli_shutdown(cli); return result; } /* Try not to give the password away too easily */ if (!pass_must_change) { result = cli_rpc_pipe_open_ntlmssp(cli, &ndr_table_samr.syntax_id, NCACN_NP, DCERPC_AUTH_LEVEL_PRIVACY, domain, user, old_passwd, &pipe_hnd); } else { /* * If the user password must be changed the ntlmssp bind will * fail the same way as the session setup above did. The * difference ist that with a pipe bind we don't get a good * error message, the result will be that the rpc call below * will just fail. So we do it anonymously, there's no other * way. */ result = cli_rpc_pipe_open_noauth( cli, &ndr_table_samr.syntax_id, &pipe_hnd); } if (!NT_STATUS_IS_OK(result)) { if (lp_client_lanman_auth()) { /* Use the old RAP method. */ if (!cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) { result = cli_nt_error(cli); if (asprintf(err_str, "machine %s rejected the " "password change: Error was : %s.\n", remote_machine, nt_errstr(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } } else { if (asprintf(err_str, "SAMR connection to machine %s " "failed. Error was %s, but LANMAN password " "changes are disabled\n", remote_machine, nt_errstr(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } } result = rpccli_samr_chgpasswd_user2(pipe_hnd, talloc_tos(), user_name, new_passwd, old_passwd); if (NT_STATUS_IS_OK(result)) { /* Great - it all worked! */ cli_shutdown(cli); return NT_STATUS_OK; } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) { /* it failed, but for reasons such as wrong password, too short etc ... */ if (asprintf(err_str, "machine %s rejected the password change: " "Error was : %s.\n", remote_machine, get_friendly_nt_error_msg(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } /* OK, that failed, so try again... */ TALLOC_FREE(pipe_hnd); /* Try anonymous NTLMSSP... */ result = cli_init_creds(cli, "", "", NULL); if (!NT_STATUS_IS_OK(result)) { cli_shutdown(cli); return result; } result = NT_STATUS_UNSUCCESSFUL; /* OK, this is ugly, but... try an anonymous pipe. */ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id, &pipe_hnd); if ( NT_STATUS_IS_OK(result) && (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user2( pipe_hnd, talloc_tos(), user_name, new_passwd, old_passwd)))) { /* Great - it all worked! */ cli_shutdown(cli); return NT_STATUS_OK; } else { if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) { /* it failed, but again it was due to things like new password too short */ if (asprintf(err_str, "machine %s rejected the " "(anonymous) password change: Error was : " "%s.\n", remote_machine, get_friendly_nt_error_msg(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } /* We have failed to change the user's password, and we think the server just might not support SAMR password changes, so fall back */ if (lp_client_lanman_auth()) { /* Use the old RAP method. */ if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) { /* SAMR failed, but the old LanMan protocol worked! */ cli_shutdown(cli); return NT_STATUS_OK; } result = cli_nt_error(cli); if (asprintf(err_str, "machine %s rejected the password " "change: Error was : %s.\n", remote_machine, nt_errstr(result)) == -1) { *err_str = NULL; } cli_shutdown(cli); return result; } else { if (asprintf(err_str, "SAMR connection to machine %s " "failed. Error was %s, but LANMAN password " "changes are disabled\n", nt_errstr(result), remote_machine) == -1) { *err_str = NULL; } cli_shutdown(cli); return NT_STATUS_UNSUCCESSFUL; } } }
bool cli_receive_nt_trans(struct cli_state *cli, char **param, unsigned int *param_len, char **data, unsigned int *data_len) { unsigned int total_data=0; unsigned int total_param=0; unsigned int this_data,this_param; uint8 eclass; uint32 ecode; bool ret = False; uint16_t mid; *data_len = *param_len = 0; mid = SVAL(cli->outbuf,smb_mid); if (!cli_receive_smb(cli)) { cli_state_seqnum_remove(cli, mid); return False; } show_msg(cli->inbuf); /* sanity check */ if (CVAL(cli->inbuf,smb_com) != SMBnttrans) { DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n", CVAL(cli->inbuf,smb_com))); cli_state_seqnum_remove(cli, mid); return(False); } /* * An NT RPC pipe call can return ERRDOS, ERRmoredata * to a trans call. This is not an error and should not * be treated as such. */ if (cli_is_dos_error(cli)) { cli_dos_error(cli, &eclass, &ecode); if (!(eclass == ERRDOS && ecode == ERRmoredata)) { goto out; } } /* * Likewise for NT_STATUS_BUFFER_TOO_SMALL */ if (cli_is_nt_error(cli)) { if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_BUFFER_TOO_SMALL)) { goto out; } } /* parse out the lengths */ total_data = IVAL(cli->inbuf,smb_ntr_TotalDataCount); total_param = IVAL(cli->inbuf,smb_ntr_TotalParameterCount); /* Only allow 16 megs. */ if (total_param > 16*1024*1024) { DEBUG(0,("cli_receive_nt_trans: param buffer too large %d\n", total_param)); goto out; } if (total_data > 16*1024*1024) { DEBUG(0,("cli_receive_nt_trans: data buffer too large %d\n", total_data)); goto out; } /* allocate it */ if (total_data) { /* We know adding 2 is safe as total_data is less * than 16mb (above). */ *data = (char *)SMB_REALLOC(*data,total_data+2); if (!(*data)) { DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data)); goto out; } } if (total_param) { /* We know adding 2 is safe as total_param is less * than 16mb (above). */ *param = (char *)SMB_REALLOC(*param,total_param+2); if (!(*param)) { DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param)); goto out; } } while (1) { this_data = SVAL(cli->inbuf,smb_ntr_DataCount); this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount); if (this_data + *data_len > total_data || this_param + *param_len > total_param) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); goto out; } if (this_data + *data_len < this_data || this_data + *data_len < *data_len || this_param + *param_len < this_param || this_param + *param_len < *param_len) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); goto out; } if (this_data) { unsigned int data_offset_out = SVAL(cli->inbuf,smb_ntr_DataDisplacement); unsigned int data_offset_in = SVAL(cli->inbuf,smb_ntr_DataOffset); if (data_offset_out > total_data || data_offset_out + this_data > total_data || data_offset_out + this_data < data_offset_out || data_offset_out + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); goto out; } if (data_offset_in > cli->bufsize || data_offset_in + this_data > cli->bufsize || data_offset_in + this_data < data_offset_in || data_offset_in + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); goto out; } memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data); } if (this_param) { unsigned int param_offset_out = SVAL(cli->inbuf,smb_ntr_ParameterDisplacement); unsigned int param_offset_in = SVAL(cli->inbuf,smb_ntr_ParameterOffset); if (param_offset_out > total_param || param_offset_out + this_param > total_param || param_offset_out + this_param < param_offset_out || param_offset_out + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_nt_trans\n")); goto out; } if (param_offset_in > cli->bufsize || param_offset_in + this_param > cli->bufsize || param_offset_in + this_param < param_offset_in || param_offset_in + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_nt_trans\n")); goto out; } memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param); } *data_len += this_data; *param_len += this_param; if (total_data <= *data_len && total_param <= *param_len) { ret = True; break; } if (!cli_receive_smb(cli)) { goto out; } show_msg(cli->inbuf); /* sanity check */ if (CVAL(cli->inbuf,smb_com) != SMBnttrans) { DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n", CVAL(cli->inbuf,smb_com))); goto out; } if (cli_is_dos_error(cli)) { cli_dos_error(cli, &eclass, &ecode); if(!(eclass == ERRDOS && ecode == ERRmoredata)) { goto out; } } /* * Likewise for NT_STATUS_BUFFER_TOO_SMALL */ if (cli_is_nt_error(cli)) { if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_BUFFER_TOO_SMALL)) { goto out; } } /* parse out the total lengths again - they can shrink! */ if (IVAL(cli->inbuf,smb_ntr_TotalDataCount) < total_data) total_data = IVAL(cli->inbuf,smb_ntr_TotalDataCount); if (IVAL(cli->inbuf,smb_ntr_TotalParameterCount) < total_param) total_param = IVAL(cli->inbuf,smb_ntr_TotalParameterCount); if (total_data <= *data_len && total_param <= *param_len) { ret = True; break; } } out: cli_state_seqnum_remove(cli, mid); if (ret) { /* Ensure the last 2 bytes of param and data are 2 null * bytes. These are malloc'ed, but not included in any * length counts. This allows cli_XX string reading functions * to safely null terminate. */ if (total_data) { SSVAL(*data,total_data,0); } if (total_param) { SSVAL(*param,total_param,0); } } return ret; }
static int join_domain_byuser(char *domain, const char *remote, char *username, char *password) { /* libsmb variables */ pstring pdc_name; struct nmb_name calling, called; struct ntuser_creds creds; struct cli_state cli; fstring 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 */ NTSTATUS result; int retval = 1; pstrcpy(pdc_name, remote ? remote : ""); /* Connect to remote machine */ ZERO_STRUCT(cli); ZERO_STRUCT(creds); ZERO_STRUCT(dest_ip); /* Make sure no nasty surprises */ 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 we are given a remote machine assume this is the PDC. */ if(remote == NULL || !strcmp(remote, "*")) { struct in_addr *ip_list; int addr_count; if (!get_dc_list(True /* PDC only*/, domain, &ip_list, &addr_count)) { fprintf(stderr, "Unable to find the domain controller for domain %s.\n", domain); return 1; } if ((addr_count < 1) || (is_zero_ip(ip_list[0]))) { fprintf(stderr, "Incorrect entries returned when finding the domain controller for domain %s.\n", domain); return 1; } if (!lookup_dc_name(global_myname, domain, &ip_list[0], pdc_name)) { fprintf(stderr, "Unable to lookup the name for the domain controller for domain %s.\n", domain); return 1; } dest_ip = ip_list[0]; } make_nmb_name(&called, pdc_name, 0x20); make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0); if (!cli_establish_connection(&cli, pdc_name, &dest_ip, &calling, &called, "IPC$", "IPC", False, True)) { if (!NT_STATUS_IS_OK(cli_nt_error(&cli))) { DEBUG(0, ("Error connecting to %s - %s\n", pdc_name,cli_errstr(&cli))); } else { DEBUG(0, ("Error connecting to %s\n", pdc_name)); } 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 (NT_STATUS_IS_OK(result)) { /* 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; const 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); mdfour( ntpw, (unsigned char *)upw.buffer, plen); } /* 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_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 NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, const int sockfd, const int pipe_index, const char *controller, struct cli_state **cli, BOOL *retry) { char *machine_password, *machine_krb5_principal; char *ipc_username, *ipc_domain, *ipc_password; BOOL got_mutex; BOOL add_failed_connection = True; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; struct sockaddr peeraddr; socklen_t peeraddr_len; struct sockaddr_in *peeraddr_in = (struct sockaddr_in *)&peeraddr; machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) { SAFE_FREE(machine_password); return NT_STATUS_NO_MEMORY; } cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password); *retry = True; got_mutex = secrets_named_mutex(controller, WINBIND_SERVER_MUTEX_WAIT_TIME); if (!got_mutex) { DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", controller)); result = NT_STATUS_POSSIBLE_DEADLOCK; goto done; } if ((*cli = cli_initialise(NULL)) == NULL) { DEBUG(1, ("Could not cli_initialize\n")); result = NT_STATUS_NO_MEMORY; goto done; } (*cli)->timeout = 10000; /* 10 seconds */ (*cli)->fd = sockfd; fstrcpy((*cli)->desthost, controller); (*cli)->use_kerberos = True; peeraddr_len = sizeof(peeraddr); if ((getpeername((*cli)->fd, &peeraddr, &peeraddr_len) != 0) || (peeraddr_len != sizeof(struct sockaddr_in)) || (peeraddr_in->sin_family != PF_INET)) { DEBUG(0,("cm_prepare_connection: %s\n", strerror(errno))); goto done; } if (ntohs(peeraddr_in->sin_port) == 139) { struct nmb_name calling; struct nmb_name called; make_nmb_name(&calling, global_myname(), 0x0); make_nmb_name(&called, "*SMBSERVER", 0x20); if (!cli_session_request(*cli, &calling, &called)) { DEBUG(8, ("cli_session_request failed for %s\n", controller)); goto done; } } cli_setup_signing_state(*cli, Undefined); if (!cli_negprot(*cli)) { DEBUG(1, ("cli_negprot failed\n")); cli_shutdown(*cli); goto done; } /* Krb5 session */ if ((lp_security() == SEC_ADS) && ((*cli)->protocol >= PROTOCOL_NT1 && (*cli)->capabilities & CAP_EXTENDED_SECURITY)) { ADS_STATUS ads_status; (*cli)->use_kerberos = True; DEBUG(5, ("connecting to %s from %s with kerberos principal " "[%s]\n", controller, global_myname(), machine_krb5_principal)); ads_status = cli_session_setup_spnego(*cli, machine_krb5_principal, machine_password, lp_workgroup()); if (!ADS_ERR_OK(ads_status)) DEBUG(4,("failed kerberos session setup with %s\n", ads_errstr(ads_status))); result = ads_ntstatus(ads_status); } if (NT_STATUS_IS_OK(result)) goto session_setup_done; /* Fall back to non-kerberos session setup */ (*cli)->use_kerberos = False; if ((((*cli)->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) != 0) && (strlen(ipc_username) > 0)) { /* Only try authenticated if we have a username */ DEBUG(5, ("connecting to %s from %s with username " "[%s]\\[%s]\n", controller, global_myname(), ipc_domain, ipc_username)); if (cli_session_setup(*cli, ipc_username, ipc_password, strlen(ipc_password)+1, ipc_password, strlen(ipc_password)+1, ipc_domain)) { DEBUG(5, ("authenticated session setup failed\n")); goto session_setup_done; } } /* Fall back to anonymous connection, this might fail later */ if (cli_session_setup(*cli, "", NULL, 0, NULL, 0, "")) { DEBUG(5, ("Connected anonymously\n")); goto session_setup_done; } result = cli_nt_error(*cli); if (NT_STATUS_IS_OK(result)) result = NT_STATUS_UNSUCCESSFUL; /* We can't session setup */ goto done; session_setup_done: if (!cli_send_tconX(*cli, "IPC$", "IPC", "", 0)) { result = cli_nt_error(*cli); DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result))); if (NT_STATUS_IS_OK(result)) result = NT_STATUS_UNSUCCESSFUL; cli_shutdown(*cli); goto done; } secrets_named_mutex_release(controller); got_mutex = False; *retry = False; /* Windows 2003 SP1 does not lie LsaOpenPolicy() over schannel. Returns RPC_NT_CANNOT_SUPPPORT (0xc0020041) for that call. So just drop it on the lsarpc pipe */ if ( (domain->primary || IS_DC) && (pipe_index!=PI_LSARPC) ) { NTSTATUS status = setup_schannel( *cli, domain->name ); if (!NT_STATUS_IS_OK(status)) { DEBUG(3,("schannel refused - continuing without " "schannel (%s)\n", nt_errstr(status))); } } /* set the domain if empty; needed for schannel connections */ if ( !*(*cli)->domain ) fstrcpy( (*cli)->domain, domain->name ); if ( !cli_nt_session_open (*cli, pipe_index) ) { result = NT_STATUS_PIPE_NOT_AVAILABLE; /* This might be a NT4 DC */ if ( is_win2k_pipe(pipe_index) ) add_failed_connection = False; cli_shutdown(*cli); goto done; } result = NT_STATUS_OK; add_failed_connection = False; done: if (got_mutex) secrets_named_mutex_release(controller); SAFE_FREE(machine_password); SAFE_FREE(machine_krb5_principal); SAFE_FREE(ipc_username); SAFE_FREE(ipc_domain); SAFE_FREE(ipc_password); if (add_failed_connection) add_failed_connection_entry(domain->name, controller, result); return result; }
static int /* O - 0 = success, non-0 = failure */ smb_print(struct cli_state * cli, /* I - SMB connection */ char *title, /* I - Title/job name */ FILE * fp) { /* I - File to print */ uint16_t fnum; /* File number */ int nbytes, /* Number of bytes read */ tbytes; /* Total bytes read */ char buffer[8192], /* Buffer for copy */ *ptr; /* Pointer into title */ /* * Sanitize the title... */ for (ptr = title; *ptr; ptr++) { if (!isalnum((int) *ptr) && !isspace((int) *ptr)) { *ptr = '_'; } } /* * Open the printer device... */ if (!NT_STATUS_IS_OK(cli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE, &fnum))) { fprintf(stderr, "ERROR: %s opening remote spool %s\n", cli_errstr(cli), title); return (get_exit_code(cli, cli_nt_error(cli))); } /* * Copy the file to the printer... */ if (fp != stdin) rewind(fp); tbytes = 0; while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { if (cli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes) { int status = get_exit_code(cli, cli_nt_error(cli)); fprintf(stderr, "ERROR: Error writing spool: %s\n", cli_errstr(cli)); fprintf(stderr, "DEBUG: Returning status %d...\n", status); cli_close(cli, fnum); return (status); } tbytes += nbytes; } if (!NT_STATUS_IS_OK(cli_close(cli, fnum))) { fprintf(stderr, "ERROR: %s closing remote spool %s\n", cli_errstr(cli), title); return (get_exit_code(cli, cli_nt_error(cli))); } else { return (0); } }
static NTSTATUS do_connect(TALLOC_CTX *ctx, const char *server, const char *share, const struct user_auth_info *auth_info, bool show_sessetup, bool force_encrypt, int max_protocol, int port, int name_type, struct cli_state **pcli) { struct cli_state *c = NULL; char *servicename; char *sharename; char *newserver, *newshare; const char *username; const char *password; NTSTATUS status; /* make a copy so we don't modify the global string 'service' */ servicename = talloc_strdup(ctx,share); if (!servicename) { return NT_STATUS_NO_MEMORY; } sharename = servicename; if (*sharename == '\\') { sharename += 2; if (server == NULL) { server = sharename; } sharename = strchr_m(sharename,'\\'); if (!sharename) { return NT_STATUS_NO_MEMORY; } *sharename = 0; sharename++; } if (server == NULL) { return NT_STATUS_INVALID_PARAMETER; } status = cli_connect_nb( server, NULL, port, name_type, NULL, get_cmdline_auth_info_signing_state(auth_info), &c); if (!NT_STATUS_IS_OK(status)) { d_printf("Connection to %s failed (Error %s)\n", server, nt_errstr(status)); return status; } if (max_protocol == 0) { max_protocol = PROTOCOL_NT1; } c->protocol = max_protocol; c->use_kerberos = get_cmdline_auth_info_use_kerberos(auth_info); c->fallback_after_kerberos = get_cmdline_auth_info_fallback_after_kerberos(auth_info); c->use_ccache = get_cmdline_auth_info_use_ccache(auth_info); DEBUG(4,(" session request ok\n")); status = cli_negprot(c); if (!NT_STATUS_IS_OK(status)) { d_printf("protocol negotiation failed: %s\n", nt_errstr(status)); cli_shutdown(c); return status; } username = get_cmdline_auth_info_username(auth_info); password = get_cmdline_auth_info_password(auth_info); status = cli_session_setup(c, username, password, strlen(password), password, strlen(password), lp_workgroup()); if (!NT_STATUS_IS_OK(status)) { /* If a password was not supplied then * try again with a null username. */ if (password[0] || !username[0] || get_cmdline_auth_info_use_kerberos(auth_info) || !NT_STATUS_IS_OK(status = cli_session_setup(c, "", "", 0, "", 0, lp_workgroup()))) { d_printf("session setup failed: %s\n", nt_errstr(status)); if (NT_STATUS_V(cli_nt_error(c)) == NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED)) d_printf("did you forget to run kinit?\n"); cli_shutdown(c); return status; } d_printf("Anonymous login successful\n"); status = cli_init_creds(c, "", lp_workgroup(), ""); } else { status = cli_init_creds(c, username, lp_workgroup(), password); } if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status))); cli_shutdown(c); return status; } if ( show_sessetup ) { if (*c->server_domain) { DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n", c->server_domain,c->server_os,c->server_type)); } else if (*c->server_os || *c->server_type) { DEBUG(0,("OS=[%s] Server=[%s]\n", c->server_os,c->server_type)); } } DEBUG(4,(" session setup ok\n")); /* here's the fun part....to support 'msdfs proxy' shares (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL here before trying to connect to the original share. cli_check_msdfs_proxy() will fail if it is a normal share. */ if ((c->capabilities & CAP_DFS) && cli_check_msdfs_proxy(ctx, c, sharename, &newserver, &newshare, force_encrypt, username, password, lp_workgroup())) { cli_shutdown(c); return do_connect(ctx, newserver, newshare, auth_info, false, force_encrypt, max_protocol, port, name_type, pcli); } /* must be a normal share */ status = cli_tcon_andx(c, sharename, "?????", password, strlen(password)+1); if (!NT_STATUS_IS_OK(status)) { d_printf("tree connect failed: %s\n", nt_errstr(status)); cli_shutdown(c); return status; } if (force_encrypt) { status = cli_cm_force_encryption(c, username, password, lp_workgroup(), sharename); if (!NT_STATUS_IS_OK(status)) { cli_shutdown(c); return status; } } DEBUG(4,(" tconx ok\n")); *pcli = c; return NT_STATUS_OK; }
static struct cli_state *do_connect( const char *server, const char *share, BOOL show_sessetup ) { struct cli_state *c; struct nmb_name called, calling; const char *server_n; struct in_addr ip; pstring servicename; char *sharename; /* make a copy so we don't modify the global string 'service' */ pstrcpy(servicename, share); sharename = servicename; if (*sharename == '\\') { server = sharename+2; sharename = strchr_m(server,'\\'); if (!sharename) return NULL; *sharename = 0; sharename++; } server_n = server; zero_ip(&ip); make_nmb_name(&calling, global_myname(), 0x0); make_nmb_name(&called , server, name_type); again: zero_ip(&ip); if (have_ip) ip = dest_ip; /* have to open a new connection */ if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) != port) || !cli_connect(c, server_n, &ip)) { d_printf("Connection to %s failed\n", server_n); return NULL; } c->protocol = max_protocol; c->use_kerberos = use_kerberos; cli_setup_signing_state(c, signing_state); if (!cli_session_request(c, &calling, &called)) { char *p; d_printf("session request to %s failed (%s)\n", called.name, cli_errstr(c)); cli_shutdown(c); if ((p=strchr_m(called.name, '.'))) { *p = 0; goto again; } if (strcmp(called.name, "*SMBSERVER")) { make_nmb_name(&called , "*SMBSERVER", 0x20); goto again; } return NULL; } DEBUG(4,(" session request ok\n")); if (!cli_negprot(c)) { d_printf("protocol negotiation failed\n"); cli_shutdown(c); return NULL; } if (!got_pass) { char *pass = getpass("Password: "******"", "", 0, "", 0, lp_workgroup())) { d_printf("session setup failed: %s\n", cli_errstr(c)); if (NT_STATUS_V(cli_nt_error(c)) == NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED)) d_printf("did you forget to run kinit?\n"); cli_shutdown(c); return NULL; } d_printf("Anonymous login successful\n"); } if ( show_sessetup ) { if (*c->server_domain) { DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n", c->server_domain,c->server_os,c->server_type)); } else if (*c->server_os || *c->server_type){ DEBUG(0,("OS=[%s] Server=[%s]\n", c->server_os,c->server_type)); } } DEBUG(4,(" session setup ok\n")); if (!cli_send_tconX(c, sharename, "?????", password, strlen(password)+1)) { d_printf("tree connect failed: %s\n", cli_errstr(c)); cli_shutdown(c); return NULL; } DEBUG(4,(" tconx ok\n")); return c; }
NTSTATUS remote_password_change(const char *remote_machine, const char *user_name, const char *old_passwd, const char *new_passwd, char **err_str) { struct nmb_name calling, called; struct cli_state *cli; struct rpc_pipe_client *pipe_hnd; struct sockaddr_storage ss; NTSTATUS result; bool pass_must_change = False; *err_str = NULL; if(!resolve_name( remote_machine, &ss, 0x20)) { asprintf(err_str, "Unable to find an IP address for machine " "%s.\n", remote_machine); return NT_STATUS_UNSUCCESSFUL; } cli = cli_initialise(); if (!cli) { return NT_STATUS_NO_MEMORY; } result = cli_connect(cli, remote_machine, &ss); if (!NT_STATUS_IS_OK(result)) { asprintf(err_str, "Unable to connect to SMB server on " "machine %s. Error was : %s.\n", remote_machine, nt_errstr(result)); cli_shutdown(cli); return result; } make_nmb_name(&calling, global_myname() , 0x0); make_nmb_name(&called , remote_machine, 0x20); if (!cli_session_request(cli, &calling, &called)) { asprintf(err_str, "machine %s rejected the session setup. " "Error was : %s.\n", remote_machine, cli_errstr(cli) ); result = cli_nt_error(cli); cli_shutdown(cli); return result; } cli->protocol = PROTOCOL_NT1; if (!cli_negprot(cli)) { asprintf(err_str, "machine %s rejected the negotiate " "protocol. Error was : %s.\n", remote_machine, cli_errstr(cli) ); result = cli_nt_error(cli); cli_shutdown(cli); return result; } /* Given things like SMB signing, restrict anonymous and the like, try an authenticated connection first */ result = cli_session_setup(cli, user_name, old_passwd, strlen(old_passwd)+1, old_passwd, strlen(old_passwd)+1, ""); if (!NT_STATUS_IS_OK(result)) { /* Password must change or Password expired are the only valid * error conditions here from where we can proceed, the rest like * account locked out or logon failure will lead to errors later * anyway */ if (!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) && !NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED)) { asprintf(err_str, "Could not connect to machine %s: " "%s\n", remote_machine, cli_errstr(cli)); cli_shutdown(cli); return result; } pass_must_change = True; /* * We should connect as the anonymous user here, in case * the server has "must change password" checked... * Thanks to <*****@*****.**> for this fix. */ result = cli_session_setup(cli, "", "", 0, "", 0, ""); if (!NT_STATUS_IS_OK(result)) { asprintf(err_str, "machine %s rejected the session " "setup. Error was : %s.\n", remote_machine, cli_errstr(cli) ); cli_shutdown(cli); return result; } cli_init_creds(cli, "", "", NULL); } else { cli_init_creds(cli, user_name, "", old_passwd); } if (!cli_send_tconX(cli, "IPC$", "IPC", "", 1)) { asprintf(err_str, "machine %s rejected the tconX on the IPC$ " "share. Error was : %s.\n", remote_machine, cli_errstr(cli) ); result = cli_nt_error(cli); cli_shutdown(cli); return result; } /* Try not to give the password away too easily */ if (!pass_must_change) { pipe_hnd = cli_rpc_pipe_open_ntlmssp(cli, PI_SAMR, PIPE_AUTH_LEVEL_PRIVACY, "", /* what domain... ? */ user_name, old_passwd, &result); } else { /* * If the user password must be changed the ntlmssp bind will * fail the same way as the session setup above did. The * difference ist that with a pipe bind we don't get a good * error message, the result will be that the rpc call below * will just fail. So we do it anonymously, there's no other * way. */ pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &result); } if (!pipe_hnd) { if (lp_client_lanman_auth()) { /* Use the old RAP method. */ if (!cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) { asprintf(err_str, "machine %s rejected the " "password change: Error was : %s.\n", remote_machine, cli_errstr(cli) ); result = cli_nt_error(cli); cli_shutdown(cli); return result; } } else { asprintf(err_str, "SAMR connection to machine %s " "failed. Error was %s, but LANMAN password " "changed are disabled\n", nt_errstr(result), remote_machine); result = cli_nt_error(cli); cli_shutdown(cli); return result; } } if (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user(pipe_hnd, pipe_hnd->mem_ctx, user_name, new_passwd, old_passwd))) { /* Great - it all worked! */ cli_shutdown(cli); return NT_STATUS_OK; } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) { /* it failed, but for reasons such as wrong password, too short etc ... */ asprintf(err_str, "machine %s rejected the password change: " "Error was : %s.\n", remote_machine, get_friendly_nt_error_msg(result)); cli_shutdown(cli); return result; } /* OK, that failed, so try again... */ cli_rpc_pipe_close(pipe_hnd); /* Try anonymous NTLMSSP... */ cli_init_creds(cli, "", "", NULL); result = NT_STATUS_UNSUCCESSFUL; /* OK, this is ugly, but... try an anonymous pipe. */ pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SAMR, &result); if ( pipe_hnd && (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user(pipe_hnd, pipe_hnd->mem_ctx, user_name, new_passwd, old_passwd)))) { /* Great - it all worked! */ cli_shutdown(cli); return NT_STATUS_OK; } else { if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) { /* it failed, but again it was due to things like new password too short */ asprintf(err_str, "machine %s rejected the " "(anonymous) password change: Error was : " "%s.\n", remote_machine, get_friendly_nt_error_msg(result)); cli_shutdown(cli); return result; } /* We have failed to change the user's password, and we think the server just might not support SAMR password changes, so fall back */ if (lp_client_lanman_auth()) { /* Use the old RAP method. */ if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) { /* SAMR failed, but the old LanMan protocol worked! */ cli_shutdown(cli); return NT_STATUS_OK; } asprintf(err_str, "machine %s rejected the password " "change: Error was : %s.\n", remote_machine, cli_errstr(cli) ); result = cli_nt_error(cli); cli_shutdown(cli); return result; } else { asprintf(err_str, "SAMR connection to machine %s " "failed. Error was %s, but LANMAN password " "changed are disabled\n", nt_errstr(result), remote_machine); cli_shutdown(cli); return NT_STATUS_UNSUCCESSFUL; } } }
static struct cli_state *do_connect( const char *server, const char *share, BOOL show_sessetup ) { struct cli_state *c = NULL; struct nmb_name called, calling; const char *server_n; struct in_addr ip; pstring servicename; char *sharename; fstring newserver, newshare; NTSTATUS status; /* make a copy so we don't modify the global string 'service' */ pstrcpy(servicename, share); sharename = servicename; if (*sharename == '\\') { server = sharename+2; sharename = strchr_m(server,'\\'); if (!sharename) return NULL; *sharename = 0; sharename++; } server_n = server; zero_ip(&ip); make_nmb_name(&calling, global_myname(), 0x0); make_nmb_name(&called , server, name_type); again: zero_ip(&ip); if (have_ip) ip = dest_ip; /* have to open a new connection */ if (!(c=cli_initialise()) || (cli_set_port(c, port) != port)) { d_printf("Connection to %s failed\n", server_n); return NULL; } status = cli_connect(c, server_n, &ip); if (!NT_STATUS_IS_OK(status)) { d_printf("Connection to %s failed (Error %s)\n", server_n, nt_errstr(status)); return NULL; } c->protocol = max_protocol; c->use_kerberos = use_kerberos; cli_setup_signing_state(c, signing_state); if (!cli_session_request(c, &calling, &called, NULL)) { char *p; d_printf("session request to %s failed (%s)\n", called.name, cli_errstr(c)); cli_shutdown(c); c = NULL; if ((p=strchr_m(called.name, '.'))) { *p = 0; goto again; } if (strcmp(called.name, "*SMBSERVER")) { make_nmb_name(&called , "*SMBSERVER", 0x20); goto again; } return NULL; } DEBUG(4,(" session request ok\n")); if (!cli_negprot(c)) { d_printf("protocol negotiation failed\n"); cli_shutdown(c); return NULL; } if (!got_pass) { char *pass = getpass("Password: "******"", "", 0, "", 0, lp_workgroup()))) { d_printf("session setup failed: %s\n", cli_errstr(c)); if (NT_STATUS_V(cli_nt_error(c)) == NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED)) d_printf("did you forget to run kinit?\n"); cli_shutdown(c); return NULL; } d_printf("Anonymous login successful\n"); } if ( show_sessetup ) { if (*c->server_domain) { DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n", c->server_domain,c->server_os,c->server_type)); } else if (*c->server_os || *c->server_type){ DEBUG(0,("OS=[%s] Server=[%s]\n", c->server_os,c->server_type)); } } DEBUG(4,(" session setup ok\n")); /* here's the fun part....to support 'msdfs proxy' shares (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL here before trying to connect to the original share. check_dfs_proxy() will fail if it is a normal share. */ if ( (c->capabilities & CAP_DFS) && cli_check_msdfs_proxy( c, sharename, newserver, newshare ) ) { cli_shutdown(c); return do_connect( newserver, newshare, False ); } /* must be a normal share */ if (!cli_send_tconX(c, sharename, "?????", password, strlen(password)+1)) { d_printf("tree connect failed: %s\n", cli_errstr(c)); cli_shutdown(c); return NULL; } DEBUG(4,(" tconx ok\n")); return c; }
bool cli_receive_trans(struct cli_state *cli,int trans, char **param, unsigned int *param_len, char **data, unsigned int *data_len) { unsigned int total_data=0; unsigned int total_param=0; unsigned int this_data,this_param; NTSTATUS status; bool ret = False; uint16_t mid; *data_len = *param_len = 0; mid = SVAL(cli->outbuf,smb_mid); if (!cli_receive_smb(cli)) { cli_state_seqnum_remove(cli, mid); return False; } show_msg(cli->inbuf); /* sanity check */ if (CVAL(cli->inbuf,smb_com) != trans) { DEBUG(0,("Expected %s response, got command 0x%02x\n", trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(cli->inbuf,smb_com))); cli_state_seqnum_remove(cli, mid); return False; } /* * An NT RPC pipe call can return ERRDOS, ERRmoredata * to a trans call. This is not an error and should not * be treated as such. Note that STATUS_NO_MORE_FILES is * returned when a trans2 findfirst/next finishes. * When setting up an encrypted transport we can also * see NT_STATUS_MORE_PROCESSING_REQUIRED here. * * Vista returns NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT if the folder * "<share>/Users/All Users" is enumerated. This is a special pseudo * folder, and the response does not have parameters (nor a parameter * length). */ status = cli_nt_error(cli); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { if (NT_STATUS_IS_ERR(status) || NT_STATUS_EQUAL(status,STATUS_NO_MORE_FILES) || NT_STATUS_EQUAL(status,NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT)) { goto out; } } /* parse out the lengths */ total_data = SVAL(cli->inbuf,smb_tdrcnt); total_param = SVAL(cli->inbuf,smb_tprcnt); /* allocate it */ if (total_data!=0) { /* We know adding 2 is safe as total_data is an * SVAL <= 0xFFFF. */ *data = (char *)SMB_REALLOC(*data,total_data+2); if (!(*data)) { DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n")); goto out; } } if (total_param!=0) { /* We know adding 2 is safe as total_param is an * SVAL <= 0xFFFF. */ *param = (char *)SMB_REALLOC(*param,total_param+2); if (!(*param)) { DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n")); goto out; } } for (;;) { this_data = SVAL(cli->inbuf,smb_drcnt); this_param = SVAL(cli->inbuf,smb_prcnt); if (this_data + *data_len > total_data || this_param + *param_len > total_param) { DEBUG(1,("Data overflow in cli_receive_trans\n")); goto out; } if (this_data + *data_len < this_data || this_data + *data_len < *data_len || this_param + *param_len < this_param || this_param + *param_len < *param_len) { DEBUG(1,("Data overflow in cli_receive_trans\n")); goto out; } if (this_data) { unsigned int data_offset_out = SVAL(cli->inbuf,smb_drdisp); unsigned int data_offset_in = SVAL(cli->inbuf,smb_droff); if (data_offset_out > total_data || data_offset_out + this_data > total_data || data_offset_out + this_data < data_offset_out || data_offset_out + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_trans\n")); goto out; } if (data_offset_in > cli->bufsize || data_offset_in + this_data > cli->bufsize || data_offset_in + this_data < data_offset_in || data_offset_in + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_trans\n")); goto out; } memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data); } if (this_param) { unsigned int param_offset_out = SVAL(cli->inbuf,smb_prdisp); unsigned int param_offset_in = SVAL(cli->inbuf,smb_proff); if (param_offset_out > total_param || param_offset_out + this_param > total_param || param_offset_out + this_param < param_offset_out || param_offset_out + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_trans\n")); goto out; } if (param_offset_in > cli->bufsize || param_offset_in + this_param > cli->bufsize || param_offset_in + this_param < param_offset_in || param_offset_in + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_trans\n")); goto out; } memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param); } *data_len += this_data; *param_len += this_param; if (total_data <= *data_len && total_param <= *param_len) { ret = True; break; } if (!cli_receive_smb(cli)) { goto out; } show_msg(cli->inbuf); /* sanity check */ if (CVAL(cli->inbuf,smb_com) != trans) { DEBUG(0,("Expected %s response, got command 0x%02x\n", trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(cli->inbuf,smb_com))); goto out; } if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { if (NT_STATUS_IS_ERR(cli_nt_error(cli))) { goto out; } } /* parse out the total lengths again - they can shrink! */ if (SVAL(cli->inbuf,smb_tdrcnt) < total_data) total_data = SVAL(cli->inbuf,smb_tdrcnt); if (SVAL(cli->inbuf,smb_tprcnt) < total_param) total_param = SVAL(cli->inbuf,smb_tprcnt); if (total_data <= *data_len && total_param <= *param_len) { ret = True; break; } } out: cli_state_seqnum_remove(cli, mid); if (ret) { /* Ensure the last 2 bytes of param and data are 2 null * bytes. These are malloc'ed, but not included in any * length counts. This allows cli_XX string reading functions * to safely null terminate. */ if (total_data) { SSVAL(*data,total_data,0); } if (total_param) { SSVAL(*param,total_param,0); } } return ret; }
static struct cli_state *smb_complete_connection(const char *myname, const char *server, int port, const char *username, const char *password, const char *workgroup, const char *share, int flags) { struct cli_state *cli; /* New connection */ NTSTATUS nt_status; /* Start the SMB connection */ nt_status = cli_start_connection( &cli, myname, server, NULL, port, Undefined, flags, NULL); if (!NT_STATUS_IS_OK(nt_status)) { return NULL; } /* We pretty much guarentee password must be valid or a pointer to a 0 char. */ if (!password) { return NULL; } if ( (username) && (*username) && (strlen(password) == 0 ) && (cli->use_kerberos) ) { /* Use kerberos authentication */ struct passwd *pw; char *cache_file; if ( !(pw = sys_getpwnam(username)) ) { fprintf(stderr,"ERROR Can not get %s uid\n", username); cli_shutdown(cli); return NULL; } /* * Get the ticket cache of the user to set KRB5CCNAME env * variable */ cache_file = get_ticket_cache( pw->pw_uid ); if ( cache_file == NULL ) { fprintf(stderr, "ERROR: Can not get the ticket cache for %s\n", username); cli_shutdown(cli); return NULL; } if ( setenv(KRB5CCNAME, cache_file, OVERWRITE) < 0 ) { fprintf(stderr, "ERROR: Can not add KRB5CCNAME to the environment"); cli_shutdown(cli); free(cache_file); return NULL; } free(cache_file); /* * Change the UID of the process to be able to read the kerberos * ticket cache */ setuid(pw->pw_uid); } if (!NT_STATUS_IS_OK(cli_session_setup(cli, username, password, strlen(password)+1, password, strlen(password)+1, workgroup))) { fprintf(stderr,"ERROR: Session setup failed: %s\n", cli_errstr(cli)); if (NT_STATUS_V(cli_nt_error(cli)) == NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED)) { fprintf(stderr, "did you forget to run kinit?\n"); } cli_shutdown(cli); return NULL; } if (!cli_send_tconX(cli, share, "?????", password, strlen(password)+1)) { fprintf(stderr, "ERROR: Tree connect failed (%s)\n", cli_errstr(cli)); cli_shutdown(cli); return NULL; } return cli; }
static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS], int fnum[NSERVERS][NCONNECTIONS][NFILES], struct record *rec) { unsigned conn = rec->conn; unsigned f = rec->f; SMB_BIG_UINT start = rec->start; SMB_BIG_UINT len = rec->len; enum brl_type op = rec->lock_type; int server; BOOL ret[NSERVERS]; NTSTATUS status[NSERVERS]; switch (rec->lock_op) { case OP_LOCK: /* set a lock */ for (server=0;server<NSERVERS;server++) { ret[server] = cli_lock64(cli[server][conn], fnum[server][conn][f], start, len, LOCK_TIMEOUT, op); status[server] = cli_nt_error(cli[server][conn]); if (!exact_error_codes && NT_STATUS_EQUAL(status[server], NT_STATUS_FILE_LOCK_CONFLICT)) { status[server] = NT_STATUS_LOCK_NOT_GRANTED; } } if (showall || !NT_STATUS_EQUAL(status[0],status[1])) { printf("lock conn=%u f=%u range=%.0f(%.0f) op=%s -> %s:%s\n", conn, f, (double)start, (double)len, op==READ_LOCK?"READ_LOCK":"WRITE_LOCK", nt_errstr(status[0]), nt_errstr(status[1])); } if (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks(); if (!NT_STATUS_EQUAL(status[0],status[1])) return False; break; case OP_UNLOCK: /* unset a lock */ for (server=0;server<NSERVERS;server++) { ret[server] = cli_unlock64(cli[server][conn], fnum[server][conn][f], start, len); status[server] = cli_nt_error(cli[server][conn]); } if (showall || (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))) { printf("unlock conn=%u f=%u range=%.0f(%.0f) -> %s:%s\n", conn, f, (double)start, (double)len, nt_errstr(status[0]), nt_errstr(status[1])); } if (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks(); if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1])) return False; break; case OP_REOPEN: /* reopen the file */ for (server=0;server<NSERVERS;server++) { cli_close(cli[server][conn], fnum[server][conn][f]); fnum[server][conn][f] = -1; } for (server=0;server<NSERVERS;server++) { fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME, O_RDWR|O_CREAT, DENY_NONE); if (fnum[server][conn][f] == -1) { printf("failed to reopen on share%d\n", server); return False; } } if (showall) { printf("reopen conn=%u f=%u\n", conn, f); show_locks(); } break; } return True; }