|
@@ -100,7 +100,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
|
|
|
hdr->ProtocolId = SMB2_PROTO_NUMBER;
|
|
|
hdr->StructureSize = cpu_to_le16(64);
|
|
|
hdr->Command = smb2_cmd;
|
|
|
- hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
|
|
|
+ if (tcon && tcon->ses && tcon->ses->server) {
|
|
|
+ struct TCP_Server_Info *server = tcon->ses->server;
|
|
|
+
|
|
|
+ spin_lock(&server->req_lock);
|
|
|
+ /* Request up to 2 credits but don't go over the limit. */
|
|
|
+ if (server->credits >= server->max_credits)
|
|
|
+ hdr->CreditRequest = cpu_to_le16(0);
|
|
|
+ else
|
|
|
+ hdr->CreditRequest = cpu_to_le16(
|
|
|
+ min_t(int, server->max_credits -
|
|
|
+ server->credits, 2));
|
|
|
+ spin_unlock(&server->req_lock);
|
|
|
+ } else {
|
|
|
+ hdr->CreditRequest = cpu_to_le16(2);
|
|
|
+ }
|
|
|
hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
|
|
|
|
|
|
if (!tcon)
|
|
@@ -236,8 +250,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
|
|
}
|
|
|
|
|
|
cifs_mark_open_files_invalid(tcon);
|
|
|
+
|
|
|
rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
|
|
|
mutex_unlock(&tcon->ses->session_mutex);
|
|
|
+
|
|
|
+ if (tcon->use_persistent)
|
|
|
+ cifs_reopen_persistent_handles(tcon);
|
|
|
+
|
|
|
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
|
|
|
if (rc)
|
|
|
goto out;
|
|
@@ -574,59 +593,42 @@ vneg_out:
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
|
-int
|
|
|
-SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
|
|
- const struct nls_table *nls_cp)
|
|
|
+struct SMB2_sess_data {
|
|
|
+ unsigned int xid;
|
|
|
+ struct cifs_ses *ses;
|
|
|
+ struct nls_table *nls_cp;
|
|
|
+ void (*func)(struct SMB2_sess_data *);
|
|
|
+ int result;
|
|
|
+ u64 previous_session;
|
|
|
+
|
|
|
+ /* we will send the SMB in three pieces:
|
|
|
+ * a fixed length beginning part, an optional
|
|
|
+ * SPNEGO blob (which can be zero length), and a
|
|
|
+ * last part which will include the strings
|
|
|
+ * and rest of bcc area. This allows us to avoid
|
|
|
+ * a large buffer 17K allocation
|
|
|
+ */
|
|
|
+ int buf0_type;
|
|
|
+ struct kvec iov[2];
|
|
|
+};
|
|
|
+
|
|
|
+static int
|
|
|
+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 smb2_sess_setup_rsp *rsp = NULL;
|
|
|
- struct kvec iov[2];
|
|
|
- int rc = 0;
|
|
|
- int resp_buftype = CIFS_NO_BUFFER;
|
|
|
- __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
|
|
|
struct TCP_Server_Info *server = ses->server;
|
|
|
- u16 blob_length = 0;
|
|
|
- struct key *spnego_key = NULL;
|
|
|
- char *security_blob = NULL;
|
|
|
- unsigned char *ntlmssp_blob = NULL;
|
|
|
- bool use_spnego = false; /* else use raw ntlmssp */
|
|
|
-
|
|
|
- cifs_dbg(FYI, "Session Setup\n");
|
|
|
-
|
|
|
- if (!server) {
|
|
|
- WARN(1, "%s: server is NULL!\n", __func__);
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * If we are here due to reconnect, free per-smb session key
|
|
|
- * in case signing was required.
|
|
|
- */
|
|
|
- kfree(ses->auth_key.response);
|
|
|
- ses->auth_key.response = NULL;
|
|
|
-
|
|
|
- /*
|
|
|
- * If memory allocation is successful, caller of this function
|
|
|
- * frees it.
|
|
|
- */
|
|
|
- ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
|
|
|
- if (!ses->ntlmssp)
|
|
|
- return -ENOMEM;
|
|
|
- ses->ntlmssp->sesskey_per_smbsess = true;
|
|
|
-
|
|
|
- /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
|
|
|
- if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
|
|
|
- ses->sectype = RawNTLMSSP;
|
|
|
-
|
|
|
-ssetup_ntlmssp_authenticate:
|
|
|
- if (phase == NtLmChallenge)
|
|
|
- phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
|
|
|
|
|
|
rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
|
req->hdr.SessionId = 0; /* First session, not a reauthenticate */
|
|
|
+
|
|
|
+ /* if reconnect, we need to send previous sess id, otherwise it is 0 */
|
|
|
+ req->PreviousSessionId = sess_data->previous_session;
|
|
|
+
|
|
|
req->Flags = 0; /* MBZ */
|
|
|
/* to enable echos and oplocks */
|
|
|
req->hdr.CreditRequest = cpu_to_le16(3);
|
|
@@ -642,199 +644,368 @@ ssetup_ntlmssp_authenticate:
|
|
|
req->Capabilities = 0;
|
|
|
req->Channel = 0; /* MBZ */
|
|
|
|
|
|
- iov[0].iov_base = (char *)req;
|
|
|
+ sess_data->iov[0].iov_base = (char *)req;
|
|
|
/* 4 for rfc1002 length field and 1 for pad */
|
|
|
- iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
|
|
|
+ sess_data->iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
|
|
|
+ /*
|
|
|
+ * This variable will be used to clear the buffer
|
|
|
+ * allocated above in case of any error in the calling function.
|
|
|
+ */
|
|
|
+ sess_data->buf0_type = CIFS_SMALL_BUFFER;
|
|
|
|
|
|
- if (ses->sectype == Kerberos) {
|
|
|
-#ifdef CONFIG_CIFS_UPCALL
|
|
|
- struct cifs_spnego_msg *msg;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- spnego_key = cifs_get_spnego_key(ses);
|
|
|
- if (IS_ERR(spnego_key)) {
|
|
|
- rc = PTR_ERR(spnego_key);
|
|
|
- spnego_key = NULL;
|
|
|
- goto ssetup_exit;
|
|
|
- }
|
|
|
+static void
|
|
|
+SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base);
|
|
|
+ sess_data->buf0_type = CIFS_NO_BUFFER;
|
|
|
+}
|
|
|
|
|
|
- msg = spnego_key->payload.data[0];
|
|
|
- /*
|
|
|
- * check version field to make sure that cifs.upcall is
|
|
|
- * sending us a response in an expected form
|
|
|
- */
|
|
|
- if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
|
|
|
- cifs_dbg(VFS,
|
|
|
- "bad cifs.upcall version. Expected %d got %d",
|
|
|
- CIFS_SPNEGO_UPCALL_VERSION, msg->version);
|
|
|
- rc = -EKEYREJECTED;
|
|
|
- goto ssetup_exit;
|
|
|
- }
|
|
|
- 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 ssetup_exit;
|
|
|
- }
|
|
|
- ses->auth_key.len = msg->sesskey_len;
|
|
|
- blob_length = msg->secblob_len;
|
|
|
- iov[1].iov_base = msg->data + msg->sesskey_len;
|
|
|
- iov[1].iov_len = blob_length;
|
|
|
-#else
|
|
|
- rc = -EOPNOTSUPP;
|
|
|
- goto ssetup_exit;
|
|
|
-#endif /* CONFIG_CIFS_UPCALL */
|
|
|
- } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */
|
|
|
- ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
|
|
|
- GFP_KERNEL);
|
|
|
- if (ntlmssp_blob == NULL) {
|
|
|
- rc = -ENOMEM;
|
|
|
- goto ssetup_exit;
|
|
|
- }
|
|
|
- build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
|
|
|
- if (use_spnego) {
|
|
|
- /* blob_length = build_spnego_ntlmssp_blob(
|
|
|
- &security_blob,
|
|
|
- sizeof(struct _NEGOTIATE_MESSAGE),
|
|
|
- ntlmssp_blob); */
|
|
|
- /* BB eventually need to add this */
|
|
|
- cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
|
|
- rc = -EOPNOTSUPP;
|
|
|
- kfree(ntlmssp_blob);
|
|
|
- goto ssetup_exit;
|
|
|
- } else {
|
|
|
- blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
|
|
|
- /* with raw NTLMSSP we don't encapsulate in SPNEGO */
|
|
|
- security_blob = ntlmssp_blob;
|
|
|
- }
|
|
|
- iov[1].iov_base = security_blob;
|
|
|
- iov[1].iov_len = blob_length;
|
|
|
- } else if (phase == NtLmAuthenticate) {
|
|
|
- req->hdr.SessionId = ses->Suid;
|
|
|
- rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
|
|
|
- nls_cp);
|
|
|
- if (rc) {
|
|
|
- cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
|
|
|
- rc);
|
|
|
- goto ssetup_exit; /* BB double check error handling */
|
|
|
- }
|
|
|
- if (use_spnego) {
|
|
|
- /* blob_length = build_spnego_ntlmssp_blob(
|
|
|
- &security_blob,
|
|
|
- blob_length,
|
|
|
- ntlmssp_blob); */
|
|
|
- cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
|
|
- rc = -EOPNOTSUPP;
|
|
|
- kfree(ntlmssp_blob);
|
|
|
- goto ssetup_exit;
|
|
|
- } else {
|
|
|
- security_blob = ntlmssp_blob;
|
|
|
- }
|
|
|
- iov[1].iov_base = security_blob;
|
|
|
- iov[1].iov_len = blob_length;
|
|
|
- } else {
|
|
|
- cifs_dbg(VFS, "illegal ntlmssp phase\n");
|
|
|
- rc = -EIO;
|
|
|
- goto ssetup_exit;
|
|
|
- }
|
|
|
+static int
|
|
|
+SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base;
|
|
|
|
|
|
/* Testing shows that buffer offset must be at location of Buffer[0] */
|
|
|
req->SecurityBufferOffset =
|
|
|
- cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
|
|
|
- 1 /* pad */ - 4 /* rfc1001 len */);
|
|
|
- req->SecurityBufferLength = cpu_to_le16(blob_length);
|
|
|
+ cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
|
|
|
+ 1 /* pad */ - 4 /* rfc1001 len */);
|
|
|
+ req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
|
|
|
|
|
|
- inc_rfc1001_len(req, blob_length - 1 /* pad */);
|
|
|
+ inc_rfc1001_len(req, sess_data->iov[1].iov_len - 1 /* pad */);
|
|
|
|
|
|
/* BB add code to build os and lm fields */
|
|
|
|
|
|
- rc = SendReceive2(xid, ses, iov, 2, &resp_buftype,
|
|
|
- CIFS_LOG_ERROR | CIFS_NEG_OP);
|
|
|
+ rc = SendReceive2(sess_data->xid, sess_data->ses,
|
|
|
+ sess_data->iov, 2,
|
|
|
+ &sess_data->buf0_type,
|
|
|
+ CIFS_LOG_ERROR | CIFS_NEG_OP);
|
|
|
|
|
|
- kfree(security_blob);
|
|
|
- rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
|
|
|
- ses->Suid = rsp->hdr.SessionId;
|
|
|
- if (resp_buftype != CIFS_NO_BUFFER &&
|
|
|
- rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
- if (phase != NtLmNegotiate) {
|
|
|
- cifs_dbg(VFS, "Unexpected more processing error\n");
|
|
|
- goto ssetup_exit;
|
|
|
- }
|
|
|
- if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
|
|
|
- le16_to_cpu(rsp->SecurityBufferOffset)) {
|
|
|
- cifs_dbg(VFS, "Invalid security buffer offset %d\n",
|
|
|
- le16_to_cpu(rsp->SecurityBufferOffset));
|
|
|
- rc = -EIO;
|
|
|
- goto ssetup_exit;
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ int rc = 0;
|
|
|
+ struct cifs_ses *ses = sess_data->ses;
|
|
|
+
|
|
|
+ mutex_lock(&ses->server->srv_mutex);
|
|
|
+ if (ses->server->sign && ses->server->ops->generate_signingkey) {
|
|
|
+ rc = ses->server->ops->generate_signingkey(ses);
|
|
|
+ kfree(ses->auth_key.response);
|
|
|
+ ses->auth_key.response = NULL;
|
|
|
+ if (rc) {
|
|
|
+ cifs_dbg(FYI,
|
|
|
+ "SMB3 session key generation failed\n");
|
|
|
+ mutex_unlock(&ses->server->srv_mutex);
|
|
|
+ goto keygen_exit;
|
|
|
}
|
|
|
+ }
|
|
|
+ if (!ses->server->session_estab) {
|
|
|
+ ses->server->sequence_number = 0x2;
|
|
|
+ ses->server->session_estab = true;
|
|
|
+ }
|
|
|
+ mutex_unlock(&ses->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);
|
|
|
|
|
|
- /* NTLMSSP Negotiate sent now processing challenge (response) */
|
|
|
- phase = NtLmChallenge; /* process ntlmssp challenge */
|
|
|
- rc = 0; /* MORE_PROCESSING is not an error here but expected */
|
|
|
- rc = decode_ntlmssp_challenge(rsp->Buffer,
|
|
|
- le16_to_cpu(rsp->SecurityBufferLength), ses);
|
|
|
+keygen_exit:
|
|
|
+ if (!ses->server->sign) {
|
|
|
+ kfree(ses->auth_key.response);
|
|
|
+ ses->auth_key.response = NULL;
|
|
|
+ }
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_CIFS_UPCALL
|
|
|
+static void
|
|
|
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct cifs_ses *ses = sess_data->ses;
|
|
|
+ struct cifs_spnego_msg *msg;
|
|
|
+ struct key *spnego_key = NULL;
|
|
|
+ struct smb2_sess_setup_rsp *rsp = NULL;
|
|
|
+
|
|
|
+ rc = SMB2_sess_alloc_buffer(sess_data);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ spnego_key = cifs_get_spnego_key(ses);
|
|
|
+ if (IS_ERR(spnego_key)) {
|
|
|
+ rc = PTR_ERR(spnego_key);
|
|
|
+ spnego_key = NULL;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
+ msg = spnego_key->payload.data[0];
|
|
|
/*
|
|
|
- * BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
|
|
|
- * but at least the raw NTLMSSP case works.
|
|
|
+ * check version field to make sure that cifs.upcall is
|
|
|
+ * sending us a response in an expected form
|
|
|
*/
|
|
|
+ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
|
|
|
+ cifs_dbg(VFS,
|
|
|
+ "bad cifs.upcall version. Expected %d got %d",
|
|
|
+ CIFS_SPNEGO_UPCALL_VERSION, msg->version);
|
|
|
+ rc = -EKEYREJECTED;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ 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;
|
|
|
+
|
|
|
+ rc = SMB2_sess_sendreceive(sess_data);
|
|
|
+ if (rc)
|
|
|
+ goto out_put_spnego_key;
|
|
|
+
|
|
|
+ rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
|
|
+ ses->Suid = rsp->hdr.SessionId;
|
|
|
+
|
|
|
+ ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
|
|
+ if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
|
|
+ cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
|
|
|
+
|
|
|
+ rc = SMB2_sess_establish_session(sess_data);
|
|
|
+out_put_spnego_key:
|
|
|
+ key_invalidate(spnego_key);
|
|
|
+ key_put(spnego_key);
|
|
|
+out:
|
|
|
+ sess_data->result = rc;
|
|
|
+ sess_data->func = NULL;
|
|
|
+ SMB2_sess_free_buffer(sess_data);
|
|
|
+}
|
|
|
+#else
|
|
|
+static void
|
|
|
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
|
|
|
+ sess_data->result = -EOPNOTSUPP;
|
|
|
+ sess_data->func = NULL;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static void
|
|
|
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data);
|
|
|
+
|
|
|
+static void
|
|
|
+SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct cifs_ses *ses = sess_data->ses;
|
|
|
+ struct smb2_sess_setup_rsp *rsp = NULL;
|
|
|
+ char *ntlmssp_blob = NULL;
|
|
|
+ bool use_spnego = false; /* else use raw ntlmssp */
|
|
|
+ u16 blob_length = 0;
|
|
|
+
|
|
|
/*
|
|
|
- * No tcon so can't do
|
|
|
- * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
|
|
|
+ * If memory allocation is successful, caller of this function
|
|
|
+ * frees it.
|
|
|
*/
|
|
|
- if (rc != 0)
|
|
|
- goto ssetup_exit;
|
|
|
+ ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
|
|
|
+ if (!ses->ntlmssp) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+ ses->ntlmssp->sesskey_per_smbsess = true;
|
|
|
+
|
|
|
+ rc = SMB2_sess_alloc_buffer(sess_data);
|
|
|
+ if (rc)
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (ntlmssp_blob == NULL) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
|
|
|
+ if (use_spnego) {
|
|
|
+ /* BB eventually need to add this */
|
|
|
+ cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
|
|
+ rc = -EOPNOTSUPP;
|
|
|
+ goto out;
|
|
|
+ } else {
|
|
|
+ blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
|
|
|
+ /* with raw NTLMSSP we don't encapsulate in SPNEGO */
|
|
|
+ }
|
|
|
+ sess_data->iov[1].iov_base = ntlmssp_blob;
|
|
|
+ sess_data->iov[1].iov_len = blob_length;
|
|
|
+
|
|
|
+ rc = SMB2_sess_sendreceive(sess_data);
|
|
|
+ rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
|
|
+
|
|
|
+ /* If true, rc here is expected and not an error */
|
|
|
+ if (sess_data->buf0_type != CIFS_NO_BUFFER &&
|
|
|
+ rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
|
+ rc = 0;
|
|
|
+
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
|
|
|
+ le16_to_cpu(rsp->SecurityBufferOffset)) {
|
|
|
+ cifs_dbg(VFS, "Invalid security buffer offset %d\n",
|
|
|
+ le16_to_cpu(rsp->SecurityBufferOffset));
|
|
|
+ rc = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ rc = decode_ntlmssp_challenge(rsp->Buffer,
|
|
|
+ le16_to_cpu(rsp->SecurityBufferLength), ses);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
|
|
|
+
|
|
|
|
|
|
+ ses->Suid = rsp->hdr.SessionId;
|
|
|
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
|
|
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
|
|
cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
|
|
|
-ssetup_exit:
|
|
|
- free_rsp_buf(resp_buftype, rsp);
|
|
|
-
|
|
|
- /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
|
|
|
- if ((phase == NtLmChallenge) && (rc == 0))
|
|
|
- goto ssetup_ntlmssp_authenticate;
|
|
|
|
|
|
+out:
|
|
|
+ kfree(ntlmssp_blob);
|
|
|
+ SMB2_sess_free_buffer(sess_data);
|
|
|
if (!rc) {
|
|
|
- mutex_lock(&server->srv_mutex);
|
|
|
- if (server->sign && server->ops->generate_signingkey) {
|
|
|
- rc = server->ops->generate_signingkey(ses);
|
|
|
- kfree(ses->auth_key.response);
|
|
|
- ses->auth_key.response = NULL;
|
|
|
- if (rc) {
|
|
|
- cifs_dbg(FYI,
|
|
|
- "SMB3 session key generation failed\n");
|
|
|
- mutex_unlock(&server->srv_mutex);
|
|
|
- goto keygen_exit;
|
|
|
- }
|
|
|
- }
|
|
|
- if (!server->session_estab) {
|
|
|
- server->sequence_number = 0x2;
|
|
|
- server->session_estab = true;
|
|
|
- }
|
|
|
- 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);
|
|
|
+ sess_data->result = 0;
|
|
|
+ sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate;
|
|
|
+ return;
|
|
|
}
|
|
|
+out_err:
|
|
|
+ kfree(ses->ntlmssp);
|
|
|
+ ses->ntlmssp = NULL;
|
|
|
+ sess_data->result = rc;
|
|
|
+ sess_data->func = NULL;
|
|
|
+}
|
|
|
|
|
|
-keygen_exit:
|
|
|
- if (!server->sign) {
|
|
|
- kfree(ses->auth_key.response);
|
|
|
- ses->auth_key.response = NULL;
|
|
|
+static void
|
|
|
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct cifs_ses *ses = sess_data->ses;
|
|
|
+ struct smb2_sess_setup_req *req;
|
|
|
+ struct smb2_sess_setup_rsp *rsp = NULL;
|
|
|
+ unsigned char *ntlmssp_blob = NULL;
|
|
|
+ bool use_spnego = false; /* else use raw ntlmssp */
|
|
|
+ u16 blob_length = 0;
|
|
|
+
|
|
|
+ rc = SMB2_sess_alloc_buffer(sess_data);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
|
|
|
+ req->hdr.SessionId = ses->Suid;
|
|
|
+
|
|
|
+ rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
|
|
|
+ sess_data->nls_cp);
|
|
|
+ if (rc) {
|
|
|
+ cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
|
|
|
+ goto out;
|
|
|
}
|
|
|
- if (spnego_key) {
|
|
|
- key_invalidate(spnego_key);
|
|
|
- key_put(spnego_key);
|
|
|
+
|
|
|
+ if (use_spnego) {
|
|
|
+ /* BB eventually need to add this */
|
|
|
+ cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
|
|
|
+ rc = -EOPNOTSUPP;
|
|
|
+ goto out;
|
|
|
}
|
|
|
+ sess_data->iov[1].iov_base = ntlmssp_blob;
|
|
|
+ sess_data->iov[1].iov_len = blob_length;
|
|
|
+
|
|
|
+ rc = SMB2_sess_sendreceive(sess_data);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
|
|
+
|
|
|
+ ses->Suid = rsp->hdr.SessionId;
|
|
|
+ ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
|
|
+ if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
|
|
+ cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
|
|
|
+
|
|
|
+ rc = SMB2_sess_establish_session(sess_data);
|
|
|
+out:
|
|
|
+ kfree(ntlmssp_blob);
|
|
|
+ SMB2_sess_free_buffer(sess_data);
|
|
|
kfree(ses->ntlmssp);
|
|
|
+ ses->ntlmssp = NULL;
|
|
|
+ sess_data->result = rc;
|
|
|
+ sess_data->func = NULL;
|
|
|
+}
|
|
|
|
|
|
+static int
|
|
|
+SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
|
|
+{
|
|
|
+ if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
|
|
|
+ ses->sectype = RawNTLMSSP;
|
|
|
+
|
|
|
+ switch (ses->sectype) {
|
|
|
+ case Kerberos:
|
|
|
+ sess_data->func = SMB2_auth_kerberos;
|
|
|
+ break;
|
|
|
+ case RawNTLMSSP:
|
|
|
+ sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype);
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+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 SMB2_sess_data *sess_data;
|
|
|
+
|
|
|
+ cifs_dbg(FYI, "Session Setup\n");
|
|
|
+
|
|
|
+ if (!server) {
|
|
|
+ WARN(1, "%s: server is NULL!\n", __func__);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
|
|
|
+ if (!sess_data)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ rc = SMB2_select_sec(ses, sess_data);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+ sess_data->xid = xid;
|
|
|
+ sess_data->ses = ses;
|
|
|
+ sess_data->buf0_type = CIFS_NO_BUFFER;
|
|
|
+ sess_data->nls_cp = (struct nls_table *) nls_cp;
|
|
|
+
|
|
|
+ while (sess_data->func)
|
|
|
+ sess_data->func(sess_data);
|
|
|
+
|
|
|
+ rc = sess_data->result;
|
|
|
+out:
|
|
|
+ kfree(sess_data);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
@@ -1164,7 +1335,7 @@ create_durable_v2_buf(struct cifs_fid *pfid)
|
|
|
|
|
|
buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
|
|
|
buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
|
|
|
- get_random_bytes(buf->dcontext.CreateGuid, 16);
|
|
|
+ generate_random_uuid(buf->dcontext.CreateGuid);
|
|
|
memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
|
|
|
|
|
|
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
|
|
@@ -2057,6 +2228,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
|
|
|
if (rdata->credits) {
|
|
|
buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
|
|
|
SMB2_MAX_BUFFER_SIZE));
|
|
|
+ buf->CreditRequest = buf->CreditCharge;
|
|
|
spin_lock(&server->req_lock);
|
|
|
server->credits += rdata->credits -
|
|
|
le16_to_cpu(buf->CreditCharge);
|
|
@@ -2243,6 +2415,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
|
|
|
if (wdata->credits) {
|
|
|
req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
|
|
|
SMB2_MAX_BUFFER_SIZE));
|
|
|
+ req->hdr.CreditRequest = req->hdr.CreditCharge;
|
|
|
spin_lock(&server->req_lock);
|
|
|
server->credits += wdata->credits -
|
|
|
le16_to_cpu(req->hdr.CreditCharge);
|