diff options
Diffstat (limited to 'fs/cifs/smb2pdu.c')
| -rw-r--r-- | fs/cifs/smb2pdu.c | 168 | 
1 files changed, 112 insertions, 56 deletions
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 05149862aea4..ed77f94dbf1d 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -252,7 +252,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)  	if (tcon == NULL)  		return 0; -	if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) +	if (smb2_command == SMB2_TREE_CONNECT)  		return 0;  	if (tcon->tidStatus == CifsExiting) { @@ -426,16 +426,9 @@ fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf,   * SMB information in the SMB header. If the return code is zero, this   * function must have filled in request_buf pointer.   */ -static int -smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, -		    void **request_buf, unsigned int *total_len) +static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, +				  void **request_buf, unsigned int *total_len)  { -	int rc; - -	rc = smb2_reconnect(smb2_command, tcon); -	if (rc) -		return rc; -  	/* BB eventually switch this to SMB2 specific small buf size */  	if (smb2_command == SMB2_SET_INFO)  		*request_buf = cifs_buf_get(); @@ -456,7 +449,31 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,  		cifs_stats_inc(&tcon->num_smbs_sent);  	} -	return rc; +	return 0; +} + +static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, +			       void **request_buf, unsigned int *total_len) +{ +	int rc; + +	rc = smb2_reconnect(smb2_command, tcon); +	if (rc) +		return rc; + +	return __smb2_plain_req_init(smb2_command, tcon, request_buf, +				     total_len); +} + +static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, +			       void **request_buf, unsigned int *total_len) +{ +	/* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ +	if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { +		return __smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, +					     total_len); +	} +	return smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, total_len);  }  /* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ @@ -791,7 +808,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)  	struct kvec rsp_iov;  	int rc = 0;  	int resp_buftype; -	struct TCP_Server_Info *server = ses->server; +	struct TCP_Server_Info *server = cifs_ses_server(ses);  	int blob_offset, blob_length;  	char *security_blob;  	int flags = CIFS_NEG_OP; @@ -813,7 +830,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)  	memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);  	memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); -	if (strcmp(ses->server->vals->version_string, +	if (strcmp(server->vals->version_string,  		   SMB3ANY_VERSION_STRING) == 0) {  		req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);  		req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); @@ -829,7 +846,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)  		total_len += 8;  	} else {  		/* otherwise send specific dialect */ -		req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id); +		req->Dialects[0] = cpu_to_le16(server->vals->protocol_id);  		req->DialectCount = cpu_to_le16(1);  		total_len += 2;  	} @@ -1171,7 +1188,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)  	int rc;  	struct cifs_ses *ses = sess_data->ses;  	struct smb2_sess_setup_req *req; -	struct TCP_Server_Info *server = ses->server; +	struct TCP_Server_Info *server = cifs_ses_server(ses);  	unsigned int total_len;  	rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req, @@ -1179,13 +1196,21 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)  	if (rc)  		return rc; -	/* First session, not a reauthenticate */ -	req->sync_hdr.SessionId = 0; - -	/* if reconnect, we need to send previous sess id, otherwise it is 0 */ -	req->PreviousSessionId = sess_data->previous_session; - -	req->Flags = 0; /* MBZ */ +	if (sess_data->ses->binding) { +		req->sync_hdr.SessionId = sess_data->ses->Suid; +		req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; +		req->PreviousSessionId = 0; +		req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; +	} else { +		/* First session, not a reauthenticate */ +		req->sync_hdr.SessionId = 0; +		/* +		 * if reconnect, we need to send previous sess id +		 * otherwise it is 0 +		 */ +		req->PreviousSessionId = sess_data->previous_session; +		req->Flags = 0; /* MBZ */ +	}  	/* enough to enable echos and oplocks and one max size write */  	req->sync_hdr.CreditRequest = cpu_to_le16(130); @@ -1258,28 +1283,33 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)  {  	int rc = 0;  	struct cifs_ses *ses = sess_data->ses; +	struct TCP_Server_Info *server = cifs_ses_server(ses); -	mutex_lock(&ses->server->srv_mutex); -	if (ses->server->ops->generate_signingkey) { -		rc = ses->server->ops->generate_signingkey(ses); +	mutex_lock(&server->srv_mutex); +	if (server->ops->generate_signingkey) { +		rc = server->ops->generate_signingkey(ses);  		if (rc) {  			cifs_dbg(FYI,  				"SMB3 session key generation failed\n"); -			mutex_unlock(&ses->server->srv_mutex); +			mutex_unlock(&server->srv_mutex);  			return rc;  		}  	} -	if (!ses->server->session_estab) { -		ses->server->sequence_number = 0x2; -		ses->server->session_estab = true; +	if (!server->session_estab) { +		server->sequence_number = 0x2; +		server->session_estab = true;  	} -	mutex_unlock(&ses->server->srv_mutex); +	mutex_unlock(&server->srv_mutex);  	cifs_dbg(FYI, "SMB2/3 session established successfully\n"); -	spin_lock(&GlobalMid_Lock); -	ses->status = CifsGood; -	ses->need_reconnect = false; -	spin_unlock(&GlobalMid_Lock); +	/* keep existing ses state if binding */ +	if (!ses->binding) { +		spin_lock(&GlobalMid_Lock); +		ses->status = CifsGood; +		ses->need_reconnect = false; +		spin_unlock(&GlobalMid_Lock); +	} +  	return rc;  } @@ -1317,16 +1347,19 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)  		goto out_put_spnego_key;  	} -	ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, -					 GFP_KERNEL); -	if (!ses->auth_key.response) { -		cifs_dbg(VFS, -			"Kerberos can't allocate (%u bytes) memory", -			msg->sesskey_len); -		rc = -ENOMEM; -		goto out_put_spnego_key; +	/* keep session key if binding */ +	if (!ses->binding) { +		ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, +						 GFP_KERNEL); +		if (!ses->auth_key.response) { +			cifs_dbg(VFS, +				 "Kerberos can't allocate (%u bytes) memory", +				 msg->sesskey_len); +			rc = -ENOMEM; +			goto out_put_spnego_key; +		} +		ses->auth_key.len = msg->sesskey_len;  	} -	ses->auth_key.len = msg->sesskey_len;  	sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;  	sess_data->iov[1].iov_len = msg->secblob_len; @@ -1336,9 +1369,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)  		goto out_put_spnego_key;  	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; -	ses->Suid = rsp->sync_hdr.SessionId; - -	ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	/* keep session id and flags if binding */ +	if (!ses->binding) { +		ses->Suid = rsp->sync_hdr.SessionId; +		ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	}  	rc = SMB2_sess_establish_session(sess_data);  out_put_spnego_key: @@ -1432,9 +1467,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)  	cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); - -	ses->Suid = rsp->sync_hdr.SessionId; -	ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	/* keep existing ses id and flags if binding */ +	if (!ses->binding) { +		ses->Suid = rsp->sync_hdr.SessionId; +		ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	}  out:  	kfree(ntlmssp_blob); @@ -1491,8 +1528,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)  	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; -	ses->Suid = rsp->sync_hdr.SessionId; -	ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	/* keep existing ses id and flags if binding */ +	if (!ses->binding) { +		ses->Suid = rsp->sync_hdr.SessionId; +		ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	}  	rc = SMB2_sess_establish_session(sess_data);  out: @@ -1509,7 +1549,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)  {  	int type; -	type = smb2_select_sectype(ses->server, ses->sectype); +	type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype);  	cifs_dbg(FYI, "sess setup type %d\n", type);  	if (type == Unspecified) {  		cifs_dbg(VFS, @@ -1537,7 +1577,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,  		const struct nls_table *nls_cp)  {  	int rc = 0; -	struct TCP_Server_Info *server = ses->server; +	struct TCP_Server_Info *server = cifs_ses_server(ses);  	struct SMB2_sess_data *sess_data;  	cifs_dbg(FYI, "Session Setup\n"); @@ -1563,7 +1603,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,  	/*  	 * Initialize the session hash with the server one.  	 */ -	memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash, +	memcpy(ses->preauth_sha_hash, server->preauth_sha_hash,  	       SMB2_PREAUTH_HASH_SIZE);  	while (sess_data->func) @@ -1807,6 +1847,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)  	if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))  		return 0; +	close_shroot(&tcon->crfid); +  	rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req,  			     &total_len);  	if (rc) @@ -2661,7 +2703,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,  	int rc;  	char *in_data_buf; -	rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len); +	rc = smb2_ioctl_req_init(opcode, tcon, (void **) &req, &total_len);  	if (rc)  		return rc; @@ -2972,7 +3014,21 @@ int  SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,  	   u64 persistent_fid, u64 volatile_fid)  { -	return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0); +	int rc; +	int tmp_rc; + +	rc = SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0); + +	/* retry close in a worker thread if this one is interrupted */ +	if (rc == -EINTR) { +		tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, +						     volatile_fid); +		if (tmp_rc) +			cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", +				 persistent_fid, tmp_rc); +	} + +	return rc;  }  int  | 

