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