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 |