[2/2] SMB2: Seperate RawNTLMSSP authentication from SMB2_sess_setup
diff mbox

Message ID 1475863882-7223-3-git-send-email-sprabhu@redhat.com
State New
Headers show

Commit Message

Sachin Prabhu Oct. 7, 2016, 6:11 p.m. UTC
We split the rawntlmssp authentication into negotiate and
authencate parts. We also clean up the code and add helpers.

Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
---
 fs/cifs/smb2pdu.c | 361 ++++++++++++++++++++++++------------------------------
 1 file changed, 162 insertions(+), 199 deletions(-)

Comments

Pavel Shilovsky Oct. 11, 2016, 6:54 p.m. UTC | #1
2016-10-07 11:11 GMT-07:00 Sachin Prabhu <sprabhu@redhat.com>:
> We split the rawntlmssp authentication into negotiate and
> authencate parts. We also clean up the code and add helpers.
>
> Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
> ---
>  fs/cifs/smb2pdu.c | 361 ++++++++++++++++++++++++------------------------------
>  1 file changed, 162 insertions(+), 199 deletions(-)
>
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 386b512..5ca5ea46 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -803,245 +803,208 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
>  }
>  #endif
>
> -int
> -SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
> -               const struct nls_table *nls_cp)
> +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)
>  {
> -       struct smb2_sess_setup_req *req;
> +       int rc;
> +       struct cifs_ses *ses = sess_data->ses;
>         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;
> -       char *security_blob = NULL;
> -       unsigned char *ntlmssp_blob = NULL;
> +       char *ntlmssp_blob = NULL;
>         bool use_spnego = false; /* else use raw ntlmssp */
> -       u64 previous_session = ses->Suid;
> -       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;
> -       sess_data->xid = xid;
> -       sess_data->ses = ses;
> -       sess_data->buf0_type = CIFS_NO_BUFFER;
> -       sess_data->nls_cp = (struct nls_table *) nls_cp;
> -       sess_data->previous_session = ses->Suid;
> -
> -       if (ses->sectype == Kerberos) {
> -               SMB2_auth_kerberos(sess_data);
> -               goto out;
> -       }
> -
> -       /*
> -        * 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;
> +       u16 blob_length = 0;
>
>         /*
>          * 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;
> +       if (!ses->ntlmssp) {
> +               rc = -ENOMEM;
> +               goto out_err;
> +       }
>         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);
> +       rc = SMB2_sess_alloc_buffer(sess_data);
>         if (rc)
> -               return rc;
> -
> -       req->hdr.SessionId = 0; /* First session, not a reauthenticate */
> +               goto out_err;
>
> -       /* if reconnect, we need to send previous sess id, otherwise it is 0 */
> -       req->PreviousSessionId = previous_session;
> +       ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
> +                              GFP_KERNEL);
> +       if (ntlmssp_blob == NULL) {
> +               rc = -ENOMEM;
> +               goto out;
> +       }
>
> -       req->Flags = 0; /* MBZ */
> -       /* to enable echos and oplocks */
> -       req->hdr.CreditRequest = cpu_to_le16(3);
> +       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;
>
> -       /* only one of SMB2 signing flags may be set in SMB2 request */
> -       if (server->sign)
> -               req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
> -       else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
> -               req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED;
> -       else
> -               req->SecurityMode = 0;
> +       rc = SMB2_sess_sendreceive(sess_data);
> +       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
>
> -       req->Capabilities = 0;
> -       req->Channel = 0; /* MBZ */
> +       /* 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;
>
> -       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;
> +       if (rc)
> +               goto out;
>
> -       if (phase == NtLmNegotiate) {
> -               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");
> +       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;
> +               goto out;
>         }
> +       rc = decode_ntlmssp_challenge(rsp->Buffer,
> +                       le16_to_cpu(rsp->SecurityBufferLength), ses);
> +       if (rc)
> +               goto out;
>
> -       /* 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);
> +       cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
>
> -       inc_rfc1001_len(req, blob_length - 1 /* pad */);
>
> -       /* BB add code to build os and lm fields */
> +       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 = SendReceive2(xid, ses, iov, 2, &resp_buftype,
> -                         CIFS_LOG_ERROR | CIFS_NEG_OP);
> +out:
> +       kfree(ntlmssp_blob);
> +       SMB2_sess_free_buffer(sess_data);
> +       if (!rc) {
> +               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;
> +}
>
> -       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;
> -               }
> +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;
>
> -               /* 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);
> +       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;
>         }
>
> -       /*
> -        * BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
> -        * but at least the raw NTLMSSP case works.
> -        */
> -       /*
> -        * No tcon so can't do
> -        * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
> -        */
> -       if (rc != 0)
> -               goto ssetup_exit;
> +       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");
> -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;
> +       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;
> +}
>
> -       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);
> +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;
>
> -               cifs_dbg(FYI, "SMB2/3 session established successfully\n");
> -               spin_lock(&GlobalMid_Lock);
> -               ses->status = CifsGood;
> -               ses->need_reconnect = false;
> -               spin_unlock(&GlobalMid_Lock);
> +       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;
>         }
>
> -keygen_exit:
> -       if (!server->sign) {
> -               kfree(ses->auth_key.response);
> -               ses->auth_key.response = NULL;
> +       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;
>         }
> -       kfree(ses->ntlmssp);
>
> -       return rc;
> -out:
> +       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;
>  }
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>

Patch
diff mbox

diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 386b512..5ca5ea46 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -803,245 +803,208 @@  SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
 }
 #endif
 
-int
-SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
-		const struct nls_table *nls_cp)
+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)
 {
-	struct smb2_sess_setup_req *req;
+	int rc;
+	struct cifs_ses *ses = sess_data->ses;
 	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;
-	char *security_blob = NULL;
-	unsigned char *ntlmssp_blob = NULL;
+	char *ntlmssp_blob = NULL;
 	bool use_spnego = false; /* else use raw ntlmssp */
-	u64 previous_session = ses->Suid;
-	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;
-	sess_data->xid = xid;
-	sess_data->ses = ses;
-	sess_data->buf0_type = CIFS_NO_BUFFER;
-	sess_data->nls_cp = (struct nls_table *) nls_cp;
-	sess_data->previous_session = ses->Suid;
-
-	if (ses->sectype == Kerberos) {
-		SMB2_auth_kerberos(sess_data);
-		goto out;
-	}
-
-	/*
-	 * 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;
+	u16 blob_length = 0;
 
 	/*
 	 * 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;
+	if (!ses->ntlmssp) {
+		rc = -ENOMEM;
+		goto out_err;
+	}
 	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);
+	rc = SMB2_sess_alloc_buffer(sess_data);
 	if (rc)
-		return rc;
-
-	req->hdr.SessionId = 0; /* First session, not a reauthenticate */
+		goto out_err;
 
-	/* if reconnect, we need to send previous sess id, otherwise it is 0 */
-	req->PreviousSessionId = previous_session;
+	ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
+			       GFP_KERNEL);
+	if (ntlmssp_blob == NULL) {
+		rc = -ENOMEM;
+		goto out;
+	}
 
-	req->Flags = 0; /* MBZ */
-	/* to enable echos and oplocks */
-	req->hdr.CreditRequest = cpu_to_le16(3);
+	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;
 
-	/* only one of SMB2 signing flags may be set in SMB2 request */
-	if (server->sign)
-		req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
-	else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
-		req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED;
-	else
-		req->SecurityMode = 0;
+	rc = SMB2_sess_sendreceive(sess_data);
+	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
 
-	req->Capabilities = 0;
-	req->Channel = 0; /* MBZ */
+	/* 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;
 
-	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;
+	if (rc)
+		goto out;
 
-	if (phase == NtLmNegotiate) {
-		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");
+	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;
+		goto out;
 	}
+	rc = decode_ntlmssp_challenge(rsp->Buffer,
+			le16_to_cpu(rsp->SecurityBufferLength), ses);
+	if (rc)
+		goto out;
 
-	/* 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);
+	cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
 
-	inc_rfc1001_len(req, blob_length - 1 /* pad */);
 
-	/* BB add code to build os and lm fields */
+	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 = SendReceive2(xid, ses, iov, 2, &resp_buftype,
-			  CIFS_LOG_ERROR | CIFS_NEG_OP);
+out:
+	kfree(ntlmssp_blob);
+	SMB2_sess_free_buffer(sess_data);
+	if (!rc) {
+		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;
+}
 
-	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;
-		}
+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;
 
-		/* 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);
+	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;
 	}
 
-	/*
-	 * BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
-	 * but at least the raw NTLMSSP case works.
-	 */
-	/*
-	 * No tcon so can't do
-	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
-	 */
-	if (rc != 0)
-		goto ssetup_exit;
+	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");
-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;
+	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;
+}
 
-	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);
+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;
 
-		cifs_dbg(FYI, "SMB2/3 session established successfully\n");
-		spin_lock(&GlobalMid_Lock);
-		ses->status = CifsGood;
-		ses->need_reconnect = false;
-		spin_unlock(&GlobalMid_Lock);
+	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;
 	}
 
-keygen_exit:
-	if (!server->sign) {
-		kfree(ses->auth_key.response);
-		ses->auth_key.response = NULL;
+	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;
 	}
-	kfree(ses->ntlmssp);
 
-	return rc;
-out:
+	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;
 }