diff options
author | Shirish Pargaonkar <shirishpargaonkar@gmail.com> | 2010-09-18 23:02:18 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2010-09-29 15:04:29 -0400 |
commit | 2b149f11978b44199954710d32c0eecf6c9efd9c (patch) | |
tree | efd48d4b64fc07a244890d1fced9c767ae4f369b /fs/cifs/sess.c | |
parent | 5f98ca9afb9c004f8948c0d40920503de447918a (diff) |
cifs NTLMv2/NTLMSSP ntlmv2 within ntlmssp autentication code
Attribue Value (AV) pairs or Target Info (TI) pairs are part of
ntlmv2 authentication.
Structure ntlmv2_resp had only definition for two av pairs.
So removed it, and now allocation of av pairs is dynamic.
For servers like Windows 7/2008, av pairs sent by server in
challege packet (type 2 in the ntlmssp exchange/negotiation) can
vary.
Server sends them during ntlmssp negotiation. So when ntlmssp is used
as an authentication mechanism, type 2 challenge packet from server
has this information. Pluck it and use the entire blob for
authenticaiton purpose. If user has not specified, extract
(netbios) domain name from the av pairs which is used to calculate
ntlmv2 hash. Servers like Windows 7 are particular about the AV pair
blob.
Servers like Windows 2003, are not very strict about the contents
of av pair blob used during ntlmv2 authentication.
So when security mechanism such as ntlmv2 is used (not ntlmv2 in ntlmssp),
there is no negotiation and so genereate a minimal blob that gets
used in ntlmv2 authentication as well as gets sent.
Fields tilen and tilbob are session specific. AV pair values are defined.
To calculate ntlmv2 response we need ti/av pair blob.
For sec mech like ntlmssp, the blob is plucked from type 2 response from
the server. From this blob, netbios name of the domain is retrieved,
if user has not already provided, to be included in the Target String
as part of ntlmv2 hash calculations.
For sec mech like ntlmv2, create a minimal, two av pair blob.
The allocated blob is freed in case of error. In case there is no error,
this blob is used in calculating ntlmv2 response (in CalcNTLMv2_response)
and is also copied on the response to the server, and then freed.
The type 3 ntlmssp response is prepared on a buffer,
5 * sizeof of struct _AUTHENTICATE_MESSAGE, an empirical value large
enough to hold _AUTHENTICATE_MESSAGE plus a blob with max possible
10 values as part of ntlmv2 response and lmv2 keys and domain, user,
workstation names etc.
Also, kerberos gets selected as a default mechanism if server supports it,
over the other security mechanisms.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/sess.c')
-rw-r--r-- | fs/cifs/sess.c | 119 |
1 files changed, 81 insertions, 38 deletions
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 88820127650e..af18a500f7e0 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
@@ -383,6 +383,9 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft, | |||
383 | static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, | 383 | static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, |
384 | struct cifsSesInfo *ses) | 384 | struct cifsSesInfo *ses) |
385 | { | 385 | { |
386 | unsigned int tioffset; /* challenge message target info area */ | ||
387 | unsigned int tilen; /* challenge message target info area length */ | ||
388 | |||
386 | CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; | 389 | CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; |
387 | 390 | ||
388 | if (blob_len < sizeof(CHALLENGE_MESSAGE)) { | 391 | if (blob_len < sizeof(CHALLENGE_MESSAGE)) { |
@@ -405,6 +408,19 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, | |||
405 | /* BB spec says that if AvId field of MsvAvTimestamp is populated then | 408 | /* BB spec says that if AvId field of MsvAvTimestamp is populated then |
406 | we must set the MIC field of the AUTHENTICATE_MESSAGE */ | 409 | we must set the MIC field of the AUTHENTICATE_MESSAGE */ |
407 | 410 | ||
411 | tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset); | ||
412 | tilen = cpu_to_le16(pblob->TargetInfoArray.Length); | ||
413 | ses->tilen = tilen; | ||
414 | if (ses->tilen) { | ||
415 | ses->tiblob = kmalloc(tilen, GFP_KERNEL); | ||
416 | if (!ses->tiblob) { | ||
417 | cERROR(1, "Challenge target info allocation failure"); | ||
418 | ses->tilen = 0; | ||
419 | return -ENOMEM; | ||
420 | } | ||
421 | memcpy(ses->tiblob, bcc_ptr + tioffset, ses->tilen); | ||
422 | } | ||
423 | |||
408 | return 0; | 424 | return 0; |
409 | } | 425 | } |
410 | 426 | ||
@@ -425,7 +441,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, | |||
425 | /* BB is NTLMV2 session security format easier to use here? */ | 441 | /* BB is NTLMV2 session security format easier to use here? */ |
426 | flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | | 442 | flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | |
427 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | | 443 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | |
428 | NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; | 444 | NTLMSSP_NEGOTIATE_NTLM; |
429 | if (ses->server->secMode & | 445 | if (ses->server->secMode & |
430 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) | 446 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) |
431 | flags |= NTLMSSP_NEGOTIATE_SIGN; | 447 | flags |= NTLMSSP_NEGOTIATE_SIGN; |
@@ -449,12 +465,14 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, | |||
449 | This function returns the length of the data in the blob */ | 465 | This function returns the length of the data in the blob */ |
450 | static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | 466 | static int build_ntlmssp_auth_blob(unsigned char *pbuffer, |
451 | struct cifsSesInfo *ses, | 467 | struct cifsSesInfo *ses, |
452 | const struct nls_table *nls_cp, bool first) | 468 | const struct nls_table *nls_cp) |
453 | { | 469 | { |
470 | int rc; | ||
471 | unsigned int size; | ||
454 | AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; | 472 | AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; |
455 | __u32 flags; | 473 | __u32 flags; |
456 | unsigned char *tmp; | 474 | unsigned char *tmp; |
457 | char ntlm_session_key[CIFS_SESS_KEY_SIZE]; | 475 | struct ntlmv2_resp ntlmv2_response = {}; |
458 | 476 | ||
459 | memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); | 477 | memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); |
460 | sec_blob->MessageType = NtLmAuthenticate; | 478 | sec_blob->MessageType = NtLmAuthenticate; |
@@ -462,7 +480,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
462 | flags = NTLMSSP_NEGOTIATE_56 | | 480 | flags = NTLMSSP_NEGOTIATE_56 | |
463 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | | 481 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | |
464 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | | 482 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | |
465 | NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; | 483 | NTLMSSP_NEGOTIATE_NTLM; |
466 | if (ses->server->secMode & | 484 | if (ses->server->secMode & |
467 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) | 485 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) |
468 | flags |= NTLMSSP_NEGOTIATE_SIGN; | 486 | flags |= NTLMSSP_NEGOTIATE_SIGN; |
@@ -477,19 +495,26 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
477 | sec_blob->LmChallengeResponse.Length = 0; | 495 | sec_blob->LmChallengeResponse.Length = 0; |
478 | sec_blob->LmChallengeResponse.MaximumLength = 0; | 496 | sec_blob->LmChallengeResponse.MaximumLength = 0; |
479 | 497 | ||
480 | /* calculate session key, BB what about adding similar ntlmv2 path? */ | ||
481 | SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key); | ||
482 | if (first) | ||
483 | cifs_calculate_session_key(&ses->server->session_key, | ||
484 | ntlm_session_key, ses->password); | ||
485 | |||
486 | memcpy(tmp, ntlm_session_key, CIFS_SESS_KEY_SIZE); | ||
487 | sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); | 498 | sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); |
488 | sec_blob->NtChallengeResponse.Length = cpu_to_le16(CIFS_SESS_KEY_SIZE); | 499 | rc = setup_ntlmv2_rsp(ses, (char *)&ntlmv2_response, nls_cp); |
489 | sec_blob->NtChallengeResponse.MaximumLength = | 500 | if (rc) { |
490 | cpu_to_le16(CIFS_SESS_KEY_SIZE); | 501 | cERROR(1, "Error %d during NTLMSSP authentication", rc); |
502 | goto setup_ntlmv2_ret; | ||
503 | } | ||
504 | size = sizeof(struct ntlmv2_resp); | ||
505 | memcpy(tmp, (char *)&ntlmv2_response, size); | ||
506 | tmp += size; | ||
507 | if (ses->tilen > 0) { | ||
508 | memcpy(tmp, ses->tiblob, ses->tilen); | ||
509 | tmp += ses->tilen; | ||
510 | } | ||
491 | 511 | ||
492 | tmp += CIFS_SESS_KEY_SIZE; | 512 | sec_blob->NtChallengeResponse.Length = cpu_to_le16(size + ses->tilen); |
513 | sec_blob->NtChallengeResponse.MaximumLength = | ||
514 | cpu_to_le16(size + ses->tilen); | ||
515 | kfree(ses->tiblob); | ||
516 | ses->tiblob = NULL; | ||
517 | ses->tilen = 0; | ||
493 | 518 | ||
494 | if (ses->domainName == NULL) { | 519 | if (ses->domainName == NULL) { |
495 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 520 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
@@ -501,7 +526,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
501 | len = cifs_strtoUCS((__le16 *)tmp, ses->domainName, | 526 | len = cifs_strtoUCS((__le16 *)tmp, ses->domainName, |
502 | MAX_USERNAME_SIZE, nls_cp); | 527 | MAX_USERNAME_SIZE, nls_cp); |
503 | len *= 2; /* unicode is 2 bytes each */ | 528 | len *= 2; /* unicode is 2 bytes each */ |
504 | len += 2; /* trailing null */ | ||
505 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 529 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
506 | sec_blob->DomainName.Length = cpu_to_le16(len); | 530 | sec_blob->DomainName.Length = cpu_to_le16(len); |
507 | sec_blob->DomainName.MaximumLength = cpu_to_le16(len); | 531 | sec_blob->DomainName.MaximumLength = cpu_to_le16(len); |
@@ -518,7 +542,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
518 | len = cifs_strtoUCS((__le16 *)tmp, ses->userName, | 542 | len = cifs_strtoUCS((__le16 *)tmp, ses->userName, |
519 | MAX_USERNAME_SIZE, nls_cp); | 543 | MAX_USERNAME_SIZE, nls_cp); |
520 | len *= 2; /* unicode is 2 bytes each */ | 544 | len *= 2; /* unicode is 2 bytes each */ |
521 | len += 2; /* trailing null */ | ||
522 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); | 545 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); |
523 | sec_blob->UserName.Length = cpu_to_le16(len); | 546 | sec_blob->UserName.Length = cpu_to_le16(len); |
524 | sec_blob->UserName.MaximumLength = cpu_to_le16(len); | 547 | sec_blob->UserName.MaximumLength = cpu_to_le16(len); |
@@ -533,6 +556,8 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
533 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); | 556 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); |
534 | sec_blob->SessionKey.Length = 0; | 557 | sec_blob->SessionKey.Length = 0; |
535 | sec_blob->SessionKey.MaximumLength = 0; | 558 | sec_blob->SessionKey.MaximumLength = 0; |
559 | |||
560 | setup_ntlmv2_ret: | ||
536 | return tmp - pbuffer; | 561 | return tmp - pbuffer; |
537 | } | 562 | } |
538 | 563 | ||
@@ -545,19 +570,6 @@ static void setup_ntlmssp_neg_req(SESSION_SETUP_ANDX *pSMB, | |||
545 | 570 | ||
546 | return; | 571 | return; |
547 | } | 572 | } |
548 | |||
549 | static int setup_ntlmssp_auth_req(SESSION_SETUP_ANDX *pSMB, | ||
550 | struct cifsSesInfo *ses, | ||
551 | const struct nls_table *nls, bool first_time) | ||
552 | { | ||
553 | int bloblen; | ||
554 | |||
555 | bloblen = build_ntlmssp_auth_blob(&pSMB->req.SecurityBlob[0], ses, nls, | ||
556 | first_time); | ||
557 | pSMB->req.SecurityBlobLength = cpu_to_le16(bloblen); | ||
558 | |||
559 | return bloblen; | ||
560 | } | ||
561 | #endif | 573 | #endif |
562 | 574 | ||
563 | int | 575 | int |
@@ -580,6 +592,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, | |||
580 | struct key *spnego_key = NULL; | 592 | struct key *spnego_key = NULL; |
581 | __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ | 593 | __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ |
582 | bool first_time; | 594 | bool first_time; |
595 | int blob_len; | ||
596 | char *ntlmsspblob = NULL; | ||
583 | 597 | ||
584 | if (ses == NULL) | 598 | if (ses == NULL) |
585 | return -EINVAL; | 599 | return -EINVAL; |
@@ -729,12 +743,24 @@ ssetup_ntlmssp_authenticate: | |||
729 | cpu_to_le16(sizeof(struct ntlmv2_resp)); | 743 | cpu_to_le16(sizeof(struct ntlmv2_resp)); |
730 | 744 | ||
731 | /* calculate session key */ | 745 | /* calculate session key */ |
732 | setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); | 746 | rc = setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); |
733 | /* FIXME: calculate MAC key */ | 747 | if (rc) { |
748 | cERROR(1, "Error %d during NTLMv2 authentication", rc); | ||
749 | kfree(v2_sess_key); | ||
750 | goto ssetup_exit; | ||
751 | } | ||
734 | memcpy(bcc_ptr, (char *)v2_sess_key, | 752 | memcpy(bcc_ptr, (char *)v2_sess_key, |
735 | sizeof(struct ntlmv2_resp)); | 753 | sizeof(struct ntlmv2_resp)); |
736 | bcc_ptr += sizeof(struct ntlmv2_resp); | 754 | bcc_ptr += sizeof(struct ntlmv2_resp); |
737 | kfree(v2_sess_key); | 755 | kfree(v2_sess_key); |
756 | if (ses->tilen > 0) { | ||
757 | memcpy(bcc_ptr, ses->tiblob, ses->tilen); | ||
758 | bcc_ptr += ses->tilen; | ||
759 | /* we never did allocate ses->domainName to free */ | ||
760 | kfree(ses->tiblob); | ||
761 | ses->tiblob = NULL; | ||
762 | ses->tilen = 0; | ||
763 | } | ||
738 | if (ses->capabilities & CAP_UNICODE) { | 764 | if (ses->capabilities & CAP_UNICODE) { |
739 | if (iov[0].iov_len % 2) { | 765 | if (iov[0].iov_len % 2) { |
740 | *bcc_ptr = 0; | 766 | *bcc_ptr = 0; |
@@ -815,12 +841,28 @@ ssetup_ntlmssp_authenticate: | |||
815 | if (phase == NtLmNegotiate) { | 841 | if (phase == NtLmNegotiate) { |
816 | setup_ntlmssp_neg_req(pSMB, ses); | 842 | setup_ntlmssp_neg_req(pSMB, ses); |
817 | iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); | 843 | iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); |
844 | iov[1].iov_base = &pSMB->req.SecurityBlob[0]; | ||
818 | } else if (phase == NtLmAuthenticate) { | 845 | } else if (phase == NtLmAuthenticate) { |
819 | int blob_len; | 846 | /* 5 is an empirical value, large enought to |
820 | blob_len = setup_ntlmssp_auth_req(pSMB, ses, | 847 | * hold authenticate message, max 10 of |
821 | nls_cp, | 848 | * av paris, doamin,user,workstation mames, |
822 | first_time); | 849 | * flags etc.. |
850 | */ | ||
851 | ntlmsspblob = kmalloc( | ||
852 | 5*sizeof(struct _AUTHENTICATE_MESSAGE), | ||
853 | GFP_KERNEL); | ||
854 | if (!ntlmsspblob) { | ||
855 | cERROR(1, "Can't allocate NTLMSSP"); | ||
856 | rc = -ENOMEM; | ||
857 | goto ssetup_exit; | ||
858 | } | ||
859 | |||
860 | blob_len = build_ntlmssp_auth_blob(ntlmsspblob, | ||
861 | ses, nls_cp); | ||
823 | iov[1].iov_len = blob_len; | 862 | iov[1].iov_len = blob_len; |
863 | iov[1].iov_base = ntlmsspblob; | ||
864 | pSMB->req.SecurityBlobLength = | ||
865 | cpu_to_le16(blob_len); | ||
824 | /* Make sure that we tell the server that we | 866 | /* Make sure that we tell the server that we |
825 | are using the uid that it just gave us back | 867 | are using the uid that it just gave us back |
826 | on the response (challenge) */ | 868 | on the response (challenge) */ |
@@ -830,7 +872,6 @@ ssetup_ntlmssp_authenticate: | |||
830 | rc = -ENOSYS; | 872 | rc = -ENOSYS; |
831 | goto ssetup_exit; | 873 | goto ssetup_exit; |
832 | } | 874 | } |
833 | iov[1].iov_base = &pSMB->req.SecurityBlob[0]; | ||
834 | /* unicode strings must be word aligned */ | 875 | /* unicode strings must be word aligned */ |
835 | if ((iov[0].iov_len + iov[1].iov_len) % 2) { | 876 | if ((iov[0].iov_len + iov[1].iov_len) % 2) { |
836 | *bcc_ptr = 0; | 877 | *bcc_ptr = 0; |
@@ -931,6 +972,8 @@ ssetup_exit: | |||
931 | key_put(spnego_key); | 972 | key_put(spnego_key); |
932 | } | 973 | } |
933 | kfree(str_area); | 974 | kfree(str_area); |
975 | kfree(ntlmsspblob); | ||
976 | ntlmsspblob = NULL; | ||
934 | if (resp_buf_type == CIFS_SMALL_BUFFER) { | 977 | if (resp_buf_type == CIFS_SMALL_BUFFER) { |
935 | cFYI(1, "ssetup freeing small buf %p", iov[0].iov_base); | 978 | cFYI(1, "ssetup freeing small buf %p", iov[0].iov_base); |
936 | cifs_small_buf_release(iov[0].iov_base); | 979 | cifs_small_buf_release(iov[0].iov_base); |