mirror of
				https://github.com/samba-team/samba.git
				synced 2025-10-30 08:23:49 +03:00 
			
		
		
		
	This allows dumping the keys and quickly feeding them into Wireshark by adding
them to ~/.wireshark/smb2_seskey_list.
Example:
  debug encryption: dumping generated session keys
  Session Id    [0000] 7D 00 00 E8 57 E0 31 01                             }...W.1.
  Session Key   [0000] 71 54 77 50 C1 DD 66 68   A8 51 D8 DE 23 F4 91 01   qTwP..fh .Q..#...
  Signing Key   [0000] B1 29 AC EF 41 30 AE D2   43 00 1F 67 87 29 BF DB   .)..A0.. C..g.)..
  App Key       [0000] 6A 88 5C 51 51 22 FF 5C   25 95 A2 5C E2 2C FC 5D   j.\QQ".\ %..\.,.]
  ServerIn Key  [0000] 20 08 EB A2 14 99 17 03   9C A5 9A BB B8 48 88 3C    ....... .....H.<
  ServerOut Key [0000] 15 AA C2 0D 19 AB 4C 26   64 E8 FC 94 B1 FE 27 5A   ......L& d.....'Z
  Wireshark configuration line
  7d0000e857e03101,71547750c1dd6668a851d8de23f49101,15aac20d19ab4c2664e8fc94b1fe275a,2008eba2149917039ca59abbb848883c
When setting
    debug encryption = yes
    debug encryption:wireshark keyfile = /home/slow/.wireshark/smb2_seskey_list
the keys are appended directly to Wireshark's keyfile. Wireshark has to be
restarted to pick them up.
Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
		
	
		
			
				
	
	
		
			3920 lines
		
	
	
		
			101 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3920 lines
		
	
	
		
			101 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|    Unix SMB/CIFS implementation.
 | |
|    client connect/disconnect routines
 | |
|    Copyright (C) Andrew Tridgell 1994-1998
 | |
|    Copyright (C) Andrew Bartlett 2001-2003
 | |
|    Copyright (C) Volker Lendecke 2011
 | |
|    Copyright (C) Jeremy Allison 2011
 | |
| 
 | |
|    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 3 of the License, or
 | |
|    (at your option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| 
 | |
| #include "includes.h"
 | |
| #include "source3/include/client.h"
 | |
| #include "source3/libsmb/proto.h"
 | |
| #include "libads/ads_status.h"
 | |
| #include "libsmb/namequery.h"
 | |
| #include "../libcli/auth/libcli_auth.h"
 | |
| #include "auth/credentials/credentials.h"
 | |
| #include "auth/gensec/gensec.h"
 | |
| #include "auth/ntlmssp/ntlmssp.h"
 | |
| #include "auth_generic.h"
 | |
| #include "../lib/util/tevent_ntstatus.h"
 | |
| #include "async_smb.h"
 | |
| #include "libsmb/nmblib.h"
 | |
| #include "libsmb/smbsock_connect.h"
 | |
| #include "librpc/ndr/libndr.h"
 | |
| #include "../libcli/smb/smbXcli_base.h"
 | |
| #include "../libcli/smb/smb_seal.h"
 | |
| #include "lib/param/param.h"
 | |
| #include "../libcli/smb/smb2_negotiate_context.h"
 | |
| 
 | |
| static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
 | |
| 					   const char *principal);
 | |
| 
 | |
| struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
 | |
| 					       const char *username,
 | |
| 					       const char *domain,
 | |
| 					       const char *realm,
 | |
| 					       const char *password,
 | |
| 					       bool use_kerberos,
 | |
| 					       bool fallback_after_kerberos,
 | |
| 					       bool use_ccache,
 | |
| 					       bool password_is_nt_hash)
 | |
| {
 | |
| 	struct loadparm_context *lp_ctx = NULL;
 | |
| 	struct cli_credentials *creds = NULL;
 | |
| 	const char *principal = NULL;
 | |
| 	enum credentials_use_kerberos creds_use_krb;
 | |
| 	char *tmp = NULL;
 | |
| 	char *p = NULL;
 | |
| 	bool ok;
 | |
| 
 | |
| 	creds = cli_credentials_init(mem_ctx);
 | |
| 	if (creds == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers());
 | |
| 	if (lp_ctx == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	ok = cli_credentials_set_conf(creds, lp_ctx);
 | |
| 	if (!ok) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	if (username == NULL) {
 | |
| 		username = "";
 | |
| 	}
 | |
| 
 | |
| 	if (strlen(username) == 0) {
 | |
| 		if (password != NULL && strlen(password) == 0) {
 | |
| 			/*
 | |
| 			 * some callers pass "" as no password
 | |
| 			 *
 | |
| 			 * gensec only handles NULL as no password.
 | |
| 			 */
 | |
| 			password = NULL;
 | |
| 		}
 | |
| 		if (password == NULL) {
 | |
| 			cli_credentials_set_anonymous(creds);
 | |
| 			return creds;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	tmp = talloc_strdup(creds, username);
 | |
| 	if (tmp == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	username = tmp;
 | |
| 
 | |
| 	/* allow for workgroups as part of the username */
 | |
| 	if ((p = strchr_m(tmp, '\\')) ||
 | |
| 	    (p = strchr_m(tmp, '/')) ||
 | |
| 	    (p = strchr_m(tmp, *lp_winbind_separator()))) {
 | |
| 		*p = 0;
 | |
| 		username = p + 1;
 | |
| 		domain = tmp;
 | |
| 	}
 | |
| 
 | |
| 	principal = username;
 | |
| 	username = cli_session_setup_get_account(creds, principal);
 | |
| 	if (username == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	ok = strequal(username, principal);
 | |
| 	if (ok) {
 | |
| 		/*
 | |
| 		 * Ok still the same, so it's not a principal
 | |
| 		 */
 | |
| 		principal = NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (use_kerberos) {
 | |
| 		if (fallback_after_kerberos) {
 | |
| 			/*
 | |
| 			 * Keep what we learned from the
 | |
| 			 * "client use kerberos" option.
 | |
| 			 */
 | |
| 			creds_use_krb = cli_credentials_get_kerberos_state(
 | |
| 				creds);
 | |
| 		} else {
 | |
| 			creds_use_krb = CRED_USE_KERBEROS_REQUIRED;
 | |
| 		}
 | |
| 	} else {
 | |
| 		creds_use_krb = CRED_USE_KERBEROS_DISABLED;
 | |
| 	}
 | |
| 
 | |
| 	cli_credentials_set_kerberos_state(creds,
 | |
| 					   creds_use_krb,
 | |
| 					   CRED_SPECIFIED);
 | |
| 
 | |
| 	if (use_ccache) {
 | |
| 		cli_credentials_add_gensec_features(creds,
 | |
| 						    GENSEC_FEATURE_NTLM_CCACHE,
 | |
| 						    CRED_SPECIFIED);
 | |
| 
 | |
| 		if (password != NULL && strlen(password) == 0) {
 | |
| 			/*
 | |
| 			 * some callers pass "" as no password
 | |
| 			 *
 | |
| 			 * GENSEC_FEATURE_NTLM_CCACHE only handles
 | |
| 			 * NULL as no password.
 | |
| 			 */
 | |
| 			password = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ok = cli_credentials_set_username(creds,
 | |
| 					  username,
 | |
| 					  CRED_SPECIFIED);
 | |
| 	if (!ok) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	if (domain != NULL) {
 | |
| 		ok = cli_credentials_set_domain(creds,
 | |
| 						domain,
 | |
| 						CRED_SPECIFIED);
 | |
| 		if (!ok) {
 | |
| 			goto fail;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (principal != NULL) {
 | |
| 		ok = cli_credentials_set_principal(creds,
 | |
| 						   principal,
 | |
| 						   CRED_SPECIFIED);
 | |
| 		if (!ok) {
 | |
| 			goto fail;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (realm != NULL) {
 | |
| 		ok = cli_credentials_set_realm(creds,
 | |
| 					       realm,
 | |
| 					       CRED_SPECIFIED);
 | |
| 		if (!ok) {
 | |
| 			goto fail;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (password != NULL && strlen(password) > 0) {
 | |
| 		if (password_is_nt_hash) {
 | |
| 			struct samr_Password nt_hash;
 | |
| 			size_t converted;
 | |
| 
 | |
| 			converted = strhex_to_str((char *)nt_hash.hash,
 | |
| 						  sizeof(nt_hash.hash),
 | |
| 						  password,
 | |
| 						  strlen(password));
 | |
| 			if (converted != sizeof(nt_hash.hash)) {
 | |
| 				goto fail;
 | |
| 			}
 | |
| 
 | |
| 			ok = cli_credentials_set_nt_hash(creds,
 | |
| 							 &nt_hash,
 | |
| 							 CRED_SPECIFIED);
 | |
| 			if (!ok) {
 | |
| 				goto fail;
 | |
| 			}
 | |
| 		} else {
 | |
| 			ok = cli_credentials_set_password(creds,
 | |
| 							  password,
 | |
| 							  CRED_SPECIFIED);
 | |
| 			if (!ok) {
 | |
| 				goto fail;
 | |
| 			}
 | |
| 		}
 | |
| 	} else if (use_kerberos && !fallback_after_kerberos) {
 | |
| 		const char *error_string = NULL;
 | |
| 		int rc;
 | |
| 
 | |
| 		rc = cli_credentials_set_ccache(creds,
 | |
| 						lp_ctx,
 | |
| 						NULL,
 | |
| 						CRED_SPECIFIED,
 | |
| 						&error_string);
 | |
| 		if (rc != 0) {
 | |
| 			fprintf(stderr,
 | |
| 				"Warning reading default "
 | |
| 				"krb5 credentials cache: %s\n",
 | |
| 				error_string);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return creds;
 | |
| fail:
 | |
| 	TALLOC_FREE(creds);
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_state_update_after_sesssetup(struct cli_state *cli,
 | |
| 						 const char *native_os,
 | |
| 						 const char *native_lm,
 | |
| 						 const char *primary_domain)
 | |
| {
 | |
| #define _VALID_STR(p) ((p) != NULL && (p)[0] != '\0')
 | |
| 
 | |
| 	if (!_VALID_STR(cli->server_os) && _VALID_STR(native_os)) {
 | |
| 		cli->server_os = talloc_strdup(cli, native_os);
 | |
| 		if (cli->server_os == NULL) {
 | |
| 			return NT_STATUS_NO_MEMORY;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!_VALID_STR(cli->server_type) && _VALID_STR(native_lm)) {
 | |
| 		cli->server_type = talloc_strdup(cli, native_lm);
 | |
| 		if (cli->server_type == NULL) {
 | |
| 			return NT_STATUS_NO_MEMORY;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!_VALID_STR(cli->server_domain) && _VALID_STR(primary_domain)) {
 | |
| 		cli->server_domain = talloc_strdup(cli, primary_domain);
 | |
| 		if (cli->server_domain == NULL) {
 | |
| 			return NT_STATUS_NO_MEMORY;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| #undef _VALID_STRING
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| /********************************************************
 | |
|  Utility function to ensure we always return at least
 | |
|  a valid char * pointer to an empty string for the
 | |
|  cli->server_os, cli->server_type and cli->server_domain
 | |
|  strings.
 | |
| *******************************************************/
 | |
| 
 | |
| static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx,
 | |
| 					const uint8_t *hdr,
 | |
| 					char **dest,
 | |
| 					uint8_t *src,
 | |
| 					size_t srclen,
 | |
| 					ssize_t *destlen)
 | |
| {
 | |
| 	*destlen = pull_string_talloc(mem_ctx,
 | |
| 				      (const char *)hdr,
 | |
| 				      SVAL(hdr, HDR_FLG2),
 | |
| 				      dest,
 | |
| 				      (char *)src,
 | |
| 				      srclen,
 | |
| 				      STR_TERMINATE);
 | |
| 	if (*destlen == -1) {
 | |
| 		return NT_STATUS_NO_MEMORY;
 | |
| 	}
 | |
| 
 | |
| 	if (*dest == NULL) {
 | |
| 		*dest = talloc_strdup(mem_ctx, "");
 | |
| 		if (*dest == NULL) {
 | |
| 			return NT_STATUS_NO_MEMORY;
 | |
| 		}
 | |
| 	}
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Work out suitable capabilities to offer the server.
 | |
| ****************************************************************************/
 | |
| 
 | |
| static uint32_t cli_session_setup_capabilities(struct cli_state *cli,
 | |
| 					       uint32_t sesssetup_capabilities)
 | |
| {
 | |
| 	uint32_t client_capabilities = smb1cli_conn_capabilities(cli->conn);
 | |
| 
 | |
| 	/*
 | |
| 	 * We only send capabilities based on the mask for:
 | |
| 	 * - client only flags
 | |
| 	 * - flags used in both directions
 | |
| 	 *
 | |
| 	 * We do not echo the server only flags, except some legacy flags.
 | |
| 	 *
 | |
| 	 * SMB_CAP_LEGACY_CLIENT_MASK contains CAP_LARGE_READX and
 | |
| 	 * CAP_LARGE_WRITEX in order to allow us to do large reads
 | |
| 	 * against old Samba releases (<= 3.6.x).
 | |
| 	 */
 | |
| 	client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_LEGACY_CLIENT_MASK);
 | |
| 
 | |
| 	/*
 | |
| 	 * Session Setup specific flags CAP_DYNAMIC_REAUTH
 | |
| 	 * and CAP_EXTENDED_SECURITY are passed by the caller.
 | |
| 	 * We need that in order to do guest logins even if
 | |
| 	 * CAP_EXTENDED_SECURITY is negotiated.
 | |
| 	 */
 | |
| 	client_capabilities &= ~(CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
 | |
| 	sesssetup_capabilities &= (CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
 | |
| 	client_capabilities |= sesssetup_capabilities;
 | |
| 
 | |
| 	return client_capabilities;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Do a NT1 guest session setup.
 | |
| ****************************************************************************/
 | |
| 
 | |
| struct cli_session_setup_guest_state {
 | |
| 	struct cli_state *cli;
 | |
| 	uint16_t vwv[13];
 | |
| 	struct iovec bytes;
 | |
| };
 | |
| 
 | |
| static void cli_session_setup_guest_done(struct tevent_req *subreq);
 | |
| 
 | |
| struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
 | |
| 						  struct tevent_context *ev,
 | |
| 						  struct cli_state *cli,
 | |
| 						  struct tevent_req **psmbreq)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_session_setup_guest_state *state;
 | |
| 	uint16_t *vwv;
 | |
| 	uint8_t *bytes;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_session_setup_guest_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->cli = cli;
 | |
| 	vwv = state->vwv;
 | |
| 
 | |
| 	SCVAL(vwv+0, 0, 0xFF);
 | |
| 	SCVAL(vwv+0, 1, 0);
 | |
| 	SSVAL(vwv+1, 0, 0);
 | |
| 	SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
 | |
| 	SSVAL(vwv+3, 0, 2);
 | |
| 	SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
 | |
| 	SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
 | |
| 	SSVAL(vwv+7, 0, 0);
 | |
| 	SSVAL(vwv+8, 0, 0);
 | |
| 	SSVAL(vwv+9, 0, 0);
 | |
| 	SSVAL(vwv+10, 0, 0);
 | |
| 	SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli, 0));
 | |
| 
 | |
| 	bytes = talloc_array(state, uint8_t, 0);
 | |
| 
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "",  1, /* username */
 | |
| 				   NULL);
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */
 | |
| 				   NULL);
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
 | |
| 
 | |
| 	if (bytes == NULL) {
 | |
| 		TALLOC_FREE(req);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	state->bytes.iov_base = (void *)bytes;
 | |
| 	state->bytes.iov_len = talloc_get_size(bytes);
 | |
| 
 | |
| 	subreq = cli_smb_req_create(state, ev, cli, SMBsesssetupX, 0, 0, 13,
 | |
| 			vwv, 1, &state->bytes);
 | |
| 	if (subreq == NULL) {
 | |
| 		TALLOC_FREE(req);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_session_setup_guest_done, req);
 | |
| 	*psmbreq = subreq;
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
 | |
| 						struct tevent_context *ev,
 | |
| 						struct cli_state *cli)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	req = cli_session_setup_guest_create(mem_ctx, ev, cli, &subreq);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	status = smb1cli_req_chain_submit(&subreq, 1);
 | |
| 	if (!NT_STATUS_IS_OK(status)) {
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_guest_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_session_setup_guest_state *state = tevent_req_data(
 | |
| 		req, struct cli_session_setup_guest_state);
 | |
| 	struct cli_state *cli = state->cli;
 | |
| 	uint32_t num_bytes;
 | |
| 	uint8_t *in;
 | |
| 	uint8_t *inhdr;
 | |
| 	uint8_t *bytes;
 | |
| 	uint8_t *p;
 | |
| 	NTSTATUS status;
 | |
| 	ssize_t ret;
 | |
| 	uint8_t wct;
 | |
| 	uint16_t *vwv;
 | |
| 
 | |
| 	status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
 | |
| 			      &num_bytes, &bytes);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (!NT_STATUS_IS_OK(status)) {
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	inhdr = in + NBT_HDR_SIZE;
 | |
| 	p = bytes;
 | |
| 
 | |
| 	cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
 | |
| 	smb1cli_session_set_action(cli->smb1.session, SVAL(vwv+2, 0));
 | |
| 
 | |
| 	status = smb_bytes_talloc_string(cli,
 | |
| 					inhdr,
 | |
| 					&cli->server_os,
 | |
| 					p,
 | |
| 					bytes+num_bytes-p,
 | |
| 					&ret);
 | |
| 
 | |
| 	if (!NT_STATUS_IS_OK(status)) {
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 	p += ret;
 | |
| 
 | |
| 	status = smb_bytes_talloc_string(cli,
 | |
| 					inhdr,
 | |
| 					&cli->server_type,
 | |
| 					p,
 | |
| 					bytes+num_bytes-p,
 | |
| 					&ret);
 | |
| 
 | |
| 	if (!NT_STATUS_IS_OK(status)) {
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 	p += ret;
 | |
| 
 | |
| 	status = smb_bytes_talloc_string(cli,
 | |
| 					inhdr,
 | |
| 					&cli->server_domain,
 | |
| 					p,
 | |
| 					bytes+num_bytes-p,
 | |
| 					&ret);
 | |
| 
 | |
| 	if (!NT_STATUS_IS_OK(status)) {
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req)
 | |
| {
 | |
| 	return tevent_req_simple_recv_ntstatus(req);
 | |
| }
 | |
| 
 | |
| /* The following is calculated from :
 | |
|  * (smb_size-4) = 35
 | |
|  * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() )
 | |
|  * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at
 | |
|  * end of packet.
 | |
|  */
 | |
| 
 | |
| #define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
 | |
| 
 | |
| struct cli_sesssetup_blob_state {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct cli_state *cli;
 | |
| 	DATA_BLOB blob;
 | |
| 	uint16_t max_blob_size;
 | |
| 
 | |
| 	DATA_BLOB this_blob;
 | |
| 	struct iovec *recv_iov;
 | |
| 
 | |
| 	NTSTATUS status;
 | |
| 	const uint8_t *inbuf;
 | |
| 	DATA_BLOB ret_blob;
 | |
| 
 | |
| 	char *out_native_os;
 | |
| 	char *out_native_lm;
 | |
| };
 | |
| 
 | |
| static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
 | |
| 				    struct tevent_req **psubreq);
 | |
| static void cli_sesssetup_blob_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_sesssetup_blob_send(TALLOC_CTX *mem_ctx,
 | |
| 						  struct tevent_context *ev,
 | |
| 						  struct cli_state *cli,
 | |
| 						  DATA_BLOB blob)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_sesssetup_blob_state *state;
 | |
| 	uint32_t usable_space;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_sesssetup_blob_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->ev = ev;
 | |
| 	state->blob = blob;
 | |
| 	state->cli = cli;
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		usable_space = UINT16_MAX;
 | |
| 	} else {
 | |
| 		usable_space = cli_state_available_size(cli,
 | |
| 				BASE_SESSSETUP_BLOB_PACKET_SIZE);
 | |
| 	}
 | |
| 
 | |
| 	if (usable_space == 0) {
 | |
| 		DEBUG(1, ("cli_session_setup_blob: cli->max_xmit too small "
 | |
| 			  "(not possible to send %u bytes)\n",
 | |
| 			  BASE_SESSSETUP_BLOB_PACKET_SIZE + 1));
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	state->max_blob_size = MIN(usable_space, 0xFFFF);
 | |
| 
 | |
| 	if (!cli_sesssetup_blob_next(state, &subreq)) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
 | |
| 				    struct tevent_req **psubreq)
 | |
| {
 | |
| 	struct tevent_req *subreq;
 | |
| 	uint16_t thistime;
 | |
| 
 | |
| 	thistime = MIN(state->blob.length, state->max_blob_size);
 | |
| 
 | |
| 	state->this_blob.data = state->blob.data;
 | |
| 	state->this_blob.length = thistime;
 | |
| 
 | |
| 	state->blob.data += thistime;
 | |
| 	state->blob.length -= thistime;
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		subreq = smb2cli_session_setup_send(state, state->ev,
 | |
| 						    state->cli->conn,
 | |
| 						    state->cli->timeout,
 | |
| 						    state->cli->smb2.session,
 | |
| 						    0, /* in_flags */
 | |
| 						    SMB2_CAP_DFS, /* in_capabilities */
 | |
| 						    0, /* in_channel */
 | |
| 						    0, /* in_previous_session_id */
 | |
| 						    &state->this_blob);
 | |
| 		if (subreq == NULL) {
 | |
| 			return false;
 | |
| 		}
 | |
| 	} else {
 | |
| 		uint16_t in_buf_size = 0;
 | |
| 		uint16_t in_mpx_max = 0;
 | |
| 		uint16_t in_vc_num = 0;
 | |
| 		uint32_t in_sess_key = 0;
 | |
| 		uint32_t in_capabilities = 0;
 | |
| 		const char *in_native_os = NULL;
 | |
| 		const char *in_native_lm = NULL;
 | |
| 
 | |
| 		in_buf_size = CLI_BUFFER_SIZE;
 | |
| 		in_mpx_max = smbXcli_conn_max_requests(state->cli->conn);
 | |
| 		in_vc_num = cli_state_get_vc_num(state->cli);
 | |
| 		in_sess_key = smb1cli_conn_server_session_key(state->cli->conn);
 | |
| 		in_capabilities = cli_session_setup_capabilities(state->cli,
 | |
| 								CAP_EXTENDED_SECURITY);
 | |
| 		in_native_os = "Unix";
 | |
| 		in_native_lm = "Samba";
 | |
| 
 | |
| 		/*
 | |
| 		 * For now we keep the same values as before,
 | |
| 		 * we may remove these in a separate commit later.
 | |
| 		 */
 | |
| 		in_mpx_max = 2;
 | |
| 		in_vc_num = 1;
 | |
| 		in_sess_key = 0;
 | |
| 
 | |
| 		subreq = smb1cli_session_setup_ext_send(state, state->ev,
 | |
| 							state->cli->conn,
 | |
| 							state->cli->timeout,
 | |
| 							state->cli->smb1.pid,
 | |
| 							state->cli->smb1.session,
 | |
| 							in_buf_size,
 | |
| 							in_mpx_max,
 | |
| 							in_vc_num,
 | |
| 							in_sess_key,
 | |
| 							state->this_blob,
 | |
| 							in_capabilities,
 | |
| 							in_native_os,
 | |
| 							in_native_lm);
 | |
| 		if (subreq == NULL) {
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 	*psubreq = subreq;
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static void cli_sesssetup_blob_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_sesssetup_blob_state *state = tevent_req_data(
 | |
| 		req, struct cli_sesssetup_blob_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		status = smb2cli_session_setup_recv(subreq, state,
 | |
| 						    &state->recv_iov,
 | |
| 						    &state->ret_blob);
 | |
| 	} else {
 | |
| 		status = smb1cli_session_setup_ext_recv(subreq, state,
 | |
| 							&state->recv_iov,
 | |
| 							&state->inbuf,
 | |
| 							&state->ret_blob,
 | |
| 							&state->out_native_os,
 | |
| 							&state->out_native_lm);
 | |
| 	}
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (!NT_STATUS_IS_OK(status)
 | |
| 	    && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	state->status = status;
 | |
| 
 | |
| 	status = cli_state_update_after_sesssetup(state->cli,
 | |
| 						  state->out_native_os,
 | |
| 						  state->out_native_lm,
 | |
| 						  NULL);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (state->blob.length != 0) {
 | |
| 		/*
 | |
| 		 * More to send
 | |
| 		 */
 | |
| 		if (!cli_sesssetup_blob_next(state, &subreq)) {
 | |
| 			tevent_req_oom(req);
 | |
| 			return;
 | |
| 		}
 | |
| 		tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req);
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req,
 | |
| 					TALLOC_CTX *mem_ctx,
 | |
| 					DATA_BLOB *pblob,
 | |
| 					const uint8_t **pinbuf,
 | |
| 					struct iovec **precv_iov)
 | |
| {
 | |
| 	struct cli_sesssetup_blob_state *state = tevent_req_data(
 | |
| 		req, struct cli_sesssetup_blob_state);
 | |
| 	NTSTATUS status;
 | |
| 	struct iovec *recv_iov;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		TALLOC_FREE(state->cli->smb2.session);
 | |
| 		cli_state_set_uid(state->cli, UID_FIELD_INVALID);
 | |
| 		tevent_req_received(req);
 | |
| 		return status;
 | |
| 	}
 | |
| 
 | |
| 	recv_iov = talloc_move(mem_ctx, &state->recv_iov);
 | |
| 	if (pblob != NULL) {
 | |
| 		*pblob = state->ret_blob;
 | |
| 	}
 | |
| 	if (pinbuf != NULL) {
 | |
| 		*pinbuf = state->inbuf;
 | |
| 	}
 | |
| 	if (precv_iov != NULL) {
 | |
| 		*precv_iov = recv_iov;
 | |
| 	}
 | |
|         /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */
 | |
| 	status = state->status;
 | |
| 	tevent_req_received(req);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Do a spnego/NTLMSSP encrypted session setup.
 | |
| ****************************************************************************/
 | |
| 
 | |
| struct cli_session_setup_gensec_state {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct cli_state *cli;
 | |
| 	struct auth_generic_state *auth_generic;
 | |
| 	bool is_anonymous;
 | |
| 	DATA_BLOB blob_in;
 | |
| 	const uint8_t *inbuf;
 | |
| 	struct iovec *recv_iov;
 | |
| 	DATA_BLOB blob_out;
 | |
| 	bool local_ready;
 | |
| 	bool remote_ready;
 | |
| 	DATA_BLOB session_key;
 | |
| };
 | |
| 
 | |
| static int cli_session_setup_gensec_state_destructor(
 | |
| 	struct cli_session_setup_gensec_state *state)
 | |
| {
 | |
| 	TALLOC_FREE(state->auth_generic);
 | |
| 	data_blob_clear_free(&state->session_key);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_gensec_local_next(struct tevent_req *req);
 | |
| static void cli_session_setup_gensec_local_done(struct tevent_req *subreq);
 | |
| static void cli_session_setup_gensec_remote_next(struct tevent_req *req);
 | |
| static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq);
 | |
| static void cli_session_setup_gensec_ready(struct tevent_req *req);
 | |
| 
 | |
| static struct tevent_req *cli_session_setup_gensec_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
 | |
| 	struct cli_credentials *creds,
 | |
| 	const char *target_service,
 | |
| 	const char *target_hostname)
 | |
| {
 | |
| 	struct tevent_req *req;
 | |
| 	struct cli_session_setup_gensec_state *state;
 | |
| 	NTSTATUS status;
 | |
| 	const DATA_BLOB *b = NULL;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_session_setup_gensec_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->ev = ev;
 | |
| 	state->cli = cli;
 | |
| 
 | |
| 	talloc_set_destructor(
 | |
| 		state, cli_session_setup_gensec_state_destructor);
 | |
| 
 | |
| 	status = auth_generic_client_prepare(state, &state->auth_generic);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	status = auth_generic_set_creds(state->auth_generic, creds);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	gensec_want_feature(state->auth_generic->gensec_security,
 | |
| 			    GENSEC_FEATURE_SESSION_KEY);
 | |
| 
 | |
| 	if (target_service != NULL) {
 | |
| 		status = gensec_set_target_service(
 | |
| 				state->auth_generic->gensec_security,
 | |
| 				target_service);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (target_hostname != NULL) {
 | |
| 		status = gensec_set_target_hostname(
 | |
| 				state->auth_generic->gensec_security,
 | |
| 				target_hostname);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	b = smbXcli_conn_server_gss_blob(cli->conn);
 | |
| 	if (b != NULL) {
 | |
| 		state->blob_in = *b;
 | |
| 	}
 | |
| 
 | |
| 	state->is_anonymous = cli_credentials_is_anonymous(state->auth_generic->credentials);
 | |
| 
 | |
| 	status = auth_generic_client_start(state->auth_generic,
 | |
| 					   GENSEC_OID_SPNEGO);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		state->cli->smb2.session = smbXcli_session_create(cli,
 | |
| 								  cli->conn);
 | |
| 		if (tevent_req_nomem(state->cli->smb2.session, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	cli_session_setup_gensec_local_next(req);
 | |
| 	if (!tevent_req_is_in_progress(req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_gensec_local_next(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_session_setup_gensec_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_session_setup_gensec_state);
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 
 | |
| 	if (state->local_ready) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = gensec_update_send(state, state->ev,
 | |
| 			state->auth_generic->gensec_security,
 | |
| 			state->blob_in);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_session_setup_gensec_local_done, req);
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_gensec_local_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req =
 | |
| 		tevent_req_callback_data(subreq,
 | |
| 		struct tevent_req);
 | |
| 	struct cli_session_setup_gensec_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_session_setup_gensec_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = gensec_update_recv(subreq, state, &state->blob_out);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	state->blob_in = data_blob_null;
 | |
| 	if (!NT_STATUS_IS_OK(status) &&
 | |
| 	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
 | |
| 	{
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (NT_STATUS_IS_OK(status)) {
 | |
| 		state->local_ready = true;
 | |
| 	}
 | |
| 
 | |
| 	if (state->local_ready && state->remote_ready) {
 | |
| 		cli_session_setup_gensec_ready(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cli_session_setup_gensec_remote_next(req);
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_gensec_remote_next(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_session_setup_gensec_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_session_setup_gensec_state);
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 
 | |
| 	if (state->remote_ready) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_sesssetup_blob_send(state, state->ev,
 | |
| 					 state->cli, state->blob_out);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_session_setup_gensec_remote_done,
 | |
| 				req);
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req =
 | |
| 		tevent_req_callback_data(subreq,
 | |
| 		struct tevent_req);
 | |
| 	struct cli_session_setup_gensec_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_session_setup_gensec_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	state->inbuf = NULL;
 | |
| 	TALLOC_FREE(state->recv_iov);
 | |
| 
 | |
| 	status = cli_sesssetup_blob_recv(subreq, state, &state->blob_in,
 | |
| 					 &state->inbuf, &state->recv_iov);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	data_blob_free(&state->blob_out);
 | |
| 	if (!NT_STATUS_IS_OK(status) &&
 | |
| 	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
 | |
| 	{
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (NT_STATUS_IS_OK(status)) {
 | |
| 		struct smbXcli_session *session = NULL;
 | |
| 		bool is_guest = false;
 | |
| 
 | |
| 		if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 			session = state->cli->smb2.session;
 | |
| 		} else {
 | |
| 			session = state->cli->smb1.session;
 | |
| 		}
 | |
| 
 | |
| 		is_guest = smbXcli_session_is_guest(session);
 | |
| 		if (is_guest) {
 | |
| 			/*
 | |
| 			 * We can't finish the gensec handshake, we don't
 | |
| 			 * have a negotiated session key.
 | |
| 			 *
 | |
| 			 * So just pretend we are completely done,
 | |
| 			 * we need to continue as anonymous from this point,
 | |
| 			 * as we can't get a session key.
 | |
| 			 *
 | |
| 			 * Note that smbXcli_session_is_guest()
 | |
| 			 * always returns false if we require signing.
 | |
| 			 */
 | |
| 			state->blob_in = data_blob_null;
 | |
| 			state->local_ready = true;
 | |
| 			state->is_anonymous = true;
 | |
| 		}
 | |
| 
 | |
| 		state->remote_ready = true;
 | |
| 	}
 | |
| 
 | |
| 	if (state->local_ready && state->remote_ready) {
 | |
| 		cli_session_setup_gensec_ready(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cli_session_setup_gensec_local_next(req);
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_gensec_ready(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_session_setup_gensec_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_session_setup_gensec_state);
 | |
| 	const char *server_domain = NULL;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (state->blob_in.length != 0) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (state->blob_out.length != 0) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * gensec_ntlmssp_server_domain() returns NULL
 | |
| 	 * if NTLMSSP is not used.
 | |
| 	 *
 | |
| 	 * We can remove this later
 | |
| 	 * and leave the server domain empty for SMB2 and above
 | |
| 	 * in future releases.
 | |
| 	 */
 | |
| 	server_domain = gensec_ntlmssp_server_domain(
 | |
| 				state->auth_generic->gensec_security);
 | |
| 
 | |
| 	if (state->cli->server_domain[0] == '\0' && server_domain != NULL) {
 | |
| 		TALLOC_FREE(state->cli->server_domain);
 | |
| 		state->cli->server_domain = talloc_strdup(state->cli,
 | |
| 					server_domain);
 | |
| 		if (state->cli->server_domain == NULL) {
 | |
| 			tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (state->is_anonymous) {
 | |
| 		/*
 | |
| 		 * Windows server does not set the
 | |
| 		 * SMB2_SESSION_FLAG_IS_NULL flag.
 | |
| 		 *
 | |
| 		 * This fix makes sure we do not try
 | |
| 		 * to verify a signature on the final
 | |
| 		 * session setup response.
 | |
| 		 */
 | |
| 		tevent_req_done(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	status = gensec_session_key(state->auth_generic->gensec_security,
 | |
| 				    state, &state->session_key);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		struct smbXcli_session *session = state->cli->smb2.session;
 | |
| 
 | |
| 		status = smb2cli_session_set_session_key(session,
 | |
| 							 state->session_key,
 | |
| 							 state->recv_iov);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return;
 | |
| 		}
 | |
| 		if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB3_00
 | |
| 		    && lp_debug_encryption())
 | |
| 		{
 | |
| 			DATA_BLOB sig, app, enc, dec;
 | |
| 			const char *wireshark_keyfile = lp_parm_const_string(
 | |
| 				GLOBAL_SECTION_SNUM,
 | |
| 				"debug encryption",
 | |
| 				"wireshark keyfile",
 | |
| 				NULL);
 | |
| 
 | |
| 			status = smb2cli_session_signing_key(session, state, &sig);
 | |
| 			if (tevent_req_nterror(req, status)) {
 | |
| 				return;
 | |
| 			}
 | |
| 			status = smbXcli_session_application_key(session, state, &app);
 | |
| 			if (tevent_req_nterror(req, status)) {
 | |
| 				return;
 | |
| 			}
 | |
| 			status = smb2cli_session_encryption_key(session, state, &enc);
 | |
| 			if (tevent_req_nterror(req, status)) {
 | |
| 				return;
 | |
| 			}
 | |
| 			status = smb2cli_session_decryption_key(session, state, &dec);
 | |
| 			if (tevent_req_nterror(req, status)) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			smbXcli_session_dump_keys(smb2cli_session_current_id(session),
 | |
| 						  &state->session_key,
 | |
| 						  smb2cli_conn_server_signing_algo(state->cli->conn),
 | |
| 						  &sig,
 | |
| 						  &app,
 | |
| 						  &enc,
 | |
| 						  &dec,
 | |
| 						  wireshark_keyfile);
 | |
| 		}
 | |
| 	} else {
 | |
| 		struct smbXcli_session *session = state->cli->smb1.session;
 | |
| 		bool active;
 | |
| 
 | |
| 		status = smb1cli_session_set_session_key(session,
 | |
| 							 state->session_key);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		active = smb1cli_conn_activate_signing(state->cli->conn,
 | |
| 						       state->session_key,
 | |
| 						       data_blob_null);
 | |
| 		if (active) {
 | |
| 			bool ok;
 | |
| 
 | |
| 			ok = smb1cli_conn_check_signing(state->cli->conn,
 | |
| 							state->inbuf, 1);
 | |
| 			if (!ok) {
 | |
| 				tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 | |
| 				return;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_session_setup_gensec_recv(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_session_setup_gensec_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_session_setup_gensec_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		cli_state_set_uid(state->cli, UID_FIELD_INVALID);
 | |
| 		return status;
 | |
| 	}
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
 | |
| 					   const char *principal)
 | |
| {
 | |
| 	char *account, *p;
 | |
| 
 | |
| 	account = talloc_strdup(mem_ctx, principal);
 | |
| 	if (account == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	p = strchr_m(account, '@');
 | |
| 	if (p != NULL) {
 | |
| 		*p = '\0';
 | |
| 	}
 | |
| 	return account;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  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.
 | |
| ****************************************************************************/
 | |
| 
 | |
| struct cli_session_setup_spnego_state {
 | |
| 	ADS_STATUS result;
 | |
| };
 | |
| 
 | |
| static void cli_session_setup_spnego_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_session_setup_spnego_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
 | |
| 	struct cli_credentials *creds)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_session_setup_spnego_state *state;
 | |
| 	const char *target_service = NULL;
 | |
| 	const char *target_hostname = NULL;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_session_setup_spnego_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	target_service = "cifs";
 | |
| 	target_hostname = smbXcli_conn_remote_name(cli->conn);
 | |
| 
 | |
| 	DBG_INFO("Connect to %s as %s using SPNEGO\n",
 | |
| 		 target_hostname,
 | |
| 		 cli_credentials_get_principal(creds, talloc_tos()));
 | |
| 
 | |
| 	subreq = cli_session_setup_gensec_send(state, ev, cli, creds,
 | |
| 					       target_service, target_hostname);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(
 | |
| 		subreq, cli_session_setup_spnego_done, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_spnego_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_session_setup_gensec_recv(subreq);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static ADS_STATUS cli_session_setup_spnego_recv(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_session_setup_spnego_state *state = tevent_req_data(
 | |
| 		req, struct cli_session_setup_spnego_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		state->result = ADS_ERROR_NT(status);
 | |
| 	}
 | |
| 
 | |
| 	return state->result;
 | |
| }
 | |
| 
 | |
| struct cli_session_setup_creds_state {
 | |
| 	struct cli_state *cli;
 | |
| 	DATA_BLOB apassword_blob;
 | |
| 	DATA_BLOB upassword_blob;
 | |
| 	DATA_BLOB lm_session_key;
 | |
| 	DATA_BLOB session_key;
 | |
| 	char *out_native_os;
 | |
| 	char *out_native_lm;
 | |
| 	char *out_primary_domain;
 | |
| };
 | |
| 
 | |
| static void cli_session_setup_creds_cleanup(struct tevent_req *req,
 | |
| 					    enum tevent_req_state req_state)
 | |
| {
 | |
| 	struct cli_session_setup_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_session_setup_creds_state);
 | |
| 
 | |
| 	if (req_state != TEVENT_REQ_RECEIVED) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * We only call data_blob_clear() as
 | |
| 	 * some of the blobs point to the same memory.
 | |
| 	 *
 | |
| 	 * We let the talloc hierarchy free the memory.
 | |
| 	 */
 | |
| 	data_blob_clear(&state->apassword_blob);
 | |
| 	data_blob_clear(&state->upassword_blob);
 | |
| 	data_blob_clear(&state->lm_session_key);
 | |
| 	data_blob_clear(&state->session_key);
 | |
| 	ZERO_STRUCTP(state);
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq);
 | |
| static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq);
 | |
| static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq);
 | |
| 
 | |
| /****************************************************************************
 | |
|  Send a session setup. The username and workgroup is in UNIX character
 | |
|  format and must be converted to DOS codepage format before sending. If the
 | |
|  password is in plaintext, the same should be done.
 | |
| ****************************************************************************/
 | |
| 
 | |
| struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
 | |
| 					struct tevent_context *ev,
 | |
| 					struct cli_state *cli,
 | |
| 					struct cli_credentials *creds)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_session_setup_creds_state *state;
 | |
| 	uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
 | |
| 	bool use_spnego = false;
 | |
| 	int flags = 0;
 | |
| 	const char *username = "";
 | |
| 	const char *domain = "";
 | |
| 	DATA_BLOB target_info = data_blob_null;
 | |
| 	DATA_BLOB challenge = data_blob_null;
 | |
| 	uint16_t in_buf_size = 0;
 | |
| 	uint16_t in_mpx_max = 0;
 | |
| 	uint16_t in_vc_num = 0;
 | |
| 	uint32_t in_sess_key = 0;
 | |
| 	const char *in_native_os = NULL;
 | |
| 	const char *in_native_lm = NULL;
 | |
| 	enum credentials_use_kerberos krb5_state =
 | |
| 		cli_credentials_get_kerberos_state(creds);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_session_setup_creds_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->cli = cli;
 | |
| 
 | |
| 	tevent_req_set_cleanup_fn(req, cli_session_setup_creds_cleanup);
 | |
| 
 | |
| 	/*
 | |
| 	 * Now work out what sort of session setup we are going to
 | |
| 	 * do. I have split this into separate functions to make the flow a bit
 | |
| 	 * easier to understand (tridge).
 | |
| 	 */
 | |
| 	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) {
 | |
| 		use_spnego = false;
 | |
| 	} else if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		use_spnego = true;
 | |
| 	} else if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) {
 | |
| 		/*
 | |
| 		 * if the server supports extended security then use SPNEGO
 | |
| 		 * even for anonymous connections.
 | |
| 		 */
 | |
| 		use_spnego = true;
 | |
| 	} else {
 | |
| 		use_spnego = false;
 | |
| 	}
 | |
| 
 | |
| 	if (use_spnego) {
 | |
| 		subreq = cli_session_setup_spnego_send(
 | |
| 			state, ev, cli, creds);
 | |
| 		if (tevent_req_nomem(subreq, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 		tevent_req_set_callback(subreq, cli_session_setup_creds_done_spnego,
 | |
| 					req);
 | |
| 		return req;
 | |
| 	}
 | |
| 
 | |
| 	if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
 | |
| 		DBG_WARNING("Kerberos authentication requested, but "
 | |
| 			    "the server does not support SPNEGO authentication\n");
 | |
| 		tevent_req_nterror(req, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
 | |
| 		/*
 | |
| 		 * SessionSetupAndX was introduced by LANMAN 1.0. So we skip
 | |
| 		 * this step against older servers.
 | |
| 		 */
 | |
| 		tevent_req_done(req);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	if (cli_credentials_is_anonymous(creds)) {
 | |
| 		/*
 | |
| 		 * Do an anonymous session setup
 | |
| 		 */
 | |
| 		goto non_spnego_creds_done;
 | |
| 	}
 | |
| 
 | |
| 	if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) {
 | |
| 		/*
 | |
| 		 * Do an anonymous session setup,
 | |
| 		 * the password is passed via the tree connect.
 | |
| 		 */
 | |
| 		goto non_spnego_creds_done;
 | |
| 	}
 | |
| 
 | |
| 	cli_credentials_get_ntlm_username_domain(creds, state,
 | |
| 						 &username,
 | |
| 						 &domain);
 | |
| 	if (tevent_req_nomem(username, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	if (tevent_req_nomem(domain, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	DBG_INFO("Connect to %s as %s using NTLM\n", domain, username);
 | |
| 
 | |
| 	if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
 | |
| 		bool use_unicode = smbXcli_conn_use_unicode(cli->conn);
 | |
| 		uint8_t *bytes = NULL;
 | |
| 		size_t bytes_len = 0;
 | |
| 		const char *pw = cli_credentials_get_password(creds);
 | |
| 		size_t pw_len = 0;
 | |
| 
 | |
| 		if (pw == NULL) {
 | |
| 			pw = "";
 | |
| 		}
 | |
| 		pw_len = strlen(pw) + 1;
 | |
| 
 | |
| 		if (!lp_client_plaintext_auth()) {
 | |
| 			DEBUG(1, ("Server requested PLAINTEXT password but "
 | |
| 				  "'client plaintext auth = no'\n"));
 | |
| 			tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 
 | |
| 		bytes = talloc_array(state, uint8_t, 0);
 | |
| 		bytes = trans2_bytes_push_str(bytes, use_unicode,
 | |
| 					      pw, pw_len, &bytes_len);
 | |
| 		if (tevent_req_nomem(bytes, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 
 | |
| 		if (use_unicode) {
 | |
| 			/*
 | |
| 			 * CAP_UNICODE, can only be negotiated by NT1.
 | |
| 			 */
 | |
| 			state->upassword_blob = data_blob_const(bytes,
 | |
| 								bytes_len);
 | |
| 		} else {
 | |
| 			state->apassword_blob = data_blob_const(bytes,
 | |
| 								bytes_len);
 | |
| 		}
 | |
| 
 | |
| 		goto non_spnego_creds_done;
 | |
| 	}
 | |
| 
 | |
| 	challenge = data_blob_const(smb1cli_conn_server_challenge(cli->conn), 8);
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
 | |
| 		if (lp_client_ntlmv2_auth() && lp_client_use_spnego()) {
 | |
| 			/*
 | |
| 			 * Don't send an NTLMv2 response without NTLMSSP if we
 | |
| 			 * want to use spnego support.
 | |
| 			 */
 | |
| 			DEBUG(1, ("Server does not support EXTENDED_SECURITY "
 | |
| 				  " but 'client use spnego = yes'"
 | |
| 				  " and 'client ntlmv2 auth = yes' is set\n"));
 | |
| 			tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 
 | |
| 		if (lp_client_ntlmv2_auth()) {
 | |
| 			flags |= CLI_CRED_NTLMv2_AUTH;
 | |
| 
 | |
| 			/*
 | |
| 			 * note that the 'domain' here is a best
 | |
| 			 * guess - we don't know the server's domain
 | |
| 			 * at this point. Windows clients also don't
 | |
| 			 * use hostname...
 | |
| 			 */
 | |
| 			target_info = NTLMv2_generate_names_blob(state,
 | |
| 								 NULL,
 | |
| 								 domain);
 | |
| 			if (tevent_req_nomem(target_info.data, req)) {
 | |
| 				return tevent_req_post(req, ev);
 | |
| 			}
 | |
| 		} else {
 | |
| 			flags |= CLI_CRED_NTLM_AUTH;
 | |
| 			if (lp_client_lanman_auth()) {
 | |
| 				flags |= CLI_CRED_LANMAN_AUTH;
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (!lp_client_lanman_auth()) {
 | |
| 			DEBUG(1, ("Server requested user level LM password but "
 | |
| 				  "'client lanman auth = no' is set.\n"));
 | |
| 			tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 
 | |
| 		flags |= CLI_CRED_LANMAN_AUTH;
 | |
| 	}
 | |
| 
 | |
| 	status = cli_credentials_get_ntlm_response(creds, state, &flags,
 | |
| 						   challenge, NULL,
 | |
| 						   target_info,
 | |
| 						   &state->apassword_blob,
 | |
| 						   &state->upassword_blob,
 | |
| 						   &state->lm_session_key,
 | |
| 						   &state->session_key);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| non_spnego_creds_done:
 | |
| 
 | |
| 	in_buf_size = CLI_BUFFER_SIZE;
 | |
| 	in_mpx_max = smbXcli_conn_max_requests(cli->conn);
 | |
| 	in_vc_num = cli_state_get_vc_num(cli);
 | |
| 	in_sess_key = smb1cli_conn_server_session_key(cli->conn);
 | |
| 	in_native_os = "Unix";
 | |
| 	in_native_lm = "Samba";
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
 | |
| 		uint32_t in_capabilities = 0;
 | |
| 
 | |
| 		in_capabilities = cli_session_setup_capabilities(cli, 0);
 | |
| 
 | |
| 		/*
 | |
| 		 * For now we keep the same values as before,
 | |
| 		 * we may remove these in a separate commit later.
 | |
| 		 */
 | |
| 		in_mpx_max = 2;
 | |
| 
 | |
| 		subreq = smb1cli_session_setup_nt1_send(state, ev,
 | |
| 							cli->conn,
 | |
| 							cli->timeout,
 | |
| 							cli->smb1.pid,
 | |
| 							cli->smb1.session,
 | |
| 							in_buf_size,
 | |
| 							in_mpx_max,
 | |
| 							in_vc_num,
 | |
| 							in_sess_key,
 | |
| 							username,
 | |
| 							domain,
 | |
| 							state->apassword_blob,
 | |
| 							state->upassword_blob,
 | |
| 							in_capabilities,
 | |
| 							in_native_os,
 | |
| 							in_native_lm);
 | |
| 		if (tevent_req_nomem(subreq, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 		tevent_req_set_callback(subreq, cli_session_setup_creds_done_nt1,
 | |
| 					req);
 | |
| 		return req;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * For now we keep the same values as before,
 | |
| 	 * we may remove these in a separate commit later.
 | |
| 	 */
 | |
| 	in_mpx_max = 2;
 | |
| 	in_vc_num = 1;
 | |
| 
 | |
| 	subreq = smb1cli_session_setup_lm21_send(state, ev,
 | |
| 						 cli->conn,
 | |
| 						 cli->timeout,
 | |
| 						 cli->smb1.pid,
 | |
| 						 cli->smb1.session,
 | |
| 						 in_buf_size,
 | |
| 						 in_mpx_max,
 | |
| 						 in_vc_num,
 | |
| 						 in_sess_key,
 | |
| 						 username,
 | |
| 						 domain,
 | |
| 						 state->apassword_blob,
 | |
| 						 in_native_os,
 | |
| 						 in_native_lm);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_session_setup_creds_done_lm21,
 | |
| 				req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	ADS_STATUS status;
 | |
| 
 | |
| 	status = cli_session_setup_spnego_recv(subreq);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (!ADS_ERR_OK(status)) {
 | |
| 		DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
 | |
| 		tevent_req_nterror(req, ads_ntstatus(status));
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_session_setup_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_session_setup_creds_state);
 | |
| 	struct cli_state *cli = state->cli;
 | |
| 	NTSTATUS status;
 | |
| 	struct iovec *recv_iov = NULL;
 | |
| 	const uint8_t *inbuf = NULL;
 | |
| 	bool ok;
 | |
| 
 | |
| 	status = smb1cli_session_setup_nt1_recv(subreq, state,
 | |
| 						&recv_iov,
 | |
| 						&inbuf,
 | |
| 						&state->out_native_os,
 | |
| 						&state->out_native_lm,
 | |
| 						&state->out_primary_domain);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		DEBUG(3, ("NT1 login failed: %s\n", nt_errstr(status)));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	status = cli_state_update_after_sesssetup(state->cli,
 | |
| 						  state->out_native_os,
 | |
| 						  state->out_native_lm,
 | |
| 						  state->out_primary_domain);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ok = smb1cli_conn_activate_signing(cli->conn,
 | |
| 					   state->session_key,
 | |
| 					   state->upassword_blob);
 | |
| 	if (ok) {
 | |
| 		ok = smb1cli_conn_check_signing(cli->conn, inbuf, 1);
 | |
| 		if (!ok) {
 | |
| 			tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (state->session_key.data) {
 | |
| 		struct smbXcli_session *session = cli->smb1.session;
 | |
| 
 | |
| 		status = smb1cli_session_set_session_key(session,
 | |
| 							 state->session_key);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_session_setup_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_session_setup_creds_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = smb1cli_session_setup_lm21_recv(subreq, state,
 | |
| 						 &state->out_native_os,
 | |
| 						 &state->out_native_lm);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		DEBUG(3, ("LM21 login failed: %s\n", nt_errstr(status)));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	status = cli_state_update_after_sesssetup(state->cli,
 | |
| 						  state->out_native_os,
 | |
| 						  state->out_native_lm,
 | |
| 						  NULL);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_session_setup_creds_recv(struct tevent_req *req)
 | |
| {
 | |
| 	return tevent_req_simple_recv_ntstatus(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_session_setup_creds(struct cli_state *cli,
 | |
| 				 struct cli_credentials *creds)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	if (smbXcli_conn_has_async_calls(cli->conn)) {
 | |
| 		return NT_STATUS_INVALID_PARAMETER;
 | |
| 	}
 | |
| 	ev = samba_tevent_context_init(talloc_tos());
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_session_setup_creds_send(ev, ev, cli, creds);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_session_setup_creds_recv(req);
 | |
|  fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_session_setup_anon(struct cli_state *cli)
 | |
| {
 | |
| 	NTSTATUS status;
 | |
| 	struct cli_credentials *creds = NULL;
 | |
| 
 | |
| 	creds = cli_credentials_init_anon(cli);
 | |
| 	if (creds == NULL) {
 | |
| 		return NT_STATUS_NO_MEMORY;
 | |
| 	}
 | |
| 
 | |
| 	status = cli_session_setup_creds(cli, creds);
 | |
| 	TALLOC_FREE(creds);
 | |
| 	if (!NT_STATUS_IS_OK(status)) {
 | |
| 		return status;
 | |
| 	}
 | |
| 
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Send a uloggoff.
 | |
| *****************************************************************************/
 | |
| 
 | |
| struct cli_ulogoff_state {
 | |
| 	struct cli_state *cli;
 | |
| 	uint16_t vwv[3];
 | |
| };
 | |
| 
 | |
| static void cli_ulogoff_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx,
 | |
| 				    struct tevent_context *ev,
 | |
| 				    struct cli_state *cli)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_ulogoff_state *state;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state, struct cli_ulogoff_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->cli = cli;
 | |
| 
 | |
| 	SCVAL(state->vwv+0, 0, 0xFF);
 | |
| 	SCVAL(state->vwv+1, 0, 0);
 | |
| 	SSVAL(state->vwv+2, 0, 0);
 | |
| 
 | |
| 	subreq = cli_smb_send(state, ev, cli, SMBulogoffX, 0, 0, 2, state->vwv,
 | |
| 			      0, NULL);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_ulogoff_done, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_ulogoff_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_ulogoff_state *state = tevent_req_data(
 | |
| 		req, struct cli_ulogoff_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	cli_state_set_uid(state->cli, UID_FIELD_INVALID);
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_ulogoff_recv(struct tevent_req *req)
 | |
| {
 | |
| 	return tevent_req_simple_recv_ntstatus(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_ulogoff(struct cli_state *cli)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		status = smb2cli_logoff(cli->conn,
 | |
| 					cli->timeout,
 | |
| 					cli->smb2.session);
 | |
| 		if (!NT_STATUS_IS_OK(status)) {
 | |
| 			return status;
 | |
| 		}
 | |
| 		smb2cli_session_set_id_and_flags(cli->smb2.session,
 | |
| 						 UINT64_MAX, 0);
 | |
| 		return NT_STATUS_OK;
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_has_async_calls(cli->conn)) {
 | |
| 		return NT_STATUS_INVALID_PARAMETER;
 | |
| 	}
 | |
| 	ev = samba_tevent_context_init(talloc_tos());
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_ulogoff_send(ev, ev, cli);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_ulogoff_recv(req);
 | |
| fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Send a tconX.
 | |
| ****************************************************************************/
 | |
| 
 | |
| struct cli_tcon_andx_state {
 | |
| 	struct cli_state *cli;
 | |
| 	uint16_t vwv[4];
 | |
| 	struct iovec bytes;
 | |
| };
 | |
| 
 | |
| static void cli_tcon_andx_done(struct tevent_req *subreq);
 | |
| 
 | |
| struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
 | |
| 					struct tevent_context *ev,
 | |
| 					struct cli_state *cli,
 | |
| 					const char *share, const char *dev,
 | |
| 					const char *pass, int passlen,
 | |
| 					struct tevent_req **psmbreq)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_tcon_andx_state *state;
 | |
| 	uint8_t p24[24];
 | |
| 	uint16_t *vwv;
 | |
| 	char *tmp = NULL;
 | |
| 	uint8_t *bytes;
 | |
| 	uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
 | |
| 	uint16_t tcon_flags = 0;
 | |
| 
 | |
| 	*psmbreq = NULL;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state, struct cli_tcon_andx_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->cli = cli;
 | |
| 	vwv = state->vwv;
 | |
| 
 | |
| 	TALLOC_FREE(cli->smb1.tcon);
 | |
| 	cli->smb1.tcon = smbXcli_tcon_create(cli);
 | |
| 	if (tevent_req_nomem(cli->smb1.tcon, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
 | |
| 
 | |
| 	cli->share = talloc_strdup(cli, share);
 | |
| 	if (!cli->share) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* in user level security don't send a password now */
 | |
| 	if (sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
 | |
| 		passlen = 1;
 | |
| 		pass = "";
 | |
| 	} else if (pass == NULL) {
 | |
| 		DEBUG(1, ("Server not using user level security and no "
 | |
| 			  "password supplied.\n"));
 | |
| 		goto access_denied;
 | |
| 	}
 | |
| 
 | |
| 	if ((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 lanman auth = no' or 'client ntlmv2 auth = yes'\n"));
 | |
| 			goto access_denied;
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * Non-encrypted passwords - convert to DOS codepage before
 | |
| 		 * encryption.
 | |
| 		 */
 | |
| 		SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn), p24);
 | |
| 		passlen = 24;
 | |
| 		pass = (const char *)p24;
 | |
| 	} else {
 | |
| 		if((sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL
 | |
| 				     |NEGOTIATE_SECURITY_CHALLENGE_RESPONSE))
 | |
| 		   == 0) {
 | |
| 			uint8_t *tmp_pass;
 | |
| 
 | |
| 			if (!lp_client_plaintext_auth() && (*pass)) {
 | |
| 				DEBUG(1, ("Server requested PLAINTEXT "
 | |
| 					  "password but "
 | |
| 					  "'client plaintext auth = no' or 'client ntlmv2 auth = yes'\n"));
 | |
| 				goto access_denied;
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * Non-encrypted passwords - convert to DOS codepage
 | |
| 			 * before using.
 | |
| 			 */
 | |
| 			tmp_pass = talloc_array(talloc_tos(), uint8_t, 0);
 | |
| 			if (tevent_req_nomem(tmp_pass, req)) {
 | |
| 				return tevent_req_post(req, ev);
 | |
| 			}
 | |
| 			tmp_pass = trans2_bytes_push_str(tmp_pass,
 | |
| 							 false, /* always DOS */
 | |
| 							 pass,
 | |
| 							 passlen,
 | |
| 							 NULL);
 | |
| 			if (tevent_req_nomem(tmp_pass, req)) {
 | |
| 				return tevent_req_post(req, ev);
 | |
| 			}
 | |
| 			pass = (const char *)tmp_pass;
 | |
| 			passlen = talloc_get_size(tmp_pass);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	tcon_flags |= TCONX_FLAG_EXTENDED_RESPONSE;
 | |
| 	tcon_flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
 | |
| 
 | |
| 	SCVAL(vwv+0, 0, 0xFF);
 | |
| 	SCVAL(vwv+0, 1, 0);
 | |
| 	SSVAL(vwv+1, 0, 0);
 | |
| 	SSVAL(vwv+2, 0, tcon_flags);
 | |
| 	SSVAL(vwv+3, 0, passlen);
 | |
| 
 | |
| 	if (passlen && pass) {
 | |
| 		bytes = (uint8_t *)talloc_memdup(state, pass, passlen);
 | |
| 	} else {
 | |
| 		bytes = talloc_array(state, uint8_t, 0);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Add the sharename
 | |
| 	 */
 | |
| 	tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s",
 | |
| 					 smbXcli_conn_remote_name(cli->conn), share);
 | |
| 	if (tmp == NULL) {
 | |
| 		TALLOC_FREE(req);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
 | |
| 				   NULL);
 | |
| 	TALLOC_FREE(tmp);
 | |
| 
 | |
| 	/*
 | |
| 	 * Add the devicetype
 | |
| 	 */
 | |
| 	tmp = talloc_strdup_upper(talloc_tos(), dev);
 | |
| 	if (tmp == NULL) {
 | |
| 		TALLOC_FREE(req);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	bytes = smb_bytes_push_str(bytes, false, tmp, strlen(tmp)+1, NULL);
 | |
| 	TALLOC_FREE(tmp);
 | |
| 
 | |
| 	if (bytes == NULL) {
 | |
| 		TALLOC_FREE(req);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	state->bytes.iov_base = (void *)bytes;
 | |
| 	state->bytes.iov_len = talloc_get_size(bytes);
 | |
| 
 | |
| 	subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 0, 4, vwv,
 | |
| 				    1, &state->bytes);
 | |
| 	if (subreq == NULL) {
 | |
| 		TALLOC_FREE(req);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_tcon_andx_done, req);
 | |
| 	*psmbreq = subreq;
 | |
| 	return req;
 | |
| 
 | |
|  access_denied:
 | |
| 	tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 | |
| 	return tevent_req_post(req, ev);
 | |
| }
 | |
| 
 | |
| struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
 | |
| 				      struct tevent_context *ev,
 | |
| 				      struct cli_state *cli,
 | |
| 				      const char *share, const char *dev,
 | |
| 				      const char *pass, int passlen)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	req = cli_tcon_andx_create(mem_ctx, ev, cli, share, dev, pass, passlen,
 | |
| 				   &subreq);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	if (subreq == NULL) {
 | |
| 		return req;
 | |
| 	}
 | |
| 	status = smb1cli_req_chain_submit(&subreq, 1);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_tcon_andx_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_tcon_andx_state *state = tevent_req_data(
 | |
| 		req, struct cli_tcon_andx_state);
 | |
| 	struct cli_state *cli = state->cli;
 | |
| 	uint8_t *in;
 | |
| 	uint8_t *inhdr;
 | |
| 	uint8_t wct;
 | |
| 	uint16_t *vwv;
 | |
| 	uint32_t num_bytes;
 | |
| 	uint8_t *bytes;
 | |
| 	NTSTATUS status;
 | |
| 	uint16_t optional_support = 0;
 | |
| 
 | |
| 	status = cli_smb_recv(subreq, state, &in, 0, &wct, &vwv,
 | |
| 			      &num_bytes, &bytes);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	inhdr = in + NBT_HDR_SIZE;
 | |
| 
 | |
| 	if (num_bytes) {
 | |
| 		if (pull_string_talloc(cli,
 | |
| 				       (const char *)inhdr,
 | |
| 				       SVAL(inhdr, HDR_FLG2),
 | |
| 				       &cli->dev,
 | |
| 				       bytes,
 | |
| 				       num_bytes,
 | |
| 				       STR_TERMINATE|STR_ASCII) == -1) {
 | |
| 			tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
 | |
| 			return;
 | |
| 		}
 | |
| 	} else {
 | |
| 		cli->dev = talloc_strdup(cli, "");
 | |
| 		if (cli->dev == NULL) {
 | |
| 			tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if ((smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) && (num_bytes == 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
 | |
| 	 */
 | |
| 
 | |
| 	if ((wct > 2) && (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN2)) {
 | |
| 		optional_support = SVAL(vwv+2, 0);
 | |
| 	}
 | |
| 
 | |
| 	if (optional_support & SMB_EXTENDED_SIGNATURES) {
 | |
| 		smb1cli_session_protect_session_key(cli->smb1.session);
 | |
| 	}
 | |
| 
 | |
| 	smb1cli_tcon_set_values(cli->smb1.tcon,
 | |
| 				SVAL(inhdr, HDR_TID),
 | |
| 				optional_support,
 | |
| 				0, /* maximal_access */
 | |
| 				0, /* guest_maximal_access */
 | |
| 				NULL, /* service */
 | |
| 				NULL); /* fs_type */
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_tcon_andx_recv(struct tevent_req *req)
 | |
| {
 | |
| 	return tevent_req_simple_recv_ntstatus(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
 | |
| 		       const char *dev, const char *pass, int passlen)
 | |
| {
 | |
| 	TALLOC_CTX *frame = talloc_stackframe();
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	if (smbXcli_conn_has_async_calls(cli->conn)) {
 | |
| 		/*
 | |
| 		 * Can't use sync call while an async call is in flight
 | |
| 		 */
 | |
| 		status = NT_STATUS_INVALID_PARAMETER;
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	ev = samba_tevent_context_init(frame);
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	req = cli_tcon_andx_send(frame, ev, cli, share, dev, pass, passlen);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	status = cli_tcon_andx_recv(req);
 | |
|  fail:
 | |
| 	TALLOC_FREE(frame);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| struct cli_tree_connect_state {
 | |
| 	struct cli_state *cli;
 | |
| };
 | |
| 
 | |
| static struct tevent_req *cli_raw_tcon_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
 | |
| 	const char *service, const char *pass, const char *dev);
 | |
| static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
 | |
| 				  uint16_t *max_xmit, uint16_t *tid);
 | |
| 
 | |
| static void cli_tree_connect_smb2_done(struct tevent_req *subreq);
 | |
| static void cli_tree_connect_andx_done(struct tevent_req *subreq);
 | |
| static void cli_tree_connect_raw_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_tree_connect_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
 | |
| 	const char *share, const char *dev, const char *pass)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_tree_connect_state *state;
 | |
| 	int passlen;
 | |
| 
 | |
| 	if (pass == NULL) {
 | |
| 		pass = "";
 | |
| 	}
 | |
| 	passlen = strlen(pass) + 1;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_tree_connect_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->cli = cli;
 | |
| 
 | |
| 	cli->share = talloc_strdup(cli, share);
 | |
| 	if (tevent_req_nomem(cli->share, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		char *unc;
 | |
| 
 | |
| 		TALLOC_FREE(cli->smb2.tcon);
 | |
| 		cli->smb2.tcon = smbXcli_tcon_create(cli);
 | |
| 		if (tevent_req_nomem(cli->smb2.tcon, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 
 | |
| 		unc = talloc_asprintf(state, "\\\\%s\\%s",
 | |
| 				      smbXcli_conn_remote_name(cli->conn),
 | |
| 				      share);
 | |
| 		if (tevent_req_nomem(unc, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 
 | |
| 		subreq = smb2cli_tcon_send(state, ev, cli->conn, cli->timeout,
 | |
| 					   cli->smb2.session, cli->smb2.tcon,
 | |
| 					   0, /* flags */
 | |
| 					   unc);
 | |
| 		if (tevent_req_nomem(subreq, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 		tevent_req_set_callback(subreq, cli_tree_connect_smb2_done,
 | |
| 					req);
 | |
| 		return req;
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN1) {
 | |
| 		subreq = cli_tcon_andx_send(state, ev, cli, share, dev,
 | |
| 					    pass, passlen);
 | |
| 		if (tevent_req_nomem(subreq, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 		tevent_req_set_callback(subreq, cli_tree_connect_andx_done,
 | |
| 					req);
 | |
| 		return req;
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_raw_tcon_send(state, ev, cli, share, pass, dev);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_tree_connect_raw_done, req);
 | |
| 
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_tree_connect_smb2_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	NTSTATUS status = smb2cli_tcon_recv(subreq);
 | |
| 	tevent_req_simple_finish_ntstatus(subreq, status);
 | |
| }
 | |
| 
 | |
| static void cli_tree_connect_andx_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	NTSTATUS status = cli_tcon_andx_recv(subreq);
 | |
| 	tevent_req_simple_finish_ntstatus(subreq, status);
 | |
| }
 | |
| 
 | |
| static void cli_tree_connect_raw_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_tree_connect_state *state = tevent_req_data(
 | |
| 		req, struct cli_tree_connect_state);
 | |
| 	NTSTATUS status;
 | |
| 	uint16_t max_xmit = 0;
 | |
| 	uint16_t tid = 0;
 | |
| 
 | |
| 	status = cli_raw_tcon_recv(subreq, &max_xmit, &tid);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	smb1cli_tcon_set_values(state->cli->smb1.tcon,
 | |
| 				tid,
 | |
| 				0, /* optional_support */
 | |
| 				0, /* maximal_access */
 | |
| 				0, /* guest_maximal_access */
 | |
| 				NULL, /* service */
 | |
| 				NULL); /* fs_type */
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_tree_connect_recv(struct tevent_req *req)
 | |
| {
 | |
| 	return tevent_req_simple_recv_ntstatus(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
 | |
| 			  const char *dev, const char *pass)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	if (smbXcli_conn_has_async_calls(cli->conn)) {
 | |
| 		return NT_STATUS_INVALID_PARAMETER;
 | |
| 	}
 | |
| 	ev = samba_tevent_context_init(talloc_tos());
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_tree_connect_send(ev, ev, cli, share, dev, pass);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_tree_connect_recv(req);
 | |
| fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_tree_connect_creds(struct cli_state *cli,
 | |
| 				const char *share, const char *dev,
 | |
| 				struct cli_credentials *creds)
 | |
| {
 | |
| 	bool need_pass = false;
 | |
| 	const char *pw = NULL;
 | |
| 
 | |
| 	/*
 | |
| 	 * We should work out if the protocol
 | |
| 	 * will make use of a password for share level
 | |
| 	 * authentication before we may cause
 | |
| 	 * the password prompt to be called.
 | |
| 	 */
 | |
| 	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
 | |
| 		uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
 | |
| 
 | |
| 		/* in user level security don't send a password now */
 | |
| 		if (!(sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
 | |
| 			need_pass = true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (need_pass && creds != NULL) {
 | |
| 		pw = cli_credentials_get_password(creds);
 | |
| 	}
 | |
| 
 | |
| 	return cli_tree_connect(cli, share, dev, pw);
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Send a tree disconnect.
 | |
| ****************************************************************************/
 | |
| 
 | |
| struct cli_tdis_state {
 | |
| 	struct cli_state *cli;
 | |
| };
 | |
| 
 | |
| static void cli_tdis_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx,
 | |
| 				 struct tevent_context *ev,
 | |
| 				 struct cli_state *cli)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_tdis_state *state;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state, struct cli_tdis_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->cli = cli;
 | |
| 
 | |
| 	subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 0, 0, NULL, 0, NULL);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_tdis_done, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_tdis_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_tdis_state *state = tevent_req_data(
 | |
| 		req, struct cli_tdis_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	TALLOC_FREE(state->cli->smb1.tcon);
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_tdis_recv(struct tevent_req *req)
 | |
| {
 | |
| 	return tevent_req_simple_recv_ntstatus(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_tdis(struct cli_state *cli)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		status = smb2cli_tdis(cli->conn,
 | |
| 				    cli->timeout,
 | |
| 				    cli->smb2.session,
 | |
| 				    cli->smb2.tcon);
 | |
| 		if (NT_STATUS_IS_OK(status)) {
 | |
| 			TALLOC_FREE(cli->smb2.tcon);
 | |
| 		}
 | |
| 		return status;
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_has_async_calls(cli->conn)) {
 | |
| 		return NT_STATUS_INVALID_PARAMETER;
 | |
| 	}
 | |
| 	ev = samba_tevent_context_init(talloc_tos());
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_tdis_send(ev, ev, cli);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_tdis_recv(req);
 | |
| fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| struct cli_connect_sock_state {
 | |
| 	struct smb_transports transports;
 | |
| 	const char **called_names;
 | |
| 	const char **calling_names;
 | |
| 	int *called_types;
 | |
| 	struct smbXcli_transport *transport;
 | |
| };
 | |
| 
 | |
| static void cli_connect_sock_done(struct tevent_req *subreq);
 | |
| 
 | |
| /*
 | |
|  * Async only if we don't have to look up the name, i.e. "pss" is set with a
 | |
|  * nonzero address.
 | |
|  */
 | |
| 
 | |
| static struct tevent_req *cli_connect_sock_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 | |
| 	const char *host, int name_type, const struct sockaddr_storage *pss,
 | |
| 	const char *myname,
 | |
| 	const struct smb_transports *transports)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_connect_sock_state *state;
 | |
| 	struct loadparm_context *lp_ctx = NULL;
 | |
| 	struct sockaddr_storage *addrs = NULL;
 | |
| 	unsigned i;
 | |
| 	unsigned num_addrs = 0;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_connect_sock_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->transports = *transports;
 | |
| 
 | |
| 	lp_ctx = loadparm_init_s3(talloc_tos(), loadparm_s3_helpers());
 | |
| 	if (tevent_req_nomem(lp_ctx, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	if ((pss == NULL) || is_zero_addr(pss)) {
 | |
| 
 | |
| 		/*
 | |
| 		 * Here we cheat. resolve_name_list is not async at all. So
 | |
| 		 * this call will only be really async if the name lookup has
 | |
| 		 * been done externally.
 | |
| 		 */
 | |
| 
 | |
| 		status = resolve_name_list(state, host, name_type,
 | |
| 					   &addrs, &num_addrs);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	} else {
 | |
| 		addrs = talloc_array(state, struct sockaddr_storage, 1);
 | |
| 		if (tevent_req_nomem(addrs, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 		addrs[0] = *pss;
 | |
| 		num_addrs = 1;
 | |
| 	}
 | |
| 
 | |
| 	state->called_names = talloc_array(state, const char *, num_addrs);
 | |
| 	if (tevent_req_nomem(state->called_names, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	state->called_types = talloc_array(state, int, num_addrs);
 | |
| 	if (tevent_req_nomem(state->called_types, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	state->calling_names = talloc_array(state, const char *, num_addrs);
 | |
| 	if (tevent_req_nomem(state->calling_names, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	for (i=0; i<num_addrs; i++) {
 | |
| 		state->called_names[i] = host;
 | |
| 		state->called_types[i] = name_type;
 | |
| 		state->calling_names[i] = myname;
 | |
| 	}
 | |
| 
 | |
| 	subreq = smbsock_any_connect_send(
 | |
| 		state, ev, lp_ctx, addrs,
 | |
| 		state->called_names, state->called_types,
 | |
| 		state->calling_names, NULL, num_addrs, &state->transports);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_connect_sock_done, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_connect_sock_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_connect_sock_state *state = tevent_req_data(
 | |
| 		req, struct cli_connect_sock_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = smbsock_any_connect_recv(subreq, state, &state->transport, NULL);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_connect_sock_recv(struct tevent_req *req,
 | |
| 				      TALLOC_CTX *mem_ctx,
 | |
| 				      struct smbXcli_transport **ptransport)
 | |
| {
 | |
| 	struct cli_connect_sock_state *state = tevent_req_data(
 | |
| 		req, struct cli_connect_sock_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		return status;
 | |
| 	}
 | |
| 	*ptransport = talloc_move(mem_ctx, &state->transport);
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| struct cli_connect_nb_state {
 | |
| 	const char *desthost;
 | |
| 	struct smb_transports transports;
 | |
| 	enum smb_signing_setting signing_state;
 | |
| 	int flags;
 | |
| 	struct cli_state *cli;
 | |
| };
 | |
| 
 | |
| static void cli_connect_nb_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_connect_nb_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 | |
| 	const char *host, const struct sockaddr_storage *dest_ss,
 | |
| 	const struct smb_transports *transports,
 | |
| 	int name_type, const char *myname,
 | |
| 	enum smb_signing_setting signing_state, int flags)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_connect_nb_state *state;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state, struct cli_connect_nb_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->signing_state = signing_state;
 | |
| 	state->flags = flags;
 | |
| 	state->transports = *transports;
 | |
| 
 | |
| 	if (host != NULL) {
 | |
| 		char *p = strchr(host, '#');
 | |
| 
 | |
| 		if (p != NULL) {
 | |
| 			name_type = strtol(p+1, NULL, 16);
 | |
| 			host = talloc_strndup(state, host, p - host);
 | |
| 			if (tevent_req_nomem(host, req)) {
 | |
| 				return tevent_req_post(req, ev);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		state->desthost = host;
 | |
| 	} else if (dest_ss != NULL) {
 | |
| 		state->desthost = print_canonical_sockaddr(state, dest_ss);
 | |
| 		if (tevent_req_nomem(state->desthost, req)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* No host or dest_ss given. Error out. */
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_connect_sock_send(state, ev, host, name_type, dest_ss,
 | |
| 				       myname, &state->transports);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_connect_nb_done, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_connect_nb_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_connect_nb_state *state = tevent_req_data(
 | |
| 		req, struct cli_connect_nb_state);
 | |
| 	struct smbXcli_transport *xtp = NULL;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_connect_sock_recv(subreq, state, &xtp);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	state->cli = cli_state_create(state, &xtp, state->desthost,
 | |
| 				      state->signing_state, state->flags);
 | |
| 	if (tevent_req_nomem(state->cli, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
 | |
| 				    TALLOC_CTX *mem_ctx,
 | |
| 				    struct cli_state **pcli)
 | |
| {
 | |
| 	struct cli_connect_nb_state *state = tevent_req_data(
 | |
| 		req, struct cli_connect_nb_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		return status;
 | |
| 	}
 | |
| 	*pcli = talloc_move(mem_ctx, &state->cli);
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_connect_nb(TALLOC_CTX *mem_ctx,
 | |
| 			const char *host,
 | |
| 			const struct sockaddr_storage *dest_ss,
 | |
| 			const struct smb_transports *transports,
 | |
| 			int name_type,
 | |
| 			const char *myname,
 | |
| 			enum smb_signing_setting signing_state,
 | |
| 			int flags,
 | |
| 			struct cli_state **pcli)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	ev = samba_tevent_context_init(mem_ctx);
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_connect_nb_send(ev, ev, host, dest_ss, transports, name_type,
 | |
| 				  myname, signing_state, flags);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(20, 0))) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_connect_nb_recv(req, mem_ctx, pcli);
 | |
| fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| struct cli_start_connection_state {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct smb_transports transports;
 | |
| 	struct cli_state *cli;
 | |
| 	int min_protocol;
 | |
| 	int max_protocol;
 | |
| 	struct smb2_negotiate_contexts *negotiate_contexts;
 | |
| };
 | |
| 
 | |
| static void cli_start_connection_connected(struct tevent_req *subreq);
 | |
| static void cli_start_connection_done(struct tevent_req *subreq);
 | |
| 
 | |
| /**
 | |
|    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_ss (optional) The destination IP, NULL for name based lookup
 | |
|    @param port (optional) The destination port (0 for default)
 | |
| */
 | |
| 
 | |
| static struct tevent_req *cli_start_connection_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 | |
| 	const char *my_name, const char *dest_host,
 | |
| 	const struct sockaddr_storage *dest_ss,
 | |
| 	const struct smb_transports *transports,
 | |
| 	enum smb_signing_setting signing_state, int flags,
 | |
| 	struct smb2_negotiate_contexts *negotiate_contexts)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_start_connection_state *state;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_start_connection_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->ev = ev;
 | |
| 	state->transports = *transports;
 | |
| 
 | |
| 	if (flags & CLI_FULL_CONNECTION_IPC) {
 | |
| 		state->min_protocol = lp_client_ipc_min_protocol();
 | |
| 		state->max_protocol = lp_client_ipc_max_protocol();
 | |
| 	} else {
 | |
| 		state->min_protocol = lp_client_min_protocol();
 | |
| 		state->max_protocol = lp_client_max_protocol();
 | |
| 	}
 | |
| 
 | |
| 	if (flags & CLI_FULL_CONNECTION_FORCE_SMB1) {
 | |
| 		state->max_protocol = MIN(state->max_protocol,
 | |
| 					  PROTOCOL_NT1);
 | |
| 		state->min_protocol = MIN(state->min_protocol,
 | |
| 					  state->max_protocol);
 | |
| 	}
 | |
| 
 | |
| 	if (flags & CLI_FULL_CONNECTION_DISABLE_SMB1) {
 | |
| 		state->min_protocol = MAX(state->min_protocol,
 | |
| 					  PROTOCOL_SMB2_02);
 | |
| 		state->max_protocol = MAX(state->max_protocol,
 | |
| 					  state->min_protocol);
 | |
| 	}
 | |
| 
 | |
| 	state->negotiate_contexts = talloc_zero(
 | |
| 		state, struct smb2_negotiate_contexts);
 | |
| 	if (tevent_req_nomem(state->negotiate_contexts, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	if (flags & CLI_FULL_CONNECTION_REQUEST_POSIX) {
 | |
| 		NTSTATUS status;
 | |
| 
 | |
| 		status = smb2_negotiate_context_add(
 | |
| 			state->negotiate_contexts,
 | |
| 			state->negotiate_contexts,
 | |
| 			SMB2_POSIX_EXTENSIONS_AVAILABLE,
 | |
| 			(const uint8_t *)SMB2_CREATE_TAG_POSIX,
 | |
| 			strlen(SMB2_CREATE_TAG_POSIX));
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (negotiate_contexts != NULL) {
 | |
| 		uint16_t i;
 | |
| 
 | |
| 		for (i=0; i<negotiate_contexts->num_contexts; i++) {
 | |
| 			struct smb2_negotiate_context *ctx =
 | |
| 				&negotiate_contexts->contexts[i];
 | |
| 			NTSTATUS status;
 | |
| 
 | |
| 			status = smb2_negotiate_context_add(
 | |
| 				state->negotiate_contexts,
 | |
| 				state->negotiate_contexts,
 | |
| 				ctx->type,
 | |
| 				ctx->data.data,
 | |
| 				ctx->data.length);
 | |
| 			if (tevent_req_nterror(req, status)) {
 | |
| 				return tevent_req_post(req, ev);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss,
 | |
| 				     &state->transports,
 | |
| 				     0x20, my_name, signing_state, flags);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_start_connection_connected, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_start_connection_connected(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_start_connection_state *state = tevent_req_data(
 | |
| 		req, struct cli_start_connection_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_connect_nb_recv(subreq, state, &state->cli);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = smbXcli_negprot_send(
 | |
| 		state,
 | |
| 		state->ev,
 | |
| 		state->cli->conn,
 | |
| 		state->cli->timeout,
 | |
| 		state->min_protocol,
 | |
| 		state->max_protocol,
 | |
| 		WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK,
 | |
| 		state->negotiate_contexts);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_start_connection_done, req);
 | |
| }
 | |
| 
 | |
| static void cli_start_connection_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_start_connection_state *state = tevent_req_data(
 | |
| 		req, struct cli_start_connection_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = smbXcli_negprot_recv(subreq, NULL, NULL);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		/* Ensure we ask for some initial credits. */
 | |
| 		smb2cli_conn_set_max_credits(state->cli->conn,
 | |
| 					     DEFAULT_SMB2_MAX_CREDITS);
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
 | |
| 					  TALLOC_CTX *mem_ctx,
 | |
| 					  struct cli_state **output_cli)
 | |
| {
 | |
| 	struct cli_start_connection_state *state = tevent_req_data(
 | |
| 		req, struct cli_start_connection_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		return status;
 | |
| 	}
 | |
| 	*output_cli = talloc_move(mem_ctx, &state->cli);
 | |
| 
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_start_connection(TALLOC_CTX *mem_ctx,
 | |
| 			      struct cli_state **output_cli,
 | |
| 			      const char *my_name,
 | |
| 			      const char *dest_host,
 | |
| 			      const struct sockaddr_storage *dest_ss,
 | |
| 			      const struct smb_transports *transports,
 | |
| 			      enum smb_signing_setting signing_state, int flags)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	ev = samba_tevent_context_init(mem_ctx);
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
 | |
| 					transports, signing_state, flags, NULL);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_start_connection_recv(req, mem_ctx, output_cli);
 | |
| fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| struct cli_smb1_setup_encryption_blob_state {
 | |
| 	uint16_t setup[1];
 | |
| 	uint8_t param[4];
 | |
| 	NTSTATUS status;
 | |
| 	DATA_BLOB out;
 | |
| 	uint16_t enc_ctx_id;
 | |
| };
 | |
| 
 | |
| static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_smb1_setup_encryption_blob_send(TALLOC_CTX *mem_ctx,
 | |
| 							struct tevent_context *ev,
 | |
| 							struct cli_state *cli,
 | |
| 							const DATA_BLOB in)
 | |
| {
 | |
| 	struct tevent_req *req = NULL;
 | |
| 	struct cli_smb1_setup_encryption_blob_state *state = NULL;
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_smb1_setup_encryption_blob_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (in.length > CLI_BUFFER_SIZE) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	SSVAL(state->setup+0,  0, TRANSACT2_SETFSINFO);
 | |
| 	SSVAL(state->param, 0, 0);
 | |
| 	SSVAL(state->param, 2, SMB_REQUEST_TRANSPORT_ENCRYPTION);
 | |
| 
 | |
| 	subreq = smb1cli_trans_send(state, ev, cli->conn,
 | |
| 				    SMBtrans2,
 | |
| 				    0, 0, /* _flags */
 | |
| 				    0, 0, /* _flags2 */
 | |
| 				    cli->timeout,
 | |
| 				    cli->smb1.pid,
 | |
| 				    cli->smb1.tcon,
 | |
| 				    cli->smb1.session,
 | |
| 				    NULL, /* pipe_name */
 | |
| 				    0, /* fid */
 | |
| 				    0, /* function */
 | |
| 				    0, /* flags */
 | |
| 				    state->setup, 1, 0,
 | |
| 				    state->param, 4, 2,
 | |
| 				    in.data, in.length, CLI_BUFFER_SIZE);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_smb1_setup_encryption_blob_done,
 | |
| 				req);
 | |
| 
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req =
 | |
| 		tevent_req_callback_data(subreq,
 | |
| 				struct tevent_req);
 | |
| 	struct cli_smb1_setup_encryption_blob_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_smb1_setup_encryption_blob_state);
 | |
| 	uint8_t *rparam=NULL, *rdata=NULL;
 | |
| 	uint32_t num_rparam, num_rdata;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = smb1cli_trans_recv(subreq, state,
 | |
| 				    NULL, /* recv_flags */
 | |
| 				    NULL, 0, NULL, /* rsetup */
 | |
| 				    &rparam, 0, &num_rparam,
 | |
| 				    &rdata, 0, &num_rdata);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	state->status = status;
 | |
| 	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 | |
| 		status = NT_STATUS_OK;
 | |
| 	}
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (num_rparam == 2) {
 | |
| 		state->enc_ctx_id = SVAL(rparam, 0);
 | |
| 	}
 | |
| 	TALLOC_FREE(rparam);
 | |
| 
 | |
| 	state->out = data_blob_const(rdata, num_rdata);
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_smb1_setup_encryption_blob_recv(struct tevent_req *req,
 | |
| 						    TALLOC_CTX *mem_ctx,
 | |
| 						    DATA_BLOB *out,
 | |
| 						    uint16_t *enc_ctx_id)
 | |
| {
 | |
| 	struct cli_smb1_setup_encryption_blob_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_smb1_setup_encryption_blob_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		tevent_req_received(req);
 | |
| 		return status;
 | |
| 	}
 | |
| 
 | |
| 	status = state->status;
 | |
| 
 | |
| 	*out = state->out;
 | |
| 	talloc_steal(mem_ctx, out->data);
 | |
| 
 | |
| 	*enc_ctx_id = state->enc_ctx_id;
 | |
| 
 | |
| 	tevent_req_received(req);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| struct cli_smb1_setup_encryption_state {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct cli_state *cli;
 | |
| 	struct smb_trans_enc_state *es;
 | |
| 	DATA_BLOB blob_in;
 | |
| 	DATA_BLOB blob_out;
 | |
| 	bool local_ready;
 | |
| 	bool remote_ready;
 | |
| };
 | |
| 
 | |
| static void cli_smb1_setup_encryption_local_next(struct tevent_req *req);
 | |
| static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq);
 | |
| static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req);
 | |
| static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq);
 | |
| static void cli_smb1_setup_encryption_ready(struct tevent_req *req);
 | |
| 
 | |
| static struct tevent_req *cli_smb1_setup_encryption_send(TALLOC_CTX *mem_ctx,
 | |
| 						struct tevent_context *ev,
 | |
| 						struct cli_state *cli,
 | |
| 						struct cli_credentials *creds)
 | |
| {
 | |
| 	struct tevent_req *req = NULL;
 | |
| 	struct cli_smb1_setup_encryption_state *state = NULL;
 | |
| 	struct auth_generic_state *ags = NULL;
 | |
| 	const DATA_BLOB *b = NULL;
 | |
| 	bool auth_requested = false;
 | |
| 	const char *target_service = NULL;
 | |
| 	const char *target_hostname = NULL;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_smb1_setup_encryption_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	state->ev = ev;
 | |
| 	state->cli = cli;
 | |
| 
 | |
| 	auth_requested = cli_credentials_authentication_requested(creds);
 | |
| 	if (!auth_requested) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	target_service = "cifs";
 | |
| 	target_hostname = smbXcli_conn_remote_name(cli->conn);
 | |
| 
 | |
| 	state->es = talloc_zero(state, struct smb_trans_enc_state);
 | |
| 	if (tevent_req_nomem(state->es, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	status = auth_generic_client_prepare(state->es, &ags);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	gensec_want_feature(ags->gensec_security,
 | |
| 			    GENSEC_FEATURE_SIGN);
 | |
| 	gensec_want_feature(ags->gensec_security,
 | |
| 			    GENSEC_FEATURE_SEAL);
 | |
| 
 | |
| 	status = auth_generic_set_creds(ags, creds);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	if (target_service != NULL) {
 | |
| 		status = gensec_set_target_service(ags->gensec_security,
 | |
| 						   target_service);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (target_hostname != NULL) {
 | |
| 		status = gensec_set_target_hostname(ags->gensec_security,
 | |
| 						    target_hostname);
 | |
| 		if (tevent_req_nterror(req, status)) {
 | |
| 			return tevent_req_post(req, ev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	gensec_set_max_update_size(ags->gensec_security,
 | |
| 				   CLI_BUFFER_SIZE);
 | |
| 
 | |
| 	b = smbXcli_conn_server_gss_blob(state->cli->conn);
 | |
| 	if (b != NULL) {
 | |
| 		state->blob_in = *b;
 | |
| 	}
 | |
| 
 | |
| 	status = auth_generic_client_start(ags, GENSEC_OID_SPNEGO);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * We only need the gensec_security part from here.
 | |
| 	 */
 | |
| 	state->es->gensec_security = talloc_move(state->es,
 | |
| 						 &ags->gensec_security);
 | |
| 	TALLOC_FREE(ags);
 | |
| 
 | |
| 	cli_smb1_setup_encryption_local_next(req);
 | |
| 	if (!tevent_req_is_in_progress(req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_smb1_setup_encryption_local_next(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_smb1_setup_encryption_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_smb1_setup_encryption_state);
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 
 | |
| 	if (state->local_ready) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = gensec_update_send(state, state->ev,
 | |
| 			state->es->gensec_security,
 | |
| 			state->blob_in);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_smb1_setup_encryption_local_done, req);
 | |
| }
 | |
| 
 | |
| static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req =
 | |
| 		tevent_req_callback_data(subreq,
 | |
| 		struct tevent_req);
 | |
| 	struct cli_smb1_setup_encryption_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_smb1_setup_encryption_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = gensec_update_recv(subreq, state, &state->blob_out);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	state->blob_in = data_blob_null;
 | |
| 	if (!NT_STATUS_IS_OK(status) &&
 | |
| 	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
 | |
| 	{
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (NT_STATUS_IS_OK(status)) {
 | |
| 		state->local_ready = true;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * We always get NT_STATUS_OK from the server even if it is not ready.
 | |
| 	 * So guess the server is ready when we are ready and already sent
 | |
| 	 * our last blob to the server.
 | |
| 	 */
 | |
| 	if (state->local_ready && state->blob_out.length == 0) {
 | |
| 		state->remote_ready = true;
 | |
| 	}
 | |
| 
 | |
| 	if (state->local_ready && state->remote_ready) {
 | |
| 		cli_smb1_setup_encryption_ready(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cli_smb1_setup_encryption_remote_next(req);
 | |
| }
 | |
| 
 | |
| static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_smb1_setup_encryption_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_smb1_setup_encryption_state);
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 
 | |
| 	if (state->remote_ready) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_smb1_setup_encryption_blob_send(state, state->ev,
 | |
| 						     state->cli, state->blob_out);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_smb1_setup_encryption_remote_done,
 | |
| 				req);
 | |
| }
 | |
| 
 | |
| static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req =
 | |
| 		tevent_req_callback_data(subreq,
 | |
| 		struct tevent_req);
 | |
| 	struct cli_smb1_setup_encryption_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_smb1_setup_encryption_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_smb1_setup_encryption_blob_recv(subreq, state,
 | |
| 						     &state->blob_in,
 | |
| 						     &state->es->enc_ctx_num);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	data_blob_free(&state->blob_out);
 | |
| 	if (!NT_STATUS_IS_OK(status) &&
 | |
| 	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
 | |
| 	{
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * We always get NT_STATUS_OK even if the server is not ready.
 | |
| 	 * So guess the server is ready when we are ready and sent
 | |
| 	 * our last blob to the server.
 | |
| 	 */
 | |
| 	if (state->local_ready) {
 | |
| 		state->remote_ready = true;
 | |
| 	}
 | |
| 
 | |
| 	if (state->local_ready && state->remote_ready) {
 | |
| 		cli_smb1_setup_encryption_ready(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cli_smb1_setup_encryption_local_next(req);
 | |
| }
 | |
| 
 | |
| static void cli_smb1_setup_encryption_ready(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_smb1_setup_encryption_state *state =
 | |
| 		tevent_req_data(req,
 | |
| 		struct cli_smb1_setup_encryption_state);
 | |
| 	struct smb_trans_enc_state *es = NULL;
 | |
| 
 | |
| 	if (state->blob_in.length != 0) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (state->blob_out.length != 0) {
 | |
| 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	es = talloc_move(state->cli->conn, &state->es);
 | |
| 	es->enc_on = true;
 | |
| 	smb1cli_conn_set_encryption(state->cli->conn, es);
 | |
| 	es = NULL;
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_smb1_setup_encryption_recv(struct tevent_req *req)
 | |
| {
 | |
| 	return tevent_req_simple_recv_ntstatus(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli,
 | |
| 				   struct cli_credentials *creds)
 | |
| {
 | |
| 	struct tevent_context *ev = NULL;
 | |
| 	struct tevent_req *req = NULL;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	ev = samba_tevent_context_init(talloc_tos());
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_smb1_setup_encryption_send(ev, ev, cli, creds);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_smb1_setup_encryption_recv(req);
 | |
|  fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|    establishes a connection right up to doing tconX, password specified.
 | |
|    @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 port (optional) The destination port (0 for default)
 | |
|    @param service (optional) The share to make the connection to.  Should be 'unqualified' in any way.
 | |
|    @param service_type The 'type' of service.
 | |
|    @param creds The used user credentials
 | |
| */
 | |
| 
 | |
| struct cli_full_connection_creds_state {
 | |
| 	struct tevent_context *ev;
 | |
| 	const char *service;
 | |
| 	const char *service_type;
 | |
| 	struct cli_credentials *creds;
 | |
| 	struct smb_transports transports;
 | |
| 	int flags;
 | |
| 	struct cli_state *cli;
 | |
| };
 | |
| 
 | |
| static int cli_full_connection_creds_state_destructor(
 | |
| 	struct cli_full_connection_creds_state *s)
 | |
| {
 | |
| 	if (s->cli != NULL) {
 | |
| 		cli_shutdown(s->cli);
 | |
| 		s->cli = NULL;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_conn_done(struct tevent_req *subreq);
 | |
| static void cli_full_connection_creds_sess_done(struct tevent_req *subreq);
 | |
| static void cli_full_connection_creds_enc_start(struct tevent_req *req);
 | |
| static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq);
 | |
| static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq);
 | |
| static void cli_full_connection_creds_enc_done(struct tevent_req *subreq);
 | |
| static void cli_full_connection_creds_enc_tdis(struct tevent_req *req);
 | |
| static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq);
 | |
| static void cli_full_connection_creds_tcon_start(struct tevent_req *req);
 | |
| static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq);
 | |
| 
 | |
| struct tevent_req *cli_full_connection_creds_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 | |
| 	const char *my_name, const char *dest_host,
 | |
| 	const struct sockaddr_storage *dest_ss,
 | |
| 	const struct smb_transports *transports,
 | |
| 	const char *service, const char *service_type,
 | |
| 	struct cli_credentials *creds,
 | |
| 	int flags,
 | |
| 	struct smb2_negotiate_contexts *negotiate_contexts)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_full_connection_creds_state *state;
 | |
| 	enum smb_signing_setting signing_state;
 | |
| 	enum smb_encryption_setting encryption_state =
 | |
| 		cli_credentials_get_smb_encryption(creds);
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state,
 | |
| 				struct cli_full_connection_creds_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	talloc_set_destructor(state, cli_full_connection_creds_state_destructor);
 | |
| 
 | |
| 	state->ev = ev;
 | |
| 	state->service = service;
 | |
| 	state->service_type = service_type;
 | |
| 	state->creds = creds;
 | |
| 	state->flags = flags;
 | |
| 	state->transports = *transports;
 | |
| 
 | |
| 	if (flags & CLI_FULL_CONNECTION_IPC) {
 | |
| 		signing_state = cli_credentials_get_smb_ipc_signing(creds);
 | |
| 	} else {
 | |
| 		signing_state = cli_credentials_get_smb_signing(creds);
 | |
| 	}
 | |
| 
 | |
| 	if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
 | |
| 		if (flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK) {
 | |
| 			encryption_state = SMB_ENCRYPTION_DESIRED;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
 | |
| 		signing_state = SMB_SIGNING_REQUIRED;
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_start_connection_send(
 | |
| 		state, ev, my_name, dest_host, dest_ss,
 | |
| 		&state->transports,
 | |
| 		signing_state, flags,
 | |
| 		negotiate_contexts);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_full_connection_creds_conn_done,
 | |
| 				req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_conn_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_start_connection_recv(subreq, state, &state->cli);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_session_setup_creds_send(
 | |
| 		state, state->ev, state->cli, state->creds);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_full_connection_creds_sess_done,
 | |
| 				req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_sess_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_session_setup_creds_recv(subreq);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 
 | |
| 	if (!NT_STATUS_IS_OK(status) &&
 | |
| 	    (state->flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
 | |
| 
 | |
| 		state->flags &= ~CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
 | |
| 
 | |
| 		state->creds = cli_credentials_init_anon(state);
 | |
| 		if (tevent_req_nomem(state->creds, req)) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		subreq = cli_session_setup_creds_send(
 | |
| 			state, state->ev, state->cli, state->creds);
 | |
| 		if (tevent_req_nomem(subreq, req)) {
 | |
| 			return;
 | |
| 		}
 | |
| 		tevent_req_set_callback(subreq,
 | |
| 					cli_full_connection_creds_sess_done,
 | |
| 					req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cli_full_connection_creds_enc_start(req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_enc_start(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	enum smb_encryption_setting encryption_state =
 | |
| 		cli_credentials_get_smb_encryption(state->creds);
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (encryption_state < SMB_ENCRYPTION_DESIRED) {
 | |
| 		cli_full_connection_creds_tcon_start(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
 | |
| 		status = smb2cli_session_encryption_on(state->cli->smb2.session);
 | |
| 		if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
 | |
| 			if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
 | |
| 				cli_full_connection_creds_tcon_start(req);
 | |
| 				return;
 | |
| 			}
 | |
| 			d_printf("Encryption required and "
 | |
| 				"server doesn't support "
 | |
| 				"SMB3 encryption - failing connect\n");
 | |
| 			tevent_req_nterror(req, status);
 | |
| 			return;
 | |
| 		} else if (!NT_STATUS_IS_OK(status)) {
 | |
| 			d_printf("Encryption required and "
 | |
| 				"setup failed with error %s.\n",
 | |
| 				nt_errstr(status));
 | |
| 			tevent_req_nterror(req, status);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		cli_full_connection_creds_tcon_start(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!SERVER_HAS_UNIX_CIFS(state->cli)) {
 | |
| 		if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
 | |
| 			cli_full_connection_creds_tcon_start(req);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		status = NT_STATUS_NOT_SUPPORTED;
 | |
| 		d_printf("Encryption required and "
 | |
| 			"server doesn't support "
 | |
| 			"SMB1 Unix Extensions - failing connect\n");
 | |
| 		tevent_req_nterror(req, status);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * We do a tcon on IPC$ just to setup the encryption,
 | |
| 	 * the real tcon will be encrypted then.
 | |
| 	 */
 | |
| 	subreq = cli_tree_connect_send(state, state->ev, state->cli,
 | |
| 				       "IPC$", "IPC", NULL);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_full_connection_creds_enc_tcon, req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_tree_connect_recv(subreq);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_unix_extensions_version_send(state, state->ev, state->cli);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_full_connection_creds_enc_ver, req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	enum smb_encryption_setting encryption_state =
 | |
| 		cli_credentials_get_smb_encryption(state->creds);
 | |
| 	uint16_t major, minor;
 | |
| 	uint32_t caplow, caphigh;
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_unix_extensions_version_recv(subreq,
 | |
| 						  &major, &minor,
 | |
| 						  &caplow,
 | |
| 						  &caphigh);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (!NT_STATUS_IS_OK(status)) {
 | |
| 		if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
 | |
| 			/* disconnect ipc$ followed by the real tree connect */
 | |
| 			cli_full_connection_creds_enc_tdis(req);
 | |
| 			return;
 | |
| 		}
 | |
| 		DEBUG(10, ("%s: cli_unix_extensions_version "
 | |
| 			   "returned %s\n", __func__, nt_errstr(status)));
 | |
| 		tevent_req_nterror(req, NT_STATUS_UNKNOWN_REVISION);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
 | |
| 		if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
 | |
| 			/* disconnect ipc$ followed by the real tree connect */
 | |
| 			cli_full_connection_creds_enc_tdis(req);
 | |
| 			return;
 | |
| 		}
 | |
| 		DEBUG(10, ("%s: CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP "
 | |
| 			   "not supported\n", __func__));
 | |
| 		tevent_req_nterror(req, NT_STATUS_UNSUPPORTED_COMPRESSION);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_smb1_setup_encryption_send(state, state->ev,
 | |
| 						state->cli,
 | |
| 						state->creds);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_full_connection_creds_enc_done,
 | |
| 				req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_enc_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_smb1_setup_encryption_recv(subreq);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* disconnect ipc$ followed by the real tree connect */
 | |
| 	cli_full_connection_creds_enc_tdis(req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_enc_tdis(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 
 | |
| 	subreq = cli_tdis_send(state, state->ev, state->cli);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_full_connection_creds_enc_finished,
 | |
| 				req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_tdis_recv(subreq);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cli_full_connection_creds_tcon_start(req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_tcon_start(struct tevent_req *req)
 | |
| {
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	struct tevent_req *subreq = NULL;
 | |
| 	const char *password = NULL;
 | |
| 
 | |
| 	if (state->service == NULL) {
 | |
| 		tevent_req_done(req);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	password = cli_credentials_get_password(state->creds);
 | |
| 
 | |
| 	subreq = cli_tree_connect_send(state, state->ev,
 | |
| 				       state->cli,
 | |
| 				       state->service,
 | |
| 				       state->service_type,
 | |
| 				       password);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq,
 | |
| 				cli_full_connection_creds_tcon_done,
 | |
| 				req);
 | |
| }
 | |
| 
 | |
| static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_tree_connect_recv(subreq);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req,
 | |
| 					TALLOC_CTX *mem_ctx,
 | |
| 					struct cli_state **output_cli)
 | |
| {
 | |
| 	struct cli_full_connection_creds_state *state = tevent_req_data(
 | |
| 		req, struct cli_full_connection_creds_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		return status;
 | |
| 	}
 | |
| 	*output_cli = talloc_move(mem_ctx, &state->cli);
 | |
| 	talloc_set_destructor(state, NULL);
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_full_connection_creds(TALLOC_CTX *mem_ctx,
 | |
| 				   struct cli_state **output_cli,
 | |
| 				   const char *my_name,
 | |
| 				   const char *dest_host,
 | |
| 				   const struct sockaddr_storage *dest_ss,
 | |
| 				   const struct smb_transports *transports,
 | |
| 				   const char *service, const char *service_type,
 | |
| 				   struct cli_credentials *creds,
 | |
| 				   int flags)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	ev = samba_tevent_context_init(mem_ctx);
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_full_connection_creds_send(
 | |
| 		ev, ev, my_name, dest_host, dest_ss, transports, service,
 | |
| 		service_type, creds, flags,
 | |
| 		NULL);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_full_connection_creds_recv(req, mem_ctx, output_cli);
 | |
|  fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  Send an old style tcon.
 | |
| ****************************************************************************/
 | |
| struct cli_raw_tcon_state {
 | |
| 	uint16_t *ret_vwv;
 | |
| };
 | |
| 
 | |
| static void cli_raw_tcon_done(struct tevent_req *subreq);
 | |
| 
 | |
| static struct tevent_req *cli_raw_tcon_send(
 | |
| 	TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
 | |
| 	const char *service, const char *pass, const char *dev)
 | |
| {
 | |
| 	struct tevent_req *req, *subreq;
 | |
| 	struct cli_raw_tcon_state *state;
 | |
| 	uint8_t *bytes;
 | |
| 
 | |
| 	req = tevent_req_create(mem_ctx, &state, struct cli_raw_tcon_state);
 | |
| 	if (req == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (!lp_client_plaintext_auth() && (*pass)) {
 | |
| 		DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
 | |
| 			  " or 'client ntlmv2 auth = yes'\n"));
 | |
| 		tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	TALLOC_FREE(cli->smb1.tcon);
 | |
| 	cli->smb1.tcon = smbXcli_tcon_create(cli);
 | |
| 	if (tevent_req_nomem(cli->smb1.tcon, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
 | |
| 
 | |
| 	bytes = talloc_array(state, uint8_t, 0);
 | |
| 	bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
 | |
| 				   service, strlen(service)+1, NULL);
 | |
| 	bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
 | |
| 				   pass, strlen(pass)+1, NULL);
 | |
| 	bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
 | |
| 	bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
 | |
| 				   dev, strlen(dev)+1, NULL);
 | |
| 
 | |
| 	if (tevent_req_nomem(bytes, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 
 | |
| 	subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, 0, NULL,
 | |
| 			      talloc_get_size(bytes), bytes);
 | |
| 	if (tevent_req_nomem(subreq, req)) {
 | |
| 		return tevent_req_post(req, ev);
 | |
| 	}
 | |
| 	tevent_req_set_callback(subreq, cli_raw_tcon_done, req);
 | |
| 	return req;
 | |
| }
 | |
| 
 | |
| static void cli_raw_tcon_done(struct tevent_req *subreq)
 | |
| {
 | |
| 	struct tevent_req *req = tevent_req_callback_data(
 | |
| 		subreq, struct tevent_req);
 | |
| 	struct cli_raw_tcon_state *state = tevent_req_data(
 | |
| 		req, struct cli_raw_tcon_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	status = cli_smb_recv(subreq, state, NULL, 2, NULL, &state->ret_vwv,
 | |
| 			      NULL, NULL);
 | |
| 	TALLOC_FREE(subreq);
 | |
| 	if (tevent_req_nterror(req, status)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	tevent_req_done(req);
 | |
| }
 | |
| 
 | |
| static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
 | |
| 				  uint16_t *max_xmit, uint16_t *tid)
 | |
| {
 | |
| 	struct cli_raw_tcon_state *state = tevent_req_data(
 | |
| 		req, struct cli_raw_tcon_state);
 | |
| 	NTSTATUS status;
 | |
| 
 | |
| 	if (tevent_req_is_nterror(req, &status)) {
 | |
| 		return status;
 | |
| 	}
 | |
| 	*max_xmit = SVAL(state->ret_vwv + 0, 0);
 | |
| 	*tid = SVAL(state->ret_vwv + 1, 0);
 | |
| 	return NT_STATUS_OK;
 | |
| }
 | |
| 
 | |
| NTSTATUS cli_raw_tcon(struct cli_state *cli,
 | |
| 		      const char *service, const char *pass, const char *dev,
 | |
| 		      uint16_t *max_xmit, uint16_t *tid)
 | |
| {
 | |
| 	struct tevent_context *ev;
 | |
| 	struct tevent_req *req;
 | |
| 	NTSTATUS status = NT_STATUS_NO_MEMORY;
 | |
| 
 | |
| 	ev = samba_tevent_context_init(talloc_tos());
 | |
| 	if (ev == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	req = cli_raw_tcon_send(ev, ev, cli, service, pass, dev);
 | |
| 	if (req == NULL) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	status = cli_raw_tcon_recv(req, max_xmit, tid);
 | |
| fail:
 | |
| 	TALLOC_FREE(ev);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /* Return a cli_state pointing at the IPC$ share for the given server */
 | |
| 
 | |
| static struct cli_state *get_ipc_connect(TALLOC_CTX *mem_ctx,
 | |
| 					 char *server,
 | |
| 					 struct sockaddr_storage *server_ss,
 | |
| 					 struct cli_credentials *creds)
 | |
| {
 | |
|         struct cli_state *cli;
 | |
| 	NTSTATUS nt_status;
 | |
| 	uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
 | |
| 	struct smb_transports ts =
 | |
| 		smb_transports_parse("client smb transports",
 | |
| 				     lp_client_smb_transports());
 | |
| 
 | |
| 	flags |= CLI_FULL_CONNECTION_FORCE_SMB1;
 | |
| 	flags |= CLI_FULL_CONNECTION_IPC;
 | |
| 
 | |
| 	nt_status = cli_full_connection_creds(mem_ctx,
 | |
| 					      &cli,
 | |
| 					      NULL,
 | |
| 					      server,
 | |
| 					      server_ss,
 | |
| 					      &ts,
 | |
| 					      "IPC$",
 | |
| 					      "IPC",
 | |
| 					      creds,
 | |
| 					      flags);
 | |
| 
 | |
| 	if (NT_STATUS_IS_OK(nt_status)) {
 | |
| 		return cli;
 | |
| 	}
 | |
| 	if (is_ipaddress(server)) {
 | |
| 	    /* windows 9* needs a correct NMB name for connections */
 | |
| 	    fstring remote_name;
 | |
| 
 | |
| 	    if (name_status_find("*", 0, 0, server_ss, remote_name)) {
 | |
| 		cli = get_ipc_connect(mem_ctx, remote_name, server_ss, creds);
 | |
| 		if (cli)
 | |
| 		    return cli;
 | |
| 	    }
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Given the IP address of a master browser on the network, return its
 | |
|  * workgroup and connect to it.
 | |
|  *
 | |
|  * This function is provided to allow additional processing beyond what
 | |
|  * get_ipc_connect_master_ip_bcast() does, e.g. to retrieve the list of master
 | |
|  * browsers and obtain each master browsers' list of domains (in case the
 | |
|  * first master browser is recently on the network and has not yet
 | |
|  * synchronized with other master browsers and therefore does not yet have the
 | |
|  * entire network browse list)
 | |
|  */
 | |
| 
 | |
| struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx,
 | |
| 				struct sockaddr_storage *mb_ip,
 | |
| 				struct cli_credentials *creds,
 | |
| 				char **pp_workgroup_out)
 | |
| {
 | |
| 	char addr[INET6_ADDRSTRLEN];
 | |
|         fstring name;
 | |
| 	struct cli_state *cli;
 | |
| 	struct sockaddr_storage server_ss;
 | |
| 
 | |
| 	*pp_workgroup_out = NULL;
 | |
| 
 | |
| 	print_sockaddr(addr, sizeof(addr), mb_ip);
 | |
|         DEBUG(99, ("Looking up name of master browser %s\n",
 | |
|                    addr));
 | |
| 
 | |
|         /*
 | |
|          * Do a name status query to find out the name of the master browser.
 | |
|          * We use <01><02>__MSBROWSE__<02>#01 if *#00 fails because a domain
 | |
|          * master browser will not respond to a wildcard query (or, at least,
 | |
|          * an NT4 server acting as the domain master browser will not).
 | |
|          *
 | |
|          * We might be able to use ONLY the query on MSBROWSE, but that's not
 | |
|          * yet been tested with all Windows versions, so until it is, leave
 | |
|          * 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, name) &&
 | |
|             !name_status_find(MSBROWSE, 1, 0x1d, mb_ip, name)) {
 | |
| 
 | |
|                 DEBUG(99, ("Could not retrieve name status for %s\n",
 | |
|                            addr));
 | |
|                 return NULL;
 | |
|         }
 | |
| 
 | |
|         if (!find_master_ip(name, &server_ss)) {
 | |
|                 DEBUG(99, ("Could not find master ip for %s\n", name));
 | |
|                 return NULL;
 | |
|         }
 | |
| 
 | |
| 	*pp_workgroup_out = talloc_strdup(ctx, name);
 | |
| 
 | |
| 	DEBUG(4, ("found master browser %s, %s\n", name, addr));
 | |
| 
 | |
| 	print_sockaddr(addr, sizeof(addr), &server_ss);
 | |
| 	cli = get_ipc_connect(ctx, addr, &server_ss, creds);
 | |
| 
 | |
| 	return cli;
 | |
| }
 |