xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_signing.c (revision 8622ec4569457733001d4982ef7f5b44427069be)
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 2011 Nexenta Systems, Inc.  All rights reserved.
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. 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/msgbuf.h>
44 #include <sys/crypto/api.h>
45 
46 #define	SMBAUTH_SESSION_KEY_SZ 16
47 #define	SMB_SIG_SIZE	8
48 #define	SMB_SIG_OFFS	14
49 
50 int
51 smb_sign_calc(struct mbuf_chain *mbc,
52     struct smb_sign *sign,
53     uint32_t seqnum,
54     unsigned char *mac_sign);
55 
56 #ifdef DEBUG
57 void smb_sign_find_seqnum(
58     uint32_t seqnum,
59     struct smb_sign *sign,
60     struct mbuf_chain *command,
61     unsigned char *mac_sig,
62     unsigned char *sr_sig,
63     boolean_t *found);
64 #define	SMB_CHECK_SEQNUM(seqnum, sign, command, mac_sig, sr_sig, found) \
65 { \
66 	if (smb_sign_debug) \
67 		smb_sign_find_seqnum(seqnum, sign, \
68 		    command, mac_sig, sr_sig, found); \
69 }
70 #else
71 #define	SMB_CHECK_SEQNUM(seqnum, sign, command, mac_sig, sr_sig, found) \
72 	{ \
73 		*found = 0; \
74 	}
75 #endif
76 
77 #ifdef DEBUG
78 void
79 smb_sign_find_seqnum(
80     uint32_t seqnum,
81     struct smb_sign *sign,
82     struct mbuf_chain *command,
83     unsigned char *mac_sig,
84     unsigned char *sr_sig,
85     boolean_t *found)
86 {
87 int start_seqnum;
88 int i;
89 
90 	/* Debug code to hunt for the sequence number */
91 	*found = B_FALSE;
92 	start_seqnum = seqnum - 10;
93 	if (start_seqnum < 0)
94 		start_seqnum = 0;
95 	for (i = start_seqnum; i <= start_seqnum + 20; i++) {
96 		(void) smb_sign_calc(command, sign, i, mac_sig);
97 		if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) {
98 			sign->seqnum = i;
99 			*found = B_TRUE;
100 			break;
101 		}
102 		cmn_err(CE_WARN, "smb_sign_find_seqnum: seqnum:%d mismatch", i);
103 	}
104 	cmn_err(CE_WARN, "smb_sign_find_seqnum: found=%d", *found);
105 }
106 #endif
107 
108 /* This holds the MD5 mechanism */
109 static	crypto_mechanism_t crypto_mech = {CRYPTO_MECHANISM_INVALID, 0, 0};
110 
111 void
112 smb_sign_g_init(void)
113 {
114 	/*
115 	 * Initialise the crypto mechanism to MD5 if it not
116 	 * already initialised.
117 	 */
118 	if (crypto_mech.cm_type == CRYPTO_MECHANISM_INVALID) {
119 		crypto_mech.cm_type = crypto_mech2id(SUN_CKM_MD5);
120 	}
121 }
122 
123 /*
124  * smb_sign_init
125  *
126  * Intializes MAC key based on the user session key and
127  * NTLM response and store it in the signing structure.
128  */
129 void
130 smb_sign_init(smb_request_t *sr, smb_session_key_t *session_key,
131 	char *resp, int resp_len)
132 {
133 	struct smb_sign *sign = &sr->session->signing;
134 
135 	if (crypto_mech.cm_type == CRYPTO_MECHANISM_INVALID) {
136 		/*
137 		 * There is no MD5 crypto mechanism
138 		 * so turn off signing
139 		 */
140 		sr->sr_cfg->skc_signing_enable = 0;
141 		sr->session->secmode &=
142 		    (~NEGOTIATE_SECURITY_SIGNATURES_ENABLED);
143 		cmn_err(CE_WARN,
144 		    "SmbSignInit: signing disabled (no MD5)");
145 		return;
146 	}
147 
148 	/* MAC key = concat (SessKey, NTLMResponse) */
149 
150 	bcopy(session_key, sign->mackey, sizeof (smb_session_key_t));
151 	bcopy(resp, &(sign->mackey[sizeof (smb_session_key_t)]),
152 	    resp_len);
153 	sign->mackey_len = sizeof (smb_session_key_t) + resp_len;
154 
155 	sr->session->signing.seqnum = 0;
156 	sr->sr_seqnum = 2;
157 	sr->reply_seqnum = 1;
158 	sign->flags = SMB_SIGNING_ENABLED;
159 }
160 
161 /*
162  * smb_sign_calc
163  *
164  * Calculates MAC signature for the given buffer and returns
165  * it in the mac_sign parameter.
166  *
167  * The sequence number is placed in the first four bytes of the signature
168  * field of the signature and the other 4 bytes are zeroed.
169  * The signature is the first 8 bytes of the MD5 result of the
170  * concatenated MAC key and the SMB message.
171  *
172  * MACsig = head(MD5(concat(MACKey, SMBMsg)), 8)
173  *
174  * where
175  *
176  *	MACKey = concat( UserSessionKey, NTLMResp )
177  *
178  * and
179  *
180  *	SMBMsg is the SMB message containing the sequence number.
181  *
182  * Return 0 if  success else -1
183  *
184  */
185 int
186 smb_sign_calc(struct mbuf_chain *mbc,
187     struct smb_sign *sign,
188     uint32_t seqnum,
189     unsigned char *mac_sign)
190 {
191 	uint32_t seq_buf[2] = {0, 0};
192 	unsigned char mac[16];
193 	struct mbuf *mbuf = mbc->chain;
194 	int offset = mbc->chain_offset;
195 	int size;
196 	int status;
197 
198 	crypto_data_t data;
199 	crypto_data_t digest;
200 	crypto_context_t crypto_ctx;
201 
202 	data.cd_format = CRYPTO_DATA_RAW;
203 	data.cd_offset = 0;
204 	data.cd_length = (size_t)-1;
205 	data.cd_miscdata = 0;
206 
207 	digest.cd_format = CRYPTO_DATA_RAW;
208 	digest.cd_offset = 0;
209 	digest.cd_length = (size_t)-1;
210 	digest.cd_miscdata = 0;
211 	digest.cd_raw.iov_base = (char *)mac;
212 	digest.cd_raw.iov_len = sizeof (mac);
213 
214 	status = crypto_digest_init(&crypto_mech, &crypto_ctx, 0);
215 	if (status != CRYPTO_SUCCESS)
216 		goto error;
217 
218 	/*
219 	 * Put the sequence number into the first 4 bytes
220 	 * of the signature field in little endian format.
221 	 * We are using a buffer to represent the signature
222 	 * rather than modifying the SMB message.
223 	 */
224 #ifdef __sparc
225 	{
226 		uint32_t temp;
227 		((uint8_t *)&temp)[0] = ((uint8_t *)&seqnum)[3];
228 		((uint8_t *)&temp)[1] = ((uint8_t *)&seqnum)[2];
229 		((uint8_t *)&temp)[2] = ((uint8_t *)&seqnum)[1];
230 		((uint8_t *)&temp)[3] = ((uint8_t *)&seqnum)[0];
231 
232 		seq_buf[0] = temp;
233 	}
234 #else
235 	seq_buf[0] = seqnum;
236 #endif
237 
238 	/* Digest the MACKey */
239 	data.cd_raw.iov_base = (char *)sign->mackey;
240 	data.cd_raw.iov_len = sign->mackey_len;
241 	data.cd_length = sign->mackey_len;
242 	status = crypto_digest_update(crypto_ctx, &data, 0);
243 	if (status != CRYPTO_SUCCESS)
244 		goto error;
245 
246 	/* Find start of data in chain */
247 	while (offset >= mbuf->m_len) {
248 		offset -= mbuf->m_len;
249 		mbuf = mbuf->m_next;
250 	}
251 
252 	/* Digest the SMB packet up to the signature field */
253 	size = SMB_SIG_OFFS;
254 	while (size >= mbuf->m_len - offset) {
255 		data.cd_raw.iov_base = &mbuf->m_data[offset];
256 		data.cd_raw.iov_len = mbuf->m_len - offset;
257 		data.cd_length = mbuf->m_len - offset;
258 		status = crypto_digest_update(crypto_ctx, &data, 0);
259 		if (status != CRYPTO_SUCCESS)
260 			goto error;
261 
262 		size -= mbuf->m_len - offset;
263 		mbuf = mbuf->m_next;
264 		offset = 0;
265 	}
266 	if (size > 0) {
267 		data.cd_raw.iov_base = &mbuf->m_data[offset];
268 		data.cd_raw.iov_len = size;
269 		data.cd_length = size;
270 		status = crypto_digest_update(crypto_ctx, &data, 0);
271 		if (status != CRYPTO_SUCCESS)
272 			goto error;
273 		offset += size;
274 	}
275 
276 	/*
277 	 * Digest in the seq_buf instead of the signature
278 	 * which has the sequence number
279 	 */
280 
281 	data.cd_raw.iov_base = (char *)seq_buf;
282 	data.cd_raw.iov_len = SMB_SIG_SIZE;
283 	data.cd_length = SMB_SIG_SIZE;
284 	status = crypto_digest_update(crypto_ctx, &data, 0);
285 	if (status != CRYPTO_SUCCESS)
286 		goto error;
287 
288 	/* Find the end of the signature field  */
289 	offset += SMB_SIG_SIZE;
290 	while (offset >= mbuf->m_len) {
291 		offset -= mbuf->m_len;
292 		mbuf = mbuf->m_next;
293 	}
294 	/* Digest the rest of the SMB packet */
295 	while (mbuf) {
296 		data.cd_raw.iov_base = &mbuf->m_data[offset];
297 		data.cd_raw.iov_len = mbuf->m_len - offset;
298 		data.cd_length = mbuf->m_len - offset;
299 		status = crypto_digest_update(crypto_ctx, &data, 0);
300 		if (status != CRYPTO_SUCCESS)
301 			goto error;
302 		mbuf = mbuf->m_next;
303 		offset = 0;
304 	}
305 	digest.cd_length = SMBAUTH_SESSION_KEY_SZ;
306 	status = crypto_digest_final(crypto_ctx, &digest, 0);
307 	if (status != CRYPTO_SUCCESS)
308 		goto error;
309 	bcopy(mac, mac_sign, SMB_SIG_SIZE);
310 	return (0);
311 error:
312 	cmn_err(CE_WARN, "SmbSignCalc: crypto error %d", status);
313 	return (-1);
314 
315 }
316 
317 
318 /*
319  * smb_sign_check_request
320  *
321  * Calculates MAC signature for the request mbuf chain
322  * using the next expected sequence number and compares
323  * it to the given signature.
324  *
325  * Note it does not check the signature for secondary transactions
326  * as their sequence number is the same as the original request.
327  *
328  * Return 0 if the signature verifies, otherwise, returns -1;
329  *
330  */
331 int
332 smb_sign_check_request(smb_request_t *sr)
333 {
334 	struct mbuf_chain command = sr->command;
335 	unsigned char mac_sig[SMB_SIG_SIZE];
336 	struct smb_sign *sign = &sr->session->signing;
337 	int rtn = 0;
338 	boolean_t found = B_TRUE;
339 
340 	/*
341 	 * Don't check secondary transactions - we dont know the sequence
342 	 * number.
343 	 */
344 	if (sr->smb_com == SMB_COM_TRANSACTION_SECONDARY ||
345 	    sr->smb_com == SMB_COM_TRANSACTION2_SECONDARY ||
346 	    sr->smb_com == SMB_COM_NT_TRANSACT_SECONDARY)
347 		return (0);
348 
349 	/* Reset the offset to begining of header */
350 	command.chain_offset = sr->orig_request_hdr;
351 
352 	/* calculate mac signature */
353 	if (smb_sign_calc(&command, sign, sr->sr_seqnum, mac_sig) != 0)
354 		return (-1);
355 
356 	/* compare the signatures */
357 	if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
358 		DTRACE_PROBE2(smb__signing__req, smb_request_t, sr,
359 		    smb_sign_t *, sr->smb_sig);
360 		cmn_err(CE_NOTE, "smb_sign_check_request: bad signature");
361 		/*
362 		 * check nearby sequence numbers in debug mode
363 		 */
364 		SMB_CHECK_SEQNUM(sr->sr_seqnum, sign, &command,
365 		    mac_sig, sr->smb_sig, &found);
366 		if (found == B_FALSE)
367 			rtn = -1;
368 	}
369 	return (rtn);
370 }
371 
372 /*
373  * smb_sign_check_secondary
374  *
375  * Calculates MAC signature for the secondary transaction mbuf chain
376  * and compares it to the given signature.
377  * Return 0 if the signature verifies, otherwise, returns -1;
378  *
379  */
380 int
381 smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum)
382 {
383 	struct mbuf_chain command = sr->command;
384 	unsigned char mac_sig[SMB_SIG_SIZE];
385 	struct smb_sign *sign = &sr->session->signing;
386 	int rtn = 0;
387 
388 	/* Reset the offset to begining of header */
389 	command.chain_offset = sr->orig_request_hdr;
390 
391 	/* calculate mac signature */
392 	if (smb_sign_calc(&command, sign, reply_seqnum - 1,
393 	    mac_sig) != 0)
394 		return (-1);
395 
396 
397 	/* compare the signatures */
398 	if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
399 		cmn_err(CE_WARN, "SmbSignCheckSecond: bad signature");
400 		rtn = -1;
401 	}
402 	/* Save the reply sequence number */
403 	sr->reply_seqnum = reply_seqnum;
404 
405 	return (rtn);
406 }
407 
408 /*
409  * smb_sign_reply
410  *
411  * Calculates MAC signature for the given mbuf chain,
412  * and write it to the signature field in the mbuf.
413  *
414  */
415 void
416 smb_sign_reply(smb_request_t *sr, struct mbuf_chain *reply)
417 {
418 	struct mbuf_chain resp;
419 	struct smb_sign *sign = &sr->session->signing;
420 	unsigned char signature[SMB_SIG_SIZE];
421 	struct mbuf *mbuf;
422 	int size = SMB_SIG_SIZE;
423 	unsigned char *sig_ptr = signature;
424 	int offset = 0;
425 
426 	if (reply)
427 		resp = *reply;
428 	else
429 		resp = sr->reply;
430 
431 	/* Reset offset to start of reply */
432 	resp.chain_offset = 0;
433 	mbuf = resp.chain;
434 
435 	/*
436 	 * Calculate MAC signature
437 	 */
438 	if (smb_sign_calc(&resp, sign, sr->reply_seqnum, signature) != 0) {
439 		cmn_err(CE_WARN, "smb_sign_reply: error in smb_sign_calc");
440 		return;
441 	}
442 
443 	/*
444 	 * Put signature in the response
445 	 *
446 	 * First find start of signature in chain (offset + signature offset)
447 	 */
448 	offset += SMB_SIG_OFFS;
449 	while (offset >= mbuf->m_len) {
450 		offset -= mbuf->m_len;
451 		mbuf = mbuf->m_next;
452 	}
453 
454 	while (size >= mbuf->m_len - offset) {
455 		(void) memcpy(&mbuf->m_data[offset],
456 		    sig_ptr, mbuf->m_len - offset);
457 		offset = 0;
458 		sig_ptr += mbuf->m_len - offset;
459 		size -= mbuf->m_len - offset;
460 		mbuf = mbuf->m_next;
461 	}
462 	if (size > 0) {
463 		(void) memcpy(&mbuf->m_data[offset], sig_ptr, size);
464 	}
465 }
466