Commit 44b522de authored by Jerome Marchand's avatar Jerome Marchand Committed by Ben Hutchings

cifs: dynamic allocation of ntlmssp blob

commit b8da344b 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: default avatarJerome Marchand <jmarchan@redhat.com>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
[bwh: Backported to 3.16: adjust context, indentation]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 30622d3d
...@@ -133,6 +133,6 @@ typedef struct _AUTHENTICATE_MESSAGE { ...@@ -133,6 +133,6 @@ typedef struct _AUTHENTICATE_MESSAGE {
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); 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); 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, struct cifs_ses *ses,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
...@@ -363,19 +363,43 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, ...@@ -363,19 +363,43 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
sec_blob->DomainName.MaximumLength = 0; sec_blob->DomainName.MaximumLength = 0;
} }
/* We do not malloc the blob, it is passed in pbuffer, because its static int size_of_ntlmssp_blob(struct cifs_ses *ses)
maximum possible size is fixed and small, making this approach cleaner. {
This function returns the length of the data in the blob */ int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len
int build_ntlmssp_auth_blob(unsigned char *pbuffer, - 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, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
int rc; int rc;
AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; AUTHENTICATE_MESSAGE *sec_blob;
__u32 flags; __u32 flags;
unsigned char *tmp; 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); memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
sec_blob->MessageType = NtLmAuthenticate; sec_blob->MessageType = NtLmAuthenticate;
...@@ -390,7 +414,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -390,7 +414,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
flags |= NTLMSSP_NEGOTIATE_KEY_XCH; flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
} }
tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE); tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
sec_blob->NegotiateFlags = cpu_to_le32(flags); sec_blob->NegotiateFlags = cpu_to_le32(flags);
sec_blob->LmChallengeResponse.BufferOffset = sec_blob->LmChallengeResponse.BufferOffset =
...@@ -398,13 +422,9 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -398,13 +422,9 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
sec_blob->LmChallengeResponse.Length = 0; sec_blob->LmChallengeResponse.Length = 0;
sec_blob->LmChallengeResponse.MaximumLength = 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) { 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, memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
ses->auth_key.len - CIFS_SESS_KEY_SIZE); ses->auth_key.len - CIFS_SESS_KEY_SIZE);
tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
...@@ -422,7 +442,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -422,7 +442,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
} }
if (ses->domainName == NULL) { 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.Length = 0;
sec_blob->DomainName.MaximumLength = 0; sec_blob->DomainName.MaximumLength = 0;
tmp += 2; tmp += 2;
...@@ -431,14 +451,14 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -431,14 +451,14 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
CIFS_MAX_DOMAINNAME_LEN, nls_cp); CIFS_MAX_DOMAINNAME_LEN, nls_cp);
len *= 2; /* unicode is 2 bytes each */ 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.Length = cpu_to_le16(len);
sec_blob->DomainName.MaximumLength = cpu_to_le16(len); sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
tmp += len; tmp += len;
} }
if (ses->user_name == NULL) { 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.Length = 0;
sec_blob->UserName.MaximumLength = 0; sec_blob->UserName.MaximumLength = 0;
tmp += 2; tmp += 2;
...@@ -447,13 +467,13 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -447,13 +467,13 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
CIFS_MAX_USERNAME_LEN, nls_cp); CIFS_MAX_USERNAME_LEN, nls_cp);
len *= 2; /* unicode is 2 bytes each */ 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.Length = cpu_to_le16(len);
sec_blob->UserName.MaximumLength = cpu_to_le16(len); sec_blob->UserName.MaximumLength = cpu_to_le16(len);
tmp += 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.Length = 0;
sec_blob->WorkstationName.MaximumLength = 0; sec_blob->WorkstationName.MaximumLength = 0;
tmp += 2; tmp += 2;
...@@ -462,19 +482,19 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -462,19 +482,19 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
(ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
&& !calc_seckey(ses)) { && !calc_seckey(ses)) {
memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); 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.Length = cpu_to_le16(CIFS_CPHTXT_SIZE);
sec_blob->SessionKey.MaximumLength = sec_blob->SessionKey.MaximumLength =
cpu_to_le16(CIFS_CPHTXT_SIZE); cpu_to_le16(CIFS_CPHTXT_SIZE);
tmp += CIFS_CPHTXT_SIZE; tmp += CIFS_CPHTXT_SIZE;
} else { } 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.Length = 0;
sec_blob->SessionKey.MaximumLength = 0; sec_blob->SessionKey.MaximumLength = 0;
} }
*buflen = tmp - *pbuffer;
setup_ntlmv2_ret: setup_ntlmv2_ret:
*buflen = tmp - pbuffer;
return rc; return rc;
} }
...@@ -547,7 +567,7 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -547,7 +567,7 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
struct key *spnego_key = NULL; struct key *spnego_key = NULL;
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
u16 blob_len; u16 blob_len;
char *ntlmsspblob = NULL; unsigned char *ntlmsspblob = NULL;
if (ses == NULL) { if (ses == NULL) {
WARN(1, "%s: ses == NULL!", __func__); WARN(1, "%s: ses == NULL!", __func__);
...@@ -809,20 +829,7 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -809,20 +829,7 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
cpu_to_le16(sizeof(NEGOTIATE_MESSAGE)); cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
break; break;
case NtLmAuthenticate: case NtLmAuthenticate:
/* rc = build_ntlmssp_auth_blob(&ntlmsspblob,
* 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;
}
rc = build_ntlmssp_auth_blob(ntlmsspblob,
&blob_len, ses, nls_cp); &blob_len, ses, nls_cp);
if (rc) if (rc)
goto ssetup_exit; goto ssetup_exit;
......
...@@ -550,7 +550,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -550,7 +550,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = ses->server;
u16 blob_length = 0; u16 blob_length = 0;
char *security_blob; char *security_blob;
char *ntlmssp_blob = NULL; unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */ bool use_spnego = false; /* else use raw ntlmssp */
cifs_dbg(FYI, "Session Setup\n"); cifs_dbg(FYI, "Session Setup\n");
...@@ -631,13 +631,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -631,13 +631,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
} }
} else if (phase == NtLmAuthenticate) { } else if (phase == NtLmAuthenticate) {
req->hdr.SessionId = ses->Suid; req->hdr.SessionId = ses->Suid;
ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
GFP_KERNEL);
if (ntlmssp_blob == NULL) {
rc = -ENOMEM;
goto ssetup_exit;
}
rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses,
nls_cp); nls_cp);
if (rc) { if (rc) {
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment