| From b8da344b74c822e966c6d19d6b2321efe82c5d97 Mon Sep 17 00:00:00 2001 |
| From: Jerome Marchand <jmarchan@redhat.com> |
| Date: Thu, 26 May 2016 11:52:25 +0200 |
| Subject: cifs: dynamic allocation of ntlmssp blob |
| |
| From: Jerome Marchand <jmarchan@redhat.com> |
| |
| commit b8da344b74c822e966c6d19d6b2321efe82c5d97 upstream. |
| |
| In sess_auth_rawntlmssp_authenticate(), the ntlmssp blob is allocated |
| statically and its size is an "empirical" 5*sizeof(struct |
| _AUTHENTICATE_MESSAGE) (320B on x86_64). I don't know where this value |
| comes from or if it was ever appropriate, but it is currently |
| insufficient: the user and domain name in UTF16 could take 1kB by |
| themselves. Because of that, build_ntlmssp_auth_blob() might corrupt |
| memory (out-of-bounds write). The size of ntlmssp_blob in |
| SMB2_sess_setup() is too small too (sizeof(struct _NEGOTIATE_MESSAGE) |
| + 500). |
| |
| This patch allocates the blob dynamically in |
| build_ntlmssp_auth_blob(). |
| |
| Signed-off-by: Jerome Marchand <jmarchan@redhat.com> |
| Signed-off-by: Steve French <smfrench@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/cifs/ntlmssp.h | 2 - |
| fs/cifs/sess.c | 76 +++++++++++++++++++++++++++++------------------------- |
| fs/cifs/smb2pdu.c | 10 +------ |
| 3 files changed, 45 insertions(+), 43 deletions(-) |
| |
| --- a/fs/cifs/ntlmssp.h |
| +++ b/fs/cifs/ntlmssp.h |
| @@ -133,6 +133,6 @@ typedef struct _AUTHENTICATE_MESSAGE { |
| |
| int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); |
| void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); |
| -int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen, |
| +int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, |
| struct cifs_ses *ses, |
| const struct nls_table *nls_cp); |
| --- a/fs/cifs/sess.c |
| +++ b/fs/cifs/sess.c |
| @@ -364,19 +364,43 @@ void build_ntlmssp_negotiate_blob(unsign |
| sec_blob->DomainName.MaximumLength = 0; |
| } |
| |
| -/* We do not malloc the blob, it is passed in pbuffer, because its |
| - maximum possible size is fixed and small, making this approach cleaner. |
| - This function returns the length of the data in the blob */ |
| -int build_ntlmssp_auth_blob(unsigned char *pbuffer, |
| +static int size_of_ntlmssp_blob(struct cifs_ses *ses) |
| +{ |
| + int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len |
| + - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; |
| + |
| + if (ses->domainName) |
| + sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); |
| + else |
| + sz += 2; |
| + |
| + if (ses->user_name) |
| + sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); |
| + else |
| + sz += 2; |
| + |
| + return sz; |
| +} |
| + |
| +int build_ntlmssp_auth_blob(unsigned char **pbuffer, |
| u16 *buflen, |
| struct cifs_ses *ses, |
| const struct nls_table *nls_cp) |
| { |
| int rc; |
| - AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; |
| + AUTHENTICATE_MESSAGE *sec_blob; |
| __u32 flags; |
| unsigned char *tmp; |
| |
| + rc = setup_ntlmv2_rsp(ses, nls_cp); |
| + if (rc) { |
| + cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); |
| + *buflen = 0; |
| + goto setup_ntlmv2_ret; |
| + } |
| + *pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL); |
| + sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer; |
| + |
| memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); |
| sec_blob->MessageType = NtLmAuthenticate; |
| |
| @@ -391,7 +415,7 @@ int build_ntlmssp_auth_blob(unsigned cha |
| flags |= NTLMSSP_NEGOTIATE_KEY_XCH; |
| } |
| |
| - tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE); |
| + tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE); |
| sec_blob->NegotiateFlags = cpu_to_le32(flags); |
| |
| sec_blob->LmChallengeResponse.BufferOffset = |
| @@ -399,13 +423,9 @@ int build_ntlmssp_auth_blob(unsigned cha |
| sec_blob->LmChallengeResponse.Length = 0; |
| sec_blob->LmChallengeResponse.MaximumLength = 0; |
| |
| - sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->NtChallengeResponse.BufferOffset = |
| + cpu_to_le32(tmp - *pbuffer); |
| if (ses->user_name != NULL) { |
| - rc = setup_ntlmv2_rsp(ses, nls_cp); |
| - if (rc) { |
| - cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); |
| - goto setup_ntlmv2_ret; |
| - } |
| memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, |
| ses->auth_key.len - CIFS_SESS_KEY_SIZE); |
| tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; |
| @@ -423,7 +443,7 @@ int build_ntlmssp_auth_blob(unsigned cha |
| } |
| |
| if (ses->domainName == NULL) { |
| - sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
| sec_blob->DomainName.Length = 0; |
| sec_blob->DomainName.MaximumLength = 0; |
| tmp += 2; |
| @@ -432,14 +452,14 @@ int build_ntlmssp_auth_blob(unsigned cha |
| len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, |
| CIFS_MAX_USERNAME_LEN, nls_cp); |
| len *= 2; /* unicode is 2 bytes each */ |
| - sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
| sec_blob->DomainName.Length = cpu_to_le16(len); |
| sec_blob->DomainName.MaximumLength = cpu_to_le16(len); |
| tmp += len; |
| } |
| |
| if (ses->user_name == NULL) { |
| - sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
| sec_blob->UserName.Length = 0; |
| sec_blob->UserName.MaximumLength = 0; |
| tmp += 2; |
| @@ -448,13 +468,13 @@ int build_ntlmssp_auth_blob(unsigned cha |
| len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, |
| CIFS_MAX_USERNAME_LEN, nls_cp); |
| len *= 2; /* unicode is 2 bytes each */ |
| - sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
| sec_blob->UserName.Length = cpu_to_le16(len); |
| sec_blob->UserName.MaximumLength = cpu_to_le16(len); |
| tmp += len; |
| } |
| |
| - sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
| sec_blob->WorkstationName.Length = 0; |
| sec_blob->WorkstationName.MaximumLength = 0; |
| tmp += 2; |
| @@ -463,19 +483,19 @@ int build_ntlmssp_auth_blob(unsigned cha |
| (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) |
| && !calc_seckey(ses)) { |
| memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); |
| - sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
| sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); |
| sec_blob->SessionKey.MaximumLength = |
| cpu_to_le16(CIFS_CPHTXT_SIZE); |
| tmp += CIFS_CPHTXT_SIZE; |
| } else { |
| - sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); |
| + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
| sec_blob->SessionKey.Length = 0; |
| sec_blob->SessionKey.MaximumLength = 0; |
| } |
| |
| + *buflen = tmp - *pbuffer; |
| setup_ntlmv2_ret: |
| - *buflen = tmp - pbuffer; |
| return rc; |
| } |
| |
| @@ -1266,7 +1286,7 @@ sess_auth_rawntlmssp_authenticate(struct |
| struct cifs_ses *ses = sess_data->ses; |
| __u16 bytes_remaining; |
| char *bcc_ptr; |
| - char *ntlmsspblob = NULL; |
| + unsigned char *ntlmsspblob = NULL; |
| u16 blob_len; |
| |
| cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); |
| @@ -1279,19 +1299,7 @@ sess_auth_rawntlmssp_authenticate(struct |
| /* 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, |
| + rc = build_ntlmssp_auth_blob(&ntlmsspblob, |
| &blob_len, ses, sess_data->nls_cp); |
| if (rc) |
| goto out_free_ntlmsspblob; |
| --- a/fs/cifs/smb2pdu.c |
| +++ b/fs/cifs/smb2pdu.c |
| @@ -591,7 +591,7 @@ SMB2_sess_setup(const unsigned int xid, |
| u16 blob_length = 0; |
| struct key *spnego_key = NULL; |
| char *security_blob = NULL; |
| - char *ntlmssp_blob = NULL; |
| + unsigned char *ntlmssp_blob = NULL; |
| bool use_spnego = false; /* else use raw ntlmssp */ |
| |
| cifs_dbg(FYI, "Session Setup\n"); |
| @@ -716,13 +716,7 @@ ssetup_ntlmssp_authenticate: |
| iov[1].iov_len = blob_length; |
| } else if (phase == NtLmAuthenticate) { |
| req->hdr.SessionId = ses->Suid; |
| - ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, |
| - GFP_KERNEL); |
| - if (ntlmssp_blob == NULL) { |
| - rc = -ENOMEM; |
| - goto ssetup_exit; |
| - } |
| - rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses, |
| + rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, |
| nls_cp); |
| if (rc) { |
| cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", |