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