diff mbox

[5/5] cifs: Separate rawntlmssp auth from CIFS_SessSetup()

Message ID 1402929328-12866-6-git-send-email-sprabhu@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sachin Prabhu June 16, 2014, 2:35 p.m. UTC
Separate rawntlmssp authentication from CIFS_SessSetup(). Also cleanup
CIFS_SessSetup() since we no longer do any auth within it.

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

Comments

Shirish Pargaonkar June 24, 2014, 3:33 a.m. UTC | #1
Looks good.

Reviewed-by: Shirish Pargaonkar <spargaonkar@suse.com>

On Mon, Jun 16, 2014 at 9:35 AM, Sachin Prabhu <sprabhu@redhat.com> wrote:
> Separate rawntlmssp authentication from CIFS_SessSetup(). Also cleanup
> CIFS_SessSetup() since we no longer do any auth within it.
>
> Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
> ---
>  fs/cifs/sess.c | 490 +++++++++++++++++++++++++++++++--------------------------
>  1 file changed, 263 insertions(+), 227 deletions(-)
>
> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
> index b413fae..c2e7c07 100644
> --- a/fs/cifs/sess.c
> +++ b/fs/cifs/sess.c
> @@ -1113,241 +1113,225 @@ sess_auth_kerberos(struct sess_data *sess_data)
>  }
>  #endif /* ! CONFIG_CIFS_UPCALL */
>
> -int
> -CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
> -              const struct nls_table *nls_cp)
> +/*
> + * The required kvec buffers have to be allocated before calling this
> + * function.
> + */
> +static int
> +_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
>  {
> -       int rc = 0;
> -       int wct;
>         struct smb_hdr *smb_buf;
> -       char *bcc_ptr;
> -       char *str_area;
>         SESSION_SETUP_ANDX *pSMB;
> +       struct cifs_ses *ses = sess_data->ses;
>         __u32 capabilities;
> -       __u16 count;
> -       int resp_buf_type;
> -       struct kvec iov[3];
> -       enum securityEnum type;
> -       __u16 action, bytes_remaining;
> -       __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
> -       u16 blob_len;
> -       char *ntlmsspblob = NULL;
> -       struct sess_data *sess_data;
> +       char *bcc_ptr;
>
> -       if (ses == NULL) {
> -               WARN(1, "%s: ses == NULL!", __func__);
> -               return -EINVAL;
> +       pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
> +       smb_buf = (struct smb_hdr *)pSMB;
> +
> +       capabilities = cifs_ssetup_hdr(ses, pSMB);
> +       if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
> +               cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
> +               return -ENOSYS;
>         }
>
> -       sess_data = kzalloc(sizeof(struct 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;
> +       pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
> +       capabilities |= CAP_EXTENDED_SECURITY;
> +       pSMB->req.Capabilities |= cpu_to_le32(capabilities);
>
> -       type = select_sectype(ses->server, ses->sectype);
> -       cifs_dbg(FYI, "sess setup type %d\n", type);
> -       if (type == Unspecified) {
> -               cifs_dbg(VFS,
> -                       "Unable to select appropriate authentication method!");
> -               return -EINVAL;
> +       bcc_ptr = sess_data->iov[2].iov_base;
> +       /* unicode strings must be word aligned */
> +       if ((sess_data->iov[0].iov_len + sess_data->iov[1].iov_len) % 2) {
> +               *bcc_ptr = 0;
> +               bcc_ptr++;
>         }
> +       unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
>
> -       switch (type) {
> -       case LANMAN:
> -               sess_auth_lanman(sess_data);
> -               goto out;
> -       case NTLM:
> -               sess_auth_ntlm(sess_data);
> -               goto out;
> -       case NTLMv2:
> -               sess_auth_ntlmv2(sess_data);
> -               goto out;
> -       case Kerberos:
> -               sess_auth_kerberos(sess_data);
> -               goto out;
> -       default:
> -               cifs_dbg(FYI, "Continuing with CIFS_SessSetup\n");
> -       }
> +       sess_data->iov[2].iov_len = (long) bcc_ptr -
> +                                       (long) sess_data->iov[2].iov_base;
>
> -       if (type == RawNTLMSSP) {
> -               /* 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 = false;
> +       return 0;
> +}
> +
> +static void
> +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data);
> +
> +static void
> +sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
> +{
> +       int rc;
> +       struct smb_hdr *smb_buf;
> +       SESSION_SETUP_ANDX *pSMB;
> +       struct cifs_ses *ses = sess_data->ses;
> +       __u16 bytes_remaining;
> +       char *bcc_ptr;
> +       u16 blob_len;
> +
> +       cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
>
> +       /*
> +        * if memory allocation is successful, caller of this function
> +        * frees it.
> +        */
> +       ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
> +       if (!ses->ntlmssp) {
> +               rc = -ENOMEM;
> +               goto out;
>         }
> +       ses->ntlmssp->sesskey_per_smbsess = false;
>
> -ssetup_ntlmssp_authenticate:
> -       if (phase == NtLmChallenge)
> -               phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
> +       /* wct = 12 */
> +       rc = sess_alloc_buffer(sess_data, 12);
> +       if (rc)
> +               goto out;
>
> -       /* same size: negotiate or auth, NTLMSSP or extended security */
> -       wct = 12;
> +       pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
>
> -       rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
> -                           (void **)&smb_buf);
> +       /* Build security blob before we assemble the request */
> +       build_ntlmssp_negotiate_blob(pSMB->req.SecurityBlob, ses);
> +       sess_data->iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
> +       sess_data->iov[1].iov_base = pSMB->req.SecurityBlob;
> +       pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
> +
> +       rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
>         if (rc)
> -               return rc;
> +               goto out;
>
> -       pSMB = (SESSION_SETUP_ANDX *)smb_buf;
> +       rc = sess_sendreceive(sess_data);
>
> -       capabilities = cifs_ssetup_hdr(ses, pSMB);
> +       pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
> +       smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
>
> -       /* 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 */
> -       iov[0].iov_base = (char *)pSMB;
> -       iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
> -
> -       /* setting this here allows the code at the end of the function
> -          to free the request buffer if there's an error */
> -       resp_buf_type = CIFS_SMALL_BUFFER;
> +       /* If true, rc here is expected and not an error */
> +       if (sess_data->buf0_type != CIFS_NO_BUFFER &&
> +           smb_buf->Status.CifsError ==
> +                       cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
> +               rc = 0;
>
> -       /* 2000 big enough to fit max user, domain, NOS name etc. */
> -       str_area = kmalloc(2000, GFP_KERNEL);
> -       if (str_area == NULL) {
> -               rc = -ENOMEM;
> -               goto ssetup_exit;
> +       if (rc)
> +               goto out;
> +
> +       cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
> +
> +       if (smb_buf->WordCount != 4) {
> +               rc = -EIO;
> +               cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
> +               goto out;
>         }
> -       bcc_ptr = str_area;
>
> -       iov[1].iov_base = NULL;
> -       iov[1].iov_len = 0;
> +       ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
> +       cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
>
> -       if (type == RawNTLMSSP) {
> -               if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
> -                       cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
> -                       rc = -ENOSYS;
> -                       goto ssetup_exit;
> -               }
> +       bytes_remaining = get_bcc(smb_buf);
> +       bcc_ptr = pByteArea(smb_buf);
>
> -               cifs_dbg(FYI, "ntlmssp session setup phase %d\n", phase);
> -               pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
> -               capabilities |= CAP_EXTENDED_SECURITY;
> -               pSMB->req.Capabilities |= cpu_to_le32(capabilities);
> -               switch(phase) {
> -               case NtLmNegotiate:
> -                       build_ntlmssp_negotiate_blob(
> -                               pSMB->req.SecurityBlob, ses);
> -                       iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
> -                       iov[1].iov_base = pSMB->req.SecurityBlob;
> -                       pSMB->req.SecurityBlobLength =
> -                               cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
> -                       break;
> -               case NtLmAuthenticate:
> -                       /*
> -                        * 5 is an empirical value, large enough to hold
> -                        * authenticate message plus max 10 of av paris,
> -                        * domain, user, workstation names, flags, etc.
> -                        */
> -                       ntlmsspblob = kzalloc(
> -                               5*sizeof(struct _AUTHENTICATE_MESSAGE),
> -                               GFP_KERNEL);
> -                       if (!ntlmsspblob) {
> -                               rc = -ENOMEM;
> -                               goto ssetup_exit;
> -                       }
> +       blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
> +       if (blob_len > bytes_remaining) {
> +               cifs_dbg(VFS, "bad security blob length %d\n",
> +                               blob_len);
> +               rc = -EINVAL;
> +               goto out;
> +       }
>
> -                       rc = build_ntlmssp_auth_blob(ntlmsspblob,
> -                                               &blob_len, ses, nls_cp);
> -                       if (rc)
> -                               goto ssetup_exit;
> -                       iov[1].iov_len = blob_len;
> -                       iov[1].iov_base = ntlmsspblob;
> -                       pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
> -                       /*
> -                        * Make sure that we tell the server that we are using
> -                        * the uid that it just gave us back on the response
> -                        * (challenge)
> -                        */
> -                       smb_buf->Uid = ses->Suid;
> -                       break;
> -               default:
> -                       cifs_dbg(VFS, "invalid phase %d\n", phase);
> -                       rc = -ENOSYS;
> -                       goto ssetup_exit;
> -               }
> -               /* unicode strings must be word aligned */
> -               if ((iov[0].iov_len + iov[1].iov_len) % 2) {
> -                       *bcc_ptr = 0;
> -                       bcc_ptr++;
> -               }
> -               unicode_oslm_strings(&bcc_ptr, nls_cp);
> -       } else {
> -               cifs_dbg(VFS, "secType %d not supported!\n", type);
> -               rc = -ENOSYS;
> -               goto ssetup_exit;
> +       rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
> +out:
> +       sess_free_buffer(sess_data);
> +
> +       if (!rc) {
> +               sess_data->func = sess_auth_rawntlmssp_authenticate;
> +               return;
>         }
>
> -       iov[2].iov_base = str_area;
> -       iov[2].iov_len = (long) bcc_ptr - (long) str_area;
> +       /* Else error. Cleanup */
> +       kfree(ses->auth_key.response);
> +       ses->auth_key.response = NULL;
> +       kfree(ses->ntlmssp);
> +       ses->ntlmssp = NULL;
>
> -       count = iov[1].iov_len + iov[2].iov_len;
> -       smb_buf->smb_buf_length =
> -               cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count);
> +       sess_data->func = NULL;
> +       sess_data->result = rc;
> +}
>
> -       put_bcc(count, smb_buf);
> +static void
> +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
> +{
> +       int rc;
> +       struct smb_hdr *smb_buf;
> +       SESSION_SETUP_ANDX *pSMB;
> +       struct cifs_ses *ses = sess_data->ses;
> +       __u16 bytes_remaining;
> +       char *bcc_ptr;
> +       char *ntlmsspblob = NULL;
> +       u16 blob_len;
>
> -       rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
> -                         CIFS_LOG_ERROR);
> -       /* SMB request buf freed in SendReceive2 */
> +       cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
>
> -       pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
> -       smb_buf = (struct smb_hdr *)iov[0].iov_base;
> +       /* wct = 12 */
> +       rc = sess_alloc_buffer(sess_data, 12);
> +       if (rc)
> +               goto out;
>
> -       if ((type == RawNTLMSSP) && (resp_buf_type != CIFS_NO_BUFFER) &&
> -           (smb_buf->Status.CifsError ==
> -                       cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) {
> -               if (phase != NtLmNegotiate) {
> -                       cifs_dbg(VFS, "Unexpected more processing error\n");
> -                       goto ssetup_exit;
> -               }
> -               /* NTLMSSP Negotiate sent now processing challenge (response) */
> -               phase = NtLmChallenge; /* process ntlmssp challenge */
> -               rc = 0; /* MORE_PROC rc is not an error here, but expected */
> +       /* Build security blob before we assemble the request */
> +       pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
> +       smb_buf = (struct smb_hdr *)pSMB;
> +       /*
> +        * 5 is an empirical value, large enough to hold
> +        * authenticate message plus max 10 of av paris,
> +        * domain, user, workstation names, flags, etc.
> +        */
> +       ntlmsspblob = kzalloc(5*sizeof(struct _AUTHENTICATE_MESSAGE),
> +                               GFP_KERNEL);
> +       if (!ntlmsspblob) {
> +               rc = -ENOMEM;
> +               goto out;
>         }
> +
> +       rc = build_ntlmssp_auth_blob(ntlmsspblob,
> +                                       &blob_len, ses, sess_data->nls_cp);
> +       if (rc)
> +               goto out_free_ntlmsspblob;
> +       sess_data->iov[1].iov_len = blob_len;
> +       sess_data->iov[1].iov_base = ntlmsspblob;
> +       pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
> +       /*
> +        * Make sure that we tell the server that we are using
> +        * the uid that it just gave us back on the response
> +        * (challenge)
> +        */
> +       smb_buf->Uid = ses->Suid;
> +
> +       rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
>         if (rc)
> -               goto ssetup_exit;
> +               goto out_free_ntlmsspblob;
>
> +       rc = sess_sendreceive(sess_data);
> +       if (rc)
> +               goto out_free_ntlmsspblob;
> +
> +       pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
> +       smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
>         if (smb_buf->WordCount != 4) {
>                 rc = -EIO;
>                 cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
> -               goto ssetup_exit;
> +               goto out_free_ntlmsspblob;
>         }
> -       action = le16_to_cpu(pSMB->resp.Action);
> -       if (action & GUEST_LOGIN)
> +
> +       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);
> -       /* response can have either 3 or 4 word count - Samba sends 3 */
> -       /* and lanman response is 3 */
> +
>         bytes_remaining = get_bcc(smb_buf);
>         bcc_ptr = pByteArea(smb_buf);
> -
>         blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
>         if (blob_len > bytes_remaining) {
>                 cifs_dbg(VFS, "bad security blob length %d\n",
> -                        blob_len);
> +                               blob_len);
>                 rc = -EINVAL;
> -               goto ssetup_exit;
> -       }
> -       if (phase == NtLmChallenge) {
> -               rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
> -               if (rc)
> -                       goto ssetup_exit;
> +               goto out_free_ntlmsspblob;
>         }
>         bcc_ptr += blob_len;
>         bytes_remaining -= blob_len;
>
> +
>         /* BB check if Unicode and decode strings */
>         if (bytes_remaining == 0) {
>                 /* no string area to decode, do nothing */
> @@ -1357,61 +1341,113 @@ ssetup_ntlmssp_authenticate:
>                         ++bcc_ptr;
>                         --bytes_remaining;
>                 }
> -               decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp);
> +               decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
> +                                     sess_data->nls_cp);
>         } else {
> -               decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp);
> +               decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
> +                                   sess_data->nls_cp);
>         }
>
> -ssetup_exit:
> -       kfree(str_area);
> +out_free_ntlmsspblob:
>         kfree(ntlmsspblob);
> -       ntlmsspblob = NULL;
> -       if (resp_buf_type == CIFS_SMALL_BUFFER) {
> -               cifs_dbg(FYI, "ssetup freeing small buf %p\n", iov[0].iov_base);
> -               cifs_small_buf_release(iov[0].iov_base);
> -       } else if (resp_buf_type == CIFS_LARGE_BUFFER)
> -               cifs_buf_release(iov[0].iov_base);
> -
> -       /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
> -       if ((phase == NtLmChallenge) && (rc == 0))
> -               goto ssetup_ntlmssp_authenticate;
> -
> -       if (!rc) {
> -               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) {
> -                                       rc = -ENOMEM;
> -                                       mutex_unlock(&ses->server->srv_mutex);
> -                                       goto keycp_exit;
> -                               }
> -                               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);
> +out:
> +       sess_free_buffer(sess_data);
>
> -               cifs_dbg(FYI, "CIFS session established successfully\n");
> -               spin_lock(&GlobalMid_Lock);
> -               ses->status = CifsGood;
> -               ses->need_reconnect = false;
> -               spin_unlock(&GlobalMid_Lock);
> -       }
> +        if (!rc)
> +               rc = sess_establish_session(sess_data);
>
> -keycp_exit:
> +       /* Cleanup */
>         kfree(ses->auth_key.response);
>         ses->auth_key.response = NULL;
>         kfree(ses->ntlmssp);
> +       ses->ntlmssp = NULL;
>
> -       return rc;
> +       sess_data->func = NULL;
> +       sess_data->result = rc;
> +}
>
> -out:
> +int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
> +{
> +       int type;
> +
> +       type = select_sectype(ses->server, ses->sectype);
> +       cifs_dbg(FYI, "sess setup type %d\n", type);
> +       if (type == Unspecified) {
> +               cifs_dbg(VFS,
> +                       "Unable to select appropriate authentication method!");
> +               return -EINVAL;
> +       }
> +
> +       switch (type) {
> +       case LANMAN:
> +               /* 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
> +               sess_data->func = sess_auth_lanman;
> +               break;
> +#else
> +               return -EOPNOTSUPP;
> +#endif
> +       case NTLM:
> +               sess_data->func = sess_auth_ntlm;
> +               break;
> +       case NTLMv2:
> +               sess_data->func = sess_auth_ntlmv2;
> +               break;
> +       case Kerberos:
> +#ifdef CONFIG_CIFS_UPCALL
> +               sess_data->func = sess_auth_kerberos;
> +               break;
> +#else
> +               cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
> +               return -ENOSYS;
> +               break;
> +#endif /* CONFIG_CIFS_UPCALL */
> +       case RawNTLMSSP:
> +               sess_data->func = sess_auth_rawntlmssp_negotiate;
> +               break;
> +       default:
> +               cifs_dbg(VFS, "secType %d not supported!\n", type);
> +               return -ENOSYS;
> +       }
> +
> +       return 0;
> +}
> +
> +int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
> +                   const struct nls_table *nls_cp)
> +{
> +       int rc = 0;
> +       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;
> +
> +       rc = 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);
> +
> +       /* Store result before we free sess_data */
>         rc = sess_data->result;
> +
> +out:
>         kfree(sess_data);
>         return rc;
>  }
> --
> 1.9.3
>
> --
> 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
--
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
diff mbox

Patch

diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index b413fae..c2e7c07 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -1113,241 +1113,225 @@  sess_auth_kerberos(struct sess_data *sess_data)
 }
 #endif /* ! CONFIG_CIFS_UPCALL */
 
-int
-CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
-	       const struct nls_table *nls_cp)
+/*
+ * The required kvec buffers have to be allocated before calling this
+ * function.
+ */
+static int
+_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
 {
-	int rc = 0;
-	int wct;
 	struct smb_hdr *smb_buf;
-	char *bcc_ptr;
-	char *str_area;
 	SESSION_SETUP_ANDX *pSMB;
+	struct cifs_ses *ses = sess_data->ses;
 	__u32 capabilities;
-	__u16 count;
-	int resp_buf_type;
-	struct kvec iov[3];
-	enum securityEnum type;
-	__u16 action, bytes_remaining;
-	__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
-	u16 blob_len;
-	char *ntlmsspblob = NULL;
-	struct sess_data *sess_data;
+	char *bcc_ptr;
 
-	if (ses == NULL) {
-		WARN(1, "%s: ses == NULL!", __func__);
-		return -EINVAL;
+	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+	smb_buf = (struct smb_hdr *)pSMB;
+
+	capabilities = cifs_ssetup_hdr(ses, pSMB);
+	if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
+		cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
+		return -ENOSYS;
 	}
 
-	sess_data = kzalloc(sizeof(struct 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;
+	pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+	capabilities |= CAP_EXTENDED_SECURITY;
+	pSMB->req.Capabilities |= cpu_to_le32(capabilities);
 
-	type = select_sectype(ses->server, ses->sectype);
-	cifs_dbg(FYI, "sess setup type %d\n", type);
-	if (type == Unspecified) {
-		cifs_dbg(VFS,
-			"Unable to select appropriate authentication method!");
-		return -EINVAL;
+	bcc_ptr = sess_data->iov[2].iov_base;
+	/* unicode strings must be word aligned */
+	if ((sess_data->iov[0].iov_len + sess_data->iov[1].iov_len) % 2) {
+		*bcc_ptr = 0;
+		bcc_ptr++;
 	}
+	unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
 
-	switch (type) {
-	case LANMAN:
-		sess_auth_lanman(sess_data);
-		goto out;
-	case NTLM:
-		sess_auth_ntlm(sess_data);
-		goto out;
-	case NTLMv2:
-		sess_auth_ntlmv2(sess_data);
-		goto out;
-	case Kerberos:
-		sess_auth_kerberos(sess_data);
-		goto out;
-	default:
-		cifs_dbg(FYI, "Continuing with CIFS_SessSetup\n");
-	}
+	sess_data->iov[2].iov_len = (long) bcc_ptr -
+					(long) sess_data->iov[2].iov_base;
 
-	if (type == RawNTLMSSP) {
-		/* 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 = false;
+	return 0;
+}
+
+static void
+sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data);
+
+static void
+sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
+{
+	int rc;
+	struct smb_hdr *smb_buf;
+	SESSION_SETUP_ANDX *pSMB;
+	struct cifs_ses *ses = sess_data->ses;
+	__u16 bytes_remaining;
+	char *bcc_ptr;
+	u16 blob_len;
+
+	cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
 
+	/*
+	 * if memory allocation is successful, caller of this function
+	 * frees it.
+	 */
+	ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
+	if (!ses->ntlmssp) {
+		rc = -ENOMEM;
+		goto out;
 	}
+	ses->ntlmssp->sesskey_per_smbsess = false;
 
-ssetup_ntlmssp_authenticate:
-	if (phase == NtLmChallenge)
-		phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
+	/* wct = 12 */
+	rc = sess_alloc_buffer(sess_data, 12);
+	if (rc)
+		goto out;
 
-	/* same size: negotiate or auth, NTLMSSP or extended security */
-	wct = 12;
+	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
 
-	rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
-			    (void **)&smb_buf);
+	/* Build security blob before we assemble the request */
+	build_ntlmssp_negotiate_blob(pSMB->req.SecurityBlob, ses);
+	sess_data->iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
+	sess_data->iov[1].iov_base = pSMB->req.SecurityBlob;
+	pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
+
+	rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
 	if (rc)
-		return rc;
+		goto out;
 
-	pSMB = (SESSION_SETUP_ANDX *)smb_buf;
+	rc = sess_sendreceive(sess_data);
 
-	capabilities = cifs_ssetup_hdr(ses, pSMB);
+	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
 
-	/* 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 */
-	iov[0].iov_base = (char *)pSMB;
-	iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
-
-	/* setting this here allows the code at the end of the function
-	   to free the request buffer if there's an error */
-	resp_buf_type = CIFS_SMALL_BUFFER;
+	/* If true, rc here is expected and not an error */
+	if (sess_data->buf0_type != CIFS_NO_BUFFER &&
+	    smb_buf->Status.CifsError ==
+			cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
+		rc = 0;
 
-	/* 2000 big enough to fit max user, domain, NOS name etc. */
-	str_area = kmalloc(2000, GFP_KERNEL);
-	if (str_area == NULL) {
-		rc = -ENOMEM;
-		goto ssetup_exit;
+	if (rc)
+		goto out;
+
+	cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
+
+	if (smb_buf->WordCount != 4) {
+		rc = -EIO;
+		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+		goto out;
 	}
-	bcc_ptr = str_area;
 
-	iov[1].iov_base = NULL;
-	iov[1].iov_len = 0;
+	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
+	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
 
-	if (type == RawNTLMSSP) {
-		if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
-			cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
-			rc = -ENOSYS;
-			goto ssetup_exit;
-		}
+	bytes_remaining = get_bcc(smb_buf);
+	bcc_ptr = pByteArea(smb_buf);
 
-		cifs_dbg(FYI, "ntlmssp session setup phase %d\n", phase);
-		pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
-		capabilities |= CAP_EXTENDED_SECURITY;
-		pSMB->req.Capabilities |= cpu_to_le32(capabilities);
-		switch(phase) {
-		case NtLmNegotiate:
-			build_ntlmssp_negotiate_blob(
-				pSMB->req.SecurityBlob, ses);
-			iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
-			iov[1].iov_base = pSMB->req.SecurityBlob;
-			pSMB->req.SecurityBlobLength =
-				cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
-			break;
-		case NtLmAuthenticate:
-			/*
-			 * 5 is an empirical value, large enough to hold
-			 * authenticate message plus max 10 of av paris,
-			 * domain, user, workstation names, flags, etc.
-			 */
-			ntlmsspblob = kzalloc(
-				5*sizeof(struct _AUTHENTICATE_MESSAGE),
-				GFP_KERNEL);
-			if (!ntlmsspblob) {
-				rc = -ENOMEM;
-				goto ssetup_exit;
-			}
+	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
+	if (blob_len > bytes_remaining) {
+		cifs_dbg(VFS, "bad security blob length %d\n",
+				blob_len);
+		rc = -EINVAL;
+		goto out;
+	}
 
-			rc = build_ntlmssp_auth_blob(ntlmsspblob,
-						&blob_len, ses, nls_cp);
-			if (rc)
-				goto ssetup_exit;
-			iov[1].iov_len = blob_len;
-			iov[1].iov_base = ntlmsspblob;
-			pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
-			/*
-			 * Make sure that we tell the server that we are using
-			 * the uid that it just gave us back on the response
-			 * (challenge)
-			 */
-			smb_buf->Uid = ses->Suid;
-			break;
-		default:
-			cifs_dbg(VFS, "invalid phase %d\n", phase);
-			rc = -ENOSYS;
-			goto ssetup_exit;
-		}
-		/* unicode strings must be word aligned */
-		if ((iov[0].iov_len + iov[1].iov_len) % 2) {
-			*bcc_ptr = 0;
-			bcc_ptr++;
-		}
-		unicode_oslm_strings(&bcc_ptr, nls_cp);
-	} else {
-		cifs_dbg(VFS, "secType %d not supported!\n", type);
-		rc = -ENOSYS;
-		goto ssetup_exit;
+	rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
+out:
+	sess_free_buffer(sess_data);
+
+	if (!rc) {
+		sess_data->func = sess_auth_rawntlmssp_authenticate;
+		return;
 	}
 
-	iov[2].iov_base = str_area;
-	iov[2].iov_len = (long) bcc_ptr - (long) str_area;
+	/* Else error. Cleanup */
+	kfree(ses->auth_key.response);
+	ses->auth_key.response = NULL;
+	kfree(ses->ntlmssp);
+	ses->ntlmssp = NULL;
 
-	count = iov[1].iov_len + iov[2].iov_len;
-	smb_buf->smb_buf_length =
-		cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count);
+	sess_data->func = NULL;
+	sess_data->result = rc;
+}
 
-	put_bcc(count, smb_buf);
+static void
+sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
+{
+	int rc;
+	struct smb_hdr *smb_buf;
+	SESSION_SETUP_ANDX *pSMB;
+	struct cifs_ses *ses = sess_data->ses;
+	__u16 bytes_remaining;
+	char *bcc_ptr;
+	char *ntlmsspblob = NULL;
+	u16 blob_len;
 
-	rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
-			  CIFS_LOG_ERROR);
-	/* SMB request buf freed in SendReceive2 */
+	cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
 
-	pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
-	smb_buf = (struct smb_hdr *)iov[0].iov_base;
+	/* wct = 12 */
+	rc = sess_alloc_buffer(sess_data, 12);
+	if (rc)
+		goto out;
 
-	if ((type == RawNTLMSSP) && (resp_buf_type != CIFS_NO_BUFFER) &&
-	    (smb_buf->Status.CifsError ==
-			cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) {
-		if (phase != NtLmNegotiate) {
-			cifs_dbg(VFS, "Unexpected more processing error\n");
-			goto ssetup_exit;
-		}
-		/* NTLMSSP Negotiate sent now processing challenge (response) */
-		phase = NtLmChallenge; /* process ntlmssp challenge */
-		rc = 0; /* MORE_PROC rc is not an error here, but expected */
+	/* Build security blob before we assemble the request */
+	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+	smb_buf = (struct smb_hdr *)pSMB;
+	/*
+	 * 5 is an empirical value, large enough to hold
+	 * authenticate message plus max 10 of av paris,
+	 * domain, user, workstation names, flags, etc.
+	 */
+	ntlmsspblob = kzalloc(5*sizeof(struct _AUTHENTICATE_MESSAGE),
+				GFP_KERNEL);
+	if (!ntlmsspblob) {
+		rc = -ENOMEM;
+		goto out;
 	}
+
+	rc = build_ntlmssp_auth_blob(ntlmsspblob,
+					&blob_len, ses, sess_data->nls_cp);
+	if (rc)
+		goto out_free_ntlmsspblob;
+	sess_data->iov[1].iov_len = blob_len;
+	sess_data->iov[1].iov_base = ntlmsspblob;
+	pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
+	/*
+	 * Make sure that we tell the server that we are using
+	 * the uid that it just gave us back on the response
+	 * (challenge)
+	 */
+	smb_buf->Uid = ses->Suid;
+
+	rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
 	if (rc)
-		goto ssetup_exit;
+		goto out_free_ntlmsspblob;
 
+	rc = sess_sendreceive(sess_data);
+	if (rc)
+		goto out_free_ntlmsspblob;
+
+	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
 	if (smb_buf->WordCount != 4) {
 		rc = -EIO;
 		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
-		goto ssetup_exit;
+		goto out_free_ntlmsspblob;
 	}
-	action = le16_to_cpu(pSMB->resp.Action);
-	if (action & GUEST_LOGIN)
+
+	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);
-	/* response can have either 3 or 4 word count - Samba sends 3 */
-	/* and lanman response is 3 */
+
 	bytes_remaining = get_bcc(smb_buf);
 	bcc_ptr = pByteArea(smb_buf);
-
 	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
 	if (blob_len > bytes_remaining) {
 		cifs_dbg(VFS, "bad security blob length %d\n",
-			 blob_len);
+				blob_len);
 		rc = -EINVAL;
-		goto ssetup_exit;
-	}
-	if (phase == NtLmChallenge) {
-		rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
-		if (rc)
-			goto ssetup_exit;
+		goto out_free_ntlmsspblob;
 	}
 	bcc_ptr += blob_len;
 	bytes_remaining -= blob_len;
 
+
 	/* BB check if Unicode and decode strings */
 	if (bytes_remaining == 0) {
 		/* no string area to decode, do nothing */
@@ -1357,61 +1341,113 @@  ssetup_ntlmssp_authenticate:
 			++bcc_ptr;
 			--bytes_remaining;
 		}
-		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp);
+		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
+				      sess_data->nls_cp);
 	} else {
-		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp);
+		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
+				    sess_data->nls_cp);
 	}
 
-ssetup_exit:
-	kfree(str_area);
+out_free_ntlmsspblob:
 	kfree(ntlmsspblob);
-	ntlmsspblob = NULL;
-	if (resp_buf_type == CIFS_SMALL_BUFFER) {
-		cifs_dbg(FYI, "ssetup freeing small buf %p\n", iov[0].iov_base);
-		cifs_small_buf_release(iov[0].iov_base);
-	} else if (resp_buf_type == CIFS_LARGE_BUFFER)
-		cifs_buf_release(iov[0].iov_base);
-
-	/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
-	if ((phase == NtLmChallenge) && (rc == 0))
-		goto ssetup_ntlmssp_authenticate;
-
-	if (!rc) {
-		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) {
-					rc = -ENOMEM;
-					mutex_unlock(&ses->server->srv_mutex);
-					goto keycp_exit;
-				}
-				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);
+out:
+	sess_free_buffer(sess_data);
 
-		cifs_dbg(FYI, "CIFS session established successfully\n");
-		spin_lock(&GlobalMid_Lock);
-		ses->status = CifsGood;
-		ses->need_reconnect = false;
-		spin_unlock(&GlobalMid_Lock);
-	}
+	 if (!rc)
+		rc = sess_establish_session(sess_data);
 
-keycp_exit:
+	/* Cleanup */
 	kfree(ses->auth_key.response);
 	ses->auth_key.response = NULL;
 	kfree(ses->ntlmssp);
+	ses->ntlmssp = NULL;
 
-	return rc;
+	sess_data->func = NULL;
+	sess_data->result = rc;
+}
 
-out:
+int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
+{
+	int type;
+
+	type = select_sectype(ses->server, ses->sectype);
+	cifs_dbg(FYI, "sess setup type %d\n", type);
+	if (type == Unspecified) {
+		cifs_dbg(VFS,
+			"Unable to select appropriate authentication method!");
+		return -EINVAL;
+	}
+
+	switch (type) {
+	case LANMAN:
+		/* 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
+		sess_data->func = sess_auth_lanman;
+		break;
+#else
+		return -EOPNOTSUPP;
+#endif
+	case NTLM:
+		sess_data->func = sess_auth_ntlm;
+		break;
+	case NTLMv2:
+		sess_data->func = sess_auth_ntlmv2;
+		break;
+	case Kerberos:
+#ifdef CONFIG_CIFS_UPCALL
+		sess_data->func = sess_auth_kerberos;
+		break;
+#else
+		cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
+		return -ENOSYS;
+		break;
+#endif /* CONFIG_CIFS_UPCALL */
+	case RawNTLMSSP:
+		sess_data->func = sess_auth_rawntlmssp_negotiate;
+		break;
+	default:
+		cifs_dbg(VFS, "secType %d not supported!\n", type);
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
+		    const struct nls_table *nls_cp)
+{
+	int rc = 0;
+	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;
+
+	rc = 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);
+
+	/* Store result before we free sess_data */
 	rc = sess_data->result;
+
+out:
 	kfree(sess_data);
 	return rc;
 }