diff mbox

[1/4] cifs: Split lanman auth from CIFS_SessSetup()

Message ID 1399036891-15689-2-git-send-email-sprabhu@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sachin Prabhu May 2, 2014, 1:21 p.m. UTC
In preparation for splitting CIFS_SessSetup() into smaller more
manageable chunks, we first add helper functions.

We then proceed to split out lanman auth out of CIFS_SessSetup()

Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
---
 fs/cifs/sess.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 268 insertions(+), 45 deletions(-)

Comments

Jeff Layton May 2, 2014, 7:23 p.m. UTC | #1
On Fri,  2 May 2014 14:21:28 +0100
Sachin Prabhu <sprabhu@redhat.com> wrote:

> In preparation for splitting CIFS_SessSetup() into smaller more
> manageable chunks, we first add helper functions.
> 
> We then proceed to split out lanman auth out of CIFS_SessSetup()
> 
> Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
> ---
>  fs/cifs/sess.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++---------
>  1 file changed, 268 insertions(+), 45 deletions(-)
> 
> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
> index e87387d..c6aa60b 100644
> --- a/fs/cifs/sess.c
> +++ b/fs/cifs/sess.c
> @@ -520,6 +520,250 @@ select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
>  	}
>  }
>  
> +struct sess_data {
> +	unsigned int xid;
> +	struct cifs_ses *ses;
> +	struct nls_table *nls_cp;
> +	void (*func)(struct sess_data *);
> +	int result;
> +
> +	/* 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
> +	 */
> +	struct kvec iov[3];
> +	int buf0_type;
> +};

nit: You should probably make the ints snuggle up to one another, as
you'll end up with fewer holes and less space used.

> +
> +static int
> +sess_alloc_buffer(struct sess_data *sess_data, int wct)
> +{
> +	int rc;
> +	struct cifs_ses *ses = sess_data->ses;
> +	struct smb_hdr *smb_buf;
> +
> +	rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
> +				  (void **)&smb_buf);
> +
> +	if (rc)
> +		return rc;
> +
> +	sess_data->iov[0].iov_base = (char *)smb_buf;
> +	sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
> +	/*
> +	 * 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;
> +
> +	/* 2000 big enough to fit max user, domain, NOS name etc. */
> +	sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL);
> +	if (!sess_data->iov[2].iov_base) {
> +		rc = -ENOMEM;
> +		goto out_free_smb_buf;
> +	}
> +
> +	return 0;
> +
> +out_free_smb_buf:
> +	kfree(smb_buf);
> +	sess_data->iov[0].iov_base = NULL;
> +	sess_data->iov[0].iov_len = 0;
> +	sess_data->buf0_type = CIFS_NO_BUFFER;
> +	return rc;
> +}
> +
> +static int
> +sess_free_buffer(struct sess_data *sess_data)
> +{
> +
> +	if (sess_data->buf0_type == CIFS_SMALL_BUFFER) {
> +		cifs_dbg(FYI, "%s: freeing small buf %p\n", __func__,
> +					sess_data->iov[0].iov_base);
> +		cifs_small_buf_release(sess_data->iov[0].iov_base);
> +	} else if (sess_data->buf0_type == CIFS_LARGE_BUFFER) {
> +		cifs_dbg(FYI, "%s: freeing small buf %p\n", __func__,
> +					sess_data->iov[0].iov_base);
> +		cifs_buf_release(sess_data->iov[0].iov_base);
> +	}
> +

Instead of repeating the above here, you should convert all of the
places that do this sort of thing to use a common helper. e.g.
CIFSSMBRead, CIFSSMBWrite2, CIFSSMBPosixLock, etc. I'd do that cleanup
in a patch that precedes the rest of this set.

In fact, there already is such a helper called free_rsp_buf in the smb2
code. Maybe make that non-static, move it to someplace more common and
simply convert all of the places that do this to call that?

> +	kfree(sess_data->iov[2].iov_base);
> +
> +	return 0;
> +}
> +
> +static int
> +sess_establish_session(struct sess_data *sess_data)
> +{
> +	struct cifs_ses *ses = sess_data->ses;
> +
> +	mutex_lock(&ses->server->srv_mutex);
> +	if (!ses->server->session_estab) {
> +		if (ses->server->sign) {
> +			ses->server->session_key.response =
> +				kmemdup(ses->auth_key.response,
> +				ses->auth_key.len, GFP_KERNEL);
> +			if (!ses->server->session_key.response) {
> +				mutex_unlock(&ses->server->srv_mutex);
> +				return -ENOMEM;
> +			}
> +			ses->server->session_key.len =
> +						ses->auth_key.len;
> +		}
> +		ses->server->sequence_number = 0x2;
> +		ses->server->session_estab = true;
> +	}
> +	mutex_unlock(&ses->server->srv_mutex);
> +
> +	cifs_dbg(FYI, "CIFS session established successfully\n");
> +	spin_lock(&GlobalMid_Lock);
> +	ses->status = CifsGood;
> +	ses->need_reconnect = false;
> +	spin_unlock(&GlobalMid_Lock);
> +
> +	return 0;
> +}
> +
> +static int
> +sess_sendreceive(struct sess_data *sess_data)
> +{
> +	int rc;
> +	struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base;
> +	__u16 count;
> +
> +	count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
> +	smb_buf->smb_buf_length =
> +		cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count);
> +	put_bcc(count, smb_buf);
> +
> +	rc = SendReceive2(sess_data->xid, sess_data->ses,
> +			  sess_data->iov, 3 /* num_iovecs */,
> +			  &sess_data->buf0_type,
> +			  CIFS_LOG_ERROR);
> +
> +	return rc;
> +}
> +
> +/*
> + * LANMAN and plaintext are less secure and off by default.
> + * So we make this explicitly be turned on in kconfig (in the
> + * build) and turned on at runtime (changed from the default)
> + * in proc/fs/cifs or via mount parm.  Unfortunately this is
> + * needed for old Win (e.g. Win95), some obscure NAS and OS/2
> + */
> +#ifdef CONFIG_CIFS_WEAK_PW_HASH
> +static void
> +sess_auth_lanman(struct sess_data *sess_data)
> +{
> +	int rc = 0;
> +	struct smb_hdr *smb_buf;
> +	SESSION_SETUP_ANDX *pSMB;
> +	char *bcc_ptr;
> +	struct cifs_ses *ses = sess_data->ses;
> +	char lnm_session_key[CIFS_AUTH_RESP_SIZE];
> +	__u32 capabilities;
> +	__u16 bytes_remaining;
> +
> +	/* lanman 2 style sessionsetup */
> +	/* wct = 10 */
> +	rc = sess_alloc_buffer(sess_data, 10);
> +	if (rc)
> +		goto out;
> +
> +	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
> +	bcc_ptr = sess_data->iov[2].iov_base;
> +	capabilities = cifs_ssetup_hdr(ses, pSMB);
> +
> +	pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
> +
> +	/* no capabilities flags in old lanman negotiation */
> +	pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
> +
> +	/* Calculate hash with password and copy into bcc_ptr.
> +	 * Encryption Key (stored as in cryptkey) gets used if the
> +	 * security mode bit in Negottiate Protocol response states
> +	 * to use challenge/response method (i.e. Password bit is 1).
> +	 */
> +	rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
> +			      ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
> +			      true : false, lnm_session_key);
> +
> +	memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
> +	bcc_ptr += CIFS_AUTH_RESP_SIZE;
> +
> +	/*
> +	 * can not sign if LANMAN negotiated so no need
> +	 * to calculate signing key? but what if server
> +	 * changed to do higher than lanman dialect and
> +	 * we reconnected would we ever calc signing_key?
> +	 */
> +
> +	cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n");
> +	/* Unicode not allowed for LANMAN dialects */
> +	ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
> +
> +	sess_data->iov[2].iov_len = (long) bcc_ptr -
> +			(long) sess_data->iov[2].iov_base;
> +
> +	rc = sess_sendreceive(sess_data);
> +	if (rc)
> +		goto out;
> +
> +	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
> +	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
> +
> +	/* lanman response has a word count of 3 */
> +	if (smb_buf->WordCount != 3) {
> +		rc = -EIO;
> +		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
> +		goto out;
> +	}
> +
> +	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
> +		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
> +
> +	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
> +	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
> +
> +	bytes_remaining = get_bcc(smb_buf);
> +	bcc_ptr = pByteArea(smb_buf);
> +
> +	/* BB check if Unicode and decode strings */
> +	if (bytes_remaining == 0) {
> +		/* no string area to decode, do nothing */
> +	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
> +		/* unicode string area must be word-aligned */
> +		if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
> +			++bcc_ptr;
> +			--bytes_remaining;
> +		}
> +		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
> +				      sess_data->nls_cp);
> +	} else {
> +		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
> +				    sess_data->nls_cp);
> +	}
> +
> +	rc = sess_establish_session(sess_data);
> +out:
> +	sess_data->result = rc;
> +	sess_data->func = NULL;
> +	sess_free_buffer(sess_data);
> +}
> +
> +#else
> +
> +static void
> +sess_auth_lanman(struct sess_data *sess_data)
> +{
> +	sess_data->result = -EOPNOTSUPP;
> +	sess_data->func = NULL;
> +}
> +#endif
> +
>  int
>  CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
>  	       const struct nls_table *nls_cp)
> @@ -540,12 +784,20 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
>  	__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
>  	u16 blob_len;
>  	char *ntlmsspblob = NULL;
> +	struct sess_data *sess_data;
>  
>  	if (ses == NULL) {
>  		WARN(1, "%s: ses == NULL!", __func__);
>  		return -EINVAL;
>  	}
>  
> +	sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL);
> +	if (!sess_data)
> +		return -ENOMEM;
> +	sess_data->xid = xid;
> +	sess_data->ses = ses;
> +	sess_data->nls_cp = (struct nls_table *) nls_cp;
> +
>  	type = select_sectype(ses->server, ses->sectype);
>  	cifs_dbg(FYI, "sess setup type %d\n", type);
>  	if (type == Unspecified) {
> @@ -554,6 +806,15 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
>  		return -EINVAL;
>  	}
>  
> +	switch (type) {
> +	case LANMAN:
> +		sess_auth_lanman(sess_data);
> +		goto out;
> +	default:
> +		/* Continue with the rest of the function */
> +		break;

no need to explicitly "break" on last case

> +	}
> +
>  	if (type == RawNTLMSSP) {
>  		/* if memory allocation is successful, caller of this function
>  		 * frees it.
> @@ -569,17 +830,7 @@ ssetup_ntlmssp_authenticate:
>  	if (phase == NtLmChallenge)
>  		phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
>  
> -	if (type == LANMAN) {
> -#ifndef CONFIG_CIFS_WEAK_PW_HASH
> -		/* LANMAN and plaintext are less secure and off by default.
> -		So we make this explicitly be turned on in kconfig (in the
> -		build) and turned on at runtime (changed from the default)
> -		in proc/fs/cifs or via mount parm.  Unfortunately this is
> -		needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
> -		return -EOPNOTSUPP;
> -#endif
> -		wct = 10; /* lanman 2 style sessionsetup */
> -	} else if ((type == NTLM) || (type == NTLMv2)) {
> +	if ((type == NTLM) || (type == NTLMv2)) {
>  		/* For NTLMv2 failures eventually may need to retry NTLM */
>  		wct = 13; /* old style NTLM sessionsetup */
>  	} else /* same size: negotiate or auth, NTLMSSP or extended security */
> @@ -618,39 +869,7 @@ ssetup_ntlmssp_authenticate:
>  	iov[1].iov_base = NULL;
>  	iov[1].iov_len = 0;
>  
> -	if (type == LANMAN) {
> -#ifdef CONFIG_CIFS_WEAK_PW_HASH
> -		char lnm_session_key[CIFS_AUTH_RESP_SIZE];
> -
> -		pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
> -
> -		/* no capabilities flags in old lanman negotiation */
> -
> -		pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
> -
> -		/* Calculate hash with password and copy into bcc_ptr.
> -		 * Encryption Key (stored as in cryptkey) gets used if the
> -		 * security mode bit in Negottiate Protocol response states
> -		 * to use challenge/response method (i.e. Password bit is 1).
> -		 */
> -
> -		rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
> -				 ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
> -					true : false, lnm_session_key);
> -
> -		memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
> -		bcc_ptr += CIFS_AUTH_RESP_SIZE;
> -
> -		/* can not sign if LANMAN negotiated so no need
> -		to calculate signing key? but what if server
> -		changed to do higher than lanman dialect and
> -		we reconnected would we ever calc signing_key? */
> -
> -		cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n");
> -		/* Unicode not allowed for LANMAN dialects */
> -		ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
> -#endif
> -	} else if (type == NTLM) {
> +	if (type == NTLM) {
>  		pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
>  		pSMB->req_no_secext.CaseInsensitivePasswordLength =
>  			cpu_to_le16(CIFS_AUTH_RESP_SIZE);
> @@ -889,7 +1108,6 @@ ssetup_ntlmssp_authenticate:
>  		}
>  		if (phase == NtLmChallenge) {
>  			rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
> -			/* now goto beginning for ntlmssp authenticate phase */
>  			if (rc)
>  				goto ssetup_exit;
>  		}
> @@ -962,4 +1180,9 @@ keycp_exit:
>  	kfree(ses->ntlmssp);
>  
>  	return rc;
> +
> +out:
> +	rc = sess_data->result;
> +	kfree(sess_data);
> +	return rc;
>  }

Aside from the nits above, nice cleanup. It's a definite improvement.
diff mbox

Patch

diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index e87387d..c6aa60b 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -520,6 +520,250 @@  select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
 	}
 }
 
+struct sess_data {
+	unsigned int xid;
+	struct cifs_ses *ses;
+	struct nls_table *nls_cp;
+	void (*func)(struct sess_data *);
+	int result;
+
+	/* 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
+	 */
+	struct kvec iov[3];
+	int buf0_type;
+};
+
+static int
+sess_alloc_buffer(struct sess_data *sess_data, int wct)
+{
+	int rc;
+	struct cifs_ses *ses = sess_data->ses;
+	struct smb_hdr *smb_buf;
+
+	rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
+				  (void **)&smb_buf);
+
+	if (rc)
+		return rc;
+
+	sess_data->iov[0].iov_base = (char *)smb_buf;
+	sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
+	/*
+	 * 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;
+
+	/* 2000 big enough to fit max user, domain, NOS name etc. */
+	sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL);
+	if (!sess_data->iov[2].iov_base) {
+		rc = -ENOMEM;
+		goto out_free_smb_buf;
+	}
+
+	return 0;
+
+out_free_smb_buf:
+	kfree(smb_buf);
+	sess_data->iov[0].iov_base = NULL;
+	sess_data->iov[0].iov_len = 0;
+	sess_data->buf0_type = CIFS_NO_BUFFER;
+	return rc;
+}
+
+static int
+sess_free_buffer(struct sess_data *sess_data)
+{
+
+	if (sess_data->buf0_type == CIFS_SMALL_BUFFER) {
+		cifs_dbg(FYI, "%s: freeing small buf %p\n", __func__,
+					sess_data->iov[0].iov_base);
+		cifs_small_buf_release(sess_data->iov[0].iov_base);
+	} else if (sess_data->buf0_type == CIFS_LARGE_BUFFER) {
+		cifs_dbg(FYI, "%s: freeing small buf %p\n", __func__,
+					sess_data->iov[0].iov_base);
+		cifs_buf_release(sess_data->iov[0].iov_base);
+	}
+
+	kfree(sess_data->iov[2].iov_base);
+
+	return 0;
+}
+
+static int
+sess_establish_session(struct sess_data *sess_data)
+{
+	struct cifs_ses *ses = sess_data->ses;
+
+	mutex_lock(&ses->server->srv_mutex);
+	if (!ses->server->session_estab) {
+		if (ses->server->sign) {
+			ses->server->session_key.response =
+				kmemdup(ses->auth_key.response,
+				ses->auth_key.len, GFP_KERNEL);
+			if (!ses->server->session_key.response) {
+				mutex_unlock(&ses->server->srv_mutex);
+				return -ENOMEM;
+			}
+			ses->server->session_key.len =
+						ses->auth_key.len;
+		}
+		ses->server->sequence_number = 0x2;
+		ses->server->session_estab = true;
+	}
+	mutex_unlock(&ses->server->srv_mutex);
+
+	cifs_dbg(FYI, "CIFS session established successfully\n");
+	spin_lock(&GlobalMid_Lock);
+	ses->status = CifsGood;
+	ses->need_reconnect = false;
+	spin_unlock(&GlobalMid_Lock);
+
+	return 0;
+}
+
+static int
+sess_sendreceive(struct sess_data *sess_data)
+{
+	int rc;
+	struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base;
+	__u16 count;
+
+	count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
+	smb_buf->smb_buf_length =
+		cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count);
+	put_bcc(count, smb_buf);
+
+	rc = SendReceive2(sess_data->xid, sess_data->ses,
+			  sess_data->iov, 3 /* num_iovecs */,
+			  &sess_data->buf0_type,
+			  CIFS_LOG_ERROR);
+
+	return rc;
+}
+
+/*
+ * LANMAN and plaintext are less secure and off by default.
+ * So we make this explicitly be turned on in kconfig (in the
+ * build) and turned on at runtime (changed from the default)
+ * in proc/fs/cifs or via mount parm.  Unfortunately this is
+ * needed for old Win (e.g. Win95), some obscure NAS and OS/2
+ */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+static void
+sess_auth_lanman(struct sess_data *sess_data)
+{
+	int rc = 0;
+	struct smb_hdr *smb_buf;
+	SESSION_SETUP_ANDX *pSMB;
+	char *bcc_ptr;
+	struct cifs_ses *ses = sess_data->ses;
+	char lnm_session_key[CIFS_AUTH_RESP_SIZE];
+	__u32 capabilities;
+	__u16 bytes_remaining;
+
+	/* lanman 2 style sessionsetup */
+	/* wct = 10 */
+	rc = sess_alloc_buffer(sess_data, 10);
+	if (rc)
+		goto out;
+
+	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+	bcc_ptr = sess_data->iov[2].iov_base;
+	capabilities = cifs_ssetup_hdr(ses, pSMB);
+
+	pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
+
+	/* no capabilities flags in old lanman negotiation */
+	pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+
+	/* Calculate hash with password and copy into bcc_ptr.
+	 * Encryption Key (stored as in cryptkey) gets used if the
+	 * security mode bit in Negottiate Protocol response states
+	 * to use challenge/response method (i.e. Password bit is 1).
+	 */
+	rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
+			      ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
+			      true : false, lnm_session_key);
+
+	memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
+	bcc_ptr += CIFS_AUTH_RESP_SIZE;
+
+	/*
+	 * can not sign if LANMAN negotiated so no need
+	 * to calculate signing key? but what if server
+	 * changed to do higher than lanman dialect and
+	 * we reconnected would we ever calc signing_key?
+	 */
+
+	cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n");
+	/* Unicode not allowed for LANMAN dialects */
+	ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+
+	sess_data->iov[2].iov_len = (long) bcc_ptr -
+			(long) sess_data->iov[2].iov_base;
+
+	rc = sess_sendreceive(sess_data);
+	if (rc)
+		goto out;
+
+	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
+
+	/* lanman response has a word count of 3 */
+	if (smb_buf->WordCount != 3) {
+		rc = -EIO;
+		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+		goto out;
+	}
+
+	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
+		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
+
+	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
+	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
+
+	bytes_remaining = get_bcc(smb_buf);
+	bcc_ptr = pByteArea(smb_buf);
+
+	/* BB check if Unicode and decode strings */
+	if (bytes_remaining == 0) {
+		/* no string area to decode, do nothing */
+	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
+		/* unicode string area must be word-aligned */
+		if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
+			++bcc_ptr;
+			--bytes_remaining;
+		}
+		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
+				      sess_data->nls_cp);
+	} else {
+		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
+				    sess_data->nls_cp);
+	}
+
+	rc = sess_establish_session(sess_data);
+out:
+	sess_data->result = rc;
+	sess_data->func = NULL;
+	sess_free_buffer(sess_data);
+}
+
+#else
+
+static void
+sess_auth_lanman(struct sess_data *sess_data)
+{
+	sess_data->result = -EOPNOTSUPP;
+	sess_data->func = NULL;
+}
+#endif
+
 int
 CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
 	       const struct nls_table *nls_cp)
@@ -540,12 +784,20 @@  CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
 	__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
 	u16 blob_len;
 	char *ntlmsspblob = NULL;
+	struct sess_data *sess_data;
 
 	if (ses == NULL) {
 		WARN(1, "%s: ses == NULL!", __func__);
 		return -EINVAL;
 	}
 
+	sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL);
+	if (!sess_data)
+		return -ENOMEM;
+	sess_data->xid = xid;
+	sess_data->ses = ses;
+	sess_data->nls_cp = (struct nls_table *) nls_cp;
+
 	type = select_sectype(ses->server, ses->sectype);
 	cifs_dbg(FYI, "sess setup type %d\n", type);
 	if (type == Unspecified) {
@@ -554,6 +806,15 @@  CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
 		return -EINVAL;
 	}
 
+	switch (type) {
+	case LANMAN:
+		sess_auth_lanman(sess_data);
+		goto out;
+	default:
+		/* Continue with the rest of the function */
+		break;
+	}
+
 	if (type == RawNTLMSSP) {
 		/* if memory allocation is successful, caller of this function
 		 * frees it.
@@ -569,17 +830,7 @@  ssetup_ntlmssp_authenticate:
 	if (phase == NtLmChallenge)
 		phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
 
-	if (type == LANMAN) {
-#ifndef CONFIG_CIFS_WEAK_PW_HASH
-		/* LANMAN and plaintext are less secure and off by default.
-		So we make this explicitly be turned on in kconfig (in the
-		build) and turned on at runtime (changed from the default)
-		in proc/fs/cifs or via mount parm.  Unfortunately this is
-		needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
-		return -EOPNOTSUPP;
-#endif
-		wct = 10; /* lanman 2 style sessionsetup */
-	} else if ((type == NTLM) || (type == NTLMv2)) {
+	if ((type == NTLM) || (type == NTLMv2)) {
 		/* For NTLMv2 failures eventually may need to retry NTLM */
 		wct = 13; /* old style NTLM sessionsetup */
 	} else /* same size: negotiate or auth, NTLMSSP or extended security */
@@ -618,39 +869,7 @@  ssetup_ntlmssp_authenticate:
 	iov[1].iov_base = NULL;
 	iov[1].iov_len = 0;
 
-	if (type == LANMAN) {
-#ifdef CONFIG_CIFS_WEAK_PW_HASH
-		char lnm_session_key[CIFS_AUTH_RESP_SIZE];
-
-		pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
-
-		/* no capabilities flags in old lanman negotiation */
-
-		pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
-
-		/* Calculate hash with password and copy into bcc_ptr.
-		 * Encryption Key (stored as in cryptkey) gets used if the
-		 * security mode bit in Negottiate Protocol response states
-		 * to use challenge/response method (i.e. Password bit is 1).
-		 */
-
-		rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
-				 ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
-					true : false, lnm_session_key);
-
-		memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
-		bcc_ptr += CIFS_AUTH_RESP_SIZE;
-
-		/* can not sign if LANMAN negotiated so no need
-		to calculate signing key? but what if server
-		changed to do higher than lanman dialect and
-		we reconnected would we ever calc signing_key? */
-
-		cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n");
-		/* Unicode not allowed for LANMAN dialects */
-		ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
-#endif
-	} else if (type == NTLM) {
+	if (type == NTLM) {
 		pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
 		pSMB->req_no_secext.CaseInsensitivePasswordLength =
 			cpu_to_le16(CIFS_AUTH_RESP_SIZE);
@@ -889,7 +1108,6 @@  ssetup_ntlmssp_authenticate:
 		}
 		if (phase == NtLmChallenge) {
 			rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
-			/* now goto beginning for ntlmssp authenticate phase */
 			if (rc)
 				goto ssetup_exit;
 		}
@@ -962,4 +1180,9 @@  keycp_exit:
 	kfree(ses->ntlmssp);
 
 	return rc;
+
+out:
+	rc = sess_data->result;
+	kfree(sess_data);
+	return rc;
 }