X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Flibsmb%2Fcliconnect.c;h=bc690f2e02be44fdc1e4763f15b7db7a73f4211b;hb=7f25e0da7348d786a36fa14403938b4f209fb52b;hp=0f09747dbf1115d1a2ba82e94132a20f32b8b037;hpb=aab1dd4ddbe45c625a6e4502cecd20da5762739b;p=metze%2Fsamba%2Fwip.git diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 0f09747dbf11..bc690f2e02be 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -6,7 +6,7 @@ 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 + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -15,31 +15,29 @@ 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. + along with this program. If not, see . */ #include "includes.h" -extern pstring user_socket_options; - static const struct { int prot; - const char *name; -} prots[] = { - {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"}, - {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"}, - {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"}, - {PROTOCOL_LANMAN1,"LANMAN1.0"}, - {PROTOCOL_LANMAN2,"LM1.2X002"}, - {PROTOCOL_LANMAN2,"DOS LANMAN2.1"}, - {PROTOCOL_LANMAN2,"LANMAN2.1"}, - {PROTOCOL_LANMAN2,"Samba"}, - {PROTOCOL_NT1,"NT LANMAN 1.0"}, - {PROTOCOL_NT1,"NT LM 0.12"}, - {-1,NULL} + const char name[24]; +} prots[10] = { + {PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"}, + {PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"}, + {PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"}, + {PROTOCOL_LANMAN1, "LANMAN1.0"}, + {PROTOCOL_LANMAN2, "LM1.2X002"}, + {PROTOCOL_LANMAN2, "DOS LANMAN2.1"}, + {PROTOCOL_LANMAN2, "LANMAN2.1"}, + {PROTOCOL_LANMAN2, "Samba"}, + {PROTOCOL_NT1, "NT LANMAN 1.0"}, + {PROTOCOL_NT1, "NT LM 0.12"}, }; +#define STAR_SMBSERVER "*SMBSERVER" + /** * Set the user session key for a connection * @param cli The cli structure to add it too @@ -61,8 +59,8 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli, const char *pass, size_t passlen, const char *workgroup) { - DATA_BLOB session_key = data_blob(NULL, 0); - DATA_BLOB lm_response = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob_null; + DATA_BLOB lm_response = data_blob_null; fstring pword; char *p; @@ -99,7 +97,7 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli, /* send a session setup command */ memset(cli->outbuf,'\0',smb_size); - set_message(cli->outbuf,10, 0, True); + cli_set_message(cli->outbuf,10, 0, True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); @@ -169,7 +167,7 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli) uint32 capabilities = cli_session_setup_capabilities(cli); memset(cli->outbuf, '\0', smb_size); - set_message(cli->outbuf,13,0,True); + cli_set_message(cli->outbuf,13,0,True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); @@ -201,9 +199,12 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli) cli->vuid = SVAL(cli->inbuf,smb_uid); p = smb_buf(cli->inbuf); - p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); - p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE); - p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring), + -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring), + -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_domain, p, sizeof(fstring), + -1, STR_TERMINATE); if (strstr(cli->server_type, "Samba")) { cli->is_samba = True; @@ -226,10 +227,10 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli, char *p; fstring lanman; - fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING); + fstr_sprintf( lanman, "Samba %s", samba_version_string()); memset(cli->outbuf, '\0', smb_size); - set_message(cli->outbuf,13,0,True); + cli_set_message(cli->outbuf,13,0,True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); @@ -248,9 +249,16 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli, p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */ SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf))); } - else { + else { + /* For ucs2 passwords clistr_push calls ucs2_align, which causes + * the space taken by the unicode password to be one byte too + * long (as we're on an odd byte boundary here). Reduce the + * count by 1 to cope with this. Fixes smbclient against NetApp + * servers which can't cope. Fix from + * bryan.kolodziej@allenlund.com in bug #3840. + */ p += clistr_push(cli, p, pass, -1, STR_UNICODE|STR_TERMINATE); /* unicode password */ - SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf))); + SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf))-1); } p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */ @@ -271,9 +279,12 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli, cli->vuid = SVAL(cli->inbuf,smb_uid); p = smb_buf(cli->inbuf); - p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); - p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE); - p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring), + -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring), + -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_domain, p, sizeof(fstring), + -1, STR_TERMINATE); fstrcpy(cli->user_name, user); if (strstr(cli->server_type, "Samba")) { @@ -299,9 +310,9 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, const char *workgroup) { uint32 capabilities = cli_session_setup_capabilities(cli); - DATA_BLOB lm_response = data_blob(NULL, 0); - DATA_BLOB nt_response = data_blob(NULL, 0); - DATA_BLOB session_key = data_blob(NULL, 0); + DATA_BLOB lm_response = data_blob_null; + DATA_BLOB nt_response = data_blob_null; + DATA_BLOB session_key = data_blob_null; NTSTATUS result; char *p; @@ -334,7 +345,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, E_md4hash(pass, nt_hash); #ifdef LANMAN_ONLY - nt_response = data_blob(NULL, 0); + nt_response = data_blob_null; #else nt_response = data_blob(NULL, 24); SMBNTencrypt(pass,cli->secblob.data,nt_response.data); @@ -378,7 +389,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, /* send a session setup command */ memset(cli->outbuf,'\0',smb_size); - set_message(cli->outbuf,13,0,True); + cli_set_message(cli->outbuf,13,0,True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); @@ -421,9 +432,12 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, cli->vuid = SVAL(cli->inbuf,smb_uid); p = smb_buf(cli->inbuf); - p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); - p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE); - p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring), + -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring), + -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_domain, p, sizeof(fstring), + -1, STR_TERMINATE); if (strstr(cli->server_type, "Samba")) { cli->is_samba = True; @@ -448,7 +462,7 @@ end: Send a extended security session setup blob ****************************************************************************/ -static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob) +static bool cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob) { uint32 capabilities = cli_session_setup_capabilities(cli); char *p; @@ -458,11 +472,11 @@ static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob) /* send a session setup command */ memset(cli->outbuf,'\0',smb_size); - set_message(cli->outbuf,12,0,True); + cli_set_message(cli->outbuf,12,0,True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); - + SCVAL(cli->outbuf,smb_vwv0,0xFF); SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); SSVAL(cli->outbuf,smb_vwv3,2); @@ -485,7 +499,7 @@ static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob) static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli) { - DATA_BLOB blob2 = data_blob(NULL, 0); + DATA_BLOB blob2 = data_blob_null; char *p; size_t len; @@ -498,20 +512,22 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli) NT_STATUS_MORE_PROCESSING_REQUIRED)) { return blob2; } - + /* use the returned vuid from now on */ cli->vuid = SVAL(cli->inbuf,smb_uid); - + p = smb_buf(cli->inbuf); blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3)); p += blob2.length; - p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring), + -1, STR_TERMINATE); /* w2k with kerberos doesn't properly null terminate this field */ - len = smb_buflen(cli->inbuf) - PTR_DIFF(p, smb_buf(cli->inbuf)); - p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0); + len = smb_bufrem(cli->inbuf, p); + p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring), + len, 0); return blob2; } @@ -530,12 +546,13 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli) #define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22) -static BOOL cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_BLOB session_key_krb5) +static bool cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob) { int32 remaining = blob.length; int32 cur = 0; - DATA_BLOB send_blob = data_blob(NULL, 0); + DATA_BLOB send_blob = data_blob_null; int32 max_blob_size = 0; + DATA_BLOB receive_blob = data_blob_null; if (cli->max_xmit < BASE_SESSSETUP_BLOB_PACKET_SIZE + 1) { DEBUG(0,("cli_session_setup_blob: cli->max_xmit too small " @@ -553,13 +570,8 @@ static BOOL cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_B send_blob.length = max_blob_size; remaining -= max_blob_size; } else { - DATA_BLOB null_blob = data_blob(NULL, 0); - send_blob.length = remaining; remaining = 0; - - /* This is the last packet in the sequence - turn signing on. */ - cli_simple_set_signing(cli, session_key_krb5, null_blob); } send_blob.data = &blob.data[cur]; @@ -575,13 +587,15 @@ static BOOL cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_B return False; } - cli_session_setup_blob_receive(cli); + receive_blob = cli_session_setup_blob_receive(cli); + data_blob_free(&receive_blob); if (cli_is_error(cli) && !NT_STATUS_EQUAL( cli_get_nt_error(cli), NT_STATUS_MORE_PROCESSING_REQUIRED)) { - DEBUG(0, ("cli_session_setup_blob: recieve failed (%s)\n", - nt_errstr(cli_get_nt_error(cli)) )); + DEBUG(0, ("cli_session_setup_blob: receive failed " + "(%s)\n", nt_errstr(cli_get_nt_error(cli)))); + cli->vuid = 0; return False; } } @@ -605,8 +619,11 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char * { DATA_BLOB negTokenTarg; DATA_BLOB session_key_krb5; + NTSTATUS nt_status; int rc; + cli_temp_set_signing(cli); + DEBUG(2,("Doing kerberos session setup\n")); /* generate the encapsulated kerberos5 ticket */ @@ -622,23 +639,44 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char * file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length); #endif - if (!cli_session_setup_blob(cli, negTokenTarg, session_key_krb5)) { - data_blob_free(&negTokenTarg); - data_blob_free(&session_key_krb5); - ADS_ERROR_NT(cli_nt_error(cli)); + if (!cli_session_setup_blob(cli, negTokenTarg)) { + nt_status = cli_nt_error(cli); + goto nt_error; + } + + if (cli_is_error(cli)) { + nt_status = cli_nt_error(cli); + if (NT_STATUS_IS_OK(nt_status)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + } + goto nt_error; } cli_set_session_key(cli, session_key_krb5); + if (cli_simple_set_signing( + cli, session_key_krb5, data_blob_null)) { + + /* 'resign' the last message, so we get the right sequence numbers + for checking the first reply from the server */ + cli_calculate_sign_mac(cli, cli->outbuf); + + if (!cli_check_sign_mac(cli, cli->inbuf)) { + nt_status = NT_STATUS_ACCESS_DENIED; + goto nt_error; + } + } + data_blob_free(&negTokenTarg); data_blob_free(&session_key_krb5); - if (cli_is_error(cli)) { - if (NT_STATUS_IS_OK(cli_nt_error(cli))) { - return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); - } - } - return ADS_ERROR_NT(cli_nt_error(cli)); + return ADS_ERROR_NT(NT_STATUS_OK); + +nt_error: + data_blob_free(&negTokenTarg); + data_blob_free(&session_key_krb5); + cli->vuid = 0; + return ADS_ERROR_NT(nt_status); } #endif /* HAVE_KRB5 */ @@ -654,9 +692,9 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use NTSTATUS nt_status; int turn = 1; DATA_BLOB msg1; - DATA_BLOB blob = data_blob(NULL, 0); - DATA_BLOB blob_in = data_blob(NULL, 0); - DATA_BLOB blob_out = data_blob(NULL, 0); + DATA_BLOB blob = data_blob_null; + DATA_BLOB blob_in = data_blob_null; + DATA_BLOB blob_out = data_blob_null; cli_temp_set_signing(cli); @@ -687,17 +725,17 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use /* wrap it in SPNEGO */ msg1 = spnego_gen_auth(blob_out); } - + /* now send that blob on its way */ if (!cli_session_setup_blob_send(cli, msg1)) { DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n")); nt_status = NT_STATUS_UNSUCCESSFUL; } else { blob = cli_session_setup_blob_receive(cli); - + nt_status = cli_nt_error(cli); if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) { - if (cli->smb_rw_error == READ_BAD_SIG) { + if (cli->smb_rw_error == SMB_READ_BAD_SIG) { nt_status = NT_STATUS_ACCESS_DENIED; } else { nt_status = NT_STATUS_UNSUCCESSFUL; @@ -706,14 +744,14 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use } data_blob_free(&msg1); } - + if (!blob.length) { if (NT_STATUS_IS_OK(nt_status)) { nt_status = NT_STATUS_UNSUCCESSFUL; } } else if ((turn == 1) && NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - DATA_BLOB tmp_blob = data_blob(NULL, 0); + DATA_BLOB tmp_blob = data_blob_null; /* the server might give us back two challenges */ if (!spnego_parse_challenge(blob, &blob_in, &tmp_blob)) { @@ -722,7 +760,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use } data_blob_free(&tmp_blob); } else { - if (!spnego_parse_auth_response(blob, nt_status, + if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, &blob_in)) { DEBUG(3,("Failed to parse auth response\n")); if (NT_STATUS_IS_OK(nt_status) @@ -739,25 +777,17 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use if (NT_STATUS_IS_OK(nt_status)) { - DATA_BLOB key = data_blob(ntlmssp_state->session_key.data, - ntlmssp_state->session_key.length); - DATA_BLOB null_blob = data_blob(NULL, 0); - BOOL res; - fstrcpy(cli->server_domain, ntlmssp_state->server_domain); cli_set_session_key(cli, ntlmssp_state->session_key); - res = cli_simple_set_signing(cli, key, null_blob); - - data_blob_free(&key); + if (cli_simple_set_signing( + cli, ntlmssp_state->session_key, data_blob_null)) { - if (res) { - /* 'resign' the last message, so we get the right sequence numbers for checking the first reply from the server */ - cli_calculate_sign_mac(cli); - - if (!cli_check_sign_mac(cli)) { + cli_calculate_sign_mac(cli, cli->outbuf); + + if (!cli_check_sign_mac(cli, cli->inbuf)) { nt_status = NT_STATUS_ACCESS_DENIED; } } @@ -768,21 +798,29 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use ntlmssp_end(&ntlmssp_state); + if (!NT_STATUS_IS_OK(nt_status)) { + cli->vuid = 0; + } return nt_status; } /**************************************************************************** Do a spnego encrypted session setup. + + user_domain: The shortname of the domain the user/machine is a member of. + dest_realm: The realm we're connecting to, if NULL we use our default realm. ****************************************************************************/ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, - const char *pass, const char *domain) + const char *pass, const char *user_domain, + const char * dest_realm) { - char *principal; + char *principal = NULL; char *OIDs[ASN1_MAX_OIDS]; int i; - BOOL got_kerberos_mechanism = False; DATA_BLOB blob; + const char *p = NULL; + char *account = NULL; DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length)); @@ -799,8 +837,10 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, /* there is 16 bytes of GUID before the real spnego packet starts */ blob = data_blob(cli->secblob.data+16, cli->secblob.length-16); - /* the server sent us the first part of the SPNEGO exchange in the negprot - reply */ + /* The server sent us the first part of the SPNEGO exchange in the + * negprot reply. It is WRONG to depend on the principal sent in the + * negprot reply, but right now we do it. If we don't receive one, + * we try to best guess, then fall back to NTLM. */ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) { data_blob_free(&blob); return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); @@ -812,62 +852,115 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, DEBUG(3,("got OID=%s\n", OIDs[i])); if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 || strcmp(OIDs[i], OID_KERBEROS5) == 0) { - got_kerberos_mechanism = True; + cli->got_kerberos_mechanism = True; } - free(OIDs[i]); + talloc_free(OIDs[i]); } DEBUG(3,("got principal=%s\n", principal ? principal : "")); - if (got_kerberos_mechanism && (principal == NULL)) { - /* - * It is WRONG to depend on the principal sent in the negprot - * reply, but right now we do it. So for safety (don't - * segfault later) disable Kerberos when no principal was - * sent. -- VL - */ - DEBUG(1, ("Kerberos mech was offered, but no principal was " - "sent, disabling Kerberos\n")); - cli->use_kerberos = False; - } - fstrcpy(cli->user_name, user); #ifdef HAVE_KRB5 /* If password is set we reauthenticate to kerberos server * and do not store results */ - if (got_kerberos_mechanism && cli->use_kerberos) { + if (cli->got_kerberos_mechanism && cli->use_kerberos) { ADS_STATUS rc; if (pass && *pass) { int ret; - + use_in_memory_ccache(); ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL); - + if (ret){ - SAFE_FREE(principal); + TALLOC_FREE(principal); DEBUG(0, ("Kinit failed: %s\n", error_message(ret))); if (cli->fallback_after_kerberos) goto ntlmssp; return ADS_ERROR_KRB5(ret); } } - - rc = cli_session_setup_kerberos(cli, principal, domain); - if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) { - SAFE_FREE(principal); - return rc; + + /* If we get a bad principal, try to guess it if + we have a valid host NetBIOS name. + */ + if (strequal(principal, ADS_IGNORE_PRINCIPAL)) { + TALLOC_FREE(principal); + } + + if (principal == NULL && + !is_ipaddress(cli->desthost) && + !strequal(STAR_SMBSERVER, + cli->desthost)) { + char *realm = NULL; + char *machine = NULL; + char *host = NULL; + DEBUG(3,("cli_session_setup_spnego: got a " + "bad server principal, trying to guess ...\n")); + + host = strchr_m(cli->desthost, '.'); + if (host) { + machine = SMB_STRNDUP(cli->desthost, + host - cli->desthost); + } else { + machine = SMB_STRDUP(cli->desthost); + } + if (machine == NULL) { + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + if (dest_realm) { + realm = SMB_STRDUP(dest_realm); + strupper_m(realm); + } else { + realm = kerberos_get_default_realm_from_ccache(); + } + if (realm && *realm) { + principal = talloc_asprintf(NULL, "%s$@%s", + machine, realm); + if (!principal) { + SAFE_FREE(machine); + SAFE_FREE(realm); + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + DEBUG(3,("cli_session_setup_spnego: guessed " + "server principal=%s\n", + principal ? principal : "")); + } + SAFE_FREE(machine); + SAFE_FREE(realm); + } + + if (principal) { + rc = cli_session_setup_kerberos(cli, principal, + dest_realm); + if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) { + TALLOC_FREE(principal); + return rc; + } } } #endif - SAFE_FREE(principal); + TALLOC_FREE(principal); ntlmssp: - return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, user, pass, domain)); + account = talloc_strdup(talloc_tos(), user); + if (!account) { + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + /* when falling back to ntlmssp while authenticating with a machine + * account strip off the realm - gd */ + + if ((p = strchr_m(user, '@')) != NULL) { + account[PTR_DIFF(p,user)] = '\0'; + } + + return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, account, pass, user_domain)); } /**************************************************************************** @@ -876,8 +969,8 @@ ntlmssp: password is in plaintext, the same should be done. ****************************************************************************/ -NTSTATUS cli_session_setup(struct cli_state *cli, - const char *user, +NTSTATUS cli_session_setup(struct cli_state *cli, + const char *user, const char *pass, int passlen, const char *ntpass, int ntpasslen, const char *workgroup) @@ -885,8 +978,17 @@ NTSTATUS cli_session_setup(struct cli_state *cli, char *p; fstring user2; + if (user) { + fstrcpy(user2, user); + } else { + user2[0] ='\0'; + } + + if (!workgroup) { + workgroup = ""; + } + /* allow for workgroups as part of the username */ - fstrcpy(user2, user); if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) || (p=strchr_m(user2,*lp_winbind_separator()))) { *p = 0; @@ -913,8 +1015,8 @@ NTSTATUS cli_session_setup(struct cli_state *cli, if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 && !lp_client_plaintext_auth() && (*pass)) { - DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'" - " is disabled\n")); + DEBUG(1, ("Server requested plaintext password but " + "'client plaintext auth' is disabled\n")); return NT_STATUS_ACCESS_DENIED; } @@ -940,8 +1042,8 @@ NTSTATUS cli_session_setup(struct cli_state *cli, if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) { if (!lp_client_plaintext_auth() && (*pass)) { - DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'" - " is disabled\n")); + DEBUG(1, ("Server requested plaintext password but " + "'client plaintext auth' is disabled\n")); return NT_STATUS_ACCESS_DENIED; } return cli_session_setup_plaintext(cli, user, pass, workgroup); @@ -950,7 +1052,8 @@ NTSTATUS cli_session_setup(struct cli_state *cli, /* if the server supports extended security then use SPNEGO */ if (cli->capabilities & CAP_EXTENDED_SECURITY) { - ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, workgroup); + ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, + workgroup, NULL); if (!ADS_ERR_OK(status)) { DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status))); return ads_ntstatus(status); @@ -973,17 +1076,16 @@ NTSTATUS cli_session_setup(struct cli_state *cli, } return NT_STATUS_OK; - } /**************************************************************************** Send a uloggoff. *****************************************************************************/ -BOOL cli_ulogoff(struct cli_state *cli) +bool cli_ulogoff(struct cli_state *cli) { memset(cli->outbuf,'\0',smb_size); - set_message(cli->outbuf,2,0,True); + cli_set_message(cli->outbuf,2,0,True); SCVAL(cli->outbuf,smb_com,SMBulogoffX); cli_setup_packet(cli); SSVAL(cli->outbuf,smb_vwv0,0xFF); @@ -1005,7 +1107,7 @@ BOOL cli_ulogoff(struct cli_state *cli) Send a tconX. ****************************************************************************/ -BOOL cli_send_tconX(struct cli_state *cli, +bool cli_send_tconX(struct cli_state *cli, const char *share, const char *dev, const char *pass, int passlen) { fstring fullshare, pword; @@ -1027,8 +1129,9 @@ BOOL cli_send_tconX(struct cli_state *cli, if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && *pass && passlen != 24) { if (!lp_client_lanman_auth()) { - DEBUG(1, ("Server requested LANMAN password (share-level security) but 'client use lanman auth'" - " is disabled\n")); + DEBUG(1, ("Server requested LANMAN password " + "(share-level security) but " + "'client lanman auth' is disabled\n")); return False; } @@ -1040,8 +1143,9 @@ BOOL cli_send_tconX(struct cli_state *cli, } else { if((cli->sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL|NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) == 0) { if (!lp_client_plaintext_auth() && (*pass)) { - DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'" - " is disabled\n")); + DEBUG(1, ("Server requested plaintext " + "password but 'client plaintext " + "auth' is disabled\n")); return False; } @@ -1049,7 +1153,7 @@ BOOL cli_send_tconX(struct cli_state *cli, * Non-encrypted passwords - convert to DOS codepage before using. */ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE); - + } else { if (passlen) { memcpy(pword, pass, passlen); @@ -1060,11 +1164,12 @@ BOOL cli_send_tconX(struct cli_state *cli, slprintf(fullshare, sizeof(fullshare)-1, "\\\\%s\\%s", cli->desthost, share); - set_message(cli->outbuf,4, 0, True); + cli_set_message(cli->outbuf,4, 0, True); SCVAL(cli->outbuf,smb_com,SMBtconX); cli_setup_packet(cli); SSVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,TCONX_FLAG_EXTENDED_RESPONSE); SSVAL(cli->outbuf,smb_vwv3,passlen); p = smb_buf(cli->outbuf); @@ -1084,14 +1189,15 @@ BOOL cli_send_tconX(struct cli_state *cli, if (cli_is_error(cli)) return False; - clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE|STR_ASCII); + clistr_pull(cli->inbuf, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), + -1, STR_TERMINATE|STR_ASCII); if (cli->protocol >= PROTOCOL_NT1 && smb_buflen(cli->inbuf) == 3) { /* almost certainly win95 - enable bug fixes */ cli->win95 = True; } - + /* Make sure that we have the optional support 16-bit field. WCT > 2 */ /* Avoids issues when connecting to Win9x boxes sharing files */ @@ -1107,18 +1213,18 @@ BOOL cli_send_tconX(struct cli_state *cli, Send a tree disconnect. ****************************************************************************/ -BOOL cli_tdis(struct cli_state *cli) +bool cli_tdis(struct cli_state *cli) { memset(cli->outbuf,'\0',smb_size); - set_message(cli->outbuf,0,0,True); + cli_set_message(cli->outbuf,0,0,True); SCVAL(cli->outbuf,smb_com,SMBtdis); SSVAL(cli->outbuf,smb_tid,cli->cnum); cli_setup_packet(cli); - + cli_send_smb(cli); if (!cli_receive_smb(cli)) return False; - + if (cli_is_error(cli)) { return False; } @@ -1131,7 +1237,7 @@ BOOL cli_tdis(struct cli_state *cli) Send a negprot command. ****************************************************************************/ -void cli_negprot_send(struct cli_state *cli) +void cli_negprot_sendsync(struct cli_state *cli) { char *p; int numprots; @@ -1142,12 +1248,13 @@ void cli_negprot_send(struct cli_state *cli) memset(cli->outbuf,'\0',smb_size); /* setup the protocol strings */ - set_message(cli->outbuf,0,0,True); + cli_set_message(cli->outbuf,0,0,True); p = smb_buf(cli->outbuf); - for (numprots=0; - prots[numprots].name && prots[numprots].prot<=cli->protocol; - numprots++) { + for (numprots=0; numprots < ARRAY_SIZE(prots); numprots++) { + if (prots[numprots].prot > cli->protocol) { + break; + } *p++ = 2; p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE); } @@ -1165,70 +1272,89 @@ void cli_negprot_send(struct cli_state *cli) Send a negprot command. ****************************************************************************/ -BOOL cli_negprot(struct cli_state *cli) +struct async_req *cli_negprot_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli) { - char *p; + struct async_req *result; + uint8_t *bytes = NULL; int numprots; - int plength; if (cli->protocol < PROTOCOL_NT1) cli->use_spnego = False; - memset(cli->outbuf,'\0',smb_size); - /* setup the protocol strings */ - for (plength=0,numprots=0; - prots[numprots].name && prots[numprots].prot<=cli->protocol; - numprots++) - plength += strlen(prots[numprots].name)+2; - - set_message(cli->outbuf,0,plength,True); - - p = smb_buf(cli->outbuf); - for (numprots=0; - prots[numprots].name && prots[numprots].prot<=cli->protocol; - numprots++) { - *p++ = 2; - p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE); + for (numprots=0; numprots < ARRAY_SIZE(prots); numprots++) { + uint8_t c = 2; + if (prots[numprots].prot > cli->protocol) { + break; + } + bytes = (uint8_t *)talloc_append_blob( + talloc_tos(), bytes, data_blob_const(&c, sizeof(c))); + if (bytes == NULL) { + return NULL; + } + bytes = smb_bytes_push_str(bytes, false, prots[numprots].name); + if (bytes == NULL) { + return NULL; + } } - SCVAL(cli->outbuf,smb_com,SMBnegprot); - cli_setup_packet(cli); + result = cli_request_send(mem_ctx, ev, cli, SMBnegprot, 0, 0, NULL, 0, + talloc_get_size(bytes), bytes); + TALLOC_FREE(bytes); + return result; +} - SCVAL(smb_buf(cli->outbuf),0,2); +NTSTATUS cli_negprot_recv(struct async_req *req) +{ + struct cli_request *cli_req = talloc_get_type_abort( + req->private_data, struct cli_request); + struct cli_state *cli = cli_req->cli; + uint8_t wct; + uint16_t *vwv; + uint16_t num_bytes; + uint8_t *bytes; + NTSTATUS status; + uint16_t protnum; - cli_send_smb(cli); - if (!cli_receive_smb(cli)) - return False; + if (async_req_is_error(req, &status)) { + return status; + } - show_msg(cli->inbuf); + status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + protnum = SVAL(vwv, 0); - if (cli_is_error(cli) || - ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) { - return(False); + if ((protnum >= ARRAY_SIZE(prots)) + || (prots[protnum].prot > cli_req->cli->protocol)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; } - cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot; + cli->protocol = prots[protnum].prot; if ((cli->protocol < PROTOCOL_NT1) && cli->sign_info.mandatory_signing) { DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n")); - return False; + return NT_STATUS_ACCESS_DENIED; } if (cli->protocol >= PROTOCOL_NT1) { struct timespec ts; /* NT protocol */ - cli->sec_mode = CVAL(cli->inbuf,smb_vwv1); - cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1); - cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1); - cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1); - cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1); + cli->sec_mode = CVAL(vwv + 1, 0); + cli->max_mux = SVAL(vwv + 1, 1); + cli->max_xmit = IVAL(vwv + 3, 1); + cli->sesskey = IVAL(vwv + 7, 1); + cli->serverzone = SVALS(vwv + 15, 1); cli->serverzone *= 60; /* this time arrives in real GMT */ - ts = interpret_long_date(cli->inbuf+smb_vwv11+1); + ts = interpret_long_date(((char *)(vwv+11))+1); cli->servertime = ts.tv_sec; - cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf)); - cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1); + cli->secblob = data_blob(bytes, num_bytes); + cli->capabilities = IVAL(vwv + 9, 1); if (cli->capabilities & CAP_RAW_MODE) { cli->readbraw_supported = True; cli->writebraw_supported = True; @@ -1236,9 +1362,10 @@ BOOL cli_negprot(struct cli_state *cli) /* work out if they sent us a workgroup */ if (!(cli->capabilities & CAP_EXTENDED_SECURITY) && smb_buflen(cli->inbuf) > 8) { - clistr_pull(cli, cli->server_domain, - smb_buf(cli->inbuf)+8, sizeof(cli->server_domain), - smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN); + clistr_pull(cli->inbuf, cli->server_domain, + bytes+8, sizeof(cli->server_domain), + num_bytes-8, + STR_UNICODE|STR_NOALIGN); } /* @@ -1250,7 +1377,7 @@ BOOL cli_negprot(struct cli_state *cli) /* Fail if server says signing is mandatory and we don't want to support it. */ if (!cli->sign_info.allow_smb_signing) { DEBUG(0,("cli_negprot: SMB signing is mandatory and we have disabled it.\n")); - return False; + return NT_STATUS_ACCESS_DENIED; } cli->sign_info.negotiated_smb_signing = True; cli->sign_info.mandatory_signing = True; @@ -1258,7 +1385,7 @@ BOOL cli_negprot(struct cli_state *cli) /* Fail if client says signing is mandatory and the server doesn't support it. */ if (!(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) { DEBUG(1,("cli_negprot: SMB signing is mandatory and the server doesn't support it.\n")); - return False; + return NT_STATUS_ACCESS_DENIED; } cli->sign_info.negotiated_smb_signing = True; cli->sign_info.mandatory_signing = True; @@ -1269,24 +1396,25 @@ BOOL cli_negprot(struct cli_state *cli) if (cli->capabilities & (CAP_LARGE_READX|CAP_LARGE_WRITEX)) { SAFE_FREE(cli->outbuf); SAFE_FREE(cli->inbuf); - cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN); - cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN); - cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE; + cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN); + cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN); + cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE + LARGE_WRITEX_HDR_SIZE; } } else if (cli->protocol >= PROTOCOL_LANMAN1) { cli->use_spnego = False; - cli->sec_mode = SVAL(cli->inbuf,smb_vwv1); - cli->max_xmit = SVAL(cli->inbuf,smb_vwv2); - cli->max_mux = SVAL(cli->inbuf, smb_vwv3); - cli->sesskey = IVAL(cli->inbuf,smb_vwv6); - cli->serverzone = SVALS(cli->inbuf,smb_vwv10); + cli->sec_mode = SVAL(vwv + 1, 0); + cli->max_xmit = SVAL(vwv + 2, 0); + cli->max_mux = SVAL(vwv + 3, 0); + cli->sesskey = IVAL(vwv + 6, 0); + cli->serverzone = SVALS(vwv + 10, 0); cli->serverzone *= 60; /* this time is converted to GMT by make_unix_date */ - cli->servertime = cli_make_unix_date(cli,cli->inbuf+smb_vwv8); - cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0); - cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0); - cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf)); + cli->servertime = cli_make_unix_date( + cli, (char *)(vwv + 8)); + cli->readbraw_supported = ((SVAL(vwv + 5, 0) & 0x1) != 0); + cli->writebraw_supported = ((SVAL(vwv + 5, 0) & 0x2) != 0); + cli->secblob = data_blob(bytes, num_bytes); } else { /* the old core protocol */ cli->use_spnego = False; @@ -1300,22 +1428,61 @@ BOOL cli_negprot(struct cli_state *cli) if (getenv("CLI_FORCE_ASCII")) cli->capabilities &= ~CAP_UNICODE; - return True; + return NT_STATUS_OK; +} + +NTSTATUS cli_negprot(struct cli_state *cli) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct async_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (cli->fd_event != NULL) { + /* + * Can't use sync call while an async call is in flight + */ + cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_negprot_send(frame, ev, cli); + if (req == NULL) { + goto fail; + } + + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + status = cli_negprot_recv(req); + fail: + TALLOC_FREE(frame); + return status; } /**************************************************************************** Send a session request. See rfc1002.txt 4.3 and 4.3.2. ****************************************************************************/ -BOOL cli_session_request(struct cli_state *cli, +bool cli_session_request(struct cli_state *cli, struct nmb_name *calling, struct nmb_name *called) { char *p; int len = 4; + /* 445 doesn't have session request */ + if (cli->port == 445) + return True; + memcpy(&(cli->calling), calling, sizeof(*calling)); memcpy(&(cli->called ), called , sizeof(*called )); - + /* put in the destination name */ p = cli->outbuf+len; name_mangle(cli->called .name, p, cli->called .name_type); @@ -1326,10 +1493,6 @@ BOOL cli_session_request(struct cli_state *cli, name_mangle(cli->calling.name, p, cli->calling.name_type); len += name_len(p); - /* 445 doesn't have session request */ - if (cli->port == 445) - return True; - /* send a session request (RFC 1002) */ /* setup the packet length * Remove four bytes from the length count, since the length @@ -1360,22 +1523,28 @@ BOOL cli_session_request(struct cli_state *cli, int16 port; }; */ - int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9); + uint16_t port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9); + struct in_addr dest_ip; + NTSTATUS status; + /* SESSION RETARGET */ - putip((char *)&cli->dest_ip,cli->inbuf+4); + putip((char *)&dest_ip,cli->inbuf+4); + in_addr_to_sockaddr_storage(&cli->dest_ss, dest_ip); - cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT); - if (cli->fd == -1) + status = open_socket_out(&cli->dest_ss, port, + LONG_CONNECT_TIMEOUT, &cli->fd); + if (!NT_STATUS_IS_OK(status)) { return False; + } DEBUG(3,("Retargeted\n")); - set_socket_options(cli->fd,user_socket_options); + set_socket_options(cli->fd, lp_socket_options()); /* Try again */ { static int depth; - BOOL ret; + bool ret; if (depth > 4) { DEBUG(0,("Retarget recursion - failing\n")); return False; @@ -1395,121 +1564,222 @@ BOOL cli_session_request(struct cli_state *cli, return(True); } +static void smb_sock_connected(struct async_req *req) +{ + int *pfd = (int *)req->async.priv; + int fd; + NTSTATUS status; + + status = open_socket_out_defer_recv(req, &fd); + if (NT_STATUS_IS_OK(status)) { + *pfd = fd; + } +} + +static NTSTATUS open_smb_socket(const struct sockaddr_storage *pss, + uint16_t *port, int timeout, int *pfd) +{ + struct event_context *ev; + struct async_req *r139, *r445; + int fd139 = -1; + int fd445 = -1; + NTSTATUS status; + + if (*port != 0) { + return open_socket_out(pss, *port, timeout, pfd); + } + + ev = event_context_init(talloc_tos()); + if (ev == NULL) { + return NT_STATUS_NO_MEMORY; + } + + r445 = open_socket_out_defer_send(ev, ev, timeval_set(0, 0), + pss, 445, timeout); + r139 = open_socket_out_defer_send(ev, ev, timeval_set(0, 3000), + pss, 139, timeout); + if ((r445 == NULL) || (r139 == NULL)) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + r445->async.fn = smb_sock_connected; + r445->async.priv = &fd445; + r139->async.fn = smb_sock_connected; + r139->async.priv = &fd139; + + while ((fd139 == -1) && (r139->state < ASYNC_REQ_DONE) + && (fd445 == -1) && (r445->state < ASYNC_REQ_DONE)) { + event_loop_once(ev); + } + + if ((fd139 != -1) && (fd445 != -1)) { + close(fd139); + fd139 = -1; + } + + if (fd445 != -1) { + *port = 445; + *pfd = fd445; + status = NT_STATUS_OK; + goto done; + } + if (fd139 != -1) { + *port = 139; + *pfd = fd139; + status = NT_STATUS_OK; + goto done; + } + + status = open_socket_out_defer_recv(r445, &fd445); + done: + TALLOC_FREE(ev); + return status; +} + /**************************************************************************** Open the client sockets. ****************************************************************************/ -BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip) +NTSTATUS cli_connect(struct cli_state *cli, + const char *host, + struct sockaddr_storage *dest_ss) + { int name_type = 0x20; - char *p; + TALLOC_CTX *frame = talloc_stackframe(); + unsigned int num_addrs = 0; + unsigned int i = 0; + struct sockaddr_storage *ss_arr = NULL; + char *p = NULL; /* reasonable default hostname */ - if (!host) host = "*SMBSERVER"; + if (!host) { + host = STAR_SMBSERVER; + } fstrcpy(cli->desthost, host); /* allow hostnames of the form NAME#xx and do a netbios lookup */ if ((p = strchr(cli->desthost, '#'))) { - name_type = strtol(p+1, NULL, 16); + name_type = strtol(p+1, NULL, 16); *p = 0; } - - if (!ip || is_zero_ip(*ip)) { - if (!resolve_name(cli->desthost, &cli->dest_ip, name_type)) { - return False; + + if (!dest_ss || is_zero_addr((struct sockaddr *)dest_ss)) { + NTSTATUS status =resolve_name_list(frame, + cli->desthost, + name_type, + &ss_arr, + &num_addrs); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return NT_STATUS_BAD_NETWORK_NAME; } - if (ip) *ip = cli->dest_ip; } else { - cli->dest_ip = *ip; + num_addrs = 1; + ss_arr = TALLOC_P(frame, struct sockaddr_storage); + if (!ss_arr) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + *ss_arr = *dest_ss; } - if (getenv("LIBSMB_PROG")) { - cli->fd = sock_exec(getenv("LIBSMB_PROG")); - } else { - /* try 445 first, then 139 */ - int port = cli->port?cli->port:445; - cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, - port, cli->timeout); - if (cli->fd == -1 && cli->port == 0) { - port = 139; - cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, - port, cli->timeout); + for (i = 0; i < num_addrs; i++) { + cli->dest_ss = ss_arr[i]; + if (getenv("LIBSMB_PROG")) { + cli->fd = sock_exec(getenv("LIBSMB_PROG")); + } else { + uint16_t port = cli->port; + NTSTATUS status; + status = open_smb_socket(&cli->dest_ss, &port, + cli->timeout, &cli->fd); + if (NT_STATUS_IS_OK(status)) { + cli->port = port; + } + } + if (cli->fd == -1) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), &ss_arr[i]); + DEBUG(2,("Error connecting to %s (%s)\n", + dest_ss?addr:host,strerror(errno))); + } else { + /* Exit from loop on first connection. */ + break; } - if (cli->fd != -1) - cli->port = port; } + if (cli->fd == -1) { - DEBUG(1,("Error connecting to %s (%s)\n", - ip?inet_ntoa(*ip):host,strerror(errno))); - return False; + TALLOC_FREE(frame); + return map_nt_error_from_unix(errno); } - set_socket_options(cli->fd,user_socket_options); + if (dest_ss) { + *dest_ss = cli->dest_ss; + } - return True; + set_socket_options(cli->fd, lp_socket_options()); + + TALLOC_FREE(frame); + return NT_STATUS_OK; } /** establishes a connection to after the negprot. @param output_cli A fully initialised cli structure, non-null only on success @param dest_host The netbios name of the remote host - @param dest_ip (optional) The the destination IP, NULL for name based lookup + @param dest_ss (optional) The the destination IP, NULL for name based lookup @param port (optional) The destination port (0 for default) - @param retry BOOL. Did this connection fail with a retryable error ? + @param retry bool. Did this connection fail with a retryable error ? */ NTSTATUS cli_start_connection(struct cli_state **output_cli, const char *my_name, const char *dest_host, - struct in_addr *dest_ip, int port, + struct sockaddr_storage *dest_ss, int port, int signing_state, int flags, - BOOL *retry) + bool *retry) { NTSTATUS nt_status; struct nmb_name calling; struct nmb_name called; struct cli_state *cli; - struct in_addr ip; + struct sockaddr_storage ss; if (retry) *retry = False; if (!my_name) my_name = global_myname(); - + if (!(cli = cli_initialise())) { return NT_STATUS_NO_MEMORY; } - + make_nmb_name(&calling, my_name, 0x0); make_nmb_name(&called , dest_host, 0x20); - if (cli_set_port(cli, port) != port) { - cli_shutdown(cli); - return NT_STATUS_UNSUCCESSFUL; - } - + cli_set_port(cli, port); cli_set_timeout(cli, 10000); /* 10 seconds. */ - if (dest_ip) - ip = *dest_ip; - else - ZERO_STRUCT(ip); + if (dest_ss) { + ss = *dest_ss; + } else { + zero_sockaddr(&ss); + } again: DEBUG(3,("Connecting to host=%s\n", dest_host)); - - if (!cli_connect(cli, dest_host, &ip)) { - DEBUG(1,("cli_start_connection: failed to connect to %s (%s)\n", - nmb_namestr(&called), inet_ntoa(ip))); + + nt_status = cli_connect(cli, dest_host, &ss); + if (!NT_STATUS_IS_OK(nt_status)) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), &ss); + DEBUG(1,("cli_start_connection: failed to connect to %s (%s). Error %s\n", + nmb_namestr(&called), addr, nt_errstr(nt_status) )); cli_shutdown(cli); - if (is_zero_ip(ip)) { - return NT_STATUS_BAD_NETWORK_NAME; - } else { - return NT_STATUS_CONNECTION_REFUSED; - } + return nt_status; } if (retry) @@ -1517,14 +1787,14 @@ again: if (!cli_session_request(cli, &calling, &called)) { char *p; - DEBUG(1,("session request to %s failed (%s)\n", + DEBUG(1,("session request to %s failed (%s)\n", called.name, cli_errstr(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); + if (strcmp(called.name, STAR_SMBSERVER)) { + make_nmb_name(&called , STAR_SMBSERVER, 0x20); goto again; } return NT_STATUS_BAD_NETWORK_NAME; @@ -1537,12 +1807,14 @@ again: else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS) cli->use_kerberos = True; - if (!cli_negprot(cli)) { - DEBUG(1,("failed negprot\n")); - nt_status = cli_nt_error(cli); - if (NT_STATUS_IS_OK(nt_status)) { - nt_status = NT_STATUS_UNSUCCESSFUL; - } + if ((flags & CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS) && + cli->use_kerberos) { + cli->fallback_after_kerberos = true; + } + + nt_status = cli_negprot(cli); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("failed negprot: %s\n", nt_errstr(nt_status))); cli_shutdown(cli); return nt_status; } @@ -1563,18 +1835,18 @@ again: @param user Username, unix string @param domain User's domain @param password User's password, unencrypted unix string. - @param retry BOOL. Did this connection fail with a retryable error ? + @param retry bool. Did this connection fail with a retryable error ? */ NTSTATUS cli_full_connection(struct cli_state **output_cli, const char *my_name, const char *dest_host, - struct in_addr *dest_ip, int port, + struct sockaddr_storage *dest_ss, int port, const char *service, const char *service_type, const char *user, const char *domain, const char *password, int flags, int signing_state, - BOOL *retry) + bool *retry) { NTSTATUS nt_status; struct cli_state *cli = NULL; @@ -1586,9 +1858,10 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli, password = ""; } - nt_status = cli_start_connection(&cli, my_name, dest_host, - dest_ip, port, signing_state, flags, retry); - + nt_status = cli_start_connection(&cli, my_name, dest_host, + dest_ss, port, signing_state, + flags, retry); + if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } @@ -1612,7 +1885,7 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli, return nt_status; } } - + if (service) { if (!cli_send_tconX(cli, service, service_type, password, pw_len)) { nt_status = cli_nt_error(cli); @@ -1635,8 +1908,8 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli, Attempt a NetBIOS session request, falling back to *SMBSERVER if needed. ****************************************************************************/ -BOOL attempt_netbios_session_request(struct cli_state **ppcli, const char *srchost, const char *desthost, - struct in_addr *pdest_ip) +bool attempt_netbios_session_request(struct cli_state **ppcli, const char *srchost, const char *desthost, + struct sockaddr_storage *pdest_ss) { struct nmb_name calling, called; @@ -1648,15 +1921,16 @@ BOOL attempt_netbios_session_request(struct cli_state **ppcli, const char *srcho */ if(is_ipaddress(desthost)) { - make_nmb_name(&called, "*SMBSERVER", 0x20); + make_nmb_name(&called, STAR_SMBSERVER, 0x20); } else { make_nmb_name(&called, desthost, 0x20); } if (!cli_session_request(*ppcli, &calling, &called)) { + NTSTATUS status; struct nmb_name smbservername; - make_nmb_name(&smbservername , "*SMBSERVER", 0x20); + make_nmb_name(&smbservername, STAR_SMBSERVER, 0x20); /* * If the name wasn't *SMBSERVER then @@ -1683,7 +1957,8 @@ with error %s.\n", desthost, cli_errstr(*ppcli) )); return False; } - if (!cli_connect(*ppcli, desthost, pdest_ip) || + status = cli_connect(*ppcli, desthost, pdest_ss); + if (!NT_STATUS_IS_OK(status) || !cli_session_request(*ppcli, &calling, &smbservername)) { DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \ name *SMBSERVER with error %s\n", desthost, cli_errstr(*ppcli) )); @@ -1694,10 +1969,6 @@ name *SMBSERVER with error %s\n", desthost, cli_errstr(*ppcli) )); return True; } - - - - /**************************************************************************** Send an old style tcon. ****************************************************************************/ @@ -1708,15 +1979,15 @@ NTSTATUS cli_raw_tcon(struct cli_state *cli, char *p; if (!lp_client_plaintext_auth() && (*pass)) { - DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'" - " is disabled\n")); + DEBUG(1, ("Server requested plaintext password but 'client " + "plaintext auth' is disabled\n")); return NT_STATUS_ACCESS_DENIED; } memset(cli->outbuf,'\0',smb_size); memset(cli->inbuf,'\0',smb_size); - set_message(cli->outbuf, 0, 0, True); + cli_set_message(cli->outbuf, 0, 0, True); SCVAL(cli->outbuf,smb_com,SMBtcon); cli_setup_packet(cli); @@ -1744,18 +2015,24 @@ NTSTATUS cli_raw_tcon(struct cli_state *cli, /* Return a cli_state pointing at the IPC$ share for the given server */ -struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip, - struct user_auth_info *user_info) +struct cli_state *get_ipc_connect(char *server, + struct sockaddr_storage *server_ss, + const struct user_auth_info *user_info) { struct cli_state *cli; - pstring myname; NTSTATUS nt_status; + uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK; - get_myname(myname); - - nt_status = cli_full_connection(&cli, myname, server, server_ip, 0, "IPC$", "IPC", - user_info->username, lp_workgroup(), user_info->password, - CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK, Undefined, NULL); + if (user_info->use_kerberos) { + flags |= CLI_FULL_CONNECTION_USE_KERBEROS; + } + + nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC", + user_info->username ? user_info->username : "", + lp_workgroup(), + user_info->password ? user_info->password : "", + flags, + Undefined, NULL); if (NT_STATUS_IS_OK(nt_status)) { return cli; @@ -1763,8 +2040,8 @@ struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip, /* windows 9* needs a correct NMB name for connections */ fstring remote_name; - if (name_status_find("*", 0, 0, *server_ip, remote_name)) { - cli = get_ipc_connect(remote_name, server_ip, user_info); + if (name_status_find("*", 0, 0, server_ss, remote_name)) { + cli = get_ipc_connect(remote_name, server_ss, user_info); if (cli) return cli; } @@ -1784,14 +2061,21 @@ struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip, * entire network browse list) */ -struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring workgroup, struct user_auth_info *user_info) +struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx, + struct ip_service *mb_ip, + const struct user_auth_info *user_info, + char **pp_workgroup_out) { - static fstring name; + char addr[INET6_ADDRSTRLEN]; + fstring name; struct cli_state *cli; - struct in_addr server_ip; + struct sockaddr_storage server_ss; + + *pp_workgroup_out = NULL; + print_sockaddr(addr, sizeof(addr), &mb_ip->ss); DEBUG(99, ("Looking up name of master browser %s\n", - inet_ntoa(mb_ip->ip))); + addr)); /* * Do a name status query to find out the name of the master browser. @@ -1804,28 +2088,27 @@ struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring w * the original wildcard query as the first choice and fall back to * MSBROWSE if the wildcard query fails. */ - if (!name_status_find("*", 0, 0x1d, mb_ip->ip, name) && - !name_status_find(MSBROWSE, 1, 0x1d, mb_ip->ip, name)) { + if (!name_status_find("*", 0, 0x1d, &mb_ip->ss, name) && + !name_status_find(MSBROWSE, 1, 0x1d, &mb_ip->ss, name)) { DEBUG(99, ("Could not retrieve name status for %s\n", - inet_ntoa(mb_ip->ip))); + addr)); return NULL; } - if (!find_master_ip(name, &server_ip)) { + if (!find_master_ip(name, &server_ss)) { DEBUG(99, ("Could not find master ip for %s\n", name)); return NULL; } - pstrcpy(workgroup, name); + *pp_workgroup_out = talloc_strdup(ctx, name); - DEBUG(4, ("found master browser %s, %s\n", - name, inet_ntoa(mb_ip->ip))); + DEBUG(4, ("found master browser %s, %s\n", name, addr)); - cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info); + print_sockaddr(addr, sizeof(addr), &server_ss); + cli = get_ipc_connect(addr, &server_ss, user_info); - return cli; - + return cli; } /* @@ -1833,27 +2116,35 @@ struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring w * connect to it. */ -struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user_auth_info *user_info) +struct cli_state *get_ipc_connect_master_ip_bcast(TALLOC_CTX *ctx, + const struct user_auth_info *user_info, + char **pp_workgroup_out) { struct ip_service *ip_list; struct cli_state *cli; int i, count; + *pp_workgroup_out = NULL; + DEBUG(99, ("Do broadcast lookup for workgroups on local network\n")); - /* Go looking for workgroups by broadcasting on the local network */ + /* Go looking for workgroups by broadcasting on the local network */ - if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) { + if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list, + &count))) { DEBUG(99, ("No master browsers responded\n")); return False; } for (i = 0; i < count; i++) { - DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip))); + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), &ip_list[i].ss); + DEBUG(99, ("Found master browser %s\n", addr)); - cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, user_info); - if (cli) - return(cli); + cli = get_ipc_connect_master_ip(ctx, &ip_list[i], + user_info, pp_workgroup_out); + if (cli) + return(cli); } return NULL;