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 SMB 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
47 #define SSN_KEY_LEN 16
48 #define SMB_SIG_SIZE 8
49 #define SMB_SIG_OFFS 14
50 #define SMB_HDRLEN 32
51
52 #ifdef _LITTLE_ENDIAN
53 #define htolel(x) ((uint32_t)(x))
54 #else
55 #define htolel(x) BSWAP_32(x)
56 #endif
57
58 int
59 smb_sign_calc(struct mbuf_chain *mbc,
60 struct smb_sign *sign,
61 uint32_t seqnum,
62 unsigned char *mac_sign);
63
64 #ifdef DEBUG
65 static void
smb_sign_find_seqnum(uint32_t seqnum,struct smb_sign * sign,struct mbuf_chain * command,unsigned char * mac_sig,unsigned char * sr_sig,boolean_t * found)66 smb_sign_find_seqnum(
67 uint32_t seqnum,
68 struct smb_sign *sign,
69 struct mbuf_chain *command,
70 unsigned char *mac_sig,
71 unsigned char *sr_sig,
72 boolean_t *found)
73 {
74 int start_seqnum;
75 int i;
76
77 /* Debug code to hunt for the sequence number */
78 *found = B_FALSE;
79 start_seqnum = seqnum - 10;
80 if (start_seqnum < 0)
81 start_seqnum = 0;
82 for (i = start_seqnum; i <= start_seqnum + 20; i++) {
83 (void) smb_sign_calc(command, sign, i, mac_sig);
84 if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) {
85 sign->seqnum = i;
86 *found = B_TRUE;
87 break;
88 }
89 cmn_err(CE_WARN, "smb_sign_find_seqnum: seqnum:%d mismatch", i);
90 }
91 cmn_err(CE_WARN, "smb_sign_find_seqnum: found=%d", *found);
92 }
93 #endif
94
95 /*
96 * Called during session destroy.
97 */
98 static void
smb_sign_fini(smb_session_t * s)99 smb_sign_fini(smb_session_t *s)
100 {
101 smb_sign_mech_t *mech;
102
103 if ((mech = s->signing.mech) != NULL) {
104 kmem_free(mech, sizeof (*mech));
105 s->signing.mech = NULL;
106 }
107 }
108
109 /*
110 * smb_sign_begin
111 *
112 * Intializes MAC key based on the user session key and
113 * NTLM response and store it in the signing structure.
114 * This is what begins SMB signing.
115 */
116 int
smb_sign_begin(smb_request_t * sr,smb_token_t * token)117 smb_sign_begin(smb_request_t *sr, smb_token_t *token)
118 {
119 smb_arg_sessionsetup_t *sinfo = sr->sr_ssetup;
120 smb_session_t *session = sr->session;
121 struct smb_sign *sign = &session->signing;
122 smb_sign_mech_t *mech;
123 int rc;
124
125 /*
126 * Session-level initialization (once per session)
127 */
128 smb_rwx_rwenter(&session->s_lock, RW_WRITER);
129
130 /*
131 * Signing may already have been setup by a prior logon,
132 * in which case we're done here.
133 */
134 if (sign->mackey != NULL) {
135 smb_rwx_rwexit(&session->s_lock);
136 return (0);
137 }
138
139 /*
140 * Get the mech handle
141 */
142 if (sign->mech == NULL) {
143 mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
144 rc = smb_md5_getmech(mech);
145 if (rc != 0) {
146 kmem_free(mech, sizeof (*mech));
147 smb_rwx_rwexit(&session->s_lock);
148 return (rc);
149 }
150 sign->mech = mech;
151 session->sign_fini = smb_sign_fini;
152 }
153
154 /*
155 * Compute and store the signing (MAC) key.
156 *
157 * With extended security, the MAC key is the same as the
158 * session key (and we'll have sinfo->ssi_cspwlen == 0).
159 * With non-extended security, it's the concatenation of
160 * the session key and the "NT response" we received.
161 * (NB: no extended security yet)
162 */
163 sign->mackey_len = SSN_KEY_LEN + sinfo->ssi_cspwlen;
164 sign->mackey = kmem_alloc(sign->mackey_len, KM_SLEEP);
165 bcopy(token->tkn_session_key, sign->mackey, SSN_KEY_LEN);
166 if (sinfo->ssi_cspwlen > 0) {
167 bcopy(sinfo->ssi_cspwd, sign->mackey + SSN_KEY_LEN,
168 sinfo->ssi_cspwlen);
169 }
170
171 session->signing.seqnum = 0;
172 sr->sr_seqnum = 2;
173 sr->reply_seqnum = 1;
174 sign->flags = 0;
175
176 if (session->secmode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
177 sign->flags |= SMB_SIGNING_ENABLED;
178 if (session->secmode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED)
179 sign->flags |= SMB_SIGNING_CHECK;
180 }
181
182 smb_rwx_rwexit(&session->s_lock);
183 return (0);
184 }
185
186 /*
187 * smb_sign_calc
188 *
189 * Calculates MAC signature for the given buffer and returns
190 * it in the mac_sign parameter.
191 *
192 * The sequence number is placed in the first four bytes of the signature
193 * field of the signature and the other 4 bytes are zeroed.
194 * The signature is the first 8 bytes of the MD5 result of the
195 * concatenated MAC key and the SMB message.
196 *
197 * MACsig = head(MD5(concat(MACKey, SMBMsg)), 8)
198 *
199 * where
200 *
201 * MACKey = concat( UserSessionKey, NTLMResp )
202 *
203 * and
204 *
205 * SMBMsg is the SMB message containing the sequence number.
206 *
207 * Return 0 if success
208 *
209 */
210 int
smb_sign_calc(struct mbuf_chain * mbc,struct smb_sign * sign,uint32_t seqnum,unsigned char * mac_sign)211 smb_sign_calc(struct mbuf_chain *mbc,
212 struct smb_sign *sign,
213 uint32_t seqnum,
214 unsigned char *mac_sign)
215 {
216 smb_sign_ctx_t ctx = 0;
217 uchar_t digest[MD5_DIGEST_LENGTH];
218 uchar_t *hdrp;
219 struct mbuf *mbuf = mbc->chain;
220 int offset = mbc->chain_offset;
221 int size;
222 int rc;
223
224 /*
225 * This union is a little bit of trickery to:
226 * (1) get the sequence number int aligned, and
227 * (2) reduce the number of digest calls, at the
228 * cost of a copying 32 bytes instead of 8.
229 * Both sides of this union are 2+32 bytes.
230 */
231 union {
232 struct {
233 uint8_t skip[2]; /* not used - just alignment */
234 uint8_t raw[SMB_HDRLEN]; /* header length (32) */
235 } r;
236 struct {
237 uint8_t skip[2]; /* not used - just alignment */
238 uint8_t hdr[SMB_SIG_OFFS]; /* sig. offset (14) */
239 uint32_t sig[2]; /* MAC signature, aligned! */
240 uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
241 } s;
242 } smbhdr;
243
244 if (sign->mech == NULL || sign->mackey == NULL)
245 return (-1);
246
247 if ((rc = smb_md5_init(&ctx, sign->mech)) != 0)
248 return (rc);
249
250 /* Digest the MAC Key */
251 rc = smb_md5_update(ctx, sign->mackey, sign->mackey_len);
252 if (rc != 0)
253 return (rc);
254
255 /*
256 * Make an aligned copy of the SMB header,
257 * fill in the sequence number, and digest.
258 */
259 hdrp = (unsigned char *)&smbhdr.r.raw;
260 size = SMB_HDRLEN;
261 if (smb_mbc_peek(mbc, offset, "#c", size, hdrp) != 0)
262 return (-1);
263 smbhdr.s.sig[0] = htolel(seqnum);
264 smbhdr.s.sig[1] = 0;
265
266 rc = smb_md5_update(ctx, &smbhdr.r.raw, size);
267 if (rc != 0)
268 return (rc);
269
270 /*
271 * Digest the rest of the SMB packet, starting at the data
272 * just after the SMB header.
273 */
274 offset += size;
275 while (mbuf != NULL && (offset >= mbuf->m_len)) {
276 offset -= mbuf->m_len;
277 mbuf = mbuf->m_next;
278 }
279 if (mbuf != NULL && (size = (mbuf->m_len - offset)) > 0) {
280 rc = smb_md5_update(ctx, &mbuf->m_data[offset], size);
281 if (rc != 0)
282 return (rc);
283 offset = 0;
284 mbuf = mbuf->m_next;
285 }
286 while (mbuf != NULL) {
287 rc = smb_md5_update(ctx, mbuf->m_data, mbuf->m_len);
288 if (rc != 0)
289 return (rc);
290 mbuf = mbuf->m_next;
291 }
292 rc = smb_md5_final(ctx, digest);
293 if (rc == 0)
294 bcopy(digest, mac_sign, SMB_SIG_SIZE);
295
296 return (rc);
297 }
298
299
300 /*
301 * smb_sign_check_request
302 *
303 * Calculates MAC signature for the request mbuf chain
304 * using the next expected sequence number and compares
305 * it to the given signature.
306 *
307 * Note it does not check the signature for secondary transactions
308 * as their sequence number is the same as the original request.
309 *
310 * Return 0 if the signature verifies, otherwise, returns -1;
311 *
312 */
313 int
smb_sign_check_request(smb_request_t * sr)314 smb_sign_check_request(smb_request_t *sr)
315 {
316 struct mbuf_chain command = sr->command;
317 unsigned char mac_sig[SMB_SIG_SIZE];
318 struct smb_sign *sign = &sr->session->signing;
319 int rtn = 0;
320 boolean_t found = B_TRUE;
321
322 /*
323 * Don't check secondary transactions - we dont know the sequence
324 * number.
325 */
326 if (sr->smb_com == SMB_COM_TRANSACTION_SECONDARY ||
327 sr->smb_com == SMB_COM_TRANSACTION2_SECONDARY ||
328 sr->smb_com == SMB_COM_NT_TRANSACT_SECONDARY)
329 return (0);
330
331 /* Reset the offset to begining of header */
332 command.chain_offset = sr->orig_request_hdr;
333
334 /* calculate mac signature */
335 if (smb_sign_calc(&command, sign, sr->sr_seqnum, mac_sig) != 0)
336 return (-1);
337
338 /* compare the signatures */
339 if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
340 DTRACE_PROBE2(smb__signing__req, smb_request_t, sr,
341 smb_sign_t *, sr->smb_sig);
342 cmn_err(CE_NOTE, "smb_sign_check_request: bad signature");
343 /*
344 * check nearby sequence numbers in debug mode
345 */
346 #ifdef DEBUG
347 if (smb_sign_debug)
348 smb_sign_find_seqnum(sr->sr_seqnum, sign,
349 &command, mac_sig, sr->smb_sig, &found);
350 else
351 #endif
352 found = B_FALSE;
353
354 if (found == B_FALSE)
355 rtn = -1;
356 }
357 return (rtn);
358 }
359
360 /*
361 * smb_sign_check_secondary
362 *
363 * Calculates MAC signature for the secondary transaction mbuf chain
364 * and compares it to the given signature.
365 * Return 0 if the signature verifies, otherwise, returns -1;
366 *
367 */
368 int
smb_sign_check_secondary(smb_request_t * sr,unsigned int reply_seqnum)369 smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum)
370 {
371 struct mbuf_chain command = sr->command;
372 unsigned char mac_sig[SMB_SIG_SIZE];
373 struct smb_sign *sign = &sr->session->signing;
374 int rtn = 0;
375
376 /* Reset the offset to begining of header */
377 command.chain_offset = sr->orig_request_hdr;
378
379 /* calculate mac signature */
380 if (smb_sign_calc(&command, sign, reply_seqnum - 1,
381 mac_sig) != 0)
382 return (-1);
383
384
385 /* compare the signatures */
386 if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
387 cmn_err(CE_WARN, "SmbSignCheckSecond: bad signature");
388 rtn = -1;
389 }
390 /* Save the reply sequence number */
391 sr->reply_seqnum = reply_seqnum;
392
393 return (rtn);
394 }
395
396 /*
397 * smb_sign_reply
398 *
399 * Calculates MAC signature for the given mbuf chain,
400 * and write it to the signature field in the mbuf.
401 *
402 */
403 void
smb_sign_reply(smb_request_t * sr,struct mbuf_chain * reply)404 smb_sign_reply(smb_request_t *sr, struct mbuf_chain *reply)
405 {
406 struct mbuf_chain resp;
407 struct smb_sign *sign = &sr->session->signing;
408 unsigned char signature[SMB_SIG_SIZE];
409
410 if (reply)
411 resp = *reply;
412 else
413 resp = sr->reply;
414
415 /* Reset offset to start of reply */
416 resp.chain_offset = 0;
417
418 /*
419 * Calculate MAC signature
420 */
421 if (smb_sign_calc(&resp, sign, sr->reply_seqnum, signature) != 0) {
422 cmn_err(CE_WARN, "smb_sign_reply: error in smb_sign_calc");
423 return;
424 }
425
426 /*
427 * Put signature in the response
428 */
429 (void) smb_mbc_poke(&resp, SMB_SIG_OFFS, "#c",
430 SMB_SIG_SIZE, signature);
431 }
432