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