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