Message ID | 1402929328-12866-6-git-send-email-sprabhu@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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 --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; }
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(-)