diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/CHANGES | 3 | ||||
-rw-r--r-- | fs/cifs/sess.c | 252 |
2 files changed, 251 insertions, 4 deletions
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 43f2e0d061f6..f20c4069c220 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES | |||
@@ -8,7 +8,8 @@ accurately mark string length before allocating memory (which may help the | |||
8 | rare cases where a UTF-8 string is much larger than the UCS2 string that | 8 | rare cases where a UTF-8 string is much larger than the UCS2 string that |
9 | we converted from). Fix endianness of the vcnum field used during | 9 | we converted from). Fix endianness of the vcnum field used during |
10 | session setup to distinguish multiple mounts to same server from different | 10 | session setup to distinguish multiple mounts to same server from different |
11 | userids. | 11 | userids. Raw NTLMSSP fixed (it requires /proc/fs/cifs/experimental |
12 | flag to be set to 2, and mount must enable krb5 to turn on extended security). | ||
12 | 13 | ||
13 | Version 1.57 | 14 | Version 1.57 |
14 | ------------ | 15 | ------------ |
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index b2bdc2a33833..e68744e169b3 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
@@ -378,6 +378,186 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft, | |||
378 | return rc; | 378 | return rc; |
379 | } | 379 | } |
380 | 380 | ||
381 | static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, | ||
382 | struct cifsSesInfo *ses) | ||
383 | { | ||
384 | CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; | ||
385 | |||
386 | if (blob_len < sizeof(CHALLENGE_MESSAGE)) { | ||
387 | cERROR(1, ("challenge blob len %d too small", blob_len)); | ||
388 | return -EINVAL; | ||
389 | } | ||
390 | |||
391 | if (memcmp(pblob->Signature, "NTLMSSP", 8)) { | ||
392 | cERROR(1, ("blob signature incorrect %s", pblob->Signature)); | ||
393 | return -EINVAL; | ||
394 | } | ||
395 | if (pblob->MessageType != NtLmChallenge) { | ||
396 | cERROR(1, ("Incorrect message type %d", pblob->MessageType)); | ||
397 | return -EINVAL; | ||
398 | } | ||
399 | |||
400 | memcpy(ses->server->cryptKey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); | ||
401 | /* BB we could decode pblob->NegotiateFlags; some may be useful */ | ||
402 | /* In particular we can examine sign flags */ | ||
403 | /* BB spec says that if AvId field of MsvAvTimestamp is populated then | ||
404 | we must set the MIC field of the AUTHENTICATE_MESSAGE */ | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | #ifdef CONFIG_CIFS_EXPERIMENTAL | ||
410 | /* BB Move to ntlmssp.c eventually */ | ||
411 | |||
412 | /* We do not malloc the blob, it is passed in pbuffer, because | ||
413 | it is fixed size, and small, making this approach cleaner */ | ||
414 | static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, | ||
415 | struct cifsSesInfo *ses) | ||
416 | { | ||
417 | NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer; | ||
418 | __u32 flags; | ||
419 | |||
420 | memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); | ||
421 | sec_blob->MessageType = NtLmNegotiate; | ||
422 | |||
423 | /* BB is NTLMV2 session security format easier to use here? */ | ||
424 | flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | | ||
425 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | | ||
426 | NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; | ||
427 | if (ses->server->secMode & | ||
428 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) | ||
429 | flags |= NTLMSSP_NEGOTIATE_SIGN; | ||
430 | if (ses->server->secMode & SECMODE_SIGN_REQUIRED) | ||
431 | flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; | ||
432 | |||
433 | sec_blob->NegotiateFlags |= cpu_to_le32(flags); | ||
434 | |||
435 | sec_blob->WorkstationName.BufferOffset = 0; | ||
436 | sec_blob->WorkstationName.Length = 0; | ||
437 | sec_blob->WorkstationName.MaximumLength = 0; | ||
438 | |||
439 | /* Domain name is sent on the Challenge not Negotiate NTLMSSP request */ | ||
440 | sec_blob->DomainName.BufferOffset = 0; | ||
441 | sec_blob->DomainName.Length = 0; | ||
442 | sec_blob->DomainName.MaximumLength = 0; | ||
443 | } | ||
444 | |||
445 | /* We do not malloc the blob, it is passed in pbuffer, because its | ||
446 | maximum possible size is fixed and small, making this approach cleaner. | ||
447 | This function returns the length of the data in the blob */ | ||
448 | static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | ||
449 | struct cifsSesInfo *ses, | ||
450 | const struct nls_table *nls_cp, int first) | ||
451 | { | ||
452 | AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; | ||
453 | __u32 flags; | ||
454 | unsigned char *tmp; | ||
455 | char ntlm_session_key[CIFS_SESS_KEY_SIZE]; | ||
456 | |||
457 | memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); | ||
458 | sec_blob->MessageType = NtLmAuthenticate; | ||
459 | |||
460 | flags = NTLMSSP_NEGOTIATE_56 | | ||
461 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | | ||
462 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | | ||
463 | NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; | ||
464 | if (ses->server->secMode & | ||
465 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) | ||
466 | flags |= NTLMSSP_NEGOTIATE_SIGN; | ||
467 | if (ses->server->secMode & SECMODE_SIGN_REQUIRED) | ||
468 | flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; | ||
469 | |||
470 | tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE); | ||
471 | sec_blob->NegotiateFlags |= cpu_to_le32(flags); | ||
472 | |||
473 | sec_blob->LmChallengeResponse.BufferOffset = | ||
474 | cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE)); | ||
475 | sec_blob->LmChallengeResponse.Length = 0; | ||
476 | sec_blob->LmChallengeResponse.MaximumLength = 0; | ||
477 | |||
478 | /* calculate session key, BB what about adding similar ntlmv2 path? */ | ||
479 | SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key); | ||
480 | if (first) | ||
481 | cifs_calculate_mac_key(&ses->server->mac_signing_key, | ||
482 | ntlm_session_key, ses->password); | ||
483 | |||
484 | memcpy(tmp, ntlm_session_key, CIFS_SESS_KEY_SIZE); | ||
485 | sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
486 | sec_blob->NtChallengeResponse.Length = cpu_to_le16(CIFS_SESS_KEY_SIZE); | ||
487 | sec_blob->NtChallengeResponse.MaximumLength = | ||
488 | cpu_to_le16(CIFS_SESS_KEY_SIZE); | ||
489 | |||
490 | tmp += CIFS_SESS_KEY_SIZE; | ||
491 | |||
492 | if (ses->domainName == NULL) { | ||
493 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
494 | sec_blob->DomainName.Length = 0; | ||
495 | sec_blob->DomainName.MaximumLength = 0; | ||
496 | tmp += 2; | ||
497 | } else { | ||
498 | int len; | ||
499 | len = cifs_strtoUCS((__le16 *)tmp, ses->domainName, | ||
500 | MAX_USERNAME_SIZE, nls_cp); | ||
501 | len *= 2; /* unicode is 2 bytes each */ | ||
502 | len += 2; /* trailing null */ | ||
503 | sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
504 | sec_blob->DomainName.Length = cpu_to_le16(len); | ||
505 | sec_blob->DomainName.MaximumLength = cpu_to_le16(len); | ||
506 | tmp += len; | ||
507 | } | ||
508 | |||
509 | if (ses->userName == NULL) { | ||
510 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
511 | sec_blob->UserName.Length = 0; | ||
512 | sec_blob->UserName.MaximumLength = 0; | ||
513 | tmp += 2; | ||
514 | } else { | ||
515 | int len; | ||
516 | len = cifs_strtoUCS((__le16 *)tmp, ses->userName, | ||
517 | MAX_USERNAME_SIZE, nls_cp); | ||
518 | len *= 2; /* unicode is 2 bytes each */ | ||
519 | len += 2; /* trailing null */ | ||
520 | sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
521 | sec_blob->UserName.Length = cpu_to_le16(len); | ||
522 | sec_blob->UserName.MaximumLength = cpu_to_le16(len); | ||
523 | tmp += len; | ||
524 | } | ||
525 | |||
526 | sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
527 | sec_blob->WorkstationName.Length = 0; | ||
528 | sec_blob->WorkstationName.MaximumLength = 0; | ||
529 | tmp += 2; | ||
530 | |||
531 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
532 | sec_blob->SessionKey.Length = 0; | ||
533 | sec_blob->SessionKey.MaximumLength = 0; | ||
534 | return tmp - pbuffer; | ||
535 | } | ||
536 | |||
537 | |||
538 | static void setup_ntlmssp_neg_req(SESSION_SETUP_ANDX *pSMB, | ||
539 | struct cifsSesInfo *ses) | ||
540 | { | ||
541 | build_ntlmssp_negotiate_blob(&pSMB->req.SecurityBlob[0], ses); | ||
542 | pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE)); | ||
543 | |||
544 | return; | ||
545 | } | ||
546 | |||
547 | static int setup_ntlmssp_auth_req(SESSION_SETUP_ANDX *pSMB, | ||
548 | struct cifsSesInfo *ses, | ||
549 | const struct nls_table *nls, int first_time) | ||
550 | { | ||
551 | int bloblen; | ||
552 | |||
553 | bloblen = build_ntlmssp_auth_blob(&pSMB->req.SecurityBlob[0], ses, nls, | ||
554 | first_time); | ||
555 | pSMB->req.SecurityBlobLength = cpu_to_le16(bloblen); | ||
556 | |||
557 | return bloblen; | ||
558 | } | ||
559 | #endif | ||
560 | |||
381 | int | 561 | int |
382 | CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | 562 | CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, |
383 | const struct nls_table *nls_cp) | 563 | const struct nls_table *nls_cp) |
@@ -396,6 +576,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
396 | __u16 action; | 576 | __u16 action; |
397 | int bytes_remaining; | 577 | int bytes_remaining; |
398 | struct key *spnego_key = NULL; | 578 | struct key *spnego_key = NULL; |
579 | __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ | ||
399 | 580 | ||
400 | if (ses == NULL) | 581 | if (ses == NULL) |
401 | return -EINVAL; | 582 | return -EINVAL; |
@@ -403,6 +584,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
403 | type = ses->server->secType; | 584 | type = ses->server->secType; |
404 | 585 | ||
405 | cFYI(1, ("sess setup type %d", type)); | 586 | cFYI(1, ("sess setup type %d", type)); |
587 | ssetup_ntlmssp_authenticate: | ||
588 | if (phase == NtLmChallenge) | ||
589 | phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ | ||
590 | |||
406 | if (type == LANMAN) { | 591 | if (type == LANMAN) { |
407 | #ifndef CONFIG_CIFS_WEAK_PW_HASH | 592 | #ifndef CONFIG_CIFS_WEAK_PW_HASH |
408 | /* LANMAN and plaintext are less secure and off by default. | 593 | /* LANMAN and plaintext are less secure and off by default. |
@@ -616,9 +801,49 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
616 | goto ssetup_exit; | 801 | goto ssetup_exit; |
617 | #endif /* CONFIG_CIFS_UPCALL */ | 802 | #endif /* CONFIG_CIFS_UPCALL */ |
618 | } else { | 803 | } else { |
804 | #ifdef CONFIG_CIFS_EXPERIMENTAL | ||
805 | if ((experimEnabled > 1) && (type == RawNTLMSSP)) { | ||
806 | if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { | ||
807 | cERROR(1, ("NTLMSSP requires Unicode support")); | ||
808 | rc = -ENOSYS; | ||
809 | goto ssetup_exit; | ||
810 | } | ||
811 | |||
812 | cFYI(1, ("ntlmssp session setup phase %d", phase)); | ||
813 | pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; | ||
814 | capabilities |= CAP_EXTENDED_SECURITY; | ||
815 | pSMB->req.Capabilities |= cpu_to_le32(capabilities); | ||
816 | if (phase == NtLmNegotiate) { | ||
817 | setup_ntlmssp_neg_req(pSMB, ses); | ||
818 | iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); | ||
819 | } else if (phase == NtLmAuthenticate) { | ||
820 | int blob_len; | ||
821 | blob_len = setup_ntlmssp_auth_req(pSMB, ses, | ||
822 | nls_cp, | ||
823 | first_time); | ||
824 | iov[1].iov_len = blob_len; | ||
825 | } else { | ||
826 | cERROR(1, ("invalid phase %d", phase)); | ||
827 | rc = -ENOSYS; | ||
828 | goto ssetup_exit; | ||
829 | } | ||
830 | iov[1].iov_base = &pSMB->req.SecurityBlob[0]; | ||
831 | /* unicode strings must be word aligned */ | ||
832 | if ((iov[0].iov_len + iov[1].iov_len) % 2) { | ||
833 | *bcc_ptr = 0; | ||
834 | bcc_ptr++; | ||
835 | } | ||
836 | unicode_oslm_strings(&bcc_ptr, nls_cp); | ||
837 | } else { | ||
838 | cERROR(1, ("secType %d not supported!", type)); | ||
839 | rc = -ENOSYS; | ||
840 | goto ssetup_exit; | ||
841 | } | ||
842 | #else | ||
619 | cERROR(1, ("secType %d not supported!", type)); | 843 | cERROR(1, ("secType %d not supported!", type)); |
620 | rc = -ENOSYS; | 844 | rc = -ENOSYS; |
621 | goto ssetup_exit; | 845 | goto ssetup_exit; |
846 | #endif | ||
622 | } | 847 | } |
623 | 848 | ||
624 | iov[2].iov_base = str_area; | 849 | iov[2].iov_base = str_area; |
@@ -634,12 +859,23 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
634 | /* SMB request buf freed in SendReceive2 */ | 859 | /* SMB request buf freed in SendReceive2 */ |
635 | 860 | ||
636 | cFYI(1, ("ssetup rc from sendrecv2 is %d", rc)); | 861 | cFYI(1, ("ssetup rc from sendrecv2 is %d", rc)); |
637 | if (rc) | ||
638 | goto ssetup_exit; | ||
639 | 862 | ||
640 | pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base; | 863 | pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base; |
641 | smb_buf = (struct smb_hdr *)iov[0].iov_base; | 864 | smb_buf = (struct smb_hdr *)iov[0].iov_base; |
642 | 865 | ||
866 | if ((type == RawNTLMSSP) && (smb_buf->Status.CifsError == | ||
867 | cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) { | ||
868 | if (phase != NtLmNegotiate) { | ||
869 | cERROR(1, ("Unexpected more processing error")); | ||
870 | goto ssetup_exit; | ||
871 | } | ||
872 | /* NTLMSSP Negotiate sent now processing challenge (response) */ | ||
873 | phase = NtLmChallenge; /* process ntlmssp challenge */ | ||
874 | rc = 0; /* MORE_PROC rc is not an error here, but expected */ | ||
875 | } | ||
876 | if (rc) | ||
877 | goto ssetup_exit; | ||
878 | |||
643 | if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) { | 879 | if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) { |
644 | rc = -EIO; | 880 | rc = -EIO; |
645 | cERROR(1, ("bad word count %d", smb_buf->WordCount)); | 881 | cERROR(1, ("bad word count %d", smb_buf->WordCount)); |
@@ -658,12 +894,18 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
658 | if (smb_buf->WordCount == 4) { | 894 | if (smb_buf->WordCount == 4) { |
659 | __u16 blob_len; | 895 | __u16 blob_len; |
660 | blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); | 896 | blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); |
661 | bcc_ptr += blob_len; | ||
662 | if (blob_len > bytes_remaining) { | 897 | if (blob_len > bytes_remaining) { |
663 | cERROR(1, ("bad security blob length %d", blob_len)); | 898 | cERROR(1, ("bad security blob length %d", blob_len)); |
664 | rc = -EINVAL; | 899 | rc = -EINVAL; |
665 | goto ssetup_exit; | 900 | goto ssetup_exit; |
666 | } | 901 | } |
902 | if (phase == NtLmChallenge) { | ||
903 | rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); | ||
904 | /* now goto beginning for ntlmssp authenticate phase */ | ||
905 | if (rc) | ||
906 | goto ssetup_exit; | ||
907 | } | ||
908 | bcc_ptr += blob_len; | ||
667 | bytes_remaining -= blob_len; | 909 | bytes_remaining -= blob_len; |
668 | } | 910 | } |
669 | 911 | ||
@@ -692,5 +934,9 @@ ssetup_exit: | |||
692 | } else if (resp_buf_type == CIFS_LARGE_BUFFER) | 934 | } else if (resp_buf_type == CIFS_LARGE_BUFFER) |
693 | cifs_buf_release(iov[0].iov_base); | 935 | cifs_buf_release(iov[0].iov_base); |
694 | 936 | ||
937 | /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ | ||
938 | if ((phase == NtLmChallenge) && (rc == 0)) | ||
939 | goto ssetup_ntlmssp_authenticate; | ||
940 | |||
695 | return rc; | 941 | return rc; |
696 | } | 942 | } |