diff options
author | Jerome Marchand <jmarchan@redhat.com> | 2016-05-26 05:52:25 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2016-06-24 00:45:07 -0400 |
commit | b8da344b74c822e966c6d19d6b2321efe82c5d97 (patch) | |
tree | f4b6a50200af4e957e3ba0872e3555b74be21679 | |
parent | 202d772ba02b1deb8835a631cd8255943d1906a0 (diff) |
cifs: dynamic allocation of ntlmssp blob
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>
CC: Stable <stable@vger.kernel.org>
-rw-r--r-- | fs/cifs/ntlmssp.h | 2 | ||||
-rw-r--r-- | fs/cifs/sess.c | 76 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 10 |
3 files changed, 45 insertions, 43 deletions
diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index 848249fa120f..3079b38f0afb 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h | |||
@@ -133,6 +133,6 @@ typedef struct _AUTHENTICATE_MESSAGE { | |||
133 | 133 | ||
134 | int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); | 134 | int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); |
135 | void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); | 135 | void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); |
136 | int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen, | 136 | int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, |
137 | struct cifs_ses *ses, | 137 | struct cifs_ses *ses, |
138 | const struct nls_table *nls_cp); | 138 | const struct nls_table *nls_cp); |
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index c3d086e2bd56..a42e99c8e00e 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
@@ -364,19 +364,43 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, | |||
364 | sec_blob->DomainName.MaximumLength = 0; | 364 | sec_blob->DomainName.MaximumLength = 0; |
365 | } | 365 | } |
366 | 366 | ||
367 | /* We do not malloc the blob, it is passed in pbuffer, because its | 367 | static int size_of_ntlmssp_blob(struct cifs_ses *ses) |
368 | maximum possible size is fixed and small, making this approach cleaner. | 368 | { |
369 | This function returns the length of the data in the blob */ | 369 | int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len |
370 | int build_ntlmssp_auth_blob(unsigned char *pbuffer, | 370 | - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; |
371 | |||
372 | if (ses->domainName) | ||
373 | sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); | ||
374 | else | ||
375 | sz += 2; | ||
376 | |||
377 | if (ses->user_name) | ||
378 | sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); | ||
379 | else | ||
380 | sz += 2; | ||
381 | |||
382 | return sz; | ||
383 | } | ||
384 | |||
385 | int build_ntlmssp_auth_blob(unsigned char **pbuffer, | ||
371 | u16 *buflen, | 386 | u16 *buflen, |
372 | struct cifs_ses *ses, | 387 | struct cifs_ses *ses, |
373 | const struct nls_table *nls_cp) | 388 | const struct nls_table *nls_cp) |
374 | { | 389 | { |
375 | int rc; | 390 | int rc; |
376 | AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; | 391 | AUTHENTICATE_MESSAGE *sec_blob; |
377 | __u32 flags; | 392 | __u32 flags; |
378 | unsigned char *tmp; | 393 | unsigned char *tmp; |
379 | 394 | ||
395 | rc = setup_ntlmv2_rsp(ses, nls_cp); | ||
396 | if (rc) { | ||
397 | cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); | ||
398 | *buflen = 0; | ||
399 | goto setup_ntlmv2_ret; | ||
400 | } | ||
401 | *pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL); | ||
402 | sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer; | ||
403 | |||
380 | memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); | 404 | memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); |
381 | sec_blob->MessageType = NtLmAuthenticate; | 405 | sec_blob->MessageType = NtLmAuthenticate; |
382 | 406 | ||
@@ -391,7 +415,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
391 | flags |= NTLMSSP_NEGOTIATE_KEY_XCH; | 415 | flags |= NTLMSSP_NEGOTIATE_KEY_XCH; |
392 | } | 416 | } |
393 | 417 | ||
394 | tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE); | 418 | tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE); |
395 | sec_blob->NegotiateFlags = cpu_to_le32(flags); | 419 | sec_blob->NegotiateFlags = cpu_to_le32(flags); |
396 | 420 | ||
397 | sec_blob->LmChallengeResponse.BufferOffset = | 421 | sec_blob->LmChallengeResponse.BufferOffset = |
@@ -399,13 +423,9 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
399 | sec_blob->LmChallengeResponse.Length = 0; | 423 | sec_blob->LmChallengeResponse.Length = 0; |
400 | sec_blob->LmChallengeResponse.MaximumLength = 0; | 424 | sec_blob->LmChallengeResponse.MaximumLength = 0; |
401 | 425 | ||
402 | sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); | 426 | sec_blob->NtChallengeResponse.BufferOffset = |
427 | cpu_to_le32(tmp - *pbuffer); | ||
403 | if (ses->user_name != NULL) { | 428 | if (ses->user_name != NULL) { |
404 | rc = setup_ntlmv2_rsp(ses, nls_cp); | ||
405 | if (rc) { | ||
406 | cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); | ||
407 | goto setup_ntlmv2_ret; | ||
408 | } | ||
409 | memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, | 429 | memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, |
410 | ses->auth_key.len - CIFS_SESS_KEY_SIZE); | 430 | ses->auth_key.len - CIFS_SESS_KEY_SIZE); |
411 | tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; | 431 | tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; |
@@ -423,7 +443,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
423 | } | 443 | } |
424 | 444 | ||
425 | if (ses->domainName == NULL) { | 445 | if (ses->domainName == NULL) { |
426 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 446 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
427 | sec_blob->DomainName.Length = 0; | 447 | sec_blob->DomainName.Length = 0; |
428 | sec_blob->DomainName.MaximumLength = 0; | 448 | sec_blob->DomainName.MaximumLength = 0; |
429 | tmp += 2; | 449 | tmp += 2; |
@@ -432,14 +452,14 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
432 | len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, | 452 | len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, |
433 | CIFS_MAX_DOMAINNAME_LEN, nls_cp); | 453 | CIFS_MAX_DOMAINNAME_LEN, nls_cp); |
434 | len *= 2; /* unicode is 2 bytes each */ | 454 | len *= 2; /* unicode is 2 bytes each */ |
435 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 455 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
436 | sec_blob->DomainName.Length = cpu_to_le16(len); | 456 | sec_blob->DomainName.Length = cpu_to_le16(len); |
437 | sec_blob->DomainName.MaximumLength = cpu_to_le16(len); | 457 | sec_blob->DomainName.MaximumLength = cpu_to_le16(len); |
438 | tmp += len; | 458 | tmp += len; |
439 | } | 459 | } |
440 | 460 | ||
441 | if (ses->user_name == NULL) { | 461 | if (ses->user_name == NULL) { |
442 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 462 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
443 | sec_blob->UserName.Length = 0; | 463 | sec_blob->UserName.Length = 0; |
444 | sec_blob->UserName.MaximumLength = 0; | 464 | sec_blob->UserName.MaximumLength = 0; |
445 | tmp += 2; | 465 | tmp += 2; |
@@ -448,13 +468,13 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
448 | len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, | 468 | len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, |
449 | CIFS_MAX_USERNAME_LEN, nls_cp); | 469 | CIFS_MAX_USERNAME_LEN, nls_cp); |
450 | len *= 2; /* unicode is 2 bytes each */ | 470 | len *= 2; /* unicode is 2 bytes each */ |
451 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 471 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
452 | sec_blob->UserName.Length = cpu_to_le16(len); | 472 | sec_blob->UserName.Length = cpu_to_le16(len); |
453 | sec_blob->UserName.MaximumLength = cpu_to_le16(len); | 473 | sec_blob->UserName.MaximumLength = cpu_to_le16(len); |
454 | tmp += len; | 474 | tmp += len; |
455 | } | 475 | } |
456 | 476 | ||
457 | sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 477 | sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
458 | sec_blob->WorkstationName.Length = 0; | 478 | sec_blob->WorkstationName.Length = 0; |
459 | sec_blob->WorkstationName.MaximumLength = 0; | 479 | sec_blob->WorkstationName.MaximumLength = 0; |
460 | tmp += 2; | 480 | tmp += 2; |
@@ -463,19 +483,19 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
463 | (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) | 483 | (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) |
464 | && !calc_seckey(ses)) { | 484 | && !calc_seckey(ses)) { |
465 | memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); | 485 | memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); |
466 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); | 486 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
467 | sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); | 487 | sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); |
468 | sec_blob->SessionKey.MaximumLength = | 488 | sec_blob->SessionKey.MaximumLength = |
469 | cpu_to_le16(CIFS_CPHTXT_SIZE); | 489 | cpu_to_le16(CIFS_CPHTXT_SIZE); |
470 | tmp += CIFS_CPHTXT_SIZE; | 490 | tmp += CIFS_CPHTXT_SIZE; |
471 | } else { | 491 | } else { |
472 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); | 492 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); |
473 | sec_blob->SessionKey.Length = 0; | 493 | sec_blob->SessionKey.Length = 0; |
474 | sec_blob->SessionKey.MaximumLength = 0; | 494 | sec_blob->SessionKey.MaximumLength = 0; |
475 | } | 495 | } |
476 | 496 | ||
497 | *buflen = tmp - *pbuffer; | ||
477 | setup_ntlmv2_ret: | 498 | setup_ntlmv2_ret: |
478 | *buflen = tmp - pbuffer; | ||
479 | return rc; | 499 | return rc; |
480 | } | 500 | } |
481 | 501 | ||
@@ -1266,7 +1286,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) | |||
1266 | struct cifs_ses *ses = sess_data->ses; | 1286 | struct cifs_ses *ses = sess_data->ses; |
1267 | __u16 bytes_remaining; | 1287 | __u16 bytes_remaining; |
1268 | char *bcc_ptr; | 1288 | char *bcc_ptr; |
1269 | char *ntlmsspblob = NULL; | 1289 | unsigned char *ntlmsspblob = NULL; |
1270 | u16 blob_len; | 1290 | u16 blob_len; |
1271 | 1291 | ||
1272 | cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); | 1292 | cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); |
@@ -1279,19 +1299,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) | |||
1279 | /* Build security blob before we assemble the request */ | 1299 | /* Build security blob before we assemble the request */ |
1280 | pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; | 1300 | pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; |
1281 | smb_buf = (struct smb_hdr *)pSMB; | 1301 | smb_buf = (struct smb_hdr *)pSMB; |
1282 | /* | 1302 | rc = build_ntlmssp_auth_blob(&ntlmsspblob, |
1283 | * 5 is an empirical value, large enough to hold | ||
1284 | * authenticate message plus max 10 of av paris, | ||
1285 | * domain, user, workstation names, flags, etc. | ||
1286 | */ | ||
1287 | ntlmsspblob = kzalloc(5*sizeof(struct _AUTHENTICATE_MESSAGE), | ||
1288 | GFP_KERNEL); | ||
1289 | if (!ntlmsspblob) { | ||
1290 | rc = -ENOMEM; | ||
1291 | goto out; | ||
1292 | } | ||
1293 | |||
1294 | rc = build_ntlmssp_auth_blob(ntlmsspblob, | ||
1295 | &blob_len, ses, sess_data->nls_cp); | 1303 | &blob_len, ses, sess_data->nls_cp); |
1296 | if (rc) | 1304 | if (rc) |
1297 | goto out_free_ntlmsspblob; | 1305 | goto out_free_ntlmsspblob; |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 8f38e33d365b..c3e61a7a7c7c 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -588,7 +588,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, | |||
588 | u16 blob_length = 0; | 588 | u16 blob_length = 0; |
589 | struct key *spnego_key = NULL; | 589 | struct key *spnego_key = NULL; |
590 | char *security_blob = NULL; | 590 | char *security_blob = NULL; |
591 | char *ntlmssp_blob = NULL; | 591 | unsigned char *ntlmssp_blob = NULL; |
592 | bool use_spnego = false; /* else use raw ntlmssp */ | 592 | bool use_spnego = false; /* else use raw ntlmssp */ |
593 | 593 | ||
594 | cifs_dbg(FYI, "Session Setup\n"); | 594 | cifs_dbg(FYI, "Session Setup\n"); |
@@ -713,13 +713,7 @@ ssetup_ntlmssp_authenticate: | |||
713 | iov[1].iov_len = blob_length; | 713 | iov[1].iov_len = blob_length; |
714 | } else if (phase == NtLmAuthenticate) { | 714 | } else if (phase == NtLmAuthenticate) { |
715 | req->hdr.SessionId = ses->Suid; | 715 | req->hdr.SessionId = ses->Suid; |
716 | ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, | 716 | rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, |
717 | GFP_KERNEL); | ||
718 | if (ntlmssp_blob == NULL) { | ||
719 | rc = -ENOMEM; | ||
720 | goto ssetup_exit; | ||
721 | } | ||
722 | rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses, | ||
723 | nls_cp); | 717 | nls_cp); |
724 | if (rc) { | 718 | if (rc) { |
725 | cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", | 719 | cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", |