diff options
author | Pavel Shilovsky <pshilov@microsoft.com> | 2016-11-03 19:47:37 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2017-02-01 17:46:36 -0500 |
commit | 026e93dc0a3eefb0be060bcb9ecd8d7a7fd5c398 (patch) | |
tree | 1816fb41fc8a99d0d967a3ebb324f341b5eaee3e | |
parent | cabfb3680f78981d26c078a26e5c748531257ebb (diff) |
CIFS: Encrypt SMB3 requests before sending
This change allows to encrypt packets if it is required by a server
for SMB sessions or tree connections.
Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
-rw-r--r-- | fs/cifs/Kconfig | 2 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 13 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 2 | ||||
-rw-r--r-- | fs/cifs/connect.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 256 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 5 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 3 | ||||
-rw-r--r-- | fs/cifs/smb2transport.c | 41 |
10 files changed, 320 insertions, 10 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index ff0d1fe542f7..034f00f21390 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig | |||
@@ -174,6 +174,8 @@ config CIFS_SMB2 | |||
174 | select CRYPTO_AES | 174 | select CRYPTO_AES |
175 | select CRYPTO_SHA256 | 175 | select CRYPTO_SHA256 |
176 | select CRYPTO_CMAC | 176 | select CRYPTO_CMAC |
177 | select CRYPTO_AEAD2 | ||
178 | select CRYPTO_CCM | ||
177 | 179 | ||
178 | help | 180 | help |
179 | This enables support for the Server Message Block version 2 | 181 | This enables support for the Server Message Block version 2 |
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index d8af15f19dd8..058ac9b36f04 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/random.h> | 34 | #include <linux/random.h> |
35 | #include <linux/highmem.h> | 35 | #include <linux/highmem.h> |
36 | #include <crypto/skcipher.h> | 36 | #include <crypto/skcipher.h> |
37 | #include <crypto/aead.h> | ||
37 | 38 | ||
38 | static int | 39 | static int |
39 | cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server) | 40 | cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server) |
@@ -874,7 +875,7 @@ out: | |||
874 | } | 875 | } |
875 | 876 | ||
876 | void | 877 | void |
877 | cifs_crypto_shash_release(struct TCP_Server_Info *server) | 878 | cifs_crypto_secmech_release(struct TCP_Server_Info *server) |
878 | { | 879 | { |
879 | if (server->secmech.cmacaes) { | 880 | if (server->secmech.cmacaes) { |
880 | crypto_free_shash(server->secmech.cmacaes); | 881 | crypto_free_shash(server->secmech.cmacaes); |
@@ -896,6 +897,16 @@ cifs_crypto_shash_release(struct TCP_Server_Info *server) | |||
896 | server->secmech.hmacmd5 = NULL; | 897 | server->secmech.hmacmd5 = NULL; |
897 | } | 898 | } |
898 | 899 | ||
900 | if (server->secmech.ccmaesencrypt) { | ||
901 | crypto_free_aead(server->secmech.ccmaesencrypt); | ||
902 | server->secmech.ccmaesencrypt = NULL; | ||
903 | } | ||
904 | |||
905 | if (server->secmech.ccmaesdecrypt) { | ||
906 | crypto_free_aead(server->secmech.ccmaesdecrypt); | ||
907 | server->secmech.ccmaesdecrypt = NULL; | ||
908 | } | ||
909 | |||
899 | kfree(server->secmech.sdesccmacaes); | 910 | kfree(server->secmech.sdesccmacaes); |
900 | server->secmech.sdesccmacaes = NULL; | 911 | server->secmech.sdesccmacaes = NULL; |
901 | kfree(server->secmech.sdeschmacsha256); | 912 | kfree(server->secmech.sdeschmacsha256); |
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 956dd85d7aef..15e1db8738ae 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -1376,6 +1376,8 @@ MODULE_SOFTDEP("pre: nls"); | |||
1376 | MODULE_SOFTDEP("pre: aes"); | 1376 | MODULE_SOFTDEP("pre: aes"); |
1377 | MODULE_SOFTDEP("pre: cmac"); | 1377 | MODULE_SOFTDEP("pre: cmac"); |
1378 | MODULE_SOFTDEP("pre: sha256"); | 1378 | MODULE_SOFTDEP("pre: sha256"); |
1379 | MODULE_SOFTDEP("pre: aead2"); | ||
1380 | MODULE_SOFTDEP("pre: ccm"); | ||
1379 | #endif /* CONFIG_CIFS_SMB2 */ | 1381 | #endif /* CONFIG_CIFS_SMB2 */ |
1380 | module_init(init_cifs) | 1382 | module_init(init_cifs) |
1381 | module_exit(exit_cifs) | 1383 | module_exit(exit_cifs) |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ff7114d27a69..14196e00f79e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -136,6 +136,8 @@ struct cifs_secmech { | |||
136 | struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ | 136 | struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ |
137 | struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */ | 137 | struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */ |
138 | struct sdesc *sdesccmacaes; /* ctxt to generate smb3 signature */ | 138 | struct sdesc *sdesccmacaes; /* ctxt to generate smb3 signature */ |
139 | struct crypto_aead *ccmaesencrypt; /* smb3 encryption aead */ | ||
140 | struct crypto_aead *ccmaesdecrypt; /* smb3 decryption aead */ | ||
139 | }; | 141 | }; |
140 | 142 | ||
141 | /* per smb session structure/fields */ | 143 | /* per smb session structure/fields */ |
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 711718cd73dd..26872f54ca3f 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -445,7 +445,7 @@ extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *, | |||
445 | const struct nls_table *); | 445 | const struct nls_table *); |
446 | extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); | 446 | extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); |
447 | extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); | 447 | extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); |
448 | extern void cifs_crypto_shash_release(struct TCP_Server_Info *); | 448 | extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); |
449 | extern int calc_seckey(struct cifs_ses *); | 449 | extern int calc_seckey(struct cifs_ses *); |
450 | extern int generate_smb30signingkey(struct cifs_ses *); | 450 | extern int generate_smb30signingkey(struct cifs_ses *); |
451 | extern int generate_smb311signingkey(struct cifs_ses *); | 451 | extern int generate_smb311signingkey(struct cifs_ses *); |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 35ae49ed1f76..b84febf960e9 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -2154,7 +2154,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) | |||
2154 | server->tcpStatus = CifsExiting; | 2154 | server->tcpStatus = CifsExiting; |
2155 | spin_unlock(&GlobalMid_Lock); | 2155 | spin_unlock(&GlobalMid_Lock); |
2156 | 2156 | ||
2157 | cifs_crypto_shash_release(server); | 2157 | cifs_crypto_secmech_release(server); |
2158 | cifs_fscache_release_client_cookie(server); | 2158 | cifs_fscache_release_client_cookie(server); |
2159 | 2159 | ||
2160 | kfree(server->session_key.response); | 2160 | kfree(server->session_key.response); |
@@ -2273,7 +2273,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
2273 | return tcp_ses; | 2273 | return tcp_ses; |
2274 | 2274 | ||
2275 | out_err_crypto_release: | 2275 | out_err_crypto_release: |
2276 | cifs_crypto_shash_release(tcp_ses); | 2276 | cifs_crypto_secmech_release(tcp_ses); |
2277 | 2277 | ||
2278 | put_net(cifs_net_ns(tcp_ses)); | 2278 | put_net(cifs_net_ns(tcp_ses)); |
2279 | 2279 | ||
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index ef8b2a8363b3..54b49358eaaf 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -20,6 +20,8 @@ | |||
20 | #include <linux/pagemap.h> | 20 | #include <linux/pagemap.h> |
21 | #include <linux/vfs.h> | 21 | #include <linux/vfs.h> |
22 | #include <linux/falloc.h> | 22 | #include <linux/falloc.h> |
23 | #include <linux/scatterlist.h> | ||
24 | #include <crypto/aead.h> | ||
23 | #include "cifsglob.h" | 25 | #include "cifsglob.h" |
24 | #include "smb2pdu.h" | 26 | #include "smb2pdu.h" |
25 | #include "smb2proto.h" | 27 | #include "smb2proto.h" |
@@ -1547,6 +1549,256 @@ smb2_dir_needs_close(struct cifsFileInfo *cfile) | |||
1547 | return !cfile->invalidHandle; | 1549 | return !cfile->invalidHandle; |
1548 | } | 1550 | } |
1549 | 1551 | ||
1552 | static void | ||
1553 | fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, struct smb_rqst *old_rq) | ||
1554 | { | ||
1555 | struct smb2_sync_hdr *shdr = | ||
1556 | (struct smb2_sync_hdr *)old_rq->rq_iov[1].iov_base; | ||
1557 | unsigned int orig_len = get_rfc1002_length(old_rq->rq_iov[0].iov_base); | ||
1558 | |||
1559 | memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); | ||
1560 | tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; | ||
1561 | tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); | ||
1562 | tr_hdr->Flags = cpu_to_le16(0x01); | ||
1563 | get_random_bytes(&tr_hdr->Nonce, SMB3_AES128CMM_NONCE); | ||
1564 | memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8); | ||
1565 | inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4); | ||
1566 | inc_rfc1001_len(tr_hdr, orig_len); | ||
1567 | } | ||
1568 | |||
1569 | static struct scatterlist * | ||
1570 | init_sg(struct smb_rqst *rqst, u8 *sign) | ||
1571 | { | ||
1572 | unsigned int sg_len = rqst->rq_nvec + rqst->rq_npages + 1; | ||
1573 | unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; | ||
1574 | struct scatterlist *sg; | ||
1575 | unsigned int i; | ||
1576 | unsigned int j; | ||
1577 | |||
1578 | sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL); | ||
1579 | if (!sg) | ||
1580 | return NULL; | ||
1581 | |||
1582 | sg_init_table(sg, sg_len); | ||
1583 | sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 24, assoc_data_len); | ||
1584 | for (i = 1; i < rqst->rq_nvec; i++) | ||
1585 | sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base, | ||
1586 | rqst->rq_iov[i].iov_len); | ||
1587 | for (j = 0; i < sg_len - 1; i++, j++) { | ||
1588 | unsigned int len = (j < rqst->rq_npages - 1) ? rqst->rq_pagesz | ||
1589 | : rqst->rq_tailsz; | ||
1590 | sg_set_page(&sg[i], rqst->rq_pages[j], len, 0); | ||
1591 | } | ||
1592 | sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE); | ||
1593 | return sg; | ||
1594 | } | ||
1595 | |||
1596 | struct cifs_crypt_result { | ||
1597 | int err; | ||
1598 | struct completion completion; | ||
1599 | }; | ||
1600 | |||
1601 | static void cifs_crypt_complete(struct crypto_async_request *req, int err) | ||
1602 | { | ||
1603 | struct cifs_crypt_result *res = req->data; | ||
1604 | |||
1605 | if (err == -EINPROGRESS) | ||
1606 | return; | ||
1607 | |||
1608 | res->err = err; | ||
1609 | complete(&res->completion); | ||
1610 | } | ||
1611 | |||
1612 | /* | ||
1613 | * Encrypt or decrypt @rqst message. @rqst has the following format: | ||
1614 | * iov[0] - transform header (associate data), | ||
1615 | * iov[1-N] and pages - data to encrypt. | ||
1616 | * On success return encrypted data in iov[1-N] and pages, leave iov[0] | ||
1617 | * untouched. | ||
1618 | */ | ||
1619 | static int | ||
1620 | crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc) | ||
1621 | { | ||
1622 | struct smb2_transform_hdr *tr_hdr = | ||
1623 | (struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base; | ||
1624 | unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; | ||
1625 | struct cifs_ses *ses; | ||
1626 | int rc = 0; | ||
1627 | struct scatterlist *sg; | ||
1628 | u8 sign[SMB2_SIGNATURE_SIZE] = {}; | ||
1629 | struct aead_request *req; | ||
1630 | char *iv; | ||
1631 | unsigned int iv_len; | ||
1632 | struct cifs_crypt_result result = {0, }; | ||
1633 | struct crypto_aead *tfm; | ||
1634 | unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); | ||
1635 | |||
1636 | init_completion(&result.completion); | ||
1637 | |||
1638 | ses = smb2_find_smb_ses(server, tr_hdr->SessionId); | ||
1639 | if (!ses) { | ||
1640 | cifs_dbg(VFS, "%s: Could not find session\n", __func__); | ||
1641 | return 0; | ||
1642 | } | ||
1643 | |||
1644 | rc = smb3_crypto_aead_allocate(server); | ||
1645 | if (rc) { | ||
1646 | cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); | ||
1647 | return rc; | ||
1648 | } | ||
1649 | |||
1650 | tfm = enc ? server->secmech.ccmaesencrypt : | ||
1651 | server->secmech.ccmaesdecrypt; | ||
1652 | rc = crypto_aead_setkey(tfm, enc ? ses->smb3encryptionkey : | ||
1653 | ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); | ||
1654 | if (rc) { | ||
1655 | cifs_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); | ||
1656 | return rc; | ||
1657 | } | ||
1658 | |||
1659 | rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); | ||
1660 | if (rc) { | ||
1661 | cifs_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); | ||
1662 | return rc; | ||
1663 | } | ||
1664 | |||
1665 | req = aead_request_alloc(tfm, GFP_KERNEL); | ||
1666 | if (!req) { | ||
1667 | cifs_dbg(VFS, "%s: Failed to alloc aead request", __func__); | ||
1668 | return -ENOMEM; | ||
1669 | } | ||
1670 | |||
1671 | if (!enc) { | ||
1672 | memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); | ||
1673 | crypt_len += SMB2_SIGNATURE_SIZE; | ||
1674 | } | ||
1675 | |||
1676 | sg = init_sg(rqst, sign); | ||
1677 | if (!sg) { | ||
1678 | cifs_dbg(VFS, "%s: Failed to init sg %d", __func__, rc); | ||
1679 | goto free_req; | ||
1680 | } | ||
1681 | |||
1682 | iv_len = crypto_aead_ivsize(tfm); | ||
1683 | iv = kzalloc(iv_len, GFP_KERNEL); | ||
1684 | if (!iv) { | ||
1685 | cifs_dbg(VFS, "%s: Failed to alloc IV", __func__); | ||
1686 | goto free_sg; | ||
1687 | } | ||
1688 | iv[0] = 3; | ||
1689 | memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES128CMM_NONCE); | ||
1690 | |||
1691 | aead_request_set_crypt(req, sg, sg, crypt_len, iv); | ||
1692 | aead_request_set_ad(req, assoc_data_len); | ||
1693 | |||
1694 | aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, | ||
1695 | cifs_crypt_complete, &result); | ||
1696 | |||
1697 | rc = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req); | ||
1698 | |||
1699 | if (rc == -EINPROGRESS || rc == -EBUSY) { | ||
1700 | wait_for_completion(&result.completion); | ||
1701 | rc = result.err; | ||
1702 | } | ||
1703 | |||
1704 | if (!rc && enc) | ||
1705 | memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); | ||
1706 | |||
1707 | kfree(iv); | ||
1708 | free_sg: | ||
1709 | kfree(sg); | ||
1710 | free_req: | ||
1711 | kfree(req); | ||
1712 | return rc; | ||
1713 | } | ||
1714 | |||
1715 | static int | ||
1716 | smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, | ||
1717 | struct smb_rqst *old_rq) | ||
1718 | { | ||
1719 | struct kvec *iov; | ||
1720 | struct page **pages; | ||
1721 | struct smb2_transform_hdr *tr_hdr; | ||
1722 | unsigned int npages = old_rq->rq_npages; | ||
1723 | int i; | ||
1724 | int rc = -ENOMEM; | ||
1725 | |||
1726 | pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); | ||
1727 | if (!pages) | ||
1728 | return rc; | ||
1729 | |||
1730 | new_rq->rq_pages = pages; | ||
1731 | new_rq->rq_npages = old_rq->rq_npages; | ||
1732 | new_rq->rq_pagesz = old_rq->rq_pagesz; | ||
1733 | new_rq->rq_tailsz = old_rq->rq_tailsz; | ||
1734 | |||
1735 | for (i = 0; i < npages; i++) { | ||
1736 | pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); | ||
1737 | if (!pages[i]) | ||
1738 | goto err_free_pages; | ||
1739 | } | ||
1740 | |||
1741 | iov = kmalloc_array(old_rq->rq_nvec, sizeof(struct kvec), GFP_KERNEL); | ||
1742 | if (!iov) | ||
1743 | goto err_free_pages; | ||
1744 | |||
1745 | /* copy all iovs from the old except the 1st one (rfc1002 length) */ | ||
1746 | memcpy(&iov[1], &old_rq->rq_iov[1], | ||
1747 | sizeof(struct kvec) * (old_rq->rq_nvec - 1)); | ||
1748 | new_rq->rq_iov = iov; | ||
1749 | new_rq->rq_nvec = old_rq->rq_nvec; | ||
1750 | |||
1751 | tr_hdr = kmalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL); | ||
1752 | if (!tr_hdr) | ||
1753 | goto err_free_iov; | ||
1754 | |||
1755 | /* fill the 1st iov with a transform header */ | ||
1756 | fill_transform_hdr(tr_hdr, old_rq); | ||
1757 | new_rq->rq_iov[0].iov_base = tr_hdr; | ||
1758 | new_rq->rq_iov[0].iov_len = sizeof(struct smb2_transform_hdr); | ||
1759 | |||
1760 | /* copy pages form the old */ | ||
1761 | for (i = 0; i < npages; i++) { | ||
1762 | char *dst = kmap(new_rq->rq_pages[i]); | ||
1763 | char *src = kmap(old_rq->rq_pages[i]); | ||
1764 | unsigned int len = (i < npages - 1) ? new_rq->rq_pagesz : | ||
1765 | new_rq->rq_tailsz; | ||
1766 | memcpy(dst, src, len); | ||
1767 | kunmap(new_rq->rq_pages[i]); | ||
1768 | kunmap(old_rq->rq_pages[i]); | ||
1769 | } | ||
1770 | |||
1771 | rc = crypt_message(server, new_rq, 1); | ||
1772 | cifs_dbg(FYI, "encrypt message returned %d", rc); | ||
1773 | if (rc) | ||
1774 | goto err_free_tr_hdr; | ||
1775 | |||
1776 | return rc; | ||
1777 | |||
1778 | err_free_tr_hdr: | ||
1779 | kfree(tr_hdr); | ||
1780 | err_free_iov: | ||
1781 | kfree(iov); | ||
1782 | err_free_pages: | ||
1783 | for (i = i - 1; i >= 0; i--) | ||
1784 | put_page(pages[i]); | ||
1785 | kfree(pages); | ||
1786 | return rc; | ||
1787 | } | ||
1788 | |||
1789 | static void | ||
1790 | smb3_free_transform_rq(struct smb_rqst *rqst) | ||
1791 | { | ||
1792 | int i = rqst->rq_npages - 1; | ||
1793 | |||
1794 | for (; i >= 0; i--) | ||
1795 | put_page(rqst->rq_pages[i]); | ||
1796 | kfree(rqst->rq_pages); | ||
1797 | /* free transform header */ | ||
1798 | kfree(rqst->rq_iov[0].iov_base); | ||
1799 | kfree(rqst->rq_iov); | ||
1800 | } | ||
1801 | |||
1550 | struct smb_version_operations smb20_operations = { | 1802 | struct smb_version_operations smb20_operations = { |
1551 | .compare_fids = smb2_compare_fids, | 1803 | .compare_fids = smb2_compare_fids, |
1552 | .setup_request = smb2_setup_request, | 1804 | .setup_request = smb2_setup_request, |
@@ -1793,6 +2045,8 @@ struct smb_version_operations smb30_operations = { | |||
1793 | .dir_needs_close = smb2_dir_needs_close, | 2045 | .dir_needs_close = smb2_dir_needs_close, |
1794 | .fallocate = smb3_fallocate, | 2046 | .fallocate = smb3_fallocate, |
1795 | .enum_snapshots = smb3_enum_snapshots, | 2047 | .enum_snapshots = smb3_enum_snapshots, |
2048 | .init_transform_rq = smb3_init_transform_rq, | ||
2049 | .free_transform_rq = smb3_free_transform_rq, | ||
1796 | }; | 2050 | }; |
1797 | 2051 | ||
1798 | #ifdef CONFIG_CIFS_SMB311 | 2052 | #ifdef CONFIG_CIFS_SMB311 |
@@ -1881,6 +2135,8 @@ struct smb_version_operations smb311_operations = { | |||
1881 | .dir_needs_close = smb2_dir_needs_close, | 2135 | .dir_needs_close = smb2_dir_needs_close, |
1882 | .fallocate = smb3_fallocate, | 2136 | .fallocate = smb3_fallocate, |
1883 | .enum_snapshots = smb3_enum_snapshots, | 2137 | .enum_snapshots = smb3_enum_snapshots, |
2138 | .init_transform_rq = smb3_init_transform_rq, | ||
2139 | .free_transform_rq = smb3_free_transform_rq, | ||
1884 | }; | 2140 | }; |
1885 | #endif /* CIFS_SMB311 */ | 2141 | #endif /* CIFS_SMB311 */ |
1886 | 2142 | ||
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 35ff9fae1c27..c03b252501a1 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -134,11 +134,14 @@ struct smb2_pdu { | |||
134 | __le16 StructureSize2; /* size of wct area (varies, request specific) */ | 134 | __le16 StructureSize2; /* size of wct area (varies, request specific) */ |
135 | } __packed; | 135 | } __packed; |
136 | 136 | ||
137 | #define SMB3_AES128CMM_NONCE 11 | ||
138 | #define SMB3_AES128GCM_NONCE 12 | ||
139 | |||
137 | struct smb2_transform_hdr { | 140 | struct smb2_transform_hdr { |
138 | __be32 smb2_buf_length; /* big endian on wire */ | 141 | __be32 smb2_buf_length; /* big endian on wire */ |
139 | /* length is only two or three bytes - with | 142 | /* length is only two or three bytes - with |
140 | one or two byte type preceding it that MBZ */ | 143 | one or two byte type preceding it that MBZ */ |
141 | __u8 ProtocolId[4]; /* 0xFD 'S' 'M' 'B' */ | 144 | __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ |
142 | __u8 Signature[16]; | 145 | __u8 Signature[16]; |
143 | __u8 Nonce[16]; | 146 | __u8 Nonce[16]; |
144 | __le32 OriginalMessageSize; | 147 | __le32 OriginalMessageSize; |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index f2d511a6971b..7d30b754e132 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -56,6 +56,8 @@ extern void smb2_echo_request(struct work_struct *work); | |||
56 | extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); | 56 | extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); |
57 | extern bool smb2_is_valid_oplock_break(char *buffer, | 57 | extern bool smb2_is_valid_oplock_break(char *buffer, |
58 | struct TCP_Server_Info *srv); | 58 | struct TCP_Server_Info *srv); |
59 | extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, | ||
60 | __u64 ses_id); | ||
59 | 61 | ||
60 | extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, | 62 | extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, |
61 | struct smb2_file_all_info *src); | 63 | struct smb2_file_all_info *src); |
@@ -97,6 +99,7 @@ extern int smb2_unlock_range(struct cifsFileInfo *cfile, | |||
97 | struct file_lock *flock, const unsigned int xid); | 99 | struct file_lock *flock, const unsigned int xid); |
98 | extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); | 100 | extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); |
99 | extern void smb2_reconnect_server(struct work_struct *work); | 101 | extern void smb2_reconnect_server(struct work_struct *work); |
102 | extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); | ||
100 | 103 | ||
101 | /* | 104 | /* |
102 | * SMB2 Worker functions - most of protocol specific implementation details | 105 | * SMB2 Worker functions - most of protocol specific implementation details |
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 93b27752b634..3caa11dd957a 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <asm/processor.h> | 31 | #include <asm/processor.h> |
32 | #include <linux/mempool.h> | 32 | #include <linux/mempool.h> |
33 | #include <linux/highmem.h> | 33 | #include <linux/highmem.h> |
34 | #include <crypto/aead.h> | ||
34 | #include "smb2pdu.h" | 35 | #include "smb2pdu.h" |
35 | #include "cifsglob.h" | 36 | #include "cifsglob.h" |
36 | #include "cifsproto.h" | 37 | #include "cifsproto.h" |
@@ -114,14 +115,14 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server) | |||
114 | return 0; | 115 | return 0; |
115 | } | 116 | } |
116 | 117 | ||
117 | static struct cifs_ses * | 118 | struct cifs_ses * |
118 | smb2_find_smb_ses(struct smb2_sync_hdr *shdr, struct TCP_Server_Info *server) | 119 | smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) |
119 | { | 120 | { |
120 | struct cifs_ses *ses; | 121 | struct cifs_ses *ses; |
121 | 122 | ||
122 | spin_lock(&cifs_tcp_ses_lock); | 123 | spin_lock(&cifs_tcp_ses_lock); |
123 | list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { | 124 | list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { |
124 | if (ses->Suid != shdr->SessionId) | 125 | if (ses->Suid != ses_id) |
125 | continue; | 126 | continue; |
126 | spin_unlock(&cifs_tcp_ses_lock); | 127 | spin_unlock(&cifs_tcp_ses_lock); |
127 | return ses; | 128 | return ses; |
@@ -141,7 +142,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) | |||
141 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; | 142 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; |
142 | struct cifs_ses *ses; | 143 | struct cifs_ses *ses; |
143 | 144 | ||
144 | ses = smb2_find_smb_ses(shdr, server); | 145 | ses = smb2_find_smb_ses(server, shdr->SessionId); |
145 | if (!ses) { | 146 | if (!ses) { |
146 | cifs_dbg(VFS, "%s: Could not find session\n", __func__); | 147 | cifs_dbg(VFS, "%s: Could not find session\n", __func__); |
147 | return 0; | 148 | return 0; |
@@ -358,7 +359,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) | |||
358 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; | 359 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; |
359 | struct cifs_ses *ses; | 360 | struct cifs_ses *ses; |
360 | 361 | ||
361 | ses = smb2_find_smb_ses(shdr, server); | 362 | ses = smb2_find_smb_ses(server, shdr->SessionId); |
362 | if (!ses) { | 363 | if (!ses) { |
363 | cifs_dbg(VFS, "%s: Could not find session\n", __func__); | 364 | cifs_dbg(VFS, "%s: Could not find session\n", __func__); |
364 | return 0; | 365 | return 0; |
@@ -618,3 +619,33 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) | |||
618 | 619 | ||
619 | return mid; | 620 | return mid; |
620 | } | 621 | } |
622 | |||
623 | int | ||
624 | smb3_crypto_aead_allocate(struct TCP_Server_Info *server) | ||
625 | { | ||
626 | struct crypto_aead *tfm; | ||
627 | |||
628 | if (!server->secmech.ccmaesencrypt) { | ||
629 | tfm = crypto_alloc_aead("ccm(aes)", 0, 0); | ||
630 | if (IS_ERR(tfm)) { | ||
631 | cifs_dbg(VFS, "%s: Failed to alloc encrypt aead\n", | ||
632 | __func__); | ||
633 | return PTR_ERR(tfm); | ||
634 | } | ||
635 | server->secmech.ccmaesencrypt = tfm; | ||
636 | } | ||
637 | |||
638 | if (!server->secmech.ccmaesdecrypt) { | ||
639 | tfm = crypto_alloc_aead("ccm(aes)", 0, 0); | ||
640 | if (IS_ERR(tfm)) { | ||
641 | crypto_free_aead(server->secmech.ccmaesencrypt); | ||
642 | server->secmech.ccmaesencrypt = NULL; | ||
643 | cifs_dbg(VFS, "%s: Failed to alloc decrypt aead\n", | ||
644 | __func__); | ||
645 | return PTR_ERR(tfm); | ||
646 | } | ||
647 | server->secmech.ccmaesdecrypt = tfm; | ||
648 | } | ||
649 | |||
650 | return 0; | ||
651 | } | ||