xref: /titanic_51/usr/src/uts/common/fs/smbsrv/smb2_signing.c (revision a90cf9f29973990687fa61de9f1f6ea22e924e40)
1*a90cf9f2SGordon Ross /*
2*a90cf9f2SGordon Ross  * CDDL HEADER START
3*a90cf9f2SGordon Ross  *
4*a90cf9f2SGordon Ross  * The contents of this file are subject to the terms of the
5*a90cf9f2SGordon Ross  * Common Development and Distribution License (the "License").
6*a90cf9f2SGordon Ross  * You may not use this file except in compliance with the License.
7*a90cf9f2SGordon Ross  *
8*a90cf9f2SGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*a90cf9f2SGordon Ross  * or http://www.opensolaris.org/os/licensing.
10*a90cf9f2SGordon Ross  * See the License for the specific language governing permissions
11*a90cf9f2SGordon Ross  * and limitations under the License.
12*a90cf9f2SGordon Ross  *
13*a90cf9f2SGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14*a90cf9f2SGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*a90cf9f2SGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16*a90cf9f2SGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17*a90cf9f2SGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18*a90cf9f2SGordon Ross  *
19*a90cf9f2SGordon Ross  * CDDL HEADER END
20*a90cf9f2SGordon Ross  */
21*a90cf9f2SGordon Ross /*
22*a90cf9f2SGordon Ross  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23*a90cf9f2SGordon Ross  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24*a90cf9f2SGordon Ross  */
25*a90cf9f2SGordon Ross /*
26*a90cf9f2SGordon Ross  * These routines provide the SMB MAC signing for the SMB2 server.
27*a90cf9f2SGordon Ross  * The routines calculate the signature of a SMB message in an mbuf chain.
28*a90cf9f2SGordon Ross  *
29*a90cf9f2SGordon Ross  * The following table describes the client server
30*a90cf9f2SGordon Ross  * signing registry relationship
31*a90cf9f2SGordon Ross  *
32*a90cf9f2SGordon Ross  *		| Required	| Enabled     | Disabled
33*a90cf9f2SGordon Ross  * -------------+---------------+------------ +--------------
34*a90cf9f2SGordon Ross  * Required	| Signed	| Signed      | Fail
35*a90cf9f2SGordon Ross  * -------------+---------------+-------------+-----------------
36*a90cf9f2SGordon Ross  * Enabled	| Signed	| Signed      | Not Signed
37*a90cf9f2SGordon Ross  * -------------+---------------+-------------+----------------
38*a90cf9f2SGordon Ross  * Disabled	| Fail		| Not Signed  | Not Signed
39*a90cf9f2SGordon Ross  */
40*a90cf9f2SGordon Ross 
41*a90cf9f2SGordon Ross #include <sys/uio.h>
42*a90cf9f2SGordon Ross #include <smbsrv/smb_kproto.h>
43*a90cf9f2SGordon Ross #include <smbsrv/smb_signing.h>
44*a90cf9f2SGordon Ross #include <sys/isa_defs.h>
45*a90cf9f2SGordon Ross #include <sys/byteorder.h>
46*a90cf9f2SGordon Ross #include <sys/cmn_err.h>
47*a90cf9f2SGordon Ross 
48*a90cf9f2SGordon Ross #define	SMB2_SIG_OFFS	48
49*a90cf9f2SGordon Ross #define	SMB2_SIG_SIZE	16
50*a90cf9f2SGordon Ross 
51*a90cf9f2SGordon Ross /*
52*a90cf9f2SGordon Ross  * Called during session destroy.
53*a90cf9f2SGordon Ross  */
54*a90cf9f2SGordon Ross static void
55*a90cf9f2SGordon Ross smb2_sign_fini(smb_session_t *s)
56*a90cf9f2SGordon Ross {
57*a90cf9f2SGordon Ross 	smb_sign_mech_t *mech;
58*a90cf9f2SGordon Ross 
59*a90cf9f2SGordon Ross 	if ((mech = s->sign_mech) != NULL) {
60*a90cf9f2SGordon Ross 		kmem_free(mech, sizeof (*mech));
61*a90cf9f2SGordon Ross 		s->sign_mech = NULL;
62*a90cf9f2SGordon Ross 	}
63*a90cf9f2SGordon Ross }
64*a90cf9f2SGordon Ross 
65*a90cf9f2SGordon Ross /*
66*a90cf9f2SGordon Ross  * smb2_sign_begin
67*a90cf9f2SGordon Ross  *
68*a90cf9f2SGordon Ross  * Get the mechanism info.
69*a90cf9f2SGordon Ross  * Intializes MAC key based on the user session key and store it in
70*a90cf9f2SGordon Ross  * the signing structure.  This begins signing on this session.
71*a90cf9f2SGordon Ross  */
72*a90cf9f2SGordon Ross int
73*a90cf9f2SGordon Ross smb2_sign_begin(smb_request_t *sr, smb_token_t *token)
74*a90cf9f2SGordon Ross {
75*a90cf9f2SGordon Ross 	smb_session_t *s = sr->session;
76*a90cf9f2SGordon Ross 	smb_user_t *u = sr->uid_user;
77*a90cf9f2SGordon Ross 	struct smb_key *sign_key = &u->u_sign_key;
78*a90cf9f2SGordon Ross 	smb_sign_mech_t *mech;
79*a90cf9f2SGordon Ross 	int rc;
80*a90cf9f2SGordon Ross 
81*a90cf9f2SGordon Ross 	/*
82*a90cf9f2SGordon Ross 	 * We should normally have a session key here because
83*a90cf9f2SGordon Ross 	 * our caller filters out Anonymous and Guest logons.
84*a90cf9f2SGordon Ross 	 * However, buggy clients could get us here without a
85*a90cf9f2SGordon Ross 	 * session key, in which case we'll fail later when a
86*a90cf9f2SGordon Ross 	 * request that requires signing can't be checked.
87*a90cf9f2SGordon Ross 	 */
88*a90cf9f2SGordon Ross 	if (token->tkn_ssnkey.val == NULL || token->tkn_ssnkey.len == 0)
89*a90cf9f2SGordon Ross 		return (0);
90*a90cf9f2SGordon Ross 
91*a90cf9f2SGordon Ross 	/*
92*a90cf9f2SGordon Ross 	 * Session-level initialization (once per session)
93*a90cf9f2SGordon Ross 	 * Get mech handle, sign_fini function.
94*a90cf9f2SGordon Ross 	 */
95*a90cf9f2SGordon Ross 	smb_rwx_rwenter(&s->s_lock, RW_WRITER);
96*a90cf9f2SGordon Ross 	if (s->sign_mech == NULL) {
97*a90cf9f2SGordon Ross 		mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
98*a90cf9f2SGordon Ross 		rc = smb2_hmac_getmech(mech);
99*a90cf9f2SGordon Ross 		if (rc != 0) {
100*a90cf9f2SGordon Ross 			kmem_free(mech, sizeof (*mech));
101*a90cf9f2SGordon Ross 			smb_rwx_rwexit(&s->s_lock);
102*a90cf9f2SGordon Ross 			return (rc);
103*a90cf9f2SGordon Ross 		}
104*a90cf9f2SGordon Ross 		s->sign_mech = mech;
105*a90cf9f2SGordon Ross 		s->sign_fini = smb2_sign_fini;
106*a90cf9f2SGordon Ross 	}
107*a90cf9f2SGordon Ross 	smb_rwx_rwexit(&s->s_lock);
108*a90cf9f2SGordon Ross 
109*a90cf9f2SGordon Ross 	/*
110*a90cf9f2SGordon Ross 	 * Compute and store the signing key, which lives in
111*a90cf9f2SGordon Ross 	 * the user structure.
112*a90cf9f2SGordon Ross 	 */
113*a90cf9f2SGordon Ross 	sign_key->len = SMB2_SIG_SIZE;
114*a90cf9f2SGordon Ross 
115*a90cf9f2SGordon Ross 	/*
116*a90cf9f2SGordon Ross 	 * For SMB2, the signing key is just the first 16 bytes
117*a90cf9f2SGordon Ross 	 * of the session key (truncated or padded with zeros).
118*a90cf9f2SGordon Ross 	 * [MS-SMB2] 3.2.5.3.1
119*a90cf9f2SGordon Ross 	 */
120*a90cf9f2SGordon Ross 	bcopy(token->tkn_ssnkey.val, sign_key->key,
121*a90cf9f2SGordon Ross 	    MIN(token->tkn_ssnkey.len, sign_key->len));
122*a90cf9f2SGordon Ross 
123*a90cf9f2SGordon Ross 	mutex_enter(&u->u_mutex);
124*a90cf9f2SGordon Ross 	if (s->secmode & SMB2_NEGOTIATE_SIGNING_ENABLED)
125*a90cf9f2SGordon Ross 		u->u_sign_flags |= SMB_SIGNING_ENABLED;
126*a90cf9f2SGordon Ross 	if (s->secmode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
127*a90cf9f2SGordon Ross 		u->u_sign_flags |=
128*a90cf9f2SGordon Ross 		    SMB_SIGNING_ENABLED | SMB_SIGNING_CHECK;
129*a90cf9f2SGordon Ross 	mutex_exit(&u->u_mutex);
130*a90cf9f2SGordon Ross 
131*a90cf9f2SGordon Ross 	/*
132*a90cf9f2SGordon Ross 	 * If we just turned on signing, the current request
133*a90cf9f2SGordon Ross 	 * (an SMB2 session setup) will have come in without
134*a90cf9f2SGordon Ross 	 * SMB2_FLAGS_SIGNED (and not signed) but the response
135*a90cf9f2SGordon Ross 	 * is is supposed to be signed. [MS-SMB2] 3.3.5.5
136*a90cf9f2SGordon Ross 	 */
137*a90cf9f2SGordon Ross 	if (u->u_sign_flags & SMB_SIGNING_ENABLED)
138*a90cf9f2SGordon Ross 		sr->smb2_hdr_flags |= SMB2_FLAGS_SIGNED;
139*a90cf9f2SGordon Ross 
140*a90cf9f2SGordon Ross 	return (0);
141*a90cf9f2SGordon Ross }
142*a90cf9f2SGordon Ross 
143*a90cf9f2SGordon Ross /*
144*a90cf9f2SGordon Ross  * smb2_sign_calc
145*a90cf9f2SGordon Ross  *
146*a90cf9f2SGordon Ross  * Calculates MAC signature for the given buffer and returns
147*a90cf9f2SGordon Ross  * it in the mac_sign parameter.
148*a90cf9f2SGordon Ross  *
149*a90cf9f2SGordon Ross  * The signature is in the last 16 bytes of the SMB2 header.
150*a90cf9f2SGordon Ross  * The signature algorighm is to compute HMAC SHA256 over the
151*a90cf9f2SGordon Ross  * entire command, with the signature field set to zeros.
152*a90cf9f2SGordon Ross  *
153*a90cf9f2SGordon Ross  * Return 0 if  success else -1
154*a90cf9f2SGordon Ross  */
155*a90cf9f2SGordon Ross static int
156*a90cf9f2SGordon Ross smb2_sign_calc(smb_request_t *sr, struct mbuf_chain *mbc,
157*a90cf9f2SGordon Ross     uint8_t *digest)
158*a90cf9f2SGordon Ross {
159*a90cf9f2SGordon Ross 	uint8_t tmp_hdr[SMB2_HDR_SIZE];
160*a90cf9f2SGordon Ross 	smb_sign_ctx_t ctx = 0;
161*a90cf9f2SGordon Ross 	smb_session_t *s = sr->session;
162*a90cf9f2SGordon Ross 	smb_user_t *u = sr->uid_user;
163*a90cf9f2SGordon Ross 	struct smb_key *sign_key = &u->u_sign_key;
164*a90cf9f2SGordon Ross 	struct mbuf *mbuf;
165*a90cf9f2SGordon Ross 	int offset, resid, tlen, rc;
166*a90cf9f2SGordon Ross 
167*a90cf9f2SGordon Ross 	if (s->sign_mech == NULL || sign_key->len == 0)
168*a90cf9f2SGordon Ross 		return (-1);
169*a90cf9f2SGordon Ross 
170*a90cf9f2SGordon Ross 	rc = smb2_hmac_init(&ctx, s->sign_mech, sign_key->key, sign_key->len);
171*a90cf9f2SGordon Ross 	if (rc != 0)
172*a90cf9f2SGordon Ross 		return (rc);
173*a90cf9f2SGordon Ross 
174*a90cf9f2SGordon Ross 	/*
175*a90cf9f2SGordon Ross 	 * Work with a copy of the SMB2 header so we can
176*a90cf9f2SGordon Ross 	 * clear the signature field without modifying
177*a90cf9f2SGordon Ross 	 * the original message.
178*a90cf9f2SGordon Ross 	 */
179*a90cf9f2SGordon Ross 	tlen = SMB2_HDR_SIZE;
180*a90cf9f2SGordon Ross 	offset = mbc->chain_offset;
181*a90cf9f2SGordon Ross 	resid = mbc->max_bytes - offset;
182*a90cf9f2SGordon Ross 	if (smb_mbc_peek(mbc, offset, "#c", tlen, tmp_hdr) != 0)
183*a90cf9f2SGordon Ross 		return (-1);
184*a90cf9f2SGordon Ross 	bzero(tmp_hdr + SMB2_SIG_OFFS, SMB2_SIG_SIZE);
185*a90cf9f2SGordon Ross 	if ((rc = smb2_hmac_update(ctx, tmp_hdr, tlen)) != 0)
186*a90cf9f2SGordon Ross 		return (rc);
187*a90cf9f2SGordon Ross 	offset += tlen;
188*a90cf9f2SGordon Ross 	resid -= tlen;
189*a90cf9f2SGordon Ross 
190*a90cf9f2SGordon Ross 	/*
191*a90cf9f2SGordon Ross 	 * Digest the rest of the SMB packet, starting at the data
192*a90cf9f2SGordon Ross 	 * just after the SMB header.
193*a90cf9f2SGordon Ross 	 *
194*a90cf9f2SGordon Ross 	 * Advance to the src mbuf where we start digesting.
195*a90cf9f2SGordon Ross 	 */
196*a90cf9f2SGordon Ross 	mbuf = mbc->chain;
197*a90cf9f2SGordon Ross 	while (mbuf != NULL && (offset >= mbuf->m_len)) {
198*a90cf9f2SGordon Ross 		offset -= mbuf->m_len;
199*a90cf9f2SGordon Ross 		mbuf = mbuf->m_next;
200*a90cf9f2SGordon Ross 	}
201*a90cf9f2SGordon Ross 
202*a90cf9f2SGordon Ross 	if (mbuf == NULL)
203*a90cf9f2SGordon Ross 		return (-1);
204*a90cf9f2SGordon Ross 
205*a90cf9f2SGordon Ross 	/*
206*a90cf9f2SGordon Ross 	 * Digest the remainder of this mbuf, limited to the
207*a90cf9f2SGordon Ross 	 * residual count, and starting at the current offset.
208*a90cf9f2SGordon Ross 	 * (typically SMB2_HDR_SIZE)
209*a90cf9f2SGordon Ross 	 */
210*a90cf9f2SGordon Ross 	tlen = mbuf->m_len - offset;
211*a90cf9f2SGordon Ross 	if (tlen > resid)
212*a90cf9f2SGordon Ross 		tlen = resid;
213*a90cf9f2SGordon Ross 	rc = smb2_hmac_update(ctx, (uint8_t *)mbuf->m_data + offset, tlen);
214*a90cf9f2SGordon Ross 	if (rc != 0)
215*a90cf9f2SGordon Ross 		return (rc);
216*a90cf9f2SGordon Ross 	resid -= tlen;
217*a90cf9f2SGordon Ross 
218*a90cf9f2SGordon Ross 	/*
219*a90cf9f2SGordon Ross 	 * Digest any more mbufs in the chain.
220*a90cf9f2SGordon Ross 	 */
221*a90cf9f2SGordon Ross 	while (resid > 0) {
222*a90cf9f2SGordon Ross 		mbuf = mbuf->m_next;
223*a90cf9f2SGordon Ross 		if (mbuf == NULL)
224*a90cf9f2SGordon Ross 			return (-1);
225*a90cf9f2SGordon Ross 		tlen = mbuf->m_len;
226*a90cf9f2SGordon Ross 		if (tlen > resid)
227*a90cf9f2SGordon Ross 			tlen = resid;
228*a90cf9f2SGordon Ross 		rc = smb2_hmac_update(ctx, (uint8_t *)mbuf->m_data, tlen);
229*a90cf9f2SGordon Ross 		if (rc != 0)
230*a90cf9f2SGordon Ross 			return (rc);
231*a90cf9f2SGordon Ross 		resid -= tlen;
232*a90cf9f2SGordon Ross 	}
233*a90cf9f2SGordon Ross 
234*a90cf9f2SGordon Ross 	/*
235*a90cf9f2SGordon Ross 	 * Note: digest is _always_ SMB2_SIG_SIZE,
236*a90cf9f2SGordon Ross 	 * even if the mech uses a longer one.
237*a90cf9f2SGordon Ross 	 */
238*a90cf9f2SGordon Ross 	if ((rc = smb2_hmac_final(ctx, digest)) != 0)
239*a90cf9f2SGordon Ross 		return (rc);
240*a90cf9f2SGordon Ross 
241*a90cf9f2SGordon Ross 	return (0);
242*a90cf9f2SGordon Ross }
243*a90cf9f2SGordon Ross 
244*a90cf9f2SGordon Ross /*
245*a90cf9f2SGordon Ross  * smb2_sign_check_request
246*a90cf9f2SGordon Ross  *
247*a90cf9f2SGordon Ross  * Calculates MAC signature for the request mbuf chain
248*a90cf9f2SGordon Ross  * using the next expected sequence number and compares
249*a90cf9f2SGordon Ross  * it to the given signature.
250*a90cf9f2SGordon Ross  *
251*a90cf9f2SGordon Ross  * Note it does not check the signature for secondary transactions
252*a90cf9f2SGordon Ross  * as their sequence number is the same as the original request.
253*a90cf9f2SGordon Ross  *
254*a90cf9f2SGordon Ross  * Return 0 if the signature verifies, otherwise, returns -1;
255*a90cf9f2SGordon Ross  *
256*a90cf9f2SGordon Ross  */
257*a90cf9f2SGordon Ross int
258*a90cf9f2SGordon Ross smb2_sign_check_request(smb_request_t *sr)
259*a90cf9f2SGordon Ross {
260*a90cf9f2SGordon Ross 	uint8_t req_sig[SMB2_SIG_SIZE];
261*a90cf9f2SGordon Ross 	uint8_t vfy_sig[SMB2_SIG_SIZE];
262*a90cf9f2SGordon Ross 	struct mbuf_chain *mbc = &sr->smb_data;
263*a90cf9f2SGordon Ross 	smb_user_t *u = sr->uid_user;
264*a90cf9f2SGordon Ross 	int sig_off;
265*a90cf9f2SGordon Ross 
266*a90cf9f2SGordon Ross 	/*
267*a90cf9f2SGordon Ross 	 * Don't check commands with a zero session ID.
268*a90cf9f2SGordon Ross 	 * [MS-SMB2] 3.3.4.1.1
269*a90cf9f2SGordon Ross 	 */
270*a90cf9f2SGordon Ross 	if (sr->smb_uid == 0 || u == NULL)
271*a90cf9f2SGordon Ross 		return (0);
272*a90cf9f2SGordon Ross 
273*a90cf9f2SGordon Ross 	/* Get the request signature. */
274*a90cf9f2SGordon Ross 	sig_off = sr->smb2_cmd_hdr + SMB2_SIG_OFFS;
275*a90cf9f2SGordon Ross 	if (smb_mbc_peek(mbc, sig_off, "#c", SMB2_SIG_SIZE, req_sig) != 0)
276*a90cf9f2SGordon Ross 		return (-1);
277*a90cf9f2SGordon Ross 
278*a90cf9f2SGordon Ross 	/*
279*a90cf9f2SGordon Ross 	 * Compute the correct signature and compare.
280*a90cf9f2SGordon Ross 	 */
281*a90cf9f2SGordon Ross 	if (smb2_sign_calc(sr, mbc, vfy_sig) != 0)
282*a90cf9f2SGordon Ross 		return (-1);
283*a90cf9f2SGordon Ross 	if (memcmp(vfy_sig, req_sig, SMB2_SIG_SIZE) != 0) {
284*a90cf9f2SGordon Ross 		cmn_err(CE_NOTE, "smb2_sign_check_request: bad signature");
285*a90cf9f2SGordon Ross 		return (-1);
286*a90cf9f2SGordon Ross 	}
287*a90cf9f2SGordon Ross 
288*a90cf9f2SGordon Ross 	return (0);
289*a90cf9f2SGordon Ross }
290*a90cf9f2SGordon Ross 
291*a90cf9f2SGordon Ross /*
292*a90cf9f2SGordon Ross  * smb2_sign_reply
293*a90cf9f2SGordon Ross  *
294*a90cf9f2SGordon Ross  * Calculates MAC signature for the given mbuf chain,
295*a90cf9f2SGordon Ross  * and write it to the signature field in the mbuf.
296*a90cf9f2SGordon Ross  *
297*a90cf9f2SGordon Ross  */
298*a90cf9f2SGordon Ross void
299*a90cf9f2SGordon Ross smb2_sign_reply(smb_request_t *sr)
300*a90cf9f2SGordon Ross {
301*a90cf9f2SGordon Ross 	uint8_t reply_sig[SMB2_SIG_SIZE];
302*a90cf9f2SGordon Ross 	struct mbuf_chain tmp_mbc;
303*a90cf9f2SGordon Ross 	smb_user_t *u = sr->uid_user;
304*a90cf9f2SGordon Ross 	int hdr_off, msg_len;
305*a90cf9f2SGordon Ross 
306*a90cf9f2SGordon Ross 	if (u == NULL)
307*a90cf9f2SGordon Ross 		return;
308*a90cf9f2SGordon Ross 
309*a90cf9f2SGordon Ross 	msg_len = sr->reply.chain_offset - sr->smb2_reply_hdr;
310*a90cf9f2SGordon Ross 	(void) MBC_SHADOW_CHAIN(&tmp_mbc, &sr->reply,
311*a90cf9f2SGordon Ross 	    sr->smb2_reply_hdr, msg_len);
312*a90cf9f2SGordon Ross 
313*a90cf9f2SGordon Ross 	/*
314*a90cf9f2SGordon Ross 	 * Calculate the MAC signature for this reply.
315*a90cf9f2SGordon Ross 	 */
316*a90cf9f2SGordon Ross 	if (smb2_sign_calc(sr, &tmp_mbc, reply_sig) != 0)
317*a90cf9f2SGordon Ross 		return;
318*a90cf9f2SGordon Ross 
319*a90cf9f2SGordon Ross 	/*
320*a90cf9f2SGordon Ross 	 * Poke the signature into the response.
321*a90cf9f2SGordon Ross 	 */
322*a90cf9f2SGordon Ross 	hdr_off = sr->smb2_reply_hdr + SMB2_SIG_OFFS;
323*a90cf9f2SGordon Ross 	(void) smb_mbc_poke(&sr->reply, hdr_off, "#c",
324*a90cf9f2SGordon Ross 	    SMB2_SIG_SIZE, reply_sig);
325*a90cf9f2SGordon Ross }
326