xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb3_crypt.c (revision e5d0cebc3bbd01b8ae62cebd964dde7bb8157b02)
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 2019 Nexenta Systems, Inc.  All rights reserved.
14  * Copyright 2024 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Support for SMB3 encryption (message privacy)
19  */
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/conf.h>
24 #include <sys/proc.h>
25 #include <sys/fcntl.h>
26 #include <sys/socket.h>
27 #include <sys/kmem.h>
28 #include <sys/errno.h>
29 #include <sys/cmn_err.h>
30 #include <sys/random.h>
31 #include <sys/stream.h>
32 #include <sys/strsun.h>
33 #include <sys/sdt.h>
34 
35 #include <netsmb/smb_osdep.h>
36 #include <netsmb/smb2.h>
37 #include <netsmb/smb_conn.h>
38 #include <netsmb/smb_subr.h>
39 #include <netsmb/smb_dev.h>
40 #include <netsmb/smb_rq.h>
41 
42 #include <netsmb/nsmb_kcrypt.h>
43 
44 #define	SMB3_TFORM_HDR_SIZE	52
45 #define	SMB3_NONCE_OFFS		20
46 #define	SMB3_SIG_OFFS		4
47 
48 static const uint8_t SMB3_CRYPT_SIG[4] = { 0xFD, 'S', 'M', 'B' };
49 
50 /*
51  * Initialize crypto mechanisms we'll need.
52  * Called after negotiate.
53  */
54 void
55 nsmb_crypt_init_mech(struct smb_vc *vcp)
56 {
57 	smb_crypto_mech_t *mech;
58 	int rc;
59 
60 	if (vcp->vc3_crypt_mech != NULL)
61 		return;
62 
63 	mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
64 
65 	/* Always CCM for now. */
66 	rc = nsmb_aes_ccm_getmech(mech);
67 	if (rc != 0) {
68 		kmem_free(mech, sizeof (*mech));
69 		cmn_err(CE_NOTE, "SMB3 found no AES mechanism"
70 		    " (encryption disabled)");
71 		return;
72 	}
73 	vcp->vc3_crypt_mech = mech;
74 }
75 
76 void
77 nsmb_crypt_free_mech(struct smb_vc *vcp)
78 {
79 	smb_crypto_mech_t *mech;
80 
81 	if ((mech = vcp->vc3_crypt_mech) == NULL)
82 		return;
83 
84 	kmem_free(mech, sizeof (*mech));
85 }
86 
87 /*
88  * Initialize keys for encryption
89  * Called after session setup.
90  */
91 void
92 nsmb_crypt_init_keys(struct smb_vc *vcp)
93 {
94 
95 	/*
96 	 * If we don't have a session key, we'll fail later when a
97 	 * request that requires (en/de)cryption can't be (en/de)crypted.
98 	 * Also don't bother initializing if we don't have a mechanism.
99 	 */
100 	if (vcp->vc3_crypt_mech == NULL ||
101 	    vcp->vc_ssnkeylen <= 0)
102 		return;
103 
104 	/*
105 	 * For SMB3, the encrypt/decrypt keys are derived from
106 	 * the session key using KDF in counter mode.
107 	 */
108 	if (nsmb_kdf(vcp->vc3_encrypt_key, SMB3_KEYLEN,
109 	    vcp->vc_ssnkey, vcp->vc_ssnkeylen,
110 	    (uint8_t *)"SMB2AESCCM", 11,
111 	    (uint8_t *)"ServerIn ", 10) != 0)
112 		return;
113 
114 	if (nsmb_kdf(vcp->vc3_decrypt_key, SMB3_KEYLEN,
115 	    vcp->vc_ssnkey, vcp->vc_ssnkeylen,
116 	    (uint8_t *)"SMB2AESCCM", 11,
117 	    (uint8_t *)"ServerOut", 10) != 0)
118 		return;
119 
120 	vcp->vc3_encrypt_key_len = SMB3_KEYLEN;
121 	vcp->vc3_decrypt_key_len = SMB3_KEYLEN;
122 
123 	(void) random_get_pseudo_bytes(
124 	    (uint8_t *)&vcp->vc3_nonce_low,
125 	    sizeof (vcp->vc3_nonce_low));
126 	(void) random_get_pseudo_bytes(
127 	    (uint8_t *)&vcp->vc3_nonce_high,
128 	    sizeof (vcp->vc3_nonce_high));
129 }
130 
131 /*
132  * Encrypt the message in *mpp, in place, prepending the
133  * SMB3 transform header.
134  *
135  * Any non-zero return is an error (values not used).
136  */
137 int
138 smb3_msg_encrypt(struct smb_vc *vcp, mblk_t **mpp)
139 {
140 	smb_enc_ctx_t ctx;
141 	mblk_t *body, *thdr, *lastm;
142 	struct mbchain	mbp_store;
143 	struct mbchain *mbp = &mbp_store;
144 	uint32_t bodylen;
145 	uint8_t *authdata;
146 	size_t authlen;
147 	int rc;
148 
149 	ASSERT(RW_WRITE_HELD(&vcp->iod_rqlock));
150 
151 	if (vcp->vc3_crypt_mech == NULL ||
152 	    vcp->vc3_encrypt_key_len != SMB3_KEYLEN) {
153 		return (ENOTSUP);
154 	}
155 
156 	bzero(&ctx, sizeof (ctx));
157 	ctx.mech = *((smb_crypto_mech_t *)vcp->vc3_crypt_mech);
158 
159 	body = *mpp;
160 	bodylen = msgdsize(body);
161 
162 	/*
163 	 * Get a new "nonce".  Access to these counters is
164 	 * serialized by iod_rqlock (assert above).
165 	 */
166 	vcp->vc3_nonce_low++;
167 	if (vcp->vc3_nonce_low == 0) {
168 		vcp->vc3_nonce_low++;
169 		vcp->vc3_nonce_high++;
170 	}
171 
172 	/*
173 	 * Build the transform header, keeping pointers to the various
174 	 * parts of it that we'll need to refer to later.
175 	 */
176 	(void) mb_init(mbp);
177 	thdr = mbp->mb_top;
178 	ASSERT(MBLKTAIL(thdr) >= SMB3_TFORM_HDR_SIZE);
179 	mb_put_mem(mbp, SMB3_CRYPT_SIG, 4, MB_MSYSTEM);
180 	mb_put_mem(mbp, NULL, SMB2_SIG_SIZE, MB_MZERO);	// signature (later)
181 	mb_put_uint64le(mbp, vcp->vc3_nonce_low);
182 	mb_put_uint64le(mbp, vcp->vc3_nonce_high);
183 	/* Zero last 5 bytes of nonce per. spec. */
184 	bzero(thdr->b_wptr - 5, 5);
185 	mb_put_uint32le(mbp, bodylen);
186 	mb_put_uint16le(mbp, 0);	// reserved
187 	mb_put_uint16le(mbp, 1);	// flags
188 	mb_put_uint64le(mbp, vcp->vc2_session_id);
189 	mbp->mb_top = NULL; // keeping thdr
190 	mb_done(mbp);
191 
192 	/*
193 	 * Need pointers to the part of the transfor header
194 	 * after the signature (starting with the nonce).
195 	 */
196 	authdata = thdr->b_rptr + SMB3_NONCE_OFFS;
197 	authlen = SMB3_TFORM_HDR_SIZE - SMB3_NONCE_OFFS;
198 
199 	nsmb_crypto_init_ccm_param(&ctx,
200 	    authdata, SMB2_SIG_SIZE,
201 	    authdata, authlen, bodylen);
202 
203 	rc = nsmb_encrypt_init(&ctx,
204 	    vcp->vc3_encrypt_key, vcp->vc3_encrypt_key_len);
205 	if (rc != 0)
206 		goto errout;
207 
208 	/*
209 	 * Temporarily append the transform header onto the
210 	 * body mblk chain with its r/w pointers set to cover
211 	 * just the signature, needed for how encrypt works.
212 	 * Could just use linkb() but we need to unlink the
213 	 * block as well so just find the tail ourselves.
214 	 */
215 	ASSERT(MBLKL(thdr) == SMB3_TFORM_HDR_SIZE);
216 	thdr->b_rptr += SMB3_SIG_OFFS;
217 	thdr->b_wptr = thdr->b_rptr + SMB2_SIG_SIZE;
218 	lastm = body;
219 	while (lastm->b_cont != NULL)
220 		lastm = lastm->b_cont;
221 	lastm->b_cont = thdr;
222 
223 	/*
224 	 * The mblk chain is ready. Encrypt!
225 	 */
226 	rc = nsmb_encrypt_mblks(&ctx, body, bodylen);
227 	/* check rc below */
228 
229 	/* Unlink thdr and restore r/w pointers. */
230 	lastm->b_cont = NULL;
231 	thdr->b_rptr -= SMB3_SIG_OFFS;
232 	thdr->b_wptr = thdr->b_rptr + SMB3_TFORM_HDR_SIZE;
233 
234 	/* Now check rc from encrypt */
235 	if (rc != 0)
236 		goto errout;
237 
238 	/*
239 	 * Lastly, prepend the transform header.
240 	 */
241 	thdr->b_cont = body;
242 	*mpp = thdr;
243 	nsmb_enc_ctx_done(&ctx);
244 	return (0);
245 
246 errout:
247 	freeb(thdr);
248 	nsmb_enc_ctx_done(&ctx);
249 	return (rc);
250 }
251 
252 /*
253  * Decrypt the message in *mpp, in place, removing the
254  * SMB3 transform header.
255  *
256  * Any non-zero return is an error (values not used).
257  */
258 int
259 smb3_msg_decrypt(struct smb_vc *vcp, mblk_t **mpp)
260 {
261 	smb_enc_ctx_t ctx;
262 	uint8_t th_sig[4];
263 	mblk_t *body, *thdr, *lastm;
264 	struct mdchain	mdp_store;
265 	struct mdchain *mdp = &mdp_store;
266 	uint64_t th_ssnid;
267 	uint32_t bodylen, tlen;
268 	uint16_t th_flags;
269 	uint8_t *authdata;
270 	size_t authlen;
271 	int rc;
272 
273 	if (vcp->vc3_crypt_mech == NULL ||
274 	    vcp->vc3_encrypt_key_len != SMB3_KEYLEN) {
275 		return (ENOTSUP);
276 	}
277 
278 	bzero(&ctx, sizeof (ctx));
279 	ctx.mech = *((smb_crypto_mech_t *)vcp->vc3_crypt_mech);
280 
281 	/*
282 	 * Split off the transform header
283 	 * We need it contiguous.
284 	 */
285 	thdr = *mpp;
286 	body = m_split(thdr, SMB3_TFORM_HDR_SIZE, 1);
287 	if (body == NULL)
288 		return (ENOSR);
289 	thdr = m_pullup(thdr, SMB3_TFORM_HDR_SIZE);
290 	if (thdr == NULL)
291 		return (ENOSR);
292 
293 	/*
294 	 * Decode the transform header
295 	 */
296 	(void) md_initm(mdp, thdr);
297 	md_get_mem(mdp, th_sig, 4, MB_MSYSTEM);
298 	md_get_mem(mdp, NULL, SMB2_SIG_SIZE, MB_MZERO); // signature
299 	md_get_mem(mdp, NULL, SMB2_SIG_SIZE, MB_MZERO); // nonce
300 	md_get_uint32le(mdp, &bodylen);
301 	md_get_uint16le(mdp, NULL);	// reserved
302 	md_get_uint16le(mdp, &th_flags);
303 	md_get_uint64le(mdp, &th_ssnid);
304 	mdp->md_top = NULL; // keeping thdr
305 	md_done(mdp);
306 
307 	/*
308 	 * Validate transform header fields
309 	 */
310 	if (bcmp(th_sig, SMB3_CRYPT_SIG, 4) != 0) {
311 		rc = EPROTO;
312 		goto errout;
313 	}
314 	if (th_flags != 1 || th_ssnid != vcp->vc2_session_id) {
315 		rc = EINVAL;
316 		goto errout;
317 	}
318 
319 	/*
320 	 * Check actual body length (trim if necessary)
321 	 */
322 	tlen = msgdsize(body);
323 	if (tlen < bodylen) {
324 		rc = EINVAL;
325 		goto errout;
326 	}
327 	if (tlen > bodylen) {
328 		/* trim from tail */
329 		ssize_t adj;
330 
331 		adj = bodylen - tlen;
332 		ASSERT(adj < 0);
333 		(void) adjmsg(body, adj);
334 	}
335 
336 	/*
337 	 * Need pointers to the part of the transfor header
338 	 * after the signature (starting with the nonce).
339 	 * tlen is now length of ciphertext
340 	 */
341 	authdata = thdr->b_rptr + SMB3_NONCE_OFFS;
342 	authlen = SMB3_TFORM_HDR_SIZE - SMB3_NONCE_OFFS;
343 	tlen = bodylen + SMB2_SIG_SIZE;
344 
345 	nsmb_crypto_init_ccm_param(&ctx,
346 	    authdata, SMB2_SIG_SIZE,
347 	    authdata, authlen, tlen);
348 
349 	rc = nsmb_decrypt_init(&ctx,
350 	    vcp->vc3_decrypt_key, vcp->vc3_decrypt_key_len);
351 	if (rc != 0)
352 		goto errout;
353 
354 	/*
355 	 * Temporarily append the transform header onto the
356 	 * body mblk chain with its r/w pointers set to cover
357 	 * just the signature, needed for how decrypt works.
358 	 * Could just use linkb() but we need to unlink the
359 	 * block as well so just find the tail ourselves.
360 	 */
361 	thdr->b_rptr += SMB3_SIG_OFFS;
362 	thdr->b_wptr = thdr->b_rptr + SMB2_SIG_SIZE;
363 	lastm = body;
364 	while (lastm->b_cont != NULL)
365 		lastm = lastm->b_cont;
366 	lastm->b_cont = thdr;
367 
368 	/*
369 	 * The mblk chain is ready. Decrypt!
370 	 */
371 	rc = nsmb_decrypt_mblks(&ctx, body, tlen);
372 	/* check rc below */
373 
374 	/* Unlink thdr and restore r/w pointers. */
375 	lastm->b_cont = NULL;
376 	thdr->b_rptr -= SMB3_SIG_OFFS;
377 	thdr->b_wptr = thdr->b_rptr + SMB3_TFORM_HDR_SIZE;
378 
379 	/* Now check rc from decrypt */
380 	if (rc != 0)
381 		goto errout;
382 
383 	/*
384 	 * Lastly, discard the transform header
385 	 * and return the body.
386 	 */
387 	freeb(thdr);
388 	*mpp = body;
389 	nsmb_enc_ctx_done(&ctx);
390 	return (0);
391 
392 errout:
393 	freeb(thdr);
394 	nsmb_enc_ctx_done(&ctx);
395 	return (rc);
396 }
397