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
smb2_sign_fini(smb_session_t * s)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
smb2_sign_begin(smb_request_t * sr,smb_token_t * token)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
smb2_sign_calc(smb_request_t * sr,struct mbuf_chain * mbc,uint8_t * digest)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
smb2_sign_check_request(smb_request_t * sr)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
smb2_sign_reply(smb_request_t * sr)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