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