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