2016-10-07 11:11 GMT-07:00 Sachin Prabhu <sprabhu@xxxxxxxxxx>: > We split the rawntlmssp authentication into negotiate and > authencate parts. We also clean up the code and add helpers. > > Signed-off-by: Sachin Prabhu <sprabhu@xxxxxxxxxx> > --- > fs/cifs/smb2pdu.c | 361 ++++++++++++++++++++++++------------------------------ > 1 file changed, 162 insertions(+), 199 deletions(-) > > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c > index 386b512..5ca5ea46 100644 > --- a/fs/cifs/smb2pdu.c > +++ b/fs/cifs/smb2pdu.c > @@ -803,245 +803,208 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) > } > #endif > > -int > -SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, > - const struct nls_table *nls_cp) > +static void > +SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); > + > +static void > +SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) > { > - struct smb2_sess_setup_req *req; > + int rc; > + struct cifs_ses *ses = sess_data->ses; > struct smb2_sess_setup_rsp *rsp = NULL; > - struct kvec iov[2]; > - int rc = 0; > - int resp_buftype = CIFS_NO_BUFFER; > - __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ > - struct TCP_Server_Info *server = ses->server; > - u16 blob_length = 0; > - char *security_blob = NULL; > - unsigned char *ntlmssp_blob = NULL; > + char *ntlmssp_blob = NULL; > bool use_spnego = false; /* else use raw ntlmssp */ > - u64 previous_session = ses->Suid; > - struct SMB2_sess_data *sess_data; > - > - cifs_dbg(FYI, "Session Setup\n"); > - > - if (!server) { > - WARN(1, "%s: server is NULL!\n", __func__); > - return -EIO; > - } > - > - sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); > - if (!sess_data) > - return -ENOMEM; > - sess_data->xid = xid; > - sess_data->ses = ses; > - sess_data->buf0_type = CIFS_NO_BUFFER; > - sess_data->nls_cp = (struct nls_table *) nls_cp; > - sess_data->previous_session = ses->Suid; > - > - if (ses->sectype == Kerberos) { > - SMB2_auth_kerberos(sess_data); > - goto out; > - } > - > - /* > - * If we are here due to reconnect, free per-smb session key > - * in case signing was required. > - */ > - kfree(ses->auth_key.response); > - ses->auth_key.response = NULL; > + u16 blob_length = 0; > > /* > * If memory allocation is successful, caller of this function > * frees it. > */ > ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); > - if (!ses->ntlmssp) > - return -ENOMEM; > + if (!ses->ntlmssp) { > + rc = -ENOMEM; > + goto out_err; > + } > ses->ntlmssp->sesskey_per_smbsess = true; > > - /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ > - if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP) > - ses->sectype = RawNTLMSSP; > - > -ssetup_ntlmssp_authenticate: > - if (phase == NtLmChallenge) > - phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ > - > - rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); > + rc = SMB2_sess_alloc_buffer(sess_data); > if (rc) > - return rc; > - > - req->hdr.SessionId = 0; /* First session, not a reauthenticate */ > + goto out_err; > > - /* if reconnect, we need to send previous sess id, otherwise it is 0 */ > - req->PreviousSessionId = previous_session; > + ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), > + GFP_KERNEL); > + if (ntlmssp_blob == NULL) { > + rc = -ENOMEM; > + goto out; > + } > > - req->Flags = 0; /* MBZ */ > - /* to enable echos and oplocks */ > - req->hdr.CreditRequest = cpu_to_le16(3); > + build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); > + if (use_spnego) { > + /* BB eventually need to add this */ > + cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); > + rc = -EOPNOTSUPP; > + goto out; > + } else { > + blob_length = sizeof(struct _NEGOTIATE_MESSAGE); > + /* with raw NTLMSSP we don't encapsulate in SPNEGO */ > + } > + sess_data->iov[1].iov_base = ntlmssp_blob; > + sess_data->iov[1].iov_len = blob_length; > > - /* only one of SMB2 signing flags may be set in SMB2 request */ > - if (server->sign) > - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; > - else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ > - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; > - else > - req->SecurityMode = 0; > + rc = SMB2_sess_sendreceive(sess_data); > + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; > > - req->Capabilities = 0; > - req->Channel = 0; /* MBZ */ > + /* If true, rc here is expected and not an error */ > + if (sess_data->buf0_type != CIFS_NO_BUFFER && > + rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) > + rc = 0; > > - iov[0].iov_base = (char *)req; > - /* 4 for rfc1002 length field and 1 for pad */ > - iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; > + if (rc) > + goto out; > > - if (phase == NtLmNegotiate) { > - ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), > - GFP_KERNEL); > - if (ntlmssp_blob == NULL) { > - rc = -ENOMEM; > - goto ssetup_exit; > - } > - build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); > - if (use_spnego) { > - /* blob_length = build_spnego_ntlmssp_blob( > - &security_blob, > - sizeof(struct _NEGOTIATE_MESSAGE), > - ntlmssp_blob); */ > - /* BB eventually need to add this */ > - cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); > - rc = -EOPNOTSUPP; > - kfree(ntlmssp_blob); > - goto ssetup_exit; > - } else { > - blob_length = sizeof(struct _NEGOTIATE_MESSAGE); > - /* with raw NTLMSSP we don't encapsulate in SPNEGO */ > - security_blob = ntlmssp_blob; > - } > - iov[1].iov_base = security_blob; > - iov[1].iov_len = blob_length; > - } else if (phase == NtLmAuthenticate) { > - req->hdr.SessionId = ses->Suid; > - rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, > - nls_cp); > - if (rc) { > - cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", > - rc); > - goto ssetup_exit; /* BB double check error handling */ > - } > - if (use_spnego) { > - /* blob_length = build_spnego_ntlmssp_blob( > - &security_blob, > - blob_length, > - ntlmssp_blob); */ > - cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); > - rc = -EOPNOTSUPP; > - kfree(ntlmssp_blob); > - goto ssetup_exit; > - } else { > - security_blob = ntlmssp_blob; > - } > - iov[1].iov_base = security_blob; > - iov[1].iov_len = blob_length; > - } else { > - cifs_dbg(VFS, "illegal ntlmssp phase\n"); > + if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != > + le16_to_cpu(rsp->SecurityBufferOffset)) { > + cifs_dbg(VFS, "Invalid security buffer offset %d\n", > + le16_to_cpu(rsp->SecurityBufferOffset)); > rc = -EIO; > - goto ssetup_exit; > + goto out; > } > + rc = decode_ntlmssp_challenge(rsp->Buffer, > + le16_to_cpu(rsp->SecurityBufferLength), ses); > + if (rc) > + goto out; > > - /* Testing shows that buffer offset must be at location of Buffer[0] */ > - req->SecurityBufferOffset = > - cpu_to_le16(sizeof(struct smb2_sess_setup_req) - > - 1 /* pad */ - 4 /* rfc1001 len */); > - req->SecurityBufferLength = cpu_to_le16(blob_length); > + cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); > > - inc_rfc1001_len(req, blob_length - 1 /* pad */); > > - /* BB add code to build os and lm fields */ > + ses->Suid = rsp->hdr.SessionId; > + ses->session_flags = le16_to_cpu(rsp->SessionFlags); > + if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) > + cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); > > - rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, > - CIFS_LOG_ERROR | CIFS_NEG_OP); > +out: > + kfree(ntlmssp_blob); > + SMB2_sess_free_buffer(sess_data); > + if (!rc) { > + sess_data->result = 0; > + sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; > + return; > + } > +out_err: > + kfree(ses->ntlmssp); > + ses->ntlmssp = NULL; > + sess_data->result = rc; > + sess_data->func = NULL; > +} > > - kfree(security_blob); > - rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base; > - ses->Suid = rsp->hdr.SessionId; > - if (resp_buftype != CIFS_NO_BUFFER && > - rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) { > - if (phase != NtLmNegotiate) { > - cifs_dbg(VFS, "Unexpected more processing error\n"); > - goto ssetup_exit; > - } > - if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != > - le16_to_cpu(rsp->SecurityBufferOffset)) { > - cifs_dbg(VFS, "Invalid security buffer offset %d\n", > - le16_to_cpu(rsp->SecurityBufferOffset)); > - rc = -EIO; > - goto ssetup_exit; > - } > +static void > +SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) > +{ > + int rc; > + struct cifs_ses *ses = sess_data->ses; > + struct smb2_sess_setup_req *req; > + struct smb2_sess_setup_rsp *rsp = NULL; > + unsigned char *ntlmssp_blob = NULL; > + bool use_spnego = false; /* else use raw ntlmssp */ > + u16 blob_length = 0; > + > + rc = SMB2_sess_alloc_buffer(sess_data); > + if (rc) > + goto out; > + > + req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; > + req->hdr.SessionId = ses->Suid; > > - /* NTLMSSP Negotiate sent now processing challenge (response) */ > - phase = NtLmChallenge; /* process ntlmssp challenge */ > - rc = 0; /* MORE_PROCESSING is not an error here but expected */ > - rc = decode_ntlmssp_challenge(rsp->Buffer, > - le16_to_cpu(rsp->SecurityBufferLength), ses); > + rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, > + sess_data->nls_cp); > + if (rc) { > + cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); > + goto out; > } > > - /* > - * BB eventually add code for SPNEGO decoding of NtlmChallenge blob, > - * but at least the raw NTLMSSP case works. > - */ > - /* > - * No tcon so can't do > - * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); > - */ > - if (rc != 0) > - goto ssetup_exit; > + if (use_spnego) { > + /* BB eventually need to add this */ > + cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); > + rc = -EOPNOTSUPP; > + goto out; > + } > + sess_data->iov[1].iov_base = ntlmssp_blob; > + sess_data->iov[1].iov_len = blob_length; > + > + rc = SMB2_sess_sendreceive(sess_data); > + if (rc) > + goto out; > + > + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; > > + ses->Suid = rsp->hdr.SessionId; > ses->session_flags = le16_to_cpu(rsp->SessionFlags); > if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) > cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); > -ssetup_exit: > - free_rsp_buf(resp_buftype, rsp); > > - /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ > - if ((phase == NtLmChallenge) && (rc == 0)) > - goto ssetup_ntlmssp_authenticate; > + rc = SMB2_sess_establish_session(sess_data); > +out: > + kfree(ntlmssp_blob); > + SMB2_sess_free_buffer(sess_data); > + kfree(ses->ntlmssp); > + ses->ntlmssp = NULL; > + sess_data->result = rc; > + sess_data->func = NULL; > +} > > - if (!rc) { > - mutex_lock(&server->srv_mutex); > - if (server->sign && server->ops->generate_signingkey) { > - rc = server->ops->generate_signingkey(ses); > - kfree(ses->auth_key.response); > - ses->auth_key.response = NULL; > - if (rc) { > - cifs_dbg(FYI, > - "SMB3 session key generation failed\n"); > - mutex_unlock(&server->srv_mutex); > - goto keygen_exit; > - } > - } > - if (!server->session_estab) { > - server->sequence_number = 0x2; > - server->session_estab = true; > - } > - mutex_unlock(&server->srv_mutex); > +static int > +SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data) > +{ > + if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP) > + ses->sectype = RawNTLMSSP; > > - cifs_dbg(FYI, "SMB2/3 session established successfully\n"); > - spin_lock(&GlobalMid_Lock); > - ses->status = CifsGood; > - ses->need_reconnect = false; > - spin_unlock(&GlobalMid_Lock); > + switch (ses->sectype) { > + case Kerberos: > + sess_data->func = SMB2_auth_kerberos; > + break; > + case RawNTLMSSP: > + sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; > + break; > + default: > + cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype); > + return -EOPNOTSUPP; > } > > -keygen_exit: > - if (!server->sign) { > - kfree(ses->auth_key.response); > - ses->auth_key.response = NULL; > + return 0; > +} > + > +int > +SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, > + const struct nls_table *nls_cp) > +{ > + int rc = 0; > + struct TCP_Server_Info *server = ses->server; > + struct SMB2_sess_data *sess_data; > + > + cifs_dbg(FYI, "Session Setup\n"); > + > + if (!server) { > + WARN(1, "%s: server is NULL!\n", __func__); > + return -EIO; > } > - kfree(ses->ntlmssp); > > - return rc; > -out: > + sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); > + if (!sess_data) > + return -ENOMEM; > + > + rc = SMB2_select_sec(ses, sess_data); > + if (rc) > + goto out; > + sess_data->xid = xid; > + sess_data->ses = ses; > + sess_data->buf0_type = CIFS_NO_BUFFER; > + sess_data->nls_cp = (struct nls_table *) nls_cp; > + > + while (sess_data->func) > + sess_data->func(sess_data); > + > rc = sess_data->result; > +out: > kfree(sess_data); > return rc; > } > -- > 2.7.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html Reviewed-by: Pavel Shilovsky <pshilov@xxxxxxxxxxxxx> -- Best regards, Pavel Shilovsky -- 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