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