xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/nsmb_crypt_kcf.c (revision 92bbf3a88c3e22defe144058ae264d05727f701c)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2018-2021 Tintri by DDN, Inc. All rights reserved.
14  * Copyright 2020-2024 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Helper functions for SMB3 encryption using the
19  * Kernel Cryptographic Framework (KCF)
20  *
21  * There are two implementations of these functions:
22  * This one (for kernel) and another for user space:
23  * See: lib/smbclnt/libfknsmb/common/fksmb_crypt_pkcs.c
24  *
25  * Contrary to what one might assume from the file name,
26  * there should be NO SMB implementation knowledge here
27  * beyond a few carefully selected things (nsmb_kcrypt.h).
28  */
29 
30 #include <sys/types.h>
31 #include <sys/crypto/api.h>
32 
33 #include <netsmb/nsmb_kcrypt.h>
34 
35 #include <sys/cmn_err.h>
36 #include <sys/strsun.h>
37 #include <sys/sunddi.h>
38 
39 /*
40  * Common function to see if a mech is available.
41  */
42 static int
find_mech(smb_crypto_mech_t * mech,const char * name)43 find_mech(smb_crypto_mech_t *mech, const char *name)
44 {
45 	crypto_mech_type_t t;
46 
47 	t = crypto_mech2id(name);
48 	if (t == CRYPTO_MECH_INVALID) {
49 		cmn_err(CE_NOTE, "nsmb: no kcf mech: %s", name);
50 		return (-1);
51 	}
52 	mech->cm_type = t;
53 	return (0);
54 }
55 
56 /*
57  * SMB3 encryption helpers:
58  * (getmech, init, update, final)
59  */
60 
61 int
nsmb_aes_ccm_getmech(smb_crypto_mech_t * mech)62 nsmb_aes_ccm_getmech(smb_crypto_mech_t *mech)
63 {
64 	return (find_mech(mech, SUN_CKM_AES_CCM));
65 }
66 
67 int
nsmb_aes_gcm_getmech(smb_crypto_mech_t * mech)68 nsmb_aes_gcm_getmech(smb_crypto_mech_t *mech)
69 {
70 	return (find_mech(mech, SUN_CKM_AES_GCM));
71 }
72 
73 void
nsmb_crypto_init_ccm_param(smb_enc_ctx_t * ctx,uint8_t * nonce,size_t noncesize,uint8_t * auth,size_t authsize,size_t datasize)74 nsmb_crypto_init_ccm_param(smb_enc_ctx_t *ctx,
75     uint8_t *nonce, size_t noncesize,
76     uint8_t *auth, size_t authsize,
77     size_t datasize)
78 {
79 
80 	ASSERT3U(noncesize, >=, SMB3_AES_CCM_NONCE_SIZE);
81 
82 	ctx->param.ccm.ulMACSize = SMB2_SIG_SIZE;
83 	ctx->param.ccm.ulNonceSize = SMB3_AES_CCM_NONCE_SIZE;
84 	ctx->param.ccm.nonce = nonce;
85 	ctx->param.ccm.ulDataSize = datasize;
86 	ctx->param.ccm.ulAuthDataSize = authsize;
87 	ctx->param.ccm.authData = auth;
88 
89 	ctx->mech.cm_param = (caddr_t)&ctx->param.ccm;
90 	ctx->mech.cm_param_len = sizeof (ctx->param.ccm);
91 }
92 
93 void
nsmb_crypto_init_gcm_param(smb_enc_ctx_t * ctx,uint8_t * nonce,size_t noncesize,uint8_t * auth,size_t authsize)94 nsmb_crypto_init_gcm_param(smb_enc_ctx_t *ctx,
95     uint8_t *nonce, size_t noncesize,
96     uint8_t *auth, size_t authsize)
97 {
98 
99 	ASSERT3U(noncesize, >=, SMB3_AES_GCM_NONCE_SIZE);
100 
101 	ctx->param.gcm.pIv = nonce;
102 	ctx->param.gcm.ulIvLen = SMB3_AES_GCM_NONCE_SIZE;
103 	ctx->param.gcm.ulTagBits = SMB2_SIG_SIZE << 3;	/* bytes to bits */
104 	ctx->param.gcm.pAAD = auth;			/* auth data */
105 	ctx->param.gcm.ulAADLen = authsize;		/* auth data len */
106 
107 	ctx->mech.cm_param = (caddr_t)&ctx->param.gcm;
108 	ctx->mech.cm_param_len = sizeof (ctx->param.gcm);
109 }
110 
111 /*
112  * KCF doesn't need anything to happen in this call, but
113  * wants that key when we call encrypt or decrypt, so
114  * just stash the key here.
115  */
116 int
nsmb_encrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)117 nsmb_encrypt_init(smb_enc_ctx_t *ctxp,
118     uint8_t *key, size_t keylen)
119 {
120 	bzero(&ctxp->ckey, sizeof (ctxp->ckey));
121 	ctxp->ckey.ck_format = CRYPTO_KEY_RAW;
122 	ctxp->ckey.ck_data = key;
123 	ctxp->ckey.ck_length = keylen * 8; /* in bits */
124 
125 	return (0);
126 }
127 
128 int
nsmb_decrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)129 nsmb_decrypt_init(smb_enc_ctx_t *ctxp,
130     uint8_t *key, size_t keylen)
131 {
132 	bzero(&ctxp->ckey, sizeof (ctxp->ckey));
133 	ctxp->ckey.ck_format = CRYPTO_KEY_RAW;
134 	ctxp->ckey.ck_data = key;
135 	ctxp->ckey.ck_length = keylen * 8; /* in bits */
136 
137 	return (0);
138 }
139 
140 /*
141  * Nothing to cleanup after crypto_encrypt, crypto_decrypt
142  * The user space variant has work to do.
143  */
144 void
nsmb_enc_ctx_done(smb_enc_ctx_t * ctxp)145 nsmb_enc_ctx_done(smb_enc_ctx_t *ctxp)
146 {
147 }
148 
149 /*
150  * Encrypt a whole message with scatter/gather (MBLK)
151  */
152 int
nsmb_encrypt_mblks(smb_enc_ctx_t * ctxp,mblk_t * mp,size_t clearlen)153 nsmb_encrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t clearlen)
154 {
155 	crypto_data_t in_cd, out_cd;
156 	size_t inlen, outlen;
157 	int rv;
158 
159 	inlen = clearlen;
160 	outlen = clearlen + SMB2_SIG_SIZE;
161 	ASSERT(msgsize(mp) >= outlen);
162 
163 	bzero(&in_cd, sizeof (crypto_data_t));
164 	in_cd.cd_format = CRYPTO_DATA_MBLK;
165 	in_cd.cd_length = inlen;
166 	in_cd.cd_mp = mp;
167 
168 	bzero(&out_cd, sizeof (crypto_data_t));
169 	out_cd.cd_format = CRYPTO_DATA_MBLK;
170 	out_cd.cd_length = outlen;
171 	out_cd.cd_mp = mp;
172 
173 	rv = crypto_encrypt(&ctxp->mech, &in_cd,
174 	    &ctxp->ckey, NULL, &out_cd, NULL);
175 	if (rv != CRYPTO_SUCCESS) {
176 		cmn_err(CE_WARN, "nsmb: crypto_encrypt failed: 0x%x", rv);
177 		return (-1);
178 	}
179 
180 	return (0);
181 }
182 
183 /*
184  * Decrypt a whole message with scatter/gather (MBLK)
185  */
186 int
nsmb_decrypt_mblks(smb_enc_ctx_t * ctxp,mblk_t * mp,size_t cipherlen)187 nsmb_decrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t cipherlen)
188 {
189 	crypto_data_t in_cd, out_cd;
190 	size_t inlen, outlen;
191 	int rv;
192 
193 	if (cipherlen <= SMB2_SIG_SIZE)
194 		return (-1);
195 	inlen = cipherlen;
196 	outlen = cipherlen - SMB2_SIG_SIZE;
197 	ASSERT(msgsize(mp) >= inlen);
198 
199 	/* In is ciphertext */
200 	bzero(&in_cd, sizeof (crypto_data_t));
201 	in_cd.cd_format = CRYPTO_DATA_MBLK;
202 	in_cd.cd_length = inlen;
203 	in_cd.cd_mp = mp;
204 
205 	/* Out is plaintext */
206 	bzero(&out_cd, sizeof (crypto_data_t));
207 	out_cd.cd_format = CRYPTO_DATA_MBLK;
208 	out_cd.cd_length = outlen;
209 	out_cd.cd_mp = mp;
210 
211 	rv = crypto_decrypt(&ctxp->mech, &in_cd,
212 	    &ctxp->ckey, NULL, &out_cd, NULL);
213 	if (rv != CRYPTO_SUCCESS) {
214 		cmn_err(CE_WARN, "nsmb: crypto_decrypt failed: 0x%x", rv);
215 		return (-1);
216 	}
217 
218 	return (0);
219 }
220