Looks good. Reviewed-by: Shirish Pargaonkar <spargaonkar@xxxxxxxx> On Mon, Jun 16, 2014 at 9:35 AM, Sachin Prabhu <sprabhu@xxxxxxxxxx> 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@xxxxxxxxxx> > --- > 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@xxxxxxxxxxxxxxx > 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@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html