18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *   fs/cifs/smb2transport.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2002, 2011
58c2ecf20Sopenharmony_ci *                 Etersoft, 2012
68c2ecf20Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
78c2ecf20Sopenharmony_ci *              Jeremy Allison (jra@samba.org) 2006
88c2ecf20Sopenharmony_ci *              Pavel Shilovsky (pshilovsky@samba.org) 2012
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
118c2ecf20Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as published
128c2ecf20Sopenharmony_ci *   by the Free Software Foundation; either version 2.1 of the License, or
138c2ecf20Sopenharmony_ci *   (at your option) any later version.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *   This library is distributed in the hope that it will be useful,
168c2ecf20Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
178c2ecf20Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
188c2ecf20Sopenharmony_ci *   the GNU Lesser General Public License for more details.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public License
218c2ecf20Sopenharmony_ci *   along with this library; if not, write to the Free Software
228c2ecf20Sopenharmony_ci *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/fs.h>
268c2ecf20Sopenharmony_ci#include <linux/list.h>
278c2ecf20Sopenharmony_ci#include <linux/wait.h>
288c2ecf20Sopenharmony_ci#include <linux/net.h>
298c2ecf20Sopenharmony_ci#include <linux/delay.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci#include <asm/processor.h>
328c2ecf20Sopenharmony_ci#include <linux/mempool.h>
338c2ecf20Sopenharmony_ci#include <linux/highmem.h>
348c2ecf20Sopenharmony_ci#include <crypto/aead.h>
358c2ecf20Sopenharmony_ci#include "smb2pdu.h"
368c2ecf20Sopenharmony_ci#include "cifsglob.h"
378c2ecf20Sopenharmony_ci#include "cifsproto.h"
388c2ecf20Sopenharmony_ci#include "smb2proto.h"
398c2ecf20Sopenharmony_ci#include "cifs_debug.h"
408c2ecf20Sopenharmony_ci#include "smb2status.h"
418c2ecf20Sopenharmony_ci#include "smb2glob.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int
448c2ecf20Sopenharmony_cismb3_crypto_shash_allocate(struct TCP_Server_Info *server)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct cifs_secmech *p = &server->secmech;
478c2ecf20Sopenharmony_ci	int rc;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	rc = cifs_alloc_hash("hmac(sha256)",
508c2ecf20Sopenharmony_ci			     &p->hmacsha256,
518c2ecf20Sopenharmony_ci			     &p->sdeschmacsha256);
528c2ecf20Sopenharmony_ci	if (rc)
538c2ecf20Sopenharmony_ci		goto err;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	rc = cifs_alloc_hash("cmac(aes)", &p->cmacaes, &p->sdesccmacaes);
568c2ecf20Sopenharmony_ci	if (rc)
578c2ecf20Sopenharmony_ci		goto err;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_cierr:
618c2ecf20Sopenharmony_ci	cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256);
628c2ecf20Sopenharmony_ci	return rc;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciint
668c2ecf20Sopenharmony_cismb311_crypto_shash_allocate(struct TCP_Server_Info *server)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct cifs_secmech *p = &server->secmech;
698c2ecf20Sopenharmony_ci	int rc = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	rc = cifs_alloc_hash("hmac(sha256)",
728c2ecf20Sopenharmony_ci			     &p->hmacsha256,
738c2ecf20Sopenharmony_ci			     &p->sdeschmacsha256);
748c2ecf20Sopenharmony_ci	if (rc)
758c2ecf20Sopenharmony_ci		return rc;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	rc = cifs_alloc_hash("cmac(aes)", &p->cmacaes, &p->sdesccmacaes);
788c2ecf20Sopenharmony_ci	if (rc)
798c2ecf20Sopenharmony_ci		goto err;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	rc = cifs_alloc_hash("sha512", &p->sha512, &p->sdescsha512);
828c2ecf20Sopenharmony_ci	if (rc)
838c2ecf20Sopenharmony_ci		goto err;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return 0;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cierr:
888c2ecf20Sopenharmony_ci	cifs_free_hash(&p->cmacaes, &p->sdesccmacaes);
898c2ecf20Sopenharmony_ci	cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256);
908c2ecf20Sopenharmony_ci	return rc;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic
958c2ecf20Sopenharmony_ciint smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct cifs_chan *chan;
988c2ecf20Sopenharmony_ci	struct cifs_ses *ses = NULL;
998c2ecf20Sopenharmony_ci	struct TCP_Server_Info *it = NULL;
1008c2ecf20Sopenharmony_ci	int i;
1018c2ecf20Sopenharmony_ci	int rc = 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	list_for_each_entry(it, &cifs_tcp_ses_list, tcp_ses_list) {
1068c2ecf20Sopenharmony_ci		list_for_each_entry(ses, &it->smb_ses_list, smb_ses_list) {
1078c2ecf20Sopenharmony_ci			if (ses->Suid == ses_id)
1088c2ecf20Sopenharmony_ci				goto found;
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n",
1128c2ecf20Sopenharmony_ci			__func__, ses_id);
1138c2ecf20Sopenharmony_ci	rc = -ENOENT;
1148c2ecf20Sopenharmony_ci	goto out;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cifound:
1178c2ecf20Sopenharmony_ci	if (ses->binding) {
1188c2ecf20Sopenharmony_ci		/*
1198c2ecf20Sopenharmony_ci		 * If we are in the process of binding a new channel
1208c2ecf20Sopenharmony_ci		 * to an existing session, use the master connection
1218c2ecf20Sopenharmony_ci		 * session key
1228c2ecf20Sopenharmony_ci		 */
1238c2ecf20Sopenharmony_ci		memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
1248c2ecf20Sopenharmony_ci		goto out;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/*
1288c2ecf20Sopenharmony_ci	 * Otherwise, use the channel key.
1298c2ecf20Sopenharmony_ci	 */
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	for (i = 0; i < ses->chan_count; i++) {
1328c2ecf20Sopenharmony_ci		chan = ses->chans + i;
1338c2ecf20Sopenharmony_ci		if (chan->server == server) {
1348c2ecf20Sopenharmony_ci			memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE);
1358c2ecf20Sopenharmony_ci			goto out;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	cifs_dbg(VFS,
1408c2ecf20Sopenharmony_ci		 "%s: Could not find channel signing key for session 0x%llx\n",
1418c2ecf20Sopenharmony_ci		 __func__, ses_id);
1428c2ecf20Sopenharmony_ci	rc = -ENOENT;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciout:
1458c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
1468c2ecf20Sopenharmony_ci	return rc;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic struct cifs_ses *
1508c2ecf20Sopenharmony_cismb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
1558c2ecf20Sopenharmony_ci		if (ses->Suid != ses_id)
1568c2ecf20Sopenharmony_ci			continue;
1578c2ecf20Sopenharmony_ci		return ses;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return NULL;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistruct cifs_ses *
1648c2ecf20Sopenharmony_cismb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
1698c2ecf20Sopenharmony_ci	ses = smb2_find_smb_ses_unlocked(server, ses_id);
1708c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return ses;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic struct cifs_tcon *
1768c2ecf20Sopenharmony_cismb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32  tid)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
1818c2ecf20Sopenharmony_ci		if (tcon->tid != tid)
1828c2ecf20Sopenharmony_ci			continue;
1838c2ecf20Sopenharmony_ci		++tcon->tc_count;
1848c2ecf20Sopenharmony_ci		return tcon;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return NULL;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/*
1918c2ecf20Sopenharmony_ci * Obtain tcon corresponding to the tid in the given
1928c2ecf20Sopenharmony_ci * cifs_ses
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistruct cifs_tcon *
1968c2ecf20Sopenharmony_cismb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32  tid)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
1998c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
2028c2ecf20Sopenharmony_ci	ses = smb2_find_smb_ses_unlocked(server, ses_id);
2038c2ecf20Sopenharmony_ci	if (!ses) {
2048c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
2058c2ecf20Sopenharmony_ci		return NULL;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci	tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid);
2088c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return tcon;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ciint
2148c2ecf20Sopenharmony_cismb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
2158c2ecf20Sopenharmony_ci			bool allocate_crypto)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	int rc;
2188c2ecf20Sopenharmony_ci	unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
2198c2ecf20Sopenharmony_ci	unsigned char *sigptr = smb2_signature;
2208c2ecf20Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
2218c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
2228c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
2238c2ecf20Sopenharmony_ci	struct shash_desc *shash;
2248c2ecf20Sopenharmony_ci	struct crypto_shash *hash;
2258c2ecf20Sopenharmony_ci	struct sdesc *sdesc = NULL;
2268c2ecf20Sopenharmony_ci	struct smb_rqst drqst;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ses = smb2_find_smb_ses(server, shdr->SessionId);
2298c2ecf20Sopenharmony_ci	if (!ses) {
2308c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
2318c2ecf20Sopenharmony_ci		return 0;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
2358c2ecf20Sopenharmony_ci	memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (allocate_crypto) {
2388c2ecf20Sopenharmony_ci		rc = cifs_alloc_hash("hmac(sha256)", &hash, &sdesc);
2398c2ecf20Sopenharmony_ci		if (rc) {
2408c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS,
2418c2ecf20Sopenharmony_ci					"%s: sha256 alloc failed\n", __func__);
2428c2ecf20Sopenharmony_ci			return rc;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci		shash = &sdesc->shash;
2458c2ecf20Sopenharmony_ci	} else {
2468c2ecf20Sopenharmony_ci		hash = server->secmech.hmacsha256;
2478c2ecf20Sopenharmony_ci		shash = &server->secmech.sdeschmacsha256->shash;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	rc = crypto_shash_setkey(hash, ses->auth_key.response,
2518c2ecf20Sopenharmony_ci			SMB2_NTLMV2_SESSKEY_SIZE);
2528c2ecf20Sopenharmony_ci	if (rc) {
2538c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS,
2548c2ecf20Sopenharmony_ci				"%s: Could not update with response\n",
2558c2ecf20Sopenharmony_ci				__func__);
2568c2ecf20Sopenharmony_ci		goto out;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	rc = crypto_shash_init(shash);
2608c2ecf20Sopenharmony_ci	if (rc) {
2618c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not init sha256", __func__);
2628c2ecf20Sopenharmony_ci		goto out;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/*
2668c2ecf20Sopenharmony_ci	 * For SMB2+, __cifs_calc_signature() expects to sign only the actual
2678c2ecf20Sopenharmony_ci	 * data, that is, iov[0] should not contain a rfc1002 length.
2688c2ecf20Sopenharmony_ci	 *
2698c2ecf20Sopenharmony_ci	 * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to
2708c2ecf20Sopenharmony_ci	 * __cifs_calc_signature().
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_ci	drqst = *rqst;
2738c2ecf20Sopenharmony_ci	if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
2748c2ecf20Sopenharmony_ci		rc = crypto_shash_update(shash, iov[0].iov_base,
2758c2ecf20Sopenharmony_ci					 iov[0].iov_len);
2768c2ecf20Sopenharmony_ci		if (rc) {
2778c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS,
2788c2ecf20Sopenharmony_ci					"%s: Could not update with payload\n",
2798c2ecf20Sopenharmony_ci					__func__);
2808c2ecf20Sopenharmony_ci			goto out;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci		drqst.rq_iov++;
2838c2ecf20Sopenharmony_ci		drqst.rq_nvec--;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
2878c2ecf20Sopenharmony_ci	if (!rc)
2888c2ecf20Sopenharmony_ci		memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ciout:
2918c2ecf20Sopenharmony_ci	if (allocate_crypto)
2928c2ecf20Sopenharmony_ci		cifs_free_hash(&hash, &sdesc);
2938c2ecf20Sopenharmony_ci	return rc;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int generate_key(struct cifs_ses *ses, struct kvec label,
2978c2ecf20Sopenharmony_ci			struct kvec context, __u8 *key, unsigned int key_size)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	unsigned char zero = 0x0;
3008c2ecf20Sopenharmony_ci	__u8 i[4] = {0, 0, 0, 1};
3018c2ecf20Sopenharmony_ci	__u8 L128[4] = {0, 0, 0, 128};
3028c2ecf20Sopenharmony_ci	__u8 L256[4] = {0, 0, 1, 0};
3038c2ecf20Sopenharmony_ci	int rc = 0;
3048c2ecf20Sopenharmony_ci	unsigned char prfhash[SMB2_HMACSHA256_SIZE];
3058c2ecf20Sopenharmony_ci	unsigned char *hashptr = prfhash;
3068c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
3098c2ecf20Sopenharmony_ci	memset(key, 0x0, key_size);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	rc = smb3_crypto_shash_allocate(server);
3128c2ecf20Sopenharmony_ci	if (rc) {
3138c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__);
3148c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	rc = crypto_shash_setkey(server->secmech.hmacsha256,
3188c2ecf20Sopenharmony_ci		ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
3198c2ecf20Sopenharmony_ci	if (rc) {
3208c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__);
3218c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
3258c2ecf20Sopenharmony_ci	if (rc) {
3268c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__);
3278c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
3318c2ecf20Sopenharmony_ci				i, 4);
3328c2ecf20Sopenharmony_ci	if (rc) {
3338c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__);
3348c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
3388c2ecf20Sopenharmony_ci				label.iov_base, label.iov_len);
3398c2ecf20Sopenharmony_ci	if (rc) {
3408c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__);
3418c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
3458c2ecf20Sopenharmony_ci				&zero, 1);
3468c2ecf20Sopenharmony_ci	if (rc) {
3478c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__);
3488c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
3528c2ecf20Sopenharmony_ci				context.iov_base, context.iov_len);
3538c2ecf20Sopenharmony_ci	if (rc) {
3548c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__);
3558c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) ||
3598c2ecf20Sopenharmony_ci		(server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) {
3608c2ecf20Sopenharmony_ci		rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
3618c2ecf20Sopenharmony_ci				L256, 4);
3628c2ecf20Sopenharmony_ci	} else {
3638c2ecf20Sopenharmony_ci		rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
3648c2ecf20Sopenharmony_ci				L128, 4);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	if (rc) {
3678c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__);
3688c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
3728c2ecf20Sopenharmony_ci				hashptr);
3738c2ecf20Sopenharmony_ci	if (rc) {
3748c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
3758c2ecf20Sopenharmony_ci		goto smb3signkey_ret;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	memcpy(key, hashptr, key_size);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cismb3signkey_ret:
3818c2ecf20Sopenharmony_ci	return rc;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistruct derivation {
3858c2ecf20Sopenharmony_ci	struct kvec label;
3868c2ecf20Sopenharmony_ci	struct kvec context;
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistruct derivation_triplet {
3908c2ecf20Sopenharmony_ci	struct derivation signing;
3918c2ecf20Sopenharmony_ci	struct derivation encryption;
3928c2ecf20Sopenharmony_ci	struct derivation decryption;
3938c2ecf20Sopenharmony_ci};
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int
3968c2ecf20Sopenharmony_cigenerate_smb3signingkey(struct cifs_ses *ses,
3978c2ecf20Sopenharmony_ci			const struct derivation_triplet *ptriplet)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	int rc;
4008c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
4018c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
4028c2ecf20Sopenharmony_ci#endif
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/*
4058c2ecf20Sopenharmony_ci	 * All channels use the same encryption/decryption keys but
4068c2ecf20Sopenharmony_ci	 * they have their own signing key.
4078c2ecf20Sopenharmony_ci	 *
4088c2ecf20Sopenharmony_ci	 * When we generate the keys, check if it is for a new channel
4098c2ecf20Sopenharmony_ci	 * (binding) in which case we only need to generate a signing
4108c2ecf20Sopenharmony_ci	 * key and store it in the channel as to not overwrite the
4118c2ecf20Sopenharmony_ci	 * master connection signing key stored in the session
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (ses->binding) {
4158c2ecf20Sopenharmony_ci		rc = generate_key(ses, ptriplet->signing.label,
4168c2ecf20Sopenharmony_ci				  ptriplet->signing.context,
4178c2ecf20Sopenharmony_ci				  cifs_ses_binding_channel(ses)->signkey,
4188c2ecf20Sopenharmony_ci				  SMB3_SIGN_KEY_SIZE);
4198c2ecf20Sopenharmony_ci		if (rc)
4208c2ecf20Sopenharmony_ci			return rc;
4218c2ecf20Sopenharmony_ci	} else {
4228c2ecf20Sopenharmony_ci		rc = generate_key(ses, ptriplet->signing.label,
4238c2ecf20Sopenharmony_ci				  ptriplet->signing.context,
4248c2ecf20Sopenharmony_ci				  ses->smb3signingkey,
4258c2ecf20Sopenharmony_ci				  SMB3_SIGN_KEY_SIZE);
4268c2ecf20Sopenharmony_ci		if (rc)
4278c2ecf20Sopenharmony_ci			return rc;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		memcpy(ses->chans[0].signkey, ses->smb3signingkey,
4308c2ecf20Sopenharmony_ci		       SMB3_SIGN_KEY_SIZE);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		rc = generate_key(ses, ptriplet->encryption.label,
4338c2ecf20Sopenharmony_ci				  ptriplet->encryption.context,
4348c2ecf20Sopenharmony_ci				  ses->smb3encryptionkey,
4358c2ecf20Sopenharmony_ci				  SMB3_ENC_DEC_KEY_SIZE);
4368c2ecf20Sopenharmony_ci		if (rc)
4378c2ecf20Sopenharmony_ci			return rc;
4388c2ecf20Sopenharmony_ci		rc = generate_key(ses, ptriplet->decryption.label,
4398c2ecf20Sopenharmony_ci				  ptriplet->decryption.context,
4408c2ecf20Sopenharmony_ci				  ses->smb3decryptionkey,
4418c2ecf20Sopenharmony_ci				  SMB3_ENC_DEC_KEY_SIZE);
4428c2ecf20Sopenharmony_ci		if (rc)
4438c2ecf20Sopenharmony_ci			return rc;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
4478c2ecf20Sopenharmony_ci	cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__);
4488c2ecf20Sopenharmony_ci	/*
4498c2ecf20Sopenharmony_ci	 * The session id is opaque in terms of endianness, so we can't
4508c2ecf20Sopenharmony_ci	 * print it as a long long. we dump it as we got it on the wire
4518c2ecf20Sopenharmony_ci	 */
4528c2ecf20Sopenharmony_ci	cifs_dbg(VFS, "Session Id    %*ph\n", (int)sizeof(ses->Suid),
4538c2ecf20Sopenharmony_ci			&ses->Suid);
4548c2ecf20Sopenharmony_ci	cifs_dbg(VFS, "Cipher type   %d\n", server->cipher_type);
4558c2ecf20Sopenharmony_ci	cifs_dbg(VFS, "Session Key   %*ph\n",
4568c2ecf20Sopenharmony_ci		 SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
4578c2ecf20Sopenharmony_ci	cifs_dbg(VFS, "Signing Key   %*ph\n",
4588c2ecf20Sopenharmony_ci		 SMB3_SIGN_KEY_SIZE, ses->smb3signingkey);
4598c2ecf20Sopenharmony_ci	if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) ||
4608c2ecf20Sopenharmony_ci		(server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) {
4618c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "ServerIn Key  %*ph\n",
4628c2ecf20Sopenharmony_ci				SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3encryptionkey);
4638c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "ServerOut Key %*ph\n",
4648c2ecf20Sopenharmony_ci				SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3decryptionkey);
4658c2ecf20Sopenharmony_ci	} else {
4668c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "ServerIn Key  %*ph\n",
4678c2ecf20Sopenharmony_ci				SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3encryptionkey);
4688c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "ServerOut Key %*ph\n",
4698c2ecf20Sopenharmony_ci				SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey);
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci#endif
4728c2ecf20Sopenharmony_ci	return rc;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ciint
4768c2ecf20Sopenharmony_cigenerate_smb30signingkey(struct cifs_ses *ses)
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct derivation_triplet triplet;
4808c2ecf20Sopenharmony_ci	struct derivation *d;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	d = &triplet.signing;
4838c2ecf20Sopenharmony_ci	d->label.iov_base = "SMB2AESCMAC";
4848c2ecf20Sopenharmony_ci	d->label.iov_len = 12;
4858c2ecf20Sopenharmony_ci	d->context.iov_base = "SmbSign";
4868c2ecf20Sopenharmony_ci	d->context.iov_len = 8;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	d = &triplet.encryption;
4898c2ecf20Sopenharmony_ci	d->label.iov_base = "SMB2AESCCM";
4908c2ecf20Sopenharmony_ci	d->label.iov_len = 11;
4918c2ecf20Sopenharmony_ci	d->context.iov_base = "ServerIn ";
4928c2ecf20Sopenharmony_ci	d->context.iov_len = 10;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	d = &triplet.decryption;
4958c2ecf20Sopenharmony_ci	d->label.iov_base = "SMB2AESCCM";
4968c2ecf20Sopenharmony_ci	d->label.iov_len = 11;
4978c2ecf20Sopenharmony_ci	d->context.iov_base = "ServerOut";
4988c2ecf20Sopenharmony_ci	d->context.iov_len = 10;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	return generate_smb3signingkey(ses, &triplet);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ciint
5048c2ecf20Sopenharmony_cigenerate_smb311signingkey(struct cifs_ses *ses)
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	struct derivation_triplet triplet;
5088c2ecf20Sopenharmony_ci	struct derivation *d;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	d = &triplet.signing;
5118c2ecf20Sopenharmony_ci	d->label.iov_base = "SMBSigningKey";
5128c2ecf20Sopenharmony_ci	d->label.iov_len = 14;
5138c2ecf20Sopenharmony_ci	d->context.iov_base = ses->preauth_sha_hash;
5148c2ecf20Sopenharmony_ci	d->context.iov_len = 64;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	d = &triplet.encryption;
5178c2ecf20Sopenharmony_ci	d->label.iov_base = "SMBC2SCipherKey";
5188c2ecf20Sopenharmony_ci	d->label.iov_len = 16;
5198c2ecf20Sopenharmony_ci	d->context.iov_base = ses->preauth_sha_hash;
5208c2ecf20Sopenharmony_ci	d->context.iov_len = 64;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	d = &triplet.decryption;
5238c2ecf20Sopenharmony_ci	d->label.iov_base = "SMBS2CCipherKey";
5248c2ecf20Sopenharmony_ci	d->label.iov_len = 16;
5258c2ecf20Sopenharmony_ci	d->context.iov_base = ses->preauth_sha_hash;
5268c2ecf20Sopenharmony_ci	d->context.iov_len = 64;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	return generate_smb3signingkey(ses, &triplet);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ciint
5328c2ecf20Sopenharmony_cismb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
5338c2ecf20Sopenharmony_ci			bool allocate_crypto)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	int rc;
5368c2ecf20Sopenharmony_ci	unsigned char smb3_signature[SMB2_CMACAES_SIZE];
5378c2ecf20Sopenharmony_ci	unsigned char *sigptr = smb3_signature;
5388c2ecf20Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
5398c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
5408c2ecf20Sopenharmony_ci	struct shash_desc *shash;
5418c2ecf20Sopenharmony_ci	struct crypto_shash *hash;
5428c2ecf20Sopenharmony_ci	struct sdesc *sdesc = NULL;
5438c2ecf20Sopenharmony_ci	struct smb_rqst drqst;
5448c2ecf20Sopenharmony_ci	u8 key[SMB3_SIGN_KEY_SIZE];
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	rc = smb2_get_sign_key(shdr->SessionId, server, key);
5478c2ecf20Sopenharmony_ci	if (rc)
5488c2ecf20Sopenharmony_ci		return 0;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (allocate_crypto) {
5518c2ecf20Sopenharmony_ci		rc = cifs_alloc_hash("cmac(aes)", &hash, &sdesc);
5528c2ecf20Sopenharmony_ci		if (rc)
5538c2ecf20Sopenharmony_ci			return rc;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		shash = &sdesc->shash;
5568c2ecf20Sopenharmony_ci	} else {
5578c2ecf20Sopenharmony_ci		hash = server->secmech.cmacaes;
5588c2ecf20Sopenharmony_ci		shash = &server->secmech.sdesccmacaes->shash;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
5628c2ecf20Sopenharmony_ci	memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	rc = crypto_shash_setkey(hash, key, SMB2_CMACAES_SIZE);
5658c2ecf20Sopenharmony_ci	if (rc) {
5668c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
5678c2ecf20Sopenharmony_ci		goto out;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/*
5718c2ecf20Sopenharmony_ci	 * we already allocate sdesccmacaes when we init smb3 signing key,
5728c2ecf20Sopenharmony_ci	 * so unlike smb2 case we do not have to check here if secmech are
5738c2ecf20Sopenharmony_ci	 * initialized
5748c2ecf20Sopenharmony_ci	 */
5758c2ecf20Sopenharmony_ci	rc = crypto_shash_init(shash);
5768c2ecf20Sopenharmony_ci	if (rc) {
5778c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
5788c2ecf20Sopenharmony_ci		goto out;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/*
5828c2ecf20Sopenharmony_ci	 * For SMB2+, __cifs_calc_signature() expects to sign only the actual
5838c2ecf20Sopenharmony_ci	 * data, that is, iov[0] should not contain a rfc1002 length.
5848c2ecf20Sopenharmony_ci	 *
5858c2ecf20Sopenharmony_ci	 * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to
5868c2ecf20Sopenharmony_ci	 * __cifs_calc_signature().
5878c2ecf20Sopenharmony_ci	 */
5888c2ecf20Sopenharmony_ci	drqst = *rqst;
5898c2ecf20Sopenharmony_ci	if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
5908c2ecf20Sopenharmony_ci		rc = crypto_shash_update(shash, iov[0].iov_base,
5918c2ecf20Sopenharmony_ci					 iov[0].iov_len);
5928c2ecf20Sopenharmony_ci		if (rc) {
5938c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "%s: Could not update with payload\n",
5948c2ecf20Sopenharmony_ci				 __func__);
5958c2ecf20Sopenharmony_ci			goto out;
5968c2ecf20Sopenharmony_ci		}
5978c2ecf20Sopenharmony_ci		drqst.rq_iov++;
5988c2ecf20Sopenharmony_ci		drqst.rq_nvec--;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
6028c2ecf20Sopenharmony_ci	if (!rc)
6038c2ecf20Sopenharmony_ci		memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ciout:
6068c2ecf20Sopenharmony_ci	if (allocate_crypto)
6078c2ecf20Sopenharmony_ci		cifs_free_hash(&hash, &sdesc);
6088c2ecf20Sopenharmony_ci	return rc;
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/* must be called with server->srv_mutex held */
6128c2ecf20Sopenharmony_cistatic int
6138c2ecf20Sopenharmony_cismb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	int rc = 0;
6168c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr;
6178c2ecf20Sopenharmony_ci	struct smb2_sess_setup_req *ssr;
6188c2ecf20Sopenharmony_ci	bool is_binding;
6198c2ecf20Sopenharmony_ci	bool is_signed;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
6228c2ecf20Sopenharmony_ci	ssr = (struct smb2_sess_setup_req *)shdr;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	is_binding = shdr->Command == SMB2_SESSION_SETUP &&
6258c2ecf20Sopenharmony_ci		(ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
6268c2ecf20Sopenharmony_ci	is_signed = shdr->Flags & SMB2_FLAGS_SIGNED;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (!is_signed)
6298c2ecf20Sopenharmony_ci		return 0;
6308c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsNeedNegotiate)
6318c2ecf20Sopenharmony_ci		return 0;
6328c2ecf20Sopenharmony_ci	if (!is_binding && !server->session_estab) {
6338c2ecf20Sopenharmony_ci		strncpy(shdr->Signature, "BSRSPYL", 8);
6348c2ecf20Sopenharmony_ci		return 0;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	rc = server->ops->calc_signature(rqst, server, false);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	return rc;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ciint
6438c2ecf20Sopenharmony_cismb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	unsigned int rc;
6468c2ecf20Sopenharmony_ci	char server_response_sig[SMB2_SIGNATURE_SIZE];
6478c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr =
6488c2ecf20Sopenharmony_ci			(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	if ((shdr->Command == SMB2_NEGOTIATE) ||
6518c2ecf20Sopenharmony_ci	    (shdr->Command == SMB2_SESSION_SETUP) ||
6528c2ecf20Sopenharmony_ci	    (shdr->Command == SMB2_OPLOCK_BREAK) ||
6538c2ecf20Sopenharmony_ci	    server->ignore_signature ||
6548c2ecf20Sopenharmony_ci	    (!server->session_estab))
6558c2ecf20Sopenharmony_ci		return 0;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/*
6588c2ecf20Sopenharmony_ci	 * BB what if signatures are supposed to be on for session but
6598c2ecf20Sopenharmony_ci	 * server does not send one? BB
6608c2ecf20Sopenharmony_ci	 */
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Do not need to verify session setups with signature "BSRSPYL " */
6638c2ecf20Sopenharmony_ci	if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0)
6648c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n",
6658c2ecf20Sopenharmony_ci			 shdr->Command);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	/*
6688c2ecf20Sopenharmony_ci	 * Save off the origiginal signature so we can modify the smb and check
6698c2ecf20Sopenharmony_ci	 * our calculated signature against what the server sent.
6708c2ecf20Sopenharmony_ci	 */
6718c2ecf20Sopenharmony_ci	memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	rc = server->ops->calc_signature(rqst, server, true);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (rc)
6788c2ecf20Sopenharmony_ci		return rc;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) {
6818c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n",
6828c2ecf20Sopenharmony_ci			shdr->Command, shdr->MessageId);
6838c2ecf20Sopenharmony_ci		return -EACCES;
6848c2ecf20Sopenharmony_ci	} else
6858c2ecf20Sopenharmony_ci		return 0;
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci/*
6898c2ecf20Sopenharmony_ci * Set message id for the request. Should be called after wait_for_free_request
6908c2ecf20Sopenharmony_ci * and when srv_mutex is held.
6918c2ecf20Sopenharmony_ci */
6928c2ecf20Sopenharmony_cistatic inline void
6938c2ecf20Sopenharmony_cismb2_seq_num_into_buf(struct TCP_Server_Info *server,
6948c2ecf20Sopenharmony_ci		      struct smb2_sync_hdr *shdr)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	unsigned int i, num = le16_to_cpu(shdr->CreditCharge);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	shdr->MessageId = get_next_mid64(server);
6998c2ecf20Sopenharmony_ci	/* skip message numbers according to CreditCharge field */
7008c2ecf20Sopenharmony_ci	for (i = 1; i < num; i++)
7018c2ecf20Sopenharmony_ci		get_next_mid(server);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic struct mid_q_entry *
7058c2ecf20Sopenharmony_cismb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
7068c2ecf20Sopenharmony_ci		     struct TCP_Server_Info *server)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct mid_q_entry *temp;
7098c2ecf20Sopenharmony_ci	unsigned int credits = le16_to_cpu(shdr->CreditCharge);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (server == NULL) {
7128c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n");
7138c2ecf20Sopenharmony_ci		return NULL;
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
7178c2ecf20Sopenharmony_ci	memset(temp, 0, sizeof(struct mid_q_entry));
7188c2ecf20Sopenharmony_ci	kref_init(&temp->refcount);
7198c2ecf20Sopenharmony_ci	temp->mid = le64_to_cpu(shdr->MessageId);
7208c2ecf20Sopenharmony_ci	temp->credits = credits > 0 ? credits : 1;
7218c2ecf20Sopenharmony_ci	temp->pid = current->pid;
7228c2ecf20Sopenharmony_ci	temp->command = shdr->Command; /* Always LE */
7238c2ecf20Sopenharmony_ci	temp->when_alloc = jiffies;
7248c2ecf20Sopenharmony_ci	temp->server = server;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	/*
7278c2ecf20Sopenharmony_ci	 * The default is for the mid to be synchronous, so the
7288c2ecf20Sopenharmony_ci	 * default callback just wakes up the current task.
7298c2ecf20Sopenharmony_ci	 */
7308c2ecf20Sopenharmony_ci	get_task_struct(current);
7318c2ecf20Sopenharmony_ci	temp->creator = current;
7328c2ecf20Sopenharmony_ci	temp->callback = cifs_wake_up_task;
7338c2ecf20Sopenharmony_ci	temp->callback_data = current;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	atomic_inc(&midCount);
7368c2ecf20Sopenharmony_ci	temp->mid_state = MID_REQUEST_ALLOCATED;
7378c2ecf20Sopenharmony_ci	trace_smb3_cmd_enter(shdr->TreeId, shdr->SessionId,
7388c2ecf20Sopenharmony_ci		le16_to_cpu(shdr->Command), temp->mid);
7398c2ecf20Sopenharmony_ci	return temp;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int
7438c2ecf20Sopenharmony_cismb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
7448c2ecf20Sopenharmony_ci		   struct smb2_sync_hdr *shdr, struct mid_q_entry **mid)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsExiting)
7478c2ecf20Sopenharmony_ci		return -ENOENT;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsNeedReconnect) {
7508c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
7518c2ecf20Sopenharmony_ci		return -EAGAIN;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsNeedNegotiate &&
7558c2ecf20Sopenharmony_ci	   shdr->Command != SMB2_NEGOTIATE)
7568c2ecf20Sopenharmony_ci		return -EAGAIN;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (ses->status == CifsNew) {
7598c2ecf20Sopenharmony_ci		if ((shdr->Command != SMB2_SESSION_SETUP) &&
7608c2ecf20Sopenharmony_ci		    (shdr->Command != SMB2_NEGOTIATE))
7618c2ecf20Sopenharmony_ci			return -EAGAIN;
7628c2ecf20Sopenharmony_ci		/* else ok - we are setting up session */
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (ses->status == CifsExiting) {
7668c2ecf20Sopenharmony_ci		if (shdr->Command != SMB2_LOGOFF)
7678c2ecf20Sopenharmony_ci			return -EAGAIN;
7688c2ecf20Sopenharmony_ci		/* else ok - we are shutting down the session */
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	*mid = smb2_mid_entry_alloc(shdr, server);
7728c2ecf20Sopenharmony_ci	if (*mid == NULL)
7738c2ecf20Sopenharmony_ci		return -ENOMEM;
7748c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
7758c2ecf20Sopenharmony_ci	list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
7768c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	return 0;
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ciint
7828c2ecf20Sopenharmony_cismb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
7838c2ecf20Sopenharmony_ci		   bool log_error)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	unsigned int len = mid->resp_buf_size;
7868c2ecf20Sopenharmony_ci	struct kvec iov[1];
7878c2ecf20Sopenharmony_ci	struct smb_rqst rqst = { .rq_iov = iov,
7888c2ecf20Sopenharmony_ci				 .rq_nvec = 1 };
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	iov[0].iov_base = (char *)mid->resp_buf;
7918c2ecf20Sopenharmony_ci	iov[0].iov_len = len;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	dump_smb(mid->resp_buf, min_t(u32, 80, len));
7948c2ecf20Sopenharmony_ci	/* convert the length into a more usable form */
7958c2ecf20Sopenharmony_ci	if (len > 24 && server->sign && !mid->decrypted) {
7968c2ecf20Sopenharmony_ci		int rc;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci		rc = smb2_verify_signature(&rqst, server);
7998c2ecf20Sopenharmony_ci		if (rc)
8008c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
8018c2ecf20Sopenharmony_ci				 rc);
8028c2ecf20Sopenharmony_ci	}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	return map_smb2_to_linux_error(mid->resp_buf, log_error);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistruct mid_q_entry *
8088c2ecf20Sopenharmony_cismb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
8098c2ecf20Sopenharmony_ci		   struct smb_rqst *rqst)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	int rc;
8128c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr =
8138c2ecf20Sopenharmony_ci			(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
8148c2ecf20Sopenharmony_ci	struct mid_q_entry *mid;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	smb2_seq_num_into_buf(server, shdr);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	rc = smb2_get_mid_entry(ses, server, shdr, &mid);
8198c2ecf20Sopenharmony_ci	if (rc) {
8208c2ecf20Sopenharmony_ci		revert_current_mid_from_hdr(server, shdr);
8218c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	rc = smb2_sign_rqst(rqst, server);
8258c2ecf20Sopenharmony_ci	if (rc) {
8268c2ecf20Sopenharmony_ci		revert_current_mid_from_hdr(server, shdr);
8278c2ecf20Sopenharmony_ci		cifs_delete_mid(mid);
8288c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	return mid;
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_cistruct mid_q_entry *
8358c2ecf20Sopenharmony_cismb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	int rc;
8388c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr =
8398c2ecf20Sopenharmony_ci			(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
8408c2ecf20Sopenharmony_ci	struct mid_q_entry *mid;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsNeedNegotiate &&
8438c2ecf20Sopenharmony_ci	   shdr->Command != SMB2_NEGOTIATE)
8448c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	smb2_seq_num_into_buf(server, shdr);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	mid = smb2_mid_entry_alloc(shdr, server);
8498c2ecf20Sopenharmony_ci	if (mid == NULL) {
8508c2ecf20Sopenharmony_ci		revert_current_mid_from_hdr(server, shdr);
8518c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	rc = smb2_sign_rqst(rqst, server);
8558c2ecf20Sopenharmony_ci	if (rc) {
8568c2ecf20Sopenharmony_ci		revert_current_mid_from_hdr(server, shdr);
8578c2ecf20Sopenharmony_ci		DeleteMidQEntry(mid);
8588c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	return mid;
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ciint
8658c2ecf20Sopenharmony_cismb3_crypto_aead_allocate(struct TCP_Server_Info *server)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct crypto_aead *tfm;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (!server->secmech.ccmaesencrypt) {
8708c2ecf20Sopenharmony_ci		if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) ||
8718c2ecf20Sopenharmony_ci		    (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
8728c2ecf20Sopenharmony_ci			tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
8738c2ecf20Sopenharmony_ci		else
8748c2ecf20Sopenharmony_ci			tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
8758c2ecf20Sopenharmony_ci		if (IS_ERR(tfm)) {
8768c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "%s: Failed alloc encrypt aead\n",
8778c2ecf20Sopenharmony_ci				 __func__);
8788c2ecf20Sopenharmony_ci			return PTR_ERR(tfm);
8798c2ecf20Sopenharmony_ci		}
8808c2ecf20Sopenharmony_ci		server->secmech.ccmaesencrypt = tfm;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (!server->secmech.ccmaesdecrypt) {
8848c2ecf20Sopenharmony_ci		if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) ||
8858c2ecf20Sopenharmony_ci		    (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
8868c2ecf20Sopenharmony_ci			tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
8878c2ecf20Sopenharmony_ci		else
8888c2ecf20Sopenharmony_ci			tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
8898c2ecf20Sopenharmony_ci		if (IS_ERR(tfm)) {
8908c2ecf20Sopenharmony_ci			crypto_free_aead(server->secmech.ccmaesencrypt);
8918c2ecf20Sopenharmony_ci			server->secmech.ccmaesencrypt = NULL;
8928c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n",
8938c2ecf20Sopenharmony_ci				 __func__);
8948c2ecf20Sopenharmony_ci			return PTR_ERR(tfm);
8958c2ecf20Sopenharmony_ci		}
8968c2ecf20Sopenharmony_ci		server->secmech.ccmaesdecrypt = tfm;
8978c2ecf20Sopenharmony_ci	}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	return 0;
9008c2ecf20Sopenharmony_ci}
901