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 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/smbsrv/smb3_encrypt_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 (smb_kcrypt.h).
27 */
28
29 #include <security/cryptoki.h>
30 #include <security/pkcs11.h>
31 #include <smbsrv/smb_kcrypt.h>
32
33 #include <sys/cmn_err.h>
34 #include <sys/debug.h>
35 #include <stdlib.h>
36 #include <strings.h>
37
38 /*
39 * Common function to see if a mech is available.
40 */
41 static int
find_mech(CK_MECHANISM_TYPE id)42 find_mech(CK_MECHANISM_TYPE id)
43 {
44 CK_SESSION_HANDLE hdl;
45 CK_RV rv;
46
47 rv = SUNW_C_GetMechSession(id, &hdl);
48 if (rv != CKR_OK) {
49 return (-1);
50 }
51 (void) C_CloseSession(hdl);
52
53 return (0);
54 }
55
56 /*
57 * SMB3 encryption helpers:
58 * (getmech, init, update, final)
59 */
60
61 int
smb3_aes_ccm_getmech(smb_crypto_mech_t * mech)62 smb3_aes_ccm_getmech(smb_crypto_mech_t *mech)
63 {
64
65 if (find_mech(CKM_AES_CCM) != 0) {
66 cmn_err(CE_NOTE, "PKCS#11: no mech AES_CCM");
67 return (-1);
68 }
69
70 mech->mechanism = CKM_AES_CCM;
71 return (0);
72 }
73
74 int
smb3_aes_gcm_getmech(smb_crypto_mech_t * mech)75 smb3_aes_gcm_getmech(smb_crypto_mech_t *mech)
76 {
77
78 if (find_mech(CKM_AES_GCM) != 0) {
79 cmn_err(CE_NOTE, "PKCS#11: no mech CKM_AES_GCM");
80 return (-1);
81 }
82
83 mech->mechanism = CKM_AES_GCM;
84 return (0);
85 }
86
87 void
smb3_crypto_init_ccm_param(smb_enc_ctx_t * ctx,uint8_t * nonce,size_t noncesize,uint8_t * auth,size_t authsize,size_t datasize)88 smb3_crypto_init_ccm_param(smb_enc_ctx_t *ctx,
89 uint8_t *nonce, size_t noncesize,
90 uint8_t *auth, size_t authsize,
91 size_t datasize)
92 {
93
94 ASSERT3U(noncesize, >=, SMB3_AES_CCM_NONCE_SIZE);
95
96 /* CK_CCM_PARAMS */
97 ctx->param.ccm.ulDataLen = datasize;
98 ctx->param.ccm.pNonce = nonce;
99 ctx->param.ccm.ulNonceLen = SMB3_AES_CCM_NONCE_SIZE;
100 ctx->param.ccm.pAAD = auth;
101 ctx->param.ccm.ulAADLen = authsize;
102 ctx->param.ccm.ulMACLen = SMB2_SIG_SIZE;
103
104 ctx->mech.pParameter = (caddr_t)&ctx->param.ccm;
105 ctx->mech.ulParameterLen = sizeof (ctx->param.ccm);
106 }
107
108 void
smb3_crypto_init_gcm_param(smb_enc_ctx_t * ctx,uint8_t * nonce,size_t noncesize,uint8_t * auth,size_t authsize)109 smb3_crypto_init_gcm_param(smb_enc_ctx_t *ctx,
110 uint8_t *nonce, size_t noncesize,
111 uint8_t *auth, size_t authsize)
112 {
113
114 ASSERT3U(noncesize, >=, SMB3_AES_GCM_NONCE_SIZE);
115
116 /* CK_GCM_PARAMS */
117 ctx->param.gcm.pIv = nonce;
118 ctx->param.gcm.ulIvLen = SMB3_AES_GCM_NONCE_SIZE;
119 ctx->param.gcm.pAAD = auth; /* auth data */
120 ctx->param.gcm.ulAADLen = authsize; /* auth data len */
121 ctx->param.gcm.ulTagBits = SMB2_SIG_SIZE << 3; /* bytes to bits */
122
123 ctx->mech.pParameter = (caddr_t)&ctx->param.gcm;
124 ctx->mech.ulParameterLen = sizeof (ctx->param.gcm);
125 }
126
127 /*
128 * Start the KCF encrypt session, load the key
129 * If this returns zero, the caller should call
130 * smb3_enc_ctx_done to cleanup the context,
131 * even if there are intervening errors.
132 */
133 int
smb3_encrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)134 smb3_encrypt_init(smb_enc_ctx_t *ctxp,
135 uint8_t *key, size_t keylen)
136 {
137 CK_OBJECT_HANDLE hkey = 0;
138 CK_MECHANISM *mech = &ctxp->mech;
139 CK_RV rv;
140
141 rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx);
142 if (rv != CKR_OK)
143 return (-1);
144
145 rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism,
146 key, keylen, &hkey);
147 if (rv != CKR_OK)
148 return (-1);
149
150 rv = C_EncryptInit(ctxp->ctx, mech, hkey);
151 if (rv != CKR_OK) {
152 cmn_err(CE_WARN, "C_EncryptInit failed: 0x%lx", rv);
153 }
154 (void) C_DestroyObject(ctxp->ctx, hkey);
155
156 return (rv == CKR_OK ? 0 : -1);
157 }
158
159 /*
160 * Start the KCF decrypt session, load the key
161 * If this returns zero, the caller should call
162 * smb3_enc_ctx_done to cleanup the context,
163 * even if there are intervening errors.
164 */
165 int
smb3_decrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)166 smb3_decrypt_init(smb_enc_ctx_t *ctxp,
167 uint8_t *key, size_t keylen)
168 {
169 CK_OBJECT_HANDLE hkey = 0;
170 CK_MECHANISM *mech = &ctxp->mech;
171 CK_RV rv;
172
173 rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx);
174 if (rv != CKR_OK)
175 return (-1);
176
177 rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism,
178 key, keylen, &hkey);
179 if (rv != CKR_OK)
180 return (-1);
181
182 rv = C_DecryptInit(ctxp->ctx, mech, hkey);
183 if (rv != CKR_OK) {
184 cmn_err(CE_WARN, "C_DecryptInit failed: 0x%lx", rv);
185 }
186 (void) C_DestroyObject(ctxp->ctx, hkey);
187
188 return (rv == CKR_OK ? 0 : -1);
189 }
190
191 /*
192 * Encrypt a whole message with scatter/gather (UIO)
193 *
194 * While the PKCS#11 implementation internally has the ability to
195 * handle scatter/gather, it currently presents no interface for it.
196 * As this library is used primarily for debugging, performance in
197 * here is not a big concern, so we'll get around the limitation of
198 * libpkcs11 by copying to/from a contiguous working buffer.
199 */
200 int
smb3_encrypt_uio(smb_enc_ctx_t * ctxp,uio_t * in,uio_t * out)201 smb3_encrypt_uio(smb_enc_ctx_t *ctxp, uio_t *in, uio_t *out)
202 {
203 uint8_t *buf = NULL;
204 size_t inlen, outlen;
205 ulong_t tlen;
206 int err, rc = -1;
207 CK_RV rv;
208
209 if (in->uio_resid <= 0)
210 return (-1);
211 inlen = in->uio_resid;
212 outlen = inlen + 16;
213 buf = malloc(outlen);
214 if (buf == NULL)
215 return (-1);
216
217 /* Copy from uio segs to buf */
218 err = uiomove(buf, inlen, UIO_WRITE, in);
219 if (err != 0)
220 goto out;
221
222 /* Encrypt in-place in our work buffer. */
223 tlen = outlen;
224 rv = C_Encrypt(ctxp->ctx, buf, inlen, buf, &tlen);
225 if (rv != CKR_OK) {
226 cmn_err(CE_WARN, "C_Encrypt failed: 0x%lx", rv);
227 goto out;
228 }
229 if (tlen != outlen) {
230 cmn_err(CE_WARN, "smb3_encrypt_uio outlen %d vs %d",
231 (int)tlen, (int)outlen);
232 goto out;
233 }
234
235 /* Copy from buf to uio segs */
236 err = uiomove(buf, outlen, UIO_READ, out);
237 if (err != 0)
238 goto out;
239
240 rc = 0;
241 out:
242 free(buf);
243
244 return (rc);
245 }
246
247 int
smb3_decrypt_uio(smb_enc_ctx_t * ctxp,uio_t * in,uio_t * out)248 smb3_decrypt_uio(smb_enc_ctx_t *ctxp, uio_t *in, uio_t *out)
249 {
250 uint8_t *buf = NULL;
251 size_t inlen, outlen;
252 ulong_t tlen;
253 int err, rc = -1;
254 CK_RV rv;
255
256 if (in->uio_resid <= 16)
257 return (-1);
258 inlen = in->uio_resid;
259 outlen = inlen - 16;
260 buf = malloc(inlen);
261 if (buf == NULL)
262 return (-1);
263
264 /* Copy from uio segs to buf */
265 err = uiomove(buf, inlen, UIO_WRITE, in);
266 if (err != 0)
267 goto out;
268
269 /* Decrypt in-place in our work buffer. */
270 tlen = outlen;
271 rv = C_Decrypt(ctxp->ctx, buf, inlen, buf, &tlen);
272 if (rv != CKR_OK) {
273 cmn_err(CE_WARN, "C_Decrypt failed: 0x%lx", rv);
274 goto out;
275 }
276 if (tlen != outlen) {
277 cmn_err(CE_WARN, "smb3_decrypt_uio outlen %d vs %d",
278 (int)tlen, (int)outlen);
279 goto out;
280 }
281
282 /* Copy from buf to uio segs */
283 err = uiomove(buf, outlen, UIO_READ, out);
284 if (err != 0)
285 goto out;
286
287 rc = 0;
288 out:
289 free(buf);
290
291 return (rc);
292 }
293
294 void
smb3_enc_ctx_done(smb_enc_ctx_t * ctxp)295 smb3_enc_ctx_done(smb_enc_ctx_t *ctxp)
296 {
297 if (ctxp->ctx != 0) {
298 (void) C_CloseSession(ctxp->ctx);
299 ctxp->ctx = 0;
300 }
301 }
302