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