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