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