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