diff options
author | Sachin Prabhu <sprabhu@redhat.com> | 2014-06-16 10:35:25 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2014-08-01 00:11:15 -0400 |
commit | 80a0e63751b4d872e8ebe0f14f89fdd3f5c8989b (patch) | |
tree | f194344e9c9d60865905a57c72a49b4eba668ab5 /fs/cifs | |
parent | 6d81ed1ec22dbe96b85a5eb6422b2ab0556f7cbc (diff) |
cifs: Split lanman auth from CIFS_SessSetup()
In preparation for splitting CIFS_SessSetup() into smaller more
manageable chunks, we first add helper functions.
We then proceed to split out lanman auth out of CIFS_SessSetup()
Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
Reviewed-by: Shirish Pargaonkar <spargaonkar@suse.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/sess.c | 303 |
1 files changed, 258 insertions, 45 deletions
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index e87387dbf39f..36d0b908f8b4 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
@@ -520,6 +520,240 @@ select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) | |||
520 | } | 520 | } |
521 | } | 521 | } |
522 | 522 | ||
523 | struct sess_data { | ||
524 | unsigned int xid; | ||
525 | struct cifs_ses *ses; | ||
526 | struct nls_table *nls_cp; | ||
527 | void (*func)(struct sess_data *); | ||
528 | int result; | ||
529 | |||
530 | /* we will send the SMB in three pieces: | ||
531 | * a fixed length beginning part, an optional | ||
532 | * SPNEGO blob (which can be zero length), and a | ||
533 | * last part which will include the strings | ||
534 | * and rest of bcc area. This allows us to avoid | ||
535 | * a large buffer 17K allocation | ||
536 | */ | ||
537 | int buf0_type; | ||
538 | struct kvec iov[3]; | ||
539 | }; | ||
540 | |||
541 | static int | ||
542 | sess_alloc_buffer(struct sess_data *sess_data, int wct) | ||
543 | { | ||
544 | int rc; | ||
545 | struct cifs_ses *ses = sess_data->ses; | ||
546 | struct smb_hdr *smb_buf; | ||
547 | |||
548 | rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, | ||
549 | (void **)&smb_buf); | ||
550 | |||
551 | if (rc) | ||
552 | return rc; | ||
553 | |||
554 | sess_data->iov[0].iov_base = (char *)smb_buf; | ||
555 | sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4; | ||
556 | /* | ||
557 | * This variable will be used to clear the buffer | ||
558 | * allocated above in case of any error in the calling function. | ||
559 | */ | ||
560 | sess_data->buf0_type = CIFS_SMALL_BUFFER; | ||
561 | |||
562 | /* 2000 big enough to fit max user, domain, NOS name etc. */ | ||
563 | sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL); | ||
564 | if (!sess_data->iov[2].iov_base) { | ||
565 | rc = -ENOMEM; | ||
566 | goto out_free_smb_buf; | ||
567 | } | ||
568 | |||
569 | return 0; | ||
570 | |||
571 | out_free_smb_buf: | ||
572 | kfree(smb_buf); | ||
573 | sess_data->iov[0].iov_base = NULL; | ||
574 | sess_data->iov[0].iov_len = 0; | ||
575 | sess_data->buf0_type = CIFS_NO_BUFFER; | ||
576 | return rc; | ||
577 | } | ||
578 | |||
579 | static void | ||
580 | sess_free_buffer(struct sess_data *sess_data) | ||
581 | { | ||
582 | |||
583 | free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base); | ||
584 | sess_data->buf0_type = CIFS_NO_BUFFER; | ||
585 | kfree(sess_data->iov[2].iov_base); | ||
586 | } | ||
587 | |||
588 | static int | ||
589 | sess_establish_session(struct sess_data *sess_data) | ||
590 | { | ||
591 | struct cifs_ses *ses = sess_data->ses; | ||
592 | |||
593 | mutex_lock(&ses->server->srv_mutex); | ||
594 | if (!ses->server->session_estab) { | ||
595 | if (ses->server->sign) { | ||
596 | ses->server->session_key.response = | ||
597 | kmemdup(ses->auth_key.response, | ||
598 | ses->auth_key.len, GFP_KERNEL); | ||
599 | if (!ses->server->session_key.response) { | ||
600 | mutex_unlock(&ses->server->srv_mutex); | ||
601 | return -ENOMEM; | ||
602 | } | ||
603 | ses->server->session_key.len = | ||
604 | ses->auth_key.len; | ||
605 | } | ||
606 | ses->server->sequence_number = 0x2; | ||
607 | ses->server->session_estab = true; | ||
608 | } | ||
609 | mutex_unlock(&ses->server->srv_mutex); | ||
610 | |||
611 | cifs_dbg(FYI, "CIFS session established successfully\n"); | ||
612 | spin_lock(&GlobalMid_Lock); | ||
613 | ses->status = CifsGood; | ||
614 | ses->need_reconnect = false; | ||
615 | spin_unlock(&GlobalMid_Lock); | ||
616 | |||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | static int | ||
621 | sess_sendreceive(struct sess_data *sess_data) | ||
622 | { | ||
623 | int rc; | ||
624 | struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base; | ||
625 | __u16 count; | ||
626 | |||
627 | count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; | ||
628 | smb_buf->smb_buf_length = | ||
629 | cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count); | ||
630 | put_bcc(count, smb_buf); | ||
631 | |||
632 | rc = SendReceive2(sess_data->xid, sess_data->ses, | ||
633 | sess_data->iov, 3 /* num_iovecs */, | ||
634 | &sess_data->buf0_type, | ||
635 | CIFS_LOG_ERROR); | ||
636 | |||
637 | return rc; | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * LANMAN and plaintext are less secure and off by default. | ||
642 | * So we make this explicitly be turned on in kconfig (in the | ||
643 | * build) and turned on at runtime (changed from the default) | ||
644 | * in proc/fs/cifs or via mount parm. Unfortunately this is | ||
645 | * needed for old Win (e.g. Win95), some obscure NAS and OS/2 | ||
646 | */ | ||
647 | #ifdef CONFIG_CIFS_WEAK_PW_HASH | ||
648 | static void | ||
649 | sess_auth_lanman(struct sess_data *sess_data) | ||
650 | { | ||
651 | int rc = 0; | ||
652 | struct smb_hdr *smb_buf; | ||
653 | SESSION_SETUP_ANDX *pSMB; | ||
654 | char *bcc_ptr; | ||
655 | struct cifs_ses *ses = sess_data->ses; | ||
656 | char lnm_session_key[CIFS_AUTH_RESP_SIZE]; | ||
657 | __u32 capabilities; | ||
658 | __u16 bytes_remaining; | ||
659 | |||
660 | /* lanman 2 style sessionsetup */ | ||
661 | /* wct = 10 */ | ||
662 | rc = sess_alloc_buffer(sess_data, 10); | ||
663 | if (rc) | ||
664 | goto out; | ||
665 | |||
666 | pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; | ||
667 | bcc_ptr = sess_data->iov[2].iov_base; | ||
668 | capabilities = cifs_ssetup_hdr(ses, pSMB); | ||
669 | |||
670 | pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; | ||
671 | |||
672 | /* no capabilities flags in old lanman negotiation */ | ||
673 | pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); | ||
674 | |||
675 | /* Calculate hash with password and copy into bcc_ptr. | ||
676 | * Encryption Key (stored as in cryptkey) gets used if the | ||
677 | * security mode bit in Negottiate Protocol response states | ||
678 | * to use challenge/response method (i.e. Password bit is 1). | ||
679 | */ | ||
680 | rc = calc_lanman_hash(ses->password, ses->server->cryptkey, | ||
681 | ses->server->sec_mode & SECMODE_PW_ENCRYPT ? | ||
682 | true : false, lnm_session_key); | ||
683 | |||
684 | memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); | ||
685 | bcc_ptr += CIFS_AUTH_RESP_SIZE; | ||
686 | |||
687 | /* | ||
688 | * can not sign if LANMAN negotiated so no need | ||
689 | * to calculate signing key? but what if server | ||
690 | * changed to do higher than lanman dialect and | ||
691 | * we reconnected would we ever calc signing_key? | ||
692 | */ | ||
693 | |||
694 | cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n"); | ||
695 | /* Unicode not allowed for LANMAN dialects */ | ||
696 | ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); | ||
697 | |||
698 | sess_data->iov[2].iov_len = (long) bcc_ptr - | ||
699 | (long) sess_data->iov[2].iov_base; | ||
700 | |||
701 | rc = sess_sendreceive(sess_data); | ||
702 | if (rc) | ||
703 | goto out; | ||
704 | |||
705 | pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; | ||
706 | smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; | ||
707 | |||
708 | /* lanman response has a word count of 3 */ | ||
709 | if (smb_buf->WordCount != 3) { | ||
710 | rc = -EIO; | ||
711 | cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); | ||
712 | goto out; | ||
713 | } | ||
714 | |||
715 | if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) | ||
716 | cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ | ||
717 | |||
718 | ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ | ||
719 | cifs_dbg(FYI, "UID = %llu\n", ses->Suid); | ||
720 | |||
721 | bytes_remaining = get_bcc(smb_buf); | ||
722 | bcc_ptr = pByteArea(smb_buf); | ||
723 | |||
724 | /* BB check if Unicode and decode strings */ | ||
725 | if (bytes_remaining == 0) { | ||
726 | /* no string area to decode, do nothing */ | ||
727 | } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { | ||
728 | /* unicode string area must be word-aligned */ | ||
729 | if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) { | ||
730 | ++bcc_ptr; | ||
731 | --bytes_remaining; | ||
732 | } | ||
733 | decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, | ||
734 | sess_data->nls_cp); | ||
735 | } else { | ||
736 | decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, | ||
737 | sess_data->nls_cp); | ||
738 | } | ||
739 | |||
740 | rc = sess_establish_session(sess_data); | ||
741 | out: | ||
742 | sess_data->result = rc; | ||
743 | sess_data->func = NULL; | ||
744 | sess_free_buffer(sess_data); | ||
745 | } | ||
746 | |||
747 | #else | ||
748 | |||
749 | static void | ||
750 | sess_auth_lanman(struct sess_data *sess_data) | ||
751 | { | ||
752 | sess_data->result = -EOPNOTSUPP; | ||
753 | sess_data->func = NULL; | ||
754 | } | ||
755 | #endif | ||
756 | |||
523 | int | 757 | int |
524 | CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, | 758 | CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, |
525 | const struct nls_table *nls_cp) | 759 | const struct nls_table *nls_cp) |
@@ -540,12 +774,21 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, | |||
540 | __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ | 774 | __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ |
541 | u16 blob_len; | 775 | u16 blob_len; |
542 | char *ntlmsspblob = NULL; | 776 | char *ntlmsspblob = NULL; |
777 | struct sess_data *sess_data; | ||
543 | 778 | ||
544 | if (ses == NULL) { | 779 | if (ses == NULL) { |
545 | WARN(1, "%s: ses == NULL!", __func__); | 780 | WARN(1, "%s: ses == NULL!", __func__); |
546 | return -EINVAL; | 781 | return -EINVAL; |
547 | } | 782 | } |
548 | 783 | ||
784 | sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); | ||
785 | if (!sess_data) | ||
786 | return -ENOMEM; | ||
787 | sess_data->xid = xid; | ||
788 | sess_data->ses = ses; | ||
789 | sess_data->buf0_type = CIFS_NO_BUFFER; | ||
790 | sess_data->nls_cp = (struct nls_table *) nls_cp; | ||
791 | |||
549 | type = select_sectype(ses->server, ses->sectype); | 792 | type = select_sectype(ses->server, ses->sectype); |
550 | cifs_dbg(FYI, "sess setup type %d\n", type); | 793 | cifs_dbg(FYI, "sess setup type %d\n", type); |
551 | if (type == Unspecified) { | 794 | if (type == Unspecified) { |
@@ -554,6 +797,14 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, | |||
554 | return -EINVAL; | 797 | return -EINVAL; |
555 | } | 798 | } |
556 | 799 | ||
800 | switch (type) { | ||
801 | case LANMAN: | ||
802 | sess_auth_lanman(sess_data); | ||
803 | goto out; | ||
804 | default: | ||
805 | cifs_dbg(FYI, "Continuing with CIFS_SessSetup\n"); | ||
806 | } | ||
807 | |||
557 | if (type == RawNTLMSSP) { | 808 | if (type == RawNTLMSSP) { |
558 | /* if memory allocation is successful, caller of this function | 809 | /* if memory allocation is successful, caller of this function |
559 | * frees it. | 810 | * frees it. |
@@ -569,17 +820,7 @@ ssetup_ntlmssp_authenticate: | |||
569 | if (phase == NtLmChallenge) | 820 | if (phase == NtLmChallenge) |
570 | phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ | 821 | phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ |
571 | 822 | ||
572 | if (type == LANMAN) { | 823 | if ((type == NTLM) || (type == NTLMv2)) { |
573 | #ifndef CONFIG_CIFS_WEAK_PW_HASH | ||
574 | /* LANMAN and plaintext are less secure and off by default. | ||
575 | So we make this explicitly be turned on in kconfig (in the | ||
576 | build) and turned on at runtime (changed from the default) | ||
577 | in proc/fs/cifs or via mount parm. Unfortunately this is | ||
578 | needed for old Win (e.g. Win95), some obscure NAS and OS/2 */ | ||
579 | return -EOPNOTSUPP; | ||
580 | #endif | ||
581 | wct = 10; /* lanman 2 style sessionsetup */ | ||
582 | } else if ((type == NTLM) || (type == NTLMv2)) { | ||
583 | /* For NTLMv2 failures eventually may need to retry NTLM */ | 824 | /* For NTLMv2 failures eventually may need to retry NTLM */ |
584 | wct = 13; /* old style NTLM sessionsetup */ | 825 | wct = 13; /* old style NTLM sessionsetup */ |
585 | } else /* same size: negotiate or auth, NTLMSSP or extended security */ | 826 | } else /* same size: negotiate or auth, NTLMSSP or extended security */ |
@@ -618,39 +859,7 @@ ssetup_ntlmssp_authenticate: | |||
618 | iov[1].iov_base = NULL; | 859 | iov[1].iov_base = NULL; |
619 | iov[1].iov_len = 0; | 860 | iov[1].iov_len = 0; |
620 | 861 | ||
621 | if (type == LANMAN) { | 862 | if (type == NTLM) { |
622 | #ifdef CONFIG_CIFS_WEAK_PW_HASH | ||
623 | char lnm_session_key[CIFS_AUTH_RESP_SIZE]; | ||
624 | |||
625 | pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; | ||
626 | |||
627 | /* no capabilities flags in old lanman negotiation */ | ||
628 | |||
629 | pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); | ||
630 | |||
631 | /* Calculate hash with password and copy into bcc_ptr. | ||
632 | * Encryption Key (stored as in cryptkey) gets used if the | ||
633 | * security mode bit in Negottiate Protocol response states | ||
634 | * to use challenge/response method (i.e. Password bit is 1). | ||
635 | */ | ||
636 | |||
637 | rc = calc_lanman_hash(ses->password, ses->server->cryptkey, | ||
638 | ses->server->sec_mode & SECMODE_PW_ENCRYPT ? | ||
639 | true : false, lnm_session_key); | ||
640 | |||
641 | memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); | ||
642 | bcc_ptr += CIFS_AUTH_RESP_SIZE; | ||
643 | |||
644 | /* can not sign if LANMAN negotiated so no need | ||
645 | to calculate signing key? but what if server | ||
646 | changed to do higher than lanman dialect and | ||
647 | we reconnected would we ever calc signing_key? */ | ||
648 | |||
649 | cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n"); | ||
650 | /* Unicode not allowed for LANMAN dialects */ | ||
651 | ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); | ||
652 | #endif | ||
653 | } else if (type == NTLM) { | ||
654 | pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); | 863 | pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); |
655 | pSMB->req_no_secext.CaseInsensitivePasswordLength = | 864 | pSMB->req_no_secext.CaseInsensitivePasswordLength = |
656 | cpu_to_le16(CIFS_AUTH_RESP_SIZE); | 865 | cpu_to_le16(CIFS_AUTH_RESP_SIZE); |
@@ -889,7 +1098,6 @@ ssetup_ntlmssp_authenticate: | |||
889 | } | 1098 | } |
890 | if (phase == NtLmChallenge) { | 1099 | if (phase == NtLmChallenge) { |
891 | rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); | 1100 | rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); |
892 | /* now goto beginning for ntlmssp authenticate phase */ | ||
893 | if (rc) | 1101 | if (rc) |
894 | goto ssetup_exit; | 1102 | goto ssetup_exit; |
895 | } | 1103 | } |
@@ -962,4 +1170,9 @@ keycp_exit: | |||
962 | kfree(ses->ntlmssp); | 1170 | kfree(ses->ntlmssp); |
963 | 1171 | ||
964 | return rc; | 1172 | return rc; |
1173 | |||
1174 | out: | ||
1175 | rc = sess_data->result; | ||
1176 | kfree(sess_data); | ||
1177 | return rc; | ||
965 | } | 1178 | } |