xref: /illumos-gate/usr/src/lib/smbclnt/libfknsmb/common/fksmb_crypt_pkcs.c (revision 48e11a6ea0245c522078ddb86a73f16c8c28b949)
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 2021-2024 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Helper functions for SMB3 encryption using PKCS#11
19  *
20  * There are two implementations of these functions:
21  * This one (for user space) and another for kernel.
22  * See: uts/common/fs/smbclnt/netsmb/smb_crypt_kcf.c
23  *
24  * Contrary to what one might assume from the file name,
25  * there should be NO SMB implementation knowledge here
26  * beyond a few carefully selected things (nsmb_kcrypt.h).
27  */
28 
29 #include <security/cryptoki.h>
30 #include <security/pkcs11.h>
31 #include <netsmb/nsmb_kcrypt.h>
32 
33 #include <sys/cmn_err.h>
34 #include <sys/debug.h>
35 #include <sys/stream.h>
36 #include <sys/strsun.h>
37 #include <stdlib.h>
38 #include <strings.h>
39 
40 size_t	msgsize(mblk_t *);
41 static int copy_mblks(void *buf, size_t buflen, enum uio_rw, mblk_t *m);
42 
43 /*
44  * Common function to see if a mech is available.
45  */
46 static int
find_mech(CK_MECHANISM_TYPE id)47 find_mech(CK_MECHANISM_TYPE id)
48 {
49 	CK_SESSION_HANDLE hdl;
50 	CK_RV rv;
51 
52 	rv = SUNW_C_GetMechSession(id, &hdl);
53 	if (rv != CKR_OK) {
54 		return (-1);
55 	}
56 	(void) C_CloseSession(hdl);
57 
58 	return (0);
59 }
60 
61 /*
62  * SMB3 encryption helpers:
63  * (getmech, init, update, final)
64  */
65 
66 int
nsmb_aes_ccm_getmech(smb_crypto_mech_t * mech)67 nsmb_aes_ccm_getmech(smb_crypto_mech_t *mech)
68 {
69 
70 	if (find_mech(CKM_AES_CCM) != 0) {
71 		cmn_err(CE_NOTE, "PKCS#11: no mech AES_CCM");
72 		return (-1);
73 	}
74 
75 	mech->mechanism = CKM_AES_CCM;
76 	return (0);
77 }
78 
79 int
nsmb_aes_gcm_getmech(smb_crypto_mech_t * mech)80 nsmb_aes_gcm_getmech(smb_crypto_mech_t *mech)
81 {
82 
83 	if (find_mech(CKM_AES_GCM) != 0) {
84 		cmn_err(CE_NOTE, "PKCS#11: no mech AES_GCM");
85 		return (-1);
86 	}
87 
88 	mech->mechanism = CKM_AES_GCM;
89 	return (0);
90 }
91 
92 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)93 nsmb_crypto_init_ccm_param(smb_enc_ctx_t *ctx,
94     uint8_t *nonce, size_t noncesize,
95     uint8_t *auth, size_t authsize,
96     size_t datasize)
97 {
98 
99 	ASSERT3U(noncesize, >=, SMB3_AES_CCM_NONCE_SIZE);
100 
101 	/* CK_CCM_PARAMS */
102 	ctx->param.ccm.ulDataLen = datasize;
103 	ctx->param.ccm.pNonce = nonce;
104 	ctx->param.ccm.ulNonceLen = SMB3_AES_CCM_NONCE_SIZE;
105 	ctx->param.ccm.pAAD = auth;
106 	ctx->param.ccm.ulAADLen = authsize;
107 	ctx->param.ccm.ulMACLen = SMB2_SIG_SIZE;
108 
109 	ctx->mech.pParameter = (caddr_t)&ctx->param.ccm;
110 	ctx->mech.ulParameterLen = sizeof (ctx->param.ccm);
111 }
112 
113 void
nsmb_crypto_init_gcm_param(smb_enc_ctx_t * ctx,uint8_t * nonce,size_t noncesize,uint8_t * auth,size_t authsize)114 nsmb_crypto_init_gcm_param(smb_enc_ctx_t *ctx,
115     uint8_t *nonce, size_t noncesize,
116     uint8_t *auth, size_t authsize)
117 {
118 
119 	ASSERT3U(noncesize, >=, SMB3_AES_GCM_NONCE_SIZE);
120 
121 	/* CK_GCM_PARAMS */
122 	ctx->param.gcm.pIv = nonce;
123 	ctx->param.gcm.ulIvLen = SMB3_AES_GCM_NONCE_SIZE;
124 	ctx->param.gcm.pAAD = auth;			/* auth data */
125 	ctx->param.gcm.ulAADLen = authsize;		/* auth data len */
126 	ctx->param.gcm.ulTagBits = SMB2_SIG_SIZE << 3;	/* bytes to bits */
127 
128 	ctx->mech.pParameter = (caddr_t)&ctx->param.gcm;
129 	ctx->mech.ulParameterLen = sizeof (ctx->param.gcm);
130 }
131 
132 /*
133  * Start the KCF encrypt session, load the key
134  * If this returns zero, the caller should call
135  * nsmb_enc_ctx_done to cleanup the context,
136  * even if there are intervening errors.
137  */
138 int
nsmb_encrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)139 nsmb_encrypt_init(smb_enc_ctx_t *ctxp,
140     uint8_t *key, size_t keylen)
141 {
142 	CK_OBJECT_HANDLE hkey = 0;
143 	CK_MECHANISM *mech = &ctxp->mech;
144 	CK_RV rv;
145 
146 	rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx);
147 	if (rv != CKR_OK)
148 		return (-1);
149 
150 	rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism,
151 	    key, keylen, &hkey);
152 	if (rv != CKR_OK)
153 		return (-1);
154 
155 	rv = C_EncryptInit(ctxp->ctx, mech, hkey);
156 	if (rv != CKR_OK) {
157 		cmn_err(CE_WARN, "C_EncryptInit failed: 0x%lx", rv);
158 	}
159 	(void) C_DestroyObject(ctxp->ctx, hkey);
160 
161 	return (rv == CKR_OK ? 0 : -1);
162 }
163 
164 /*
165  * Start the KCF decrypt session, load the key
166  * If this returns zero, the caller should call
167  * nsmb_enc_ctx_done to cleanup the context,
168  * even if there are intervening errors.
169  */
170 int
nsmb_decrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)171 nsmb_decrypt_init(smb_enc_ctx_t *ctxp,
172     uint8_t *key, size_t keylen)
173 {
174 	CK_OBJECT_HANDLE hkey = 0;
175 	CK_MECHANISM *mech = &ctxp->mech;
176 	CK_RV rv;
177 
178 	rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx);
179 	if (rv != CKR_OK)
180 		return (-1);
181 
182 	rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism,
183 	    key, keylen, &hkey);
184 	if (rv != CKR_OK)
185 		return (-1);
186 
187 	rv = C_DecryptInit(ctxp->ctx, mech, hkey);
188 	if (rv != CKR_OK) {
189 		cmn_err(CE_WARN, "C_DecryptInit failed: 0x%lx", rv);
190 	}
191 	(void) C_DestroyObject(ctxp->ctx, hkey);
192 
193 	return (rv == CKR_OK ? 0 : -1);
194 }
195 
196 void
nsmb_enc_ctx_done(smb_enc_ctx_t * ctxp)197 nsmb_enc_ctx_done(smb_enc_ctx_t *ctxp)
198 {
199 	if (ctxp->ctx != 0) {
200 		(void) C_CloseSession(ctxp->ctx);
201 		ctxp->ctx = 0;
202 	}
203 }
204 
205 /*
206  * Encrypt a whole message with scatter/gather (MBLK)
207  *
208  * While the PKCS#11 implementation internally has the ability to
209  * handle scatter/gather, it currently presents no interface for it.
210  * As this library is used primarily for debugging, performance in
211  * here is not a big concern, so we'll get around the limitation of
212  * libpkcs11 by copying to/from a contiguous working buffer.
213  */
214 int
nsmb_encrypt_mblks(smb_enc_ctx_t * ctxp,mblk_t * mp,size_t clearlen)215 nsmb_encrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t clearlen)
216 {
217 	uint8_t *buf;
218 	size_t inlen, outlen;
219 	ulong_t tlen;
220 	int err;
221 	CK_RV rv;
222 
223 	inlen = clearlen;
224 	outlen = clearlen + SMB2_SIG_SIZE;
225 	ASSERT(msgsize(mp) >= outlen);
226 
227 	buf = malloc(outlen);
228 	if (buf == NULL)
229 		return (-1);
230 
231 	/* Copy from mblk chain to buf */
232 	err = copy_mblks(buf, inlen, UIO_WRITE, mp);
233 	if (err != 0)
234 		return (-1);
235 
236 	/* Encrypt in-place in our work buffer. */
237 	tlen = outlen;
238 	rv = C_Encrypt(ctxp->ctx, buf, inlen, buf, &tlen);
239 	if (rv != CKR_OK) {
240 		cmn_err(CE_WARN, "C_Encrypt failed: 0x%lx", rv);
241 		return (-1);
242 	}
243 	if (tlen != outlen) {
244 		cmn_err(CE_WARN, "nsmb_encrypt_mblks outlen %d vs %d",
245 		    (int)tlen, (int)outlen);
246 		return (-1);
247 	}
248 
249 	/* Copy from buf to mblk segs */
250 	err = copy_mblks(buf, outlen, UIO_READ, mp);
251 	if (err != 0)
252 		return (-1);
253 
254 	return (0);
255 }
256 
257 /*
258  * Decrypt a whole message with scatter/gather (MBLK)
259  */
260 int
nsmb_decrypt_mblks(smb_enc_ctx_t * ctxp,mblk_t * mp,size_t cipherlen)261 nsmb_decrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t cipherlen)
262 {
263 	uint8_t *buf;
264 	size_t inlen, outlen;
265 	ulong_t tlen;
266 	int err;
267 	CK_RV rv;
268 
269 	if (cipherlen <= SMB2_SIG_SIZE)
270 		return (-1);
271 	inlen = cipherlen;
272 	outlen = cipherlen - SMB2_SIG_SIZE;
273 	ASSERT(msgsize(mp) >= inlen);
274 
275 	buf = malloc(inlen);
276 	if (buf == NULL)
277 		return (-1);
278 
279 	/* Copy from mblk chain to buf */
280 	err = copy_mblks(buf, inlen, UIO_WRITE, mp);
281 	if (err != 0)
282 		return (-1);
283 
284 	/* Decrypt in-place in our work buffer. */
285 	tlen = outlen;
286 	rv = C_Decrypt(ctxp->ctx, buf, inlen, buf, &tlen);
287 	if (rv != CKR_OK) {
288 		cmn_err(CE_WARN, "C_Decrypt failed: 0x%lx", rv);
289 		return (-1);
290 	}
291 	if (tlen != outlen) {
292 		cmn_err(CE_WARN, "nsmb_decrypt_mblks outlen %d vs %d",
293 		    (int)tlen, (int)outlen);
294 		return (-1);
295 	}
296 
297 	/* Copy from buf to mblk segs */
298 	err = copy_mblks(buf, outlen, UIO_READ, mp);
299 	if (err != 0)
300 		return (-1);
301 
302 	return (0);
303 }
304 
305 static int
copy_mblks(void * buf,size_t buflen,enum uio_rw rw,mblk_t * m)306 copy_mblks(void *buf, size_t buflen, enum uio_rw rw, mblk_t *m)
307 {
308 	uchar_t *p = buf;
309 	size_t rem = buflen;
310 	size_t len;
311 
312 	while (rem > 0) {
313 		if (m == NULL)
314 			return (-1);
315 		ASSERT(m->b_datap->db_type == M_DATA);
316 		len = MBLKL(m);
317 		if (len > rem)
318 			len = rem;
319 		if (rw == UIO_READ) {
320 			/* buf to mblks */
321 			bcopy(p, m->b_rptr, len);
322 		} else {
323 			/* mblks to buf */
324 			bcopy(m->b_rptr, p, len);
325 		}
326 		m = m->b_cont;
327 		p += len;
328 		rem -= len;
329 	}
330 	return (0);
331 }
332 
333 size_t
msgsize(mblk_t * mp)334 msgsize(mblk_t *mp)
335 {
336 	size_t	n = 0;
337 
338 	for (; mp != NULL; mp = mp->b_cont)
339 		n += MBLKL(mp);
340 
341 	return (n);
342 }
343