Message ID | 1475863882-7223-2-git-send-email-sprabhu@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
2016-10-07 11:11 GMT-07:00 Sachin Prabhu <sprabhu@redhat.com>: > Add helper functions and split Kerberos authentication off > SMB2_sess_setup. > > Signed-off-by: Sachin Prabhu <sprabhu@redhat.com> > --- > fs/cifs/smb2pdu.c | 276 +++++++++++++++++++++++++++++++++++++++++++++--------- > 1 file changed, 230 insertions(+), 46 deletions(-) > > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c > index d6a0456..386b512 100644 > --- a/fs/cifs/smb2pdu.c > +++ b/fs/cifs/smb2pdu.c > @@ -593,6 +593,216 @@ vneg_out: > return -EIO; > } > > +struct SMB2_sess_data { > + unsigned int xid; > + struct cifs_ses *ses; > + struct nls_table *nls_cp; > + void (*func)(struct SMB2_sess_data *); > + int result; > + u64 previous_session; > + > + /* 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 > + */ > + int buf0_type; > + struct kvec iov[2]; > +}; > + > +static int > +SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) > +{ > + int rc; > + struct cifs_ses *ses = sess_data->ses; > + struct smb2_sess_setup_req *req; > + struct TCP_Server_Info *server = ses->server; > + > + rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); > + if (rc) > + return rc; > + > + req->hdr.SessionId = 0; /* First session, not a reauthenticate */ > + > + /* if reconnect, we need to send previous sess id, otherwise it is 0 */ > + req->PreviousSessionId = sess_data->previous_session; > + > + req->Flags = 0; /* MBZ */ > + /* to enable echos and oplocks */ > + req->hdr.CreditRequest = cpu_to_le16(3); > + > + /* 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; > + > + req->Capabilities = 0; > + req->Channel = 0; /* MBZ */ > + > + sess_data->iov[0].iov_base = (char *)req; > + /* 4 for rfc1002 length field and 1 for pad */ > + sess_data->iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; > + /* > + * This variable will be used to clear the buffer > + * allocated above in case of any error in the calling function. > + */ > + sess_data->buf0_type = CIFS_SMALL_BUFFER; > + > + return 0; > +} > + > +static void > +SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) > +{ > + free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base); > + sess_data->buf0_type = CIFS_NO_BUFFER; > +} > + > +static int > +SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) > +{ > + int rc; > + struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; > + > + /* 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(sess_data->iov[1].iov_len); > + > + inc_rfc1001_len(req, sess_data->iov[1].iov_len - 1 /* pad */); > + > + /* BB add code to build os and lm fields */ > + > + rc = SendReceive2(sess_data->xid, sess_data->ses, > + sess_data->iov, 2, > + &sess_data->buf0_type, > + CIFS_LOG_ERROR | CIFS_NEG_OP); > + > + return rc; > +} > + > +static int > +SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) > +{ > + int rc = 0; > + struct cifs_ses *ses = sess_data->ses; > + > + mutex_lock(&ses->server->srv_mutex); > + if (ses->server->sign && ses->server->ops->generate_signingkey) { > + rc = ses->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(&ses->server->srv_mutex); > + goto keygen_exit; > + } > + } > + if (!ses->server->session_estab) { > + ses->server->sequence_number = 0x2; > + ses->server->session_estab = true; > + } > + mutex_unlock(&ses->server->srv_mutex); > + > + cifs_dbg(FYI, "SMB2/3 session established successfully\n"); > + spin_lock(&GlobalMid_Lock); > + ses->status = CifsGood; > + ses->need_reconnect = false; > + spin_unlock(&GlobalMid_Lock); > + > +keygen_exit: > + if (!ses->server->sign) { > + kfree(ses->auth_key.response); > + ses->auth_key.response = NULL; > + } > + return rc; > +} > + > +#ifdef CONFIG_CIFS_UPCALL > +static void > +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) > +{ > + int rc; > + struct cifs_ses *ses = sess_data->ses; > + struct cifs_spnego_msg *msg; > + struct key *spnego_key = NULL; > + struct smb2_sess_setup_rsp *rsp = NULL; > + > + rc = SMB2_sess_alloc_buffer(sess_data); > + if (rc) > + goto out; > + > + spnego_key = cifs_get_spnego_key(ses); > + if (IS_ERR(spnego_key)) { > + rc = PTR_ERR(spnego_key); > + spnego_key = NULL; > + goto out; > + } > + > + msg = spnego_key->payload.data[0]; > + /* > + * check version field to make sure that cifs.upcall is > + * sending us a response in an expected form > + */ > + if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { > + cifs_dbg(VFS, > + "bad cifs.upcall version. Expected %d got %d", > + CIFS_SPNEGO_UPCALL_VERSION, msg->version); > + rc = -EKEYREJECTED; > + goto out_put_spnego_key; > + } > + > + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, > + GFP_KERNEL); > + if (!ses->auth_key.response) { > + cifs_dbg(VFS, > + "Kerberos can't allocate (%u bytes) memory", > + msg->sesskey_len); > + rc = -ENOMEM; > + goto out_put_spnego_key; > + } > + ses->auth_key.len = msg->sesskey_len; > + > + sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; > + sess_data->iov[1].iov_len = msg->secblob_len; > + > + rc = SMB2_sess_sendreceive(sess_data); > + if (rc) > + goto out_put_spnego_key; > + > + 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"); > + > + rc = SMB2_sess_establish_session(sess_data); > +out_put_spnego_key: > + key_invalidate(spnego_key); > + key_put(spnego_key); > +out: > + sess_data->result = rc; > + sess_data->func = NULL; > + SMB2_sess_free_buffer(sess_data); > +} > +#else > +static void > +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) > +{ > + cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); > + sess_data->result = -EOPNOTSUPP; > + sess_data->func = NULL; > +} > +#endif > + > int > SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, > const struct nls_table *nls_cp) > @@ -605,11 +815,11 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, > __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ > struct TCP_Server_Info *server = ses->server; > u16 blob_length = 0; > - struct key *spnego_key = NULL; > char *security_blob = NULL; > unsigned 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"); > > @@ -618,6 +828,20 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, > 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. > @@ -670,47 +894,7 @@ ssetup_ntlmssp_authenticate: > /* 4 for rfc1002 length field and 1 for pad */ > iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; > > - if (ses->sectype == Kerberos) { > -#ifdef CONFIG_CIFS_UPCALL > - struct cifs_spnego_msg *msg; > - > - spnego_key = cifs_get_spnego_key(ses); > - if (IS_ERR(spnego_key)) { > - rc = PTR_ERR(spnego_key); > - spnego_key = NULL; > - goto ssetup_exit; > - } > - > - msg = spnego_key->payload.data[0]; > - /* > - * check version field to make sure that cifs.upcall is > - * sending us a response in an expected form > - */ > - if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { > - cifs_dbg(VFS, > - "bad cifs.upcall version. Expected %d got %d", > - CIFS_SPNEGO_UPCALL_VERSION, msg->version); > - rc = -EKEYREJECTED; > - goto ssetup_exit; > - } > - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, > - GFP_KERNEL); > - if (!ses->auth_key.response) { > - cifs_dbg(VFS, > - "Kerberos can't allocate (%u bytes) memory", > - msg->sesskey_len); > - rc = -ENOMEM; > - goto ssetup_exit; > - } > - ses->auth_key.len = msg->sesskey_len; > - blob_length = msg->secblob_len; > - iov[1].iov_base = msg->data + msg->sesskey_len; > - iov[1].iov_len = blob_length; > -#else > - rc = -EOPNOTSUPP; > - goto ssetup_exit; > -#endif /* CONFIG_CIFS_UPCALL */ > - } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */ > + if (phase == NtLmNegotiate) { > ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), > GFP_KERNEL); > if (ntlmssp_blob == NULL) { > @@ -853,13 +1037,13 @@ keygen_exit: > kfree(ses->auth_key.response); > ses->auth_key.response = NULL; > } > - if (spnego_key) { > - key_invalidate(spnego_key); > - key_put(spnego_key); > - } > kfree(ses->ntlmssp); > > return rc; > +out: > + rc = sess_data->result; > + kfree(sess_data); > + return rc; > } > > int > -- > 2.7.4 > > -- > 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 Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d6a0456..386b512 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -593,6 +593,216 @@ vneg_out: return -EIO; } +struct SMB2_sess_data { + unsigned int xid; + struct cifs_ses *ses; + struct nls_table *nls_cp; + void (*func)(struct SMB2_sess_data *); + int result; + u64 previous_session; + + /* 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 + */ + int buf0_type; + struct kvec iov[2]; +}; + +static int +SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct smb2_sess_setup_req *req; + struct TCP_Server_Info *server = ses->server; + + rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); + if (rc) + return rc; + + req->hdr.SessionId = 0; /* First session, not a reauthenticate */ + + /* if reconnect, we need to send previous sess id, otherwise it is 0 */ + req->PreviousSessionId = sess_data->previous_session; + + req->Flags = 0; /* MBZ */ + /* to enable echos and oplocks */ + req->hdr.CreditRequest = cpu_to_le16(3); + + /* 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; + + req->Capabilities = 0; + req->Channel = 0; /* MBZ */ + + sess_data->iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field and 1 for pad */ + sess_data->iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; + /* + * This variable will be used to clear the buffer + * allocated above in case of any error in the calling function. + */ + sess_data->buf0_type = CIFS_SMALL_BUFFER; + + return 0; +} + +static void +SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) +{ + free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base); + sess_data->buf0_type = CIFS_NO_BUFFER; +} + +static int +SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) +{ + int rc; + struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; + + /* 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(sess_data->iov[1].iov_len); + + inc_rfc1001_len(req, sess_data->iov[1].iov_len - 1 /* pad */); + + /* BB add code to build os and lm fields */ + + rc = SendReceive2(sess_data->xid, sess_data->ses, + sess_data->iov, 2, + &sess_data->buf0_type, + CIFS_LOG_ERROR | CIFS_NEG_OP); + + return rc; +} + +static int +SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) +{ + int rc = 0; + struct cifs_ses *ses = sess_data->ses; + + mutex_lock(&ses->server->srv_mutex); + if (ses->server->sign && ses->server->ops->generate_signingkey) { + rc = ses->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(&ses->server->srv_mutex); + goto keygen_exit; + } + } + if (!ses->server->session_estab) { + ses->server->sequence_number = 0x2; + ses->server->session_estab = true; + } + mutex_unlock(&ses->server->srv_mutex); + + cifs_dbg(FYI, "SMB2/3 session established successfully\n"); + spin_lock(&GlobalMid_Lock); + ses->status = CifsGood; + ses->need_reconnect = false; + spin_unlock(&GlobalMid_Lock); + +keygen_exit: + if (!ses->server->sign) { + kfree(ses->auth_key.response); + ses->auth_key.response = NULL; + } + return rc; +} + +#ifdef CONFIG_CIFS_UPCALL +static void +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct cifs_spnego_msg *msg; + struct key *spnego_key = NULL; + struct smb2_sess_setup_rsp *rsp = NULL; + + rc = SMB2_sess_alloc_buffer(sess_data); + if (rc) + goto out; + + spnego_key = cifs_get_spnego_key(ses); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + spnego_key = NULL; + goto out; + } + + msg = spnego_key->payload.data[0]; + /* + * check version field to make sure that cifs.upcall is + * sending us a response in an expected form + */ + if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { + cifs_dbg(VFS, + "bad cifs.upcall version. Expected %d got %d", + CIFS_SPNEGO_UPCALL_VERSION, msg->version); + rc = -EKEYREJECTED; + goto out_put_spnego_key; + } + + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, + "Kerberos can't allocate (%u bytes) memory", + msg->sesskey_len); + rc = -ENOMEM; + goto out_put_spnego_key; + } + ses->auth_key.len = msg->sesskey_len; + + sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; + sess_data->iov[1].iov_len = msg->secblob_len; + + rc = SMB2_sess_sendreceive(sess_data); + if (rc) + goto out_put_spnego_key; + + 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"); + + rc = SMB2_sess_establish_session(sess_data); +out_put_spnego_key: + key_invalidate(spnego_key); + key_put(spnego_key); +out: + sess_data->result = rc; + sess_data->func = NULL; + SMB2_sess_free_buffer(sess_data); +} +#else +static void +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) +{ + cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); + sess_data->result = -EOPNOTSUPP; + sess_data->func = NULL; +} +#endif + int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp) @@ -605,11 +815,11 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ struct TCP_Server_Info *server = ses->server; u16 blob_length = 0; - struct key *spnego_key = NULL; char *security_blob = NULL; unsigned 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"); @@ -618,6 +828,20 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, 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. @@ -670,47 +894,7 @@ ssetup_ntlmssp_authenticate: /* 4 for rfc1002 length field and 1 for pad */ iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; - if (ses->sectype == Kerberos) { -#ifdef CONFIG_CIFS_UPCALL - struct cifs_spnego_msg *msg; - - spnego_key = cifs_get_spnego_key(ses); - if (IS_ERR(spnego_key)) { - rc = PTR_ERR(spnego_key); - spnego_key = NULL; - goto ssetup_exit; - } - - msg = spnego_key->payload.data[0]; - /* - * check version field to make sure that cifs.upcall is - * sending us a response in an expected form - */ - if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { - cifs_dbg(VFS, - "bad cifs.upcall version. Expected %d got %d", - CIFS_SPNEGO_UPCALL_VERSION, msg->version); - rc = -EKEYREJECTED; - goto ssetup_exit; - } - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, - "Kerberos can't allocate (%u bytes) memory", - msg->sesskey_len); - rc = -ENOMEM; - goto ssetup_exit; - } - ses->auth_key.len = msg->sesskey_len; - blob_length = msg->secblob_len; - iov[1].iov_base = msg->data + msg->sesskey_len; - iov[1].iov_len = blob_length; -#else - rc = -EOPNOTSUPP; - goto ssetup_exit; -#endif /* CONFIG_CIFS_UPCALL */ - } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */ + if (phase == NtLmNegotiate) { ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), GFP_KERNEL); if (ntlmssp_blob == NULL) { @@ -853,13 +1037,13 @@ keygen_exit: kfree(ses->auth_key.response); ses->auth_key.response = NULL; } - if (spnego_key) { - key_invalidate(spnego_key); - key_put(spnego_key); - } kfree(ses->ntlmssp); return rc; +out: + rc = sess_data->result; + kfree(sess_data); + return rc; } int
Add helper functions and split Kerberos authentication off SMB2_sess_setup. Signed-off-by: Sachin Prabhu <sprabhu@redhat.com> --- fs/cifs/smb2pdu.c | 276 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 230 insertions(+), 46 deletions(-)