xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_signing.c (revision 08855964b9970604433f7b19dcd71cf5af5e5f14)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
24  * Copyright 2022 RackTop Systems, Inc.
25  */
26 /*
27  * These routines provide the SMB MAC signing for the SMB server.
28  * The routines calculate the signature of a SMB message in an mbuf chain.
29  *
30  * The following table describes the client server
31  * signing registry relationship
32  *
33  *		| Required	| Enabled     | Disabled
34  * -------------+---------------+------------ +--------------
35  * Required	| Signed	| Signed      | Fail
36  * -------------+---------------+-------------+-----------------
37  * Enabled	| Signed	| Signed      | Not Signed
38  * -------------+---------------+-------------+----------------
39  * Disabled	| Fail		| Not Signed  | Not Signed
40  */
41 
42 #include <sys/uio.h>
43 #include <smbsrv/smb_kproto.h>
44 #include <smbsrv/smb_kcrypt.h>
45 #include <sys/isa_defs.h>
46 #include <sys/byteorder.h>
47 
48 #define	SMB_SIG_SIZE	8
49 #define	SMB_SIG_OFFS	14
50 #define	SMB_HDRLEN	32
51 
52 #ifdef _LITTLE_ENDIAN
53 #define	htolel(x)	((uint32_t)(x))
54 #else
55 #define	htolel(x)	BSWAP_32(x)
56 #endif
57 
58 static int
59 smb_sign_calc(smb_request_t *sr, struct mbuf_chain *mbc,
60     uint32_t seqnum, unsigned char *sig);
61 
62 #ifdef DEBUG
63 uint32_t smb_sign_debug_search = 10;
64 
65 /*
66  * Debug code to search +/- for the correct sequence number.
67  * If found, correct sign->seqnum and return 0, else return -1
68  */
69 static int
70 smb_sign_find_seqnum(
71     smb_request_t *sr,
72     struct mbuf_chain *mbc,
73     unsigned char *mac_sig,
74     unsigned char *sr_sig)
75 {
76 	struct smb_sign *sign = &sr->session->signing;
77 	uint32_t i, t;
78 
79 	for (i = 1; i < smb_sign_debug_search; i++) {
80 		t = sr->sr_seqnum + i;
81 		(void) smb_sign_calc(sr, mbc, t, mac_sig);
82 		if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) {
83 			goto found;
84 		}
85 		t = sr->sr_seqnum - i;
86 		(void) smb_sign_calc(sr, mbc, t, mac_sig);
87 		if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) {
88 			goto found;
89 		}
90 	}
91 	cmn_err(CE_WARN, "smb_sign_find_seqnum: failed after %d", i);
92 	return (-1);
93 
94 found:
95 	cmn_err(CE_WARN, "smb_sign_find_seqnum: found! %d <- %d",
96 	    sign->seqnum, t);
97 	sign->seqnum = t;
98 	return (0);
99 }
100 #endif
101 
102 /*
103  * Called during session destroy.
104  */
105 static void
106 smb_sign_fini(smb_session_t *s)
107 {
108 	smb_crypto_mech_t *mech;
109 
110 	if ((mech = s->sign_mech) != NULL) {
111 		kmem_free(mech, sizeof (*mech));
112 		s->sign_mech = NULL;
113 	}
114 }
115 
116 /*
117  * smb_sign_begin
118  *
119  * Intializes MAC key based on the user session key and
120  * NTLM response and store it in the signing structure.
121  * This is what begins SMB signing.
122  */
123 void
124 smb_sign_begin(smb_request_t *sr, smb_token_t *token)
125 {
126 	smb_arg_sessionsetup_t *sinfo = sr->sr_ssetup;
127 	smb_session_t *session = sr->session;
128 	struct smb_sign *sign = &session->signing;
129 	smb_crypto_mech_t *mech;
130 	int rc;
131 
132 	/*
133 	 * We should normally have a session key here because
134 	 * our caller filters out Anonymous and Guest logons.
135 	 * However, buggy clients could get us here without a
136 	 * session key, in which case: just don't sign.
137 	 */
138 	if (token->tkn_ssnkey.val == NULL || token->tkn_ssnkey.len == 0)
139 		return;
140 
141 	/*
142 	 * Session-level initialization (once per session)
143 	 */
144 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
145 
146 	/*
147 	 * Signing may already have been setup by a prior logon,
148 	 * in which case we're done here.
149 	 */
150 	if (sign->mackey != NULL) {
151 		smb_rwx_rwexit(&session->s_lock);
152 		return;
153 	}
154 
155 	/*
156 	 * Get the mech handle
157 	 */
158 	if (session->sign_mech == NULL) {
159 		mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
160 		rc = smb_md5_getmech(mech);
161 		if (rc != 0) {
162 			kmem_free(mech, sizeof (*mech));
163 			smb_rwx_rwexit(&session->s_lock);
164 			return;
165 		}
166 		session->sign_mech = mech;
167 		session->sign_fini = smb_sign_fini;
168 	}
169 
170 	/*
171 	 * Compute and store the signing (MAC) key.
172 	 *
173 	 * With extended security, the MAC key is the same as the
174 	 * session key (and we'll have sinfo->ssi_ntpwlen == 0).
175 	 * With non-extended security, it's the concatenation of
176 	 * the session key and the "NT response" we received.
177 	 */
178 	sign->mackey_len = token->tkn_ssnkey.len + sinfo->ssi_ntpwlen;
179 	sign->mackey = kmem_alloc(sign->mackey_len, KM_SLEEP);
180 	bcopy(token->tkn_ssnkey.val, sign->mackey, token->tkn_ssnkey.len);
181 	if (sinfo->ssi_ntpwlen > 0) {
182 		bcopy(sinfo->ssi_ntpwd, sign->mackey + token->tkn_ssnkey.len,
183 		    sinfo->ssi_ntpwlen);
184 	}
185 
186 	session->signing.seqnum = 0;
187 	sr->sr_seqnum = 2;
188 	sr->reply_seqnum = 1;
189 	sign->flags = 0;
190 
191 	if (session->srv_secmode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
192 		sign->flags |= SMB_SIGNING_ENABLED;
193 		if (session->srv_secmode &
194 		    NEGOTIATE_SECURITY_SIGNATURES_REQUIRED)
195 			sign->flags |= SMB_SIGNING_CHECK;
196 	}
197 
198 	smb_rwx_rwexit(&session->s_lock);
199 }
200 
201 /*
202  * smb_sign_calc
203  *
204  * Calculates MAC signature for the given buffer and returns
205  * it in the mac_sign parameter.
206  *
207  * The sequence number is placed in the first four bytes of the signature
208  * field of the signature and the other 4 bytes are zeroed.
209  * The signature is the first 8 bytes of the MD5 result of the
210  * concatenated MAC key and the SMB message.
211  *
212  * MACsig = head(MD5(concat(MACKey, SMBMsg)), 8)
213  *
214  * where
215  *
216  *	MACKey = concat( UserSessionKey, NTLMResp )
217  *
218  * and
219  *
220  *	SMBMsg is the SMB message containing the sequence number.
221  *
222  * Return 0 if success
223  *
224  */
225 static int
226 smb_sign_calc(smb_request_t *sr, struct mbuf_chain *mbc,
227     uint32_t seqnum, unsigned char *mac_sign)
228 {
229 	smb_session_t *s = sr->session;
230 	struct smb_sign *sign = &s->signing;
231 	smb_sign_ctx_t ctx = 0;
232 	uchar_t digest[MD5_DIGEST_LENGTH];
233 	uchar_t *hdrp;
234 	struct mbuf *mbuf = mbc->chain;
235 	int offset = mbc->chain_offset;
236 	int size;
237 	int rc;
238 
239 	/*
240 	 * This union is a little bit of trickery to:
241 	 * (1) get the sequence number int aligned, and
242 	 * (2) reduce the number of digest calls, at the
243 	 * cost of a copying 32 bytes instead of 8.
244 	 * Both sides of this union are 2+32 bytes.
245 	 */
246 	union {
247 		struct {
248 			uint8_t skip[2]; /* not used - just alignment */
249 			uint8_t raw[SMB_HDRLEN];  /* header length (32) */
250 		} r;
251 		struct {
252 			uint8_t skip[2]; /* not used - just alignment */
253 			uint8_t hdr[SMB_SIG_OFFS]; /* sig. offset (14) */
254 			uint32_t sig[2]; /* MAC signature, aligned! */
255 			uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
256 		} s;
257 	} smbhdr;
258 
259 	if (s->sign_mech == NULL || sign->mackey == NULL)
260 		return (-1);
261 
262 	if ((rc = smb_md5_init(&ctx, s->sign_mech)) != 0)
263 		return (rc);
264 
265 	/* Digest the MAC Key */
266 	rc = smb_md5_update(ctx, sign->mackey, sign->mackey_len);
267 	if (rc != 0)
268 		return (rc);
269 
270 	/*
271 	 * Make an aligned copy of the SMB header,
272 	 * fill in the sequence number, and digest.
273 	 */
274 	hdrp = (unsigned char *)&smbhdr.r.raw;
275 	size = SMB_HDRLEN;
276 	if (smb_mbc_peek(mbc, offset, "#c", size, hdrp) != 0)
277 		return (-1);
278 	smbhdr.s.sig[0] = htolel(seqnum);
279 	smbhdr.s.sig[1] = 0;
280 
281 	rc = smb_md5_update(ctx, &smbhdr.r.raw, size);
282 	if (rc != 0)
283 		return (rc);
284 
285 	/*
286 	 * Digest the rest of the SMB packet, starting at the data
287 	 * just after the SMB header.
288 	 */
289 	offset += size;
290 	while (mbuf != NULL && (offset >= mbuf->m_len)) {
291 		offset -= mbuf->m_len;
292 		mbuf = mbuf->m_next;
293 	}
294 	if (mbuf != NULL && (size = (mbuf->m_len - offset)) > 0) {
295 		rc = smb_md5_update(ctx, &mbuf->m_data[offset], size);
296 		if (rc != 0)
297 			return (rc);
298 		offset = 0;
299 		mbuf = mbuf->m_next;
300 	}
301 	while (mbuf != NULL) {
302 		rc = smb_md5_update(ctx, mbuf->m_data, mbuf->m_len);
303 		if (rc != 0)
304 			return (rc);
305 		mbuf = mbuf->m_next;
306 	}
307 	rc = smb_md5_final(ctx, digest);
308 	if (rc == 0)
309 		bcopy(digest, mac_sign, SMB_SIG_SIZE);
310 
311 	return (rc);
312 }
313 
314 
315 /*
316  * smb_sign_check_request
317  *
318  * Calculates MAC signature for the request mbuf chain
319  * using the next expected sequence number and compares
320  * it to the given signature.
321  *
322  * Note it does not check the signature for secondary transactions
323  * as their sequence number is the same as the original request.
324  *
325  * Return 0 if the signature verifies, otherwise, returns -1;
326  *
327  */
328 int
329 smb_sign_check_request(smb_request_t *sr)
330 {
331 	struct mbuf_chain mbc = sr->command;
332 	unsigned char mac_sig[SMB_SIG_SIZE];
333 
334 	/*
335 	 * Don't check secondary transactions - we dont know the sequence
336 	 * number.
337 	 */
338 	if (sr->smb_com == SMB_COM_TRANSACTION_SECONDARY ||
339 	    sr->smb_com == SMB_COM_TRANSACTION2_SECONDARY ||
340 	    sr->smb_com == SMB_COM_NT_TRANSACT_SECONDARY)
341 		return (0);
342 
343 	/* Reset the offset to begining of header */
344 	mbc.chain_offset = sr->orig_request_hdr;
345 
346 	/* calculate mac signature */
347 	if (smb_sign_calc(sr, &mbc, sr->sr_seqnum, mac_sig) != 0)
348 		return (-1);
349 
350 	/* compare the signatures */
351 	if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) == 0) {
352 		/* They match! OK, we're done. */
353 		return (0);
354 	}
355 
356 	DTRACE_PROBE2(signature__mismatch, smb_request_t *, sr,
357 	    unsigned char *, mac_sig);
358 
359 	/*
360 	 * check nearby sequence numbers in debug mode
361 	 */
362 #ifdef	DEBUG
363 	if (smb_sign_debug) {
364 		return (smb_sign_find_seqnum(sr, &mbc, mac_sig, sr->smb_sig));
365 	}
366 #endif
367 	return (-1);
368 }
369 
370 /*
371  * smb_sign_check_secondary
372  *
373  * Calculates MAC signature for the secondary transaction mbuf chain
374  * and compares it to the given signature.
375  * Return 0 if the signature verifies, otherwise, returns -1;
376  *
377  */
378 int
379 smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum)
380 {
381 	struct mbuf_chain mbc = sr->command;
382 	unsigned char mac_sig[SMB_SIG_SIZE];
383 	int rtn = 0;
384 
385 	/* Reset the offset to begining of header */
386 	mbc.chain_offset = sr->orig_request_hdr;
387 
388 	/* calculate mac signature */
389 	if (smb_sign_calc(sr, &mbc, reply_seqnum - 1, mac_sig) != 0)
390 		return (-1);
391 
392 
393 	/* compare the signatures */
394 	if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
395 		cmn_err(CE_WARN, "SmbSignCheckSecond: bad signature");
396 		rtn = -1;
397 	}
398 	/* Save the reply sequence number */
399 	sr->reply_seqnum = reply_seqnum;
400 
401 	return (rtn);
402 }
403 
404 /*
405  * smb_sign_reply
406  *
407  * Calculates MAC signature for the given mbuf chain,
408  * and write it to the signature field in the mbuf.
409  *
410  */
411 void
412 smb_sign_reply(smb_request_t *sr, struct mbuf_chain *reply)
413 {
414 	struct mbuf_chain mbc;
415 	unsigned char mac[SMB_SIG_SIZE];
416 
417 	if (reply)
418 		mbc = *reply;
419 	else
420 		mbc = sr->reply;
421 
422 	/* Reset offset to start of reply */
423 	mbc.chain_offset = 0;
424 
425 	/*
426 	 * Calculate MAC signature
427 	 */
428 	if (smb_sign_calc(sr, &mbc, sr->reply_seqnum, mac) != 0) {
429 		cmn_err(CE_WARN, "smb_sign_reply: error in smb_sign_calc");
430 		return;
431 	}
432 
433 	/*
434 	 * Put signature in the response
435 	 */
436 	(void) smb_mbc_poke(&mbc, SMB_SIG_OFFS, "#c",
437 	    SMB_SIG_SIZE, mac);
438 }
439