xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_signing.c (revision 5e989a96186a37eb528fb7bb4d28a150874ec799)
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  */
24 /*
25  * These routines provide the SMB MAC signing for the SMB server.
26  * The routines calculate the signature of a SMB message in an mbuf chain.
27  *
28  * The following table describes the client server
29  * signing registry relationship
30  *
31  *		| Required	| Enabled     | Disabled
32  * -------------+---------------+------------ +--------------
33  * Required	| Signed	| Signed      | Fail
34  * -------------+---------------+-------------+-----------------
35  * Enabled	| Signed	| Signed      | Not Signed
36  * -------------+---------------+-------------+----------------
37  * Disabled	| Fail		| Not Signed  | Not Signed
38  */
39 
40 #include <sys/uio.h>
41 #include <smbsrv/smb_kproto.h>
42 #include <smbsrv/msgbuf.h>
43 #include <sys/crypto/api.h>
44 
45 #define	SMBAUTH_SESSION_KEY_SZ 16
46 #define	SMB_SIG_SIZE	8
47 #define	SMB_SIG_OFFS	14
48 
49 int
50 smb_sign_calc(struct mbuf_chain *mbc,
51     struct smb_sign *sign,
52     uint32_t seqnum,
53     unsigned char *mac_sign);
54 
55 #ifdef DEBUG
56 void smb_sign_find_seqnum(
57     uint32_t seqnum,
58     struct smb_sign *sign,
59     struct mbuf_chain *command,
60     unsigned char *mac_sig,
61     unsigned char *sr_sig,
62     boolean_t *found);
63 #define	SMB_CHECK_SEQNUM(seqnum, sign, command, mac_sig, sr_sig, found) \
64 { \
65 	if (smb_sign_debug) \
66 		smb_sign_find_seqnum(seqnum, sign, \
67 		    command, mac_sig, sr_sig, found); \
68 }
69 #else
70 #define	SMB_CHECK_SEQNUM(seqnum, sign, command, mac_sig, sr_sig, found) \
71 	{ \
72 		*found = 0; \
73 	}
74 #endif
75 
76 #ifdef DEBUG
77 void
78 smb_sign_find_seqnum(
79     uint32_t seqnum,
80     struct smb_sign *sign,
81     struct mbuf_chain *command,
82     unsigned char *mac_sig,
83     unsigned char *sr_sig,
84     boolean_t *found)
85 {
86 int start_seqnum;
87 int i;
88 
89 	/* Debug code to hunt for the sequence number */
90 	*found = B_FALSE;
91 	start_seqnum = seqnum - 10;
92 	if (start_seqnum < 0)
93 		start_seqnum = 0;
94 	for (i = start_seqnum; i <= start_seqnum + 20; i++) {
95 		(void) smb_sign_calc(command, sign, i, mac_sig);
96 		if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) {
97 			sign->seqnum = i;
98 			*found = B_TRUE;
99 			break;
100 		}
101 		cmn_err(CE_WARN, "smb_sign_find_seqnum: seqnum:%d mismatch", i);
102 	}
103 	cmn_err(CE_WARN, "smb_sign_find_seqnum: found=%d", *found);
104 }
105 #endif
106 
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->session->signing.seqnum = 0;
150 	sr->sr_seqnum = 2;
151 	sr->reply_seqnum = 1;
152 	sign->flags = SMB_SIGNING_ENABLED;
153 
154 }
155 
156 /*
157  * smb_sign_calc
158  *
159  * Calculates MAC signature for the given buffer and returns
160  * it in the mac_sign parameter.
161  *
162  * The sequence number is placed in the first four bytes of the signature
163  * field of the signature and the other 4 bytes are zeroed.
164  * The signature is the first 8 bytes of the MD5 result of the
165  * concatenated MAC key and the SMB message.
166  *
167  * MACsig = head(MD5(concat(MACKey, SMBMsg)), 8)
168  *
169  * where
170  *
171  *	MACKey = concat( UserSessionKey, NTLMResp )
172  *
173  * and
174  *
175  *	SMBMsg is the SMB message containing the sequence number.
176  *
177  * Return 0 if  success else -1
178  *
179  */
180 int
181 smb_sign_calc(struct mbuf_chain *mbc,
182     struct smb_sign *sign,
183     uint32_t seqnum,
184     unsigned char *mac_sign)
185 {
186 	uint32_t seq_buf[2] = {0, 0};
187 	unsigned char mac[16];
188 	struct mbuf *mbuf = mbc->chain;
189 	int offset = mbc->chain_offset;
190 	int size;
191 	int status;
192 
193 	crypto_data_t data;
194 	crypto_data_t digest;
195 	crypto_context_t crypto_ctx;
196 
197 	data.cd_format = CRYPTO_DATA_RAW;
198 	data.cd_offset = 0;
199 	data.cd_length = (size_t)-1;
200 	data.cd_miscdata = 0;
201 
202 	digest.cd_format = CRYPTO_DATA_RAW;
203 	digest.cd_offset = 0;
204 	digest.cd_length = (size_t)-1;
205 	digest.cd_miscdata = 0;
206 	digest.cd_raw.iov_base = (char *)mac;
207 	digest.cd_raw.iov_len = sizeof (mac);
208 
209 	status = crypto_digest_init(&crypto_mech, &crypto_ctx, 0);
210 	if (status != CRYPTO_SUCCESS)
211 		goto error;
212 
213 	/*
214 	 * Put the sequence number into the first 4 bytes
215 	 * of the signature field in little endian format.
216 	 * We are using a buffer to represent the signature
217 	 * rather than modifying the SMB message.
218 	 */
219 #ifdef __sparc
220 	{
221 		uint32_t temp;
222 		((uint8_t *)&temp)[0] = ((uint8_t *)&seqnum)[3];
223 		((uint8_t *)&temp)[1] = ((uint8_t *)&seqnum)[2];
224 		((uint8_t *)&temp)[2] = ((uint8_t *)&seqnum)[1];
225 		((uint8_t *)&temp)[3] = ((uint8_t *)&seqnum)[0];
226 
227 		seq_buf[0] = temp;
228 	}
229 #else
230 	seq_buf[0] = seqnum;
231 #endif
232 
233 	/* Digest the MACKey */
234 	data.cd_raw.iov_base = (char *)sign->mackey;
235 	data.cd_raw.iov_len = sign->mackey_len;
236 	data.cd_length = sign->mackey_len;
237 	status = crypto_digest_update(crypto_ctx, &data, 0);
238 	if (status != CRYPTO_SUCCESS)
239 		goto error;
240 
241 	/* Find start of data in chain */
242 	while (offset >= mbuf->m_len) {
243 		offset -= mbuf->m_len;
244 		mbuf = mbuf->m_next;
245 	}
246 
247 	/* Digest the SMB packet up to the signature field */
248 	size = SMB_SIG_OFFS;
249 	while (size >= mbuf->m_len - offset) {
250 		data.cd_raw.iov_base = &mbuf->m_data[offset];
251 		data.cd_raw.iov_len = mbuf->m_len - offset;
252 		data.cd_length = mbuf->m_len - offset;
253 		status = crypto_digest_update(crypto_ctx, &data, 0);
254 		if (status != CRYPTO_SUCCESS)
255 			goto error;
256 
257 		size -= mbuf->m_len - offset;
258 		mbuf = mbuf->m_next;
259 		offset = 0;
260 	}
261 	if (size > 0) {
262 		data.cd_raw.iov_base = &mbuf->m_data[offset];
263 		data.cd_raw.iov_len = size;
264 		data.cd_length = size;
265 		status = crypto_digest_update(crypto_ctx, &data, 0);
266 		if (status != CRYPTO_SUCCESS)
267 			goto error;
268 		offset += size;
269 	}
270 
271 	/*
272 	 * Digest in the seq_buf instead of the signature
273 	 * which has the sequence number
274 	 */
275 
276 	data.cd_raw.iov_base = (char *)seq_buf;
277 	data.cd_raw.iov_len = SMB_SIG_SIZE;
278 	data.cd_length = SMB_SIG_SIZE;
279 	status = crypto_digest_update(crypto_ctx, &data, 0);
280 	if (status != CRYPTO_SUCCESS)
281 		goto error;
282 
283 	/* Find the end of the signature field  */
284 	offset += SMB_SIG_SIZE;
285 	while (offset >= mbuf->m_len) {
286 		offset -= mbuf->m_len;
287 		mbuf = mbuf->m_next;
288 	}
289 	/* Digest the rest of the SMB packet */
290 	while (mbuf) {
291 		data.cd_raw.iov_base = &mbuf->m_data[offset];
292 		data.cd_raw.iov_len = mbuf->m_len - offset;
293 		data.cd_length = mbuf->m_len - offset;
294 		status = crypto_digest_update(crypto_ctx, &data, 0);
295 		if (status != CRYPTO_SUCCESS)
296 			goto error;
297 		mbuf = mbuf->m_next;
298 		offset = 0;
299 	}
300 	digest.cd_length = SMBAUTH_SESSION_KEY_SZ;
301 	status = crypto_digest_final(crypto_ctx, &digest, 0);
302 	if (status != CRYPTO_SUCCESS)
303 		goto error;
304 	bcopy(mac, mac_sign, SMB_SIG_SIZE);
305 	return (0);
306 error:
307 	cmn_err(CE_WARN, "SmbSignCalc: crypto error %d", status);
308 	return (-1);
309 
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 command = sr->command;
330 	unsigned char mac_sig[SMB_SIG_SIZE];
331 	struct smb_sign *sign = &sr->session->signing;
332 	int rtn = 0;
333 	boolean_t found = B_TRUE;
334 
335 	/*
336 	 * Don't check secondary transactions - we dont know the sequence
337 	 * number.
338 	 */
339 	if (sr->smb_com == SMB_COM_TRANSACTION_SECONDARY ||
340 	    sr->smb_com == SMB_COM_TRANSACTION2_SECONDARY ||
341 	    sr->smb_com == SMB_COM_NT_TRANSACT_SECONDARY)
342 		return (0);
343 
344 	/* Reset the offset to begining of header */
345 	command.chain_offset = sr->orig_request_hdr;
346 
347 	/* calculate mac signature */
348 	if (smb_sign_calc(&command, sign, sr->sr_seqnum, mac_sig) != 0)
349 		return (-1);
350 
351 	/* compare the signatures */
352 	if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
353 		DTRACE_PROBE2(smb__signing__req, smb_request_t, sr,
354 		    smb_sign_t *, sr->smb_sig);
355 		cmn_err(CE_NOTE, "smb_sign_check_request: bad signature");
356 		/*
357 		 * check nearby sequence numbers in debug mode
358 		 */
359 		SMB_CHECK_SEQNUM(sr->sr_seqnum, sign, &command,
360 		    mac_sig, sr->smb_sig, &found);
361 		if (found == B_FALSE)
362 			rtn = -1;
363 	}
364 	return (rtn);
365 }
366 
367 /*
368  * smb_sign_check_secondary
369  *
370  * Calculates MAC signature for the secondary transaction mbuf chain
371  * and compares it to the given signature.
372  * Return 0 if the signature verifies, otherwise, returns -1;
373  *
374  */
375 int
376 smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum)
377 {
378 	struct mbuf_chain command = sr->command;
379 	unsigned char mac_sig[SMB_SIG_SIZE];
380 	struct smb_sign *sign = &sr->session->signing;
381 	int rtn = 0;
382 
383 	/* Reset the offset to begining of header */
384 	command.chain_offset = sr->orig_request_hdr;
385 
386 	/* calculate mac signature */
387 	if (smb_sign_calc(&command, sign, reply_seqnum - 1,
388 	    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 resp;
414 	struct smb_sign *sign = &sr->session->signing;
415 	unsigned char signature[SMB_SIG_SIZE];
416 	struct mbuf *mbuf;
417 	int size = SMB_SIG_SIZE;
418 	unsigned char *sig_ptr = signature;
419 	int offset = 0;
420 
421 	if (reply)
422 		resp = *reply;
423 	else
424 		resp = sr->reply;
425 
426 	/* Reset offset to start of reply */
427 	resp.chain_offset = 0;
428 	mbuf = resp.chain;
429 
430 	/*
431 	 * Calculate MAC signature
432 	 */
433 	if (smb_sign_calc(&resp, sign, sr->reply_seqnum, signature) != 0) {
434 		cmn_err(CE_WARN, "smb_sign_reply: error in smb_sign_calc");
435 		return;
436 	}
437 
438 	/*
439 	 * Put signature in the response
440 	 *
441 	 * First find start of signature in chain (offset + signature offset)
442 	 */
443 	offset += SMB_SIG_OFFS;
444 	while (offset >= mbuf->m_len) {
445 		offset -= mbuf->m_len;
446 		mbuf = mbuf->m_next;
447 	}
448 
449 	while (size >= mbuf->m_len - offset) {
450 		(void) memcpy(&mbuf->m_data[offset],
451 		    sig_ptr, mbuf->m_len - offset);
452 		offset = 0;
453 		sig_ptr += mbuf->m_len - offset;
454 		size -= mbuf->m_len - offset;
455 		mbuf = mbuf->m_next;
456 	}
457 	if (size > 0) {
458 		(void) memcpy(&mbuf->m_data[offset], sig_ptr, size);
459 	}
460 }
461