diff options
| -rw-r--r-- | fs/cifs/Kconfig | 3 | ||||
| -rw-r--r-- | fs/cifs/cifsencrypt.c | 113 | ||||
| -rw-r--r-- | fs/cifs/cifsglob.h | 26 | ||||
| -rw-r--r-- | fs/cifs/cifspdu.h | 6 | ||||
| -rw-r--r-- | fs/cifs/cifsproto.h | 4 | ||||
| -rw-r--r-- | fs/cifs/connect.c | 16 | ||||
| -rw-r--r-- | fs/cifs/sess.c | 26 |
7 files changed, 184 insertions, 10 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 917b7d449bb2..0ed213970ced 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig | |||
| @@ -2,6 +2,9 @@ config CIFS | |||
| 2 | tristate "CIFS support (advanced network filesystem, SMBFS successor)" | 2 | tristate "CIFS support (advanced network filesystem, SMBFS successor)" |
| 3 | depends on INET | 3 | depends on INET |
| 4 | select NLS | 4 | select NLS |
| 5 | select CRYPTO | ||
| 6 | select CRYPTO_MD5 | ||
| 7 | select CRYPTO_ARC4 | ||
| 5 | help | 8 | help |
| 6 | This is the client VFS module for the Common Internet File System | 9 | This is the client VFS module for the Common Internet File System |
| 7 | (CIFS) protocol which is the successor to the Server Message Block | 10 | (CIFS) protocol which is the successor to the Server Message Block |
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 987b479d55dd..eaa2327ee7af 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c | |||
| @@ -541,6 +541,119 @@ setup_ntlmv2_rsp_ret: | |||
| 541 | return rc; | 541 | return rc; |
| 542 | } | 542 | } |
| 543 | 543 | ||
| 544 | int | ||
| 545 | calc_seckey(struct cifsSesInfo *ses) | ||
| 546 | { | ||
| 547 | int rc; | ||
| 548 | struct crypto_blkcipher *tfm_arc4; | ||
| 549 | struct scatterlist sgin, sgout; | ||
| 550 | struct blkcipher_desc desc; | ||
| 551 | unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ | ||
| 552 | |||
| 553 | get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); | ||
| 554 | |||
| 555 | tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); | ||
| 556 | if (!tfm_arc4 || IS_ERR(tfm_arc4)) { | ||
| 557 | cERROR(1, "could not allocate crypto API arc4\n"); | ||
| 558 | return PTR_ERR(tfm_arc4); | ||
| 559 | } | ||
| 560 | |||
| 561 | desc.tfm = tfm_arc4; | ||
| 562 | |||
| 563 | crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response, | ||
| 564 | CIFS_SESS_KEY_SIZE); | ||
| 565 | |||
| 566 | sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE); | ||
| 567 | sg_init_one(&sgout, ses->ntlmssp.ciphertext, CIFS_CPHTXT_SIZE); | ||
| 568 | |||
| 569 | rc = crypto_blkcipher_encrypt(&desc, &sgout, &sgin, CIFS_CPHTXT_SIZE); | ||
| 570 | if (rc) { | ||
| 571 | cERROR(1, "could not encrypt session key rc: %d\n", rc); | ||
| 572 | crypto_free_blkcipher(tfm_arc4); | ||
| 573 | return rc; | ||
| 574 | } | ||
| 575 | |||
| 576 | /* make secondary_key/nonce as session key */ | ||
| 577 | memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); | ||
| 578 | /* and make len as that of session key only */ | ||
| 579 | ses->auth_key.len = CIFS_SESS_KEY_SIZE; | ||
| 580 | |||
| 581 | crypto_free_blkcipher(tfm_arc4); | ||
| 582 | |||
| 583 | return 0; | ||
| 584 | } | ||
| 585 | |||
| 586 | void | ||
| 587 | cifs_crypto_shash_release(struct TCP_Server_Info *server) | ||
| 588 | { | ||
| 589 | if (server->secmech.md5) | ||
| 590 | crypto_free_shash(server->secmech.md5); | ||
| 591 | |||
| 592 | if (server->secmech.hmacmd5) | ||
| 593 | crypto_free_shash(server->secmech.hmacmd5); | ||
| 594 | |||
| 595 | kfree(server->secmech.sdeschmacmd5); | ||
| 596 | |||
| 597 | kfree(server->secmech.sdescmd5); | ||
| 598 | } | ||
| 599 | |||
| 600 | int | ||
| 601 | cifs_crypto_shash_allocate(struct TCP_Server_Info *server) | ||
| 602 | { | ||
| 603 | int rc; | ||
| 604 | unsigned int size; | ||
| 605 | |||
| 606 | server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0); | ||
| 607 | if (!server->secmech.hmacmd5 || | ||
| 608 | IS_ERR(server->secmech.hmacmd5)) { | ||
| 609 | cERROR(1, "could not allocate crypto hmacmd5\n"); | ||
| 610 | return PTR_ERR(server->secmech.hmacmd5); | ||
| 611 | } | ||
| 612 | |||
| 613 | server->secmech.md5 = crypto_alloc_shash("md5", 0, 0); | ||
| 614 | if (!server->secmech.md5 || IS_ERR(server->secmech.md5)) { | ||
| 615 | cERROR(1, "could not allocate crypto md5\n"); | ||
| 616 | rc = PTR_ERR(server->secmech.md5); | ||
| 617 | goto crypto_allocate_md5_fail; | ||
| 618 | } | ||
| 619 | |||
| 620 | size = sizeof(struct shash_desc) + | ||
| 621 | crypto_shash_descsize(server->secmech.hmacmd5); | ||
| 622 | server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL); | ||
| 623 | if (!server->secmech.sdeschmacmd5) { | ||
| 624 | cERROR(1, "cifs_crypto_shash_allocate: can't alloc hmacmd5\n"); | ||
| 625 | rc = -ENOMEM; | ||
| 626 | goto crypto_allocate_hmacmd5_sdesc_fail; | ||
| 627 | } | ||
| 628 | server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5; | ||
| 629 | server->secmech.sdeschmacmd5->shash.flags = 0x0; | ||
| 630 | |||
| 631 | |||
| 632 | size = sizeof(struct shash_desc) + | ||
| 633 | crypto_shash_descsize(server->secmech.md5); | ||
| 634 | server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL); | ||
| 635 | if (!server->secmech.sdescmd5) { | ||
| 636 | cERROR(1, "cifs_crypto_shash_allocate: can't alloc md5\n"); | ||
| 637 | rc = -ENOMEM; | ||
| 638 | goto crypto_allocate_md5_sdesc_fail; | ||
| 639 | } | ||
| 640 | server->secmech.sdescmd5->shash.tfm = server->secmech.md5; | ||
| 641 | server->secmech.sdescmd5->shash.flags = 0x0; | ||
| 642 | |||
| 643 | return 0; | ||
| 644 | |||
| 645 | crypto_allocate_md5_sdesc_fail: | ||
| 646 | kfree(server->secmech.sdeschmacmd5); | ||
| 647 | |||
| 648 | crypto_allocate_hmacmd5_sdesc_fail: | ||
| 649 | crypto_free_shash(server->secmech.md5); | ||
| 650 | |||
| 651 | crypto_allocate_md5_fail: | ||
| 652 | crypto_free_shash(server->secmech.hmacmd5); | ||
| 653 | |||
| 654 | return rc; | ||
| 655 | } | ||
| 656 | |||
| 544 | void CalcNTLMv2_response(const struct cifsSesInfo *ses) | 657 | void CalcNTLMv2_response(const struct cifsSesInfo *ses) |
| 545 | { | 658 | { |
| 546 | unsigned int offset = CIFS_SESS_KEY_SIZE + 8; | 659 | unsigned int offset = CIFS_SESS_KEY_SIZE + 8; |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 66f76b2d270b..7ca5f6d8ed80 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
| @@ -25,6 +25,9 @@ | |||
| 25 | #include <linux/workqueue.h> | 25 | #include <linux/workqueue.h> |
| 26 | #include "cifs_fs_sb.h" | 26 | #include "cifs_fs_sb.h" |
| 27 | #include "cifsacl.h" | 27 | #include "cifsacl.h" |
| 28 | #include <crypto/internal/hash.h> | ||
| 29 | #include <linux/scatterlist.h> | ||
| 30 | |||
| 28 | /* | 31 | /* |
| 29 | * The sizes of various internal tables and strings | 32 | * The sizes of various internal tables and strings |
| 30 | */ | 33 | */ |
| @@ -102,6 +105,27 @@ struct session_key { | |||
| 102 | char *response; | 105 | char *response; |
| 103 | }; | 106 | }; |
| 104 | 107 | ||
| 108 | /* crypto security descriptor definition */ | ||
| 109 | struct sdesc { | ||
| 110 | struct shash_desc shash; | ||
| 111 | char ctx[]; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /* crypto hashing related structure/fields, not speicific to a sec mech */ | ||
| 115 | struct cifs_secmech { | ||
| 116 | struct crypto_shash *hmacmd5; /* hmac-md5 hash function */ | ||
| 117 | struct crypto_shash *md5; /* md5 hash function */ | ||
| 118 | struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */ | ||
| 119 | struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ | ||
| 120 | }; | ||
| 121 | |||
| 122 | /* per smb connection structure/fields */ | ||
| 123 | struct ntlmssp_auth { | ||
| 124 | __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */ | ||
| 125 | __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */ | ||
| 126 | unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ | ||
| 127 | }; | ||
| 128 | |||
| 105 | struct cifs_cred { | 129 | struct cifs_cred { |
| 106 | int uid; | 130 | int uid; |
| 107 | int gid; | 131 | int gid; |
| @@ -178,6 +202,7 @@ struct TCP_Server_Info { | |||
| 178 | struct session_key session_key; | 202 | struct session_key session_key; |
| 179 | unsigned long lstrp; /* when we got last response from this server */ | 203 | unsigned long lstrp; /* when we got last response from this server */ |
| 180 | u16 dialect; /* dialect index that server chose */ | 204 | u16 dialect; /* dialect index that server chose */ |
| 205 | struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ | ||
| 181 | /* extended security flavors that server supports */ | 206 | /* extended security flavors that server supports */ |
| 182 | bool sec_kerberos; /* supports plain Kerberos */ | 207 | bool sec_kerberos; /* supports plain Kerberos */ |
| 183 | bool sec_mskerberos; /* supports legacy MS Kerberos */ | 208 | bool sec_mskerberos; /* supports legacy MS Kerberos */ |
| @@ -220,6 +245,7 @@ struct cifsSesInfo { | |||
| 220 | char ntlmv2_hash[16]; | 245 | char ntlmv2_hash[16]; |
| 221 | unsigned int tilen; /* length of the target info blob */ | 246 | unsigned int tilen; /* length of the target info blob */ |
| 222 | unsigned char *tiblob; /* target info blob in challenge response */ | 247 | unsigned char *tiblob; /* target info blob in challenge response */ |
| 248 | struct ntlmssp_auth ntlmssp; /* ciphertext, flags */ | ||
| 223 | bool need_reconnect:1; /* connection reset, uid now invalid */ | 249 | bool need_reconnect:1; /* connection reset, uid now invalid */ |
| 224 | }; | 250 | }; |
| 225 | /* no more than one of the following three session flags may be set */ | 251 | /* no more than one of the following three session flags may be set */ |
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index a152cd6db99b..de36b09763a8 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h | |||
| @@ -140,6 +140,12 @@ | |||
| 140 | */ | 140 | */ |
| 141 | #define CIFS_SESS_KEY_SIZE (16) | 141 | #define CIFS_SESS_KEY_SIZE (16) |
| 142 | 142 | ||
| 143 | #define CIFS_CLIENT_CHALLENGE_SIZE (8) | ||
| 144 | #define CIFS_SERVER_CHALLENGE_SIZE (8) | ||
| 145 | #define CIFS_HMAC_MD5_HASH_SIZE (16) | ||
| 146 | #define CIFS_CPHTXT_SIZE (16) | ||
| 147 | #define CIFS_NTHASH_SIZE (16) | ||
| 148 | |||
| 143 | /* | 149 | /* |
| 144 | * Maximum user name length | 150 | * Maximum user name length |
| 145 | */ | 151 | */ |
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 8c2d0cf1a62f..1e4728bcf065 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
| @@ -368,6 +368,10 @@ extern void SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *); | |||
| 368 | extern int setup_ntlm_response(struct cifsSesInfo *); | 368 | extern int setup_ntlm_response(struct cifsSesInfo *); |
| 369 | extern void CalcNTLMv2_response(const struct cifsSesInfo *); | 369 | extern void CalcNTLMv2_response(const struct cifsSesInfo *); |
| 370 | extern int setup_ntlmv2_rsp(struct cifsSesInfo *, const struct nls_table *); | 370 | extern int setup_ntlmv2_rsp(struct cifsSesInfo *, const struct nls_table *); |
| 371 | extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); | ||
| 372 | extern void cifs_crypto_shash_release(struct TCP_Server_Info *); | ||
| 373 | extern int calc_seckey(struct cifsSesInfo *); | ||
| 374 | |||
| 371 | #ifdef CONFIG_CIFS_WEAK_PW_HASH | 375 | #ifdef CONFIG_CIFS_WEAK_PW_HASH |
| 372 | extern void calc_lanman_hash(const char *password, const char *cryptkey, | 376 | extern void calc_lanman_hash(const char *password, const char *cryptkey, |
| 373 | bool encrypt, char *lnm_session_key); | 377 | bool encrypt, char *lnm_session_key); |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index dd9a4ae1d21d..04239a7ff320 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
| @@ -1563,6 +1563,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) | |||
| 1563 | server->tcpStatus = CifsExiting; | 1563 | server->tcpStatus = CifsExiting; |
| 1564 | spin_unlock(&GlobalMid_Lock); | 1564 | spin_unlock(&GlobalMid_Lock); |
| 1565 | 1565 | ||
| 1566 | cifs_crypto_shash_release(server); | ||
| 1566 | cifs_fscache_release_client_cookie(server); | 1567 | cifs_fscache_release_client_cookie(server); |
| 1567 | 1568 | ||
| 1568 | kfree(server->session_key.response); | 1569 | kfree(server->session_key.response); |
| @@ -1621,10 +1622,16 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
| 1621 | goto out_err; | 1622 | goto out_err; |
| 1622 | } | 1623 | } |
| 1623 | 1624 | ||
| 1625 | rc = cifs_crypto_shash_allocate(tcp_ses); | ||
| 1626 | if (rc) { | ||
| 1627 | cERROR(1, "could not setup hash structures rc %d", rc); | ||
| 1628 | goto out_err; | ||
| 1629 | } | ||
| 1630 | |||
| 1624 | tcp_ses->hostname = extract_hostname(volume_info->UNC); | 1631 | tcp_ses->hostname = extract_hostname(volume_info->UNC); |
| 1625 | if (IS_ERR(tcp_ses->hostname)) { | 1632 | if (IS_ERR(tcp_ses->hostname)) { |
| 1626 | rc = PTR_ERR(tcp_ses->hostname); | 1633 | rc = PTR_ERR(tcp_ses->hostname); |
| 1627 | goto out_err; | 1634 | goto out_err2; |
| 1628 | } | 1635 | } |
| 1629 | 1636 | ||
| 1630 | tcp_ses->noblocksnd = volume_info->noblocksnd; | 1637 | tcp_ses->noblocksnd = volume_info->noblocksnd; |
| @@ -1668,7 +1675,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
| 1668 | } | 1675 | } |
| 1669 | if (rc < 0) { | 1676 | if (rc < 0) { |
| 1670 | cERROR(1, "Error connecting to socket. Aborting operation"); | 1677 | cERROR(1, "Error connecting to socket. Aborting operation"); |
| 1671 | goto out_err; | 1678 | goto out_err2; |
| 1672 | } | 1679 | } |
| 1673 | 1680 | ||
| 1674 | /* | 1681 | /* |
| @@ -1682,7 +1689,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
| 1682 | rc = PTR_ERR(tcp_ses->tsk); | 1689 | rc = PTR_ERR(tcp_ses->tsk); |
| 1683 | cERROR(1, "error %d create cifsd thread", rc); | 1690 | cERROR(1, "error %d create cifsd thread", rc); |
| 1684 | module_put(THIS_MODULE); | 1691 | module_put(THIS_MODULE); |
| 1685 | goto out_err; | 1692 | goto out_err2; |
| 1686 | } | 1693 | } |
| 1687 | 1694 | ||
| 1688 | /* thread spawned, put it on the list */ | 1695 | /* thread spawned, put it on the list */ |
| @@ -1694,6 +1701,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
| 1694 | 1701 | ||
| 1695 | return tcp_ses; | 1702 | return tcp_ses; |
| 1696 | 1703 | ||
| 1704 | out_err2: | ||
| 1705 | cifs_crypto_shash_release(tcp_ses); | ||
| 1706 | |||
| 1697 | out_err: | 1707 | out_err: |
| 1698 | if (tcp_ses) { | 1708 | if (tcp_ses) { |
| 1699 | if (!IS_ERR(tcp_ses->hostname)) | 1709 | if (!IS_ERR(tcp_ses->hostname)) |
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index b2934683bd08..d998c4f7aae5 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
| @@ -404,7 +404,7 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, | |||
| 404 | /* In particular we can examine sign flags */ | 404 | /* In particular we can examine sign flags */ |
| 405 | /* BB spec says that if AvId field of MsvAvTimestamp is populated then | 405 | /* BB spec says that if AvId field of MsvAvTimestamp is populated then |
| 406 | we must set the MIC field of the AUTHENTICATE_MESSAGE */ | 406 | we must set the MIC field of the AUTHENTICATE_MESSAGE */ |
| 407 | 407 | ses->ntlmssp.server_flags = le32_to_cpu(pblob->NegotiateFlags); | |
| 408 | tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset); | 408 | tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset); |
| 409 | tilen = cpu_to_le16(pblob->TargetInfoArray.Length); | 409 | tilen = cpu_to_le16(pblob->TargetInfoArray.Length); |
| 410 | ses->tilen = tilen; | 410 | ses->tilen = tilen; |
| @@ -440,10 +440,12 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, | |||
| 440 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | | 440 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | |
| 441 | NTLMSSP_NEGOTIATE_NTLM; | 441 | NTLMSSP_NEGOTIATE_NTLM; |
| 442 | if (ses->server->secMode & | 442 | if (ses->server->secMode & |
| 443 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) | 443 | (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { |
| 444 | flags |= NTLMSSP_NEGOTIATE_SIGN; | 444 | flags |= NTLMSSP_NEGOTIATE_SIGN; |
| 445 | if (ses->server->secMode & SECMODE_SIGN_REQUIRED) | 445 | if (!ses->server->session_estab) |
| 446 | flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; | 446 | flags |= NTLMSSP_NEGOTIATE_KEY_XCH | |
| 447 | NTLMSSP_NEGOTIATE_EXTENDED_SEC; | ||
| 448 | } | ||
| 447 | 449 | ||
| 448 | sec_blob->NegotiateFlags |= cpu_to_le32(flags); | 450 | sec_blob->NegotiateFlags |= cpu_to_le32(flags); |
| 449 | 451 | ||
| @@ -543,9 +545,19 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | |||
| 543 | sec_blob->WorkstationName.MaximumLength = 0; | 545 | sec_blob->WorkstationName.MaximumLength = 0; |
| 544 | tmp += 2; | 546 | tmp += 2; |
| 545 | 547 | ||
| 546 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); | 548 | if ((ses->ntlmssp.server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && |
| 547 | sec_blob->SessionKey.Length = 0; | 549 | !calc_seckey(ses)) { |
| 548 | sec_blob->SessionKey.MaximumLength = 0; | 550 | memcpy(tmp, ses->ntlmssp.ciphertext, CIFS_CPHTXT_SIZE); |
| 551 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
| 552 | sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); | ||
| 553 | sec_blob->SessionKey.MaximumLength = | ||
| 554 | cpu_to_le16(CIFS_CPHTXT_SIZE); | ||
| 555 | tmp += CIFS_CPHTXT_SIZE; | ||
| 556 | } else { | ||
| 557 | sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); | ||
| 558 | sec_blob->SessionKey.Length = 0; | ||
| 559 | sec_blob->SessionKey.MaximumLength = 0; | ||
| 560 | } | ||
| 549 | 561 | ||
| 550 | setup_ntlmv2_ret: | 562 | setup_ntlmv2_ret: |
| 551 | *buflen = tmp - pbuffer; | 563 | *buflen = tmp - pbuffer; |
