xref: /illumos-gate/usr/src/lib/smbclnt/libfknsmb/common/fksmb_crypt_pkcs.c (revision 489f6310fe8952e87fc1dce8af87990fcfd90f18)
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-2025 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 <umem.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
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
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
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
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
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
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
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
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
215 nsmb_encrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t clearlen)
216 {
217 	uint8_t *buf = NULL;
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 = umem_alloc(outlen, 0);
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 		goto out;
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 		err = -1;
242 		goto out;
243 	}
244 	if (tlen != outlen) {
245 		cmn_err(CE_WARN, "nsmb_encrypt_mblks outlen %d vs %d",
246 		    (int)tlen, (int)outlen);
247 		err = -1;
248 		goto out;
249 	}
250 
251 	/* Copy from buf to mblk segs */
252 	err = copy_mblks(buf, outlen, UIO_READ, mp);
253 
254 out:
255 	if (buf != NULL)
256 		umem_free(buf, outlen);
257 
258 	return (err);
259 }
260 
261 /*
262  * Decrypt a whole message with scatter/gather (MBLK)
263  */
264 int
265 nsmb_decrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t cipherlen)
266 {
267 	uint8_t *buf = NULL;
268 	size_t inlen, outlen;
269 	ulong_t tlen;
270 	int err;
271 	CK_RV rv;
272 
273 	if (cipherlen <= SMB2_SIG_SIZE)
274 		return (-1);
275 	inlen = cipherlen;
276 	outlen = cipherlen - SMB2_SIG_SIZE;
277 	ASSERT(msgsize(mp) >= inlen);
278 
279 	buf = umem_alloc(inlen, 0);
280 	if (buf == NULL)
281 		return (-1);
282 
283 	/* Copy from mblk chain to buf */
284 	err = copy_mblks(buf, inlen, UIO_WRITE, mp);
285 	if (err != 0)
286 		goto out;
287 
288 	/* Decrypt in-place in our work buffer. */
289 	tlen = outlen;
290 	rv = C_Decrypt(ctxp->ctx, buf, inlen, buf, &tlen);
291 	if (rv != CKR_OK) {
292 		cmn_err(CE_WARN, "C_Decrypt failed: 0x%lx", rv);
293 		err = -1;
294 		goto out;
295 	}
296 	if (tlen != outlen) {
297 		cmn_err(CE_WARN, "nsmb_decrypt_mblks outlen %d vs %d",
298 		    (int)tlen, (int)outlen);
299 		err = -1;
300 		goto out;
301 	}
302 
303 	/* Copy from buf to mblk segs */
304 	err = copy_mblks(buf, outlen, UIO_READ, mp);
305 
306 out:
307 	if (buf != NULL)
308 		umem_free(buf, inlen);
309 
310 	return (err);
311 }
312 
313 static int
314 copy_mblks(void *buf, size_t buflen, enum uio_rw rw, mblk_t *m)
315 {
316 	uchar_t *p = buf;
317 	size_t rem = buflen;
318 	size_t len;
319 
320 	while (rem > 0) {
321 		if (m == NULL)
322 			return (-1);
323 		ASSERT(m->b_datap->db_type == M_DATA);
324 		len = MBLKL(m);
325 		if (len > rem)
326 			len = rem;
327 		if (rw == UIO_READ) {
328 			/* buf to mblks */
329 			bcopy(p, m->b_rptr, len);
330 		} else {
331 			/* mblks to buf */
332 			bcopy(m->b_rptr, p, len);
333 		}
334 		m = m->b_cont;
335 		p += len;
336 		rem -= len;
337 	}
338 	return (0);
339 }
340 
341 size_t
342 msgsize(mblk_t *mp)
343 {
344 	size_t	n = 0;
345 
346 	for (; mp != NULL; mp = mp->b_cont)
347 		n += MBLKL(mp);
348 
349 	return (n);
350 }
351