1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015-2021 Tintri by DDN, Inc. All rights reserved.
14 * Copyright 2020-2024 RackTop Systems, Inc.
15 */
16
17
18 #include <smbsrv/smb2_kproto.h>
19 #include <smbsrv/smb_kstat.h>
20 #include <smbsrv/smb2.h>
21
22 #define SMB2_ASYNCID(sr) (sr->smb2_messageid ^ (1ULL << 62))
23
24 smb_sdrc_t smb2_invalid_cmd(smb_request_t *);
25 static void smb2_tq_work(void *);
26 static void smb2sr_run_postwork(smb_request_t *);
27 static int smb3_decrypt_msg(smb_request_t *);
28
29 static const smb_disp_entry_t
30 smb2_disp_table[SMB2__NCMDS] = {
31
32 /* text-name, pre, func, post, cmd-code, dialect, flags */
33
34 { "smb2_negotiate", NULL,
35 smb2_negotiate, NULL, 0, 0,
36 SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
37
38 { "smb2_session_setup", NULL,
39 smb2_session_setup, NULL, 0, 0,
40 SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
41
42 { "smb2_logoff", NULL,
43 smb2_logoff, NULL, 0, 0,
44 SDDF_SUPPRESS_TID },
45
46 { "smb2_tree_connect", NULL,
47 smb2_tree_connect, NULL, 0, 0,
48 SDDF_SUPPRESS_TID },
49
50 { "smb2_tree_disconn", NULL,
51 smb2_tree_disconn, NULL, 0, 0 },
52
53 { "smb2_create", NULL,
54 smb2_create, NULL, 0, 0 },
55
56 { "smb2_close", NULL,
57 smb2_close, NULL, 0, 0 },
58
59 { "smb2_flush", NULL,
60 smb2_flush, NULL, 0, 0 },
61
62 { "smb2_read", NULL,
63 smb2_read, NULL, 0, 0 },
64
65 { "smb2_write", NULL,
66 smb2_write, NULL, 0, 0 },
67
68 { "smb2_lock", NULL,
69 smb2_lock, NULL, 0, 0 },
70
71 { "smb2_ioctl", NULL,
72 smb2_ioctl, NULL, 0, 0 },
73
74 { "smb2_cancel", NULL,
75 smb2_cancel, NULL, 0, 0,
76 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
77
78 { "smb2_echo", NULL,
79 smb2_echo, NULL, 0, 0,
80 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
81
82 { "smb2_query_dir", NULL,
83 smb2_query_dir, NULL, 0, 0 },
84
85 { "smb2_change_notify", NULL,
86 smb2_change_notify, NULL, 0, 0 },
87
88 { "smb2_query_info", NULL,
89 smb2_query_info, NULL, 0, 0 },
90
91 { "smb2_set_info", NULL,
92 smb2_set_info, NULL, 0, 0 },
93
94 { "smb2_oplock_break_ack", NULL,
95 smb2_oplock_break_ack, NULL, 0, 0 },
96
97 { "smb2_invalid_cmd", NULL,
98 smb2_invalid_cmd, NULL, 0, 0,
99 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
100 };
101
102 smb_sdrc_t
smb2_invalid_cmd(smb_request_t * sr)103 smb2_invalid_cmd(smb_request_t *sr)
104 {
105 #ifdef DEBUG
106 cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code",
107 sr->session->ip_addr_str);
108 #endif
109 sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
110 return (SDRC_DROP_VC);
111 }
112
113 /*
114 * This is the SMB2 handler for new smb requests, called from
115 * smb_session_reader after SMB negotiate is done. For most SMB2
116 * requests, we just enqueue them for the smb_session_worker to
117 * execute via the task queue, so they can block for resources
118 * without stopping the reader thread. A few protocol messages
119 * are special cases and are handled directly here in the reader
120 * thread so they don't wait for taskq scheduling.
121 *
122 * This function must either enqueue the new request for
123 * execution via the task queue, or execute it directly
124 * and then free it. If this returns non-zero, the caller
125 * will drop the session.
126 */
127 int
smb2sr_newrq(smb_request_t * sr)128 smb2sr_newrq(smb_request_t *sr)
129 {
130 struct mbuf_chain *mbc = &sr->command;
131 taskqid_t tqid;
132 uint32_t magic;
133 int rc, skip;
134
135 if (smb_mbc_peek(mbc, 0, "l", &magic) != 0)
136 goto drop;
137
138 /* 0xFD S M B */
139 if (magic == SMB3_ENCRYPTED_MAGIC) {
140 if (smb3_decrypt_msg(sr) != 0)
141 goto drop;
142 /*
143 * Should now be looking at an un-encrypted
144 * SMB2 message header.
145 */
146 if (smb_mbc_peek(mbc, 0, "l", &magic) != 0)
147 goto drop;
148 }
149
150 if (magic != SMB2_PROTOCOL_MAGIC)
151 goto drop;
152
153 /*
154 * Walk the SMB2 commands in this compound message and
155 * keep track of the range of message IDs it uses.
156 */
157 for (;;) {
158 if (smb2_decode_header(sr) != 0)
159 goto drop;
160
161 /*
162 * Cancel requests are special: They refer to
163 * an earlier message ID (or an async. ID),
164 * never a new ID, and are never compounded.
165 * This is intentionally not "goto drop"
166 * because rc may be zero (success).
167 */
168 if (sr->smb2_cmd_code == SMB2_CANCEL) {
169 rc = smb2_newrq_cancel(sr);
170 smb_request_free(sr);
171 return (rc);
172 }
173
174 /*
175 * Keep track of the total credits in this compound
176 * and the first (real) message ID (not: 0, -1)
177 * While we're looking, verify that all (real) IDs
178 * are (first <= ID < (first + msg_credits))
179 */
180 if (sr->smb2_credit_charge == 0)
181 sr->smb2_credit_charge = 1;
182 sr->smb2_total_credits += sr->smb2_credit_charge;
183
184 if (sr->smb2_messageid != 0 &&
185 sr->smb2_messageid != UINT64_MAX) {
186
187 if (sr->smb2_first_msgid == 0)
188 sr->smb2_first_msgid = sr->smb2_messageid;
189
190 if (sr->smb2_messageid < sr->smb2_first_msgid ||
191 sr->smb2_messageid >= (sr->smb2_first_msgid +
192 sr->smb2_total_credits)) {
193 long long id = (long long) sr->smb2_messageid;
194 cmn_err(CE_WARN, "clnt %s msg ID 0x%llx "
195 "out of sequence in compound",
196 sr->session->ip_addr_str, id);
197 }
198 }
199
200 /* Normal loop exit on next == zero */
201 if (sr->smb2_next_command == 0)
202 break;
203
204 /* Abundance of caution... */
205 if (sr->smb2_next_command < SMB2_HDR_SIZE)
206 goto drop;
207
208 /* Advance to the next header. */
209 skip = sr->smb2_next_command - SMB2_HDR_SIZE;
210 if (MBC_ROOM_FOR(mbc, skip) == 0)
211 goto drop;
212 mbc->chain_offset += skip;
213 }
214 /* Rewind back to the top. */
215 mbc->chain_offset = 0;
216
217 /*
218 * Submit the request to the task queue, which calls
219 * smb2_tq_work when the workload permits.
220 */
221 sr->sr_time_submitted = gethrtime();
222 sr->sr_state = SMB_REQ_STATE_SUBMITTED;
223 smb_srqueue_waitq_enter(sr->session->s_srqueue);
224 tqid = taskq_dispatch(sr->sr_server->sv_worker_pool,
225 smb2_tq_work, sr, TQ_SLEEP);
226 VERIFY(tqid != TASKQID_INVALID);
227
228 return (0);
229
230 drop:
231 smb_request_free(sr);
232 return (-1);
233 }
234
235 static void
smb2_tq_work(void * arg)236 smb2_tq_work(void *arg)
237 {
238 smb_request_t *sr;
239 smb_srqueue_t *srq;
240
241 sr = (smb_request_t *)arg;
242 SMB_REQ_VALID(sr);
243
244 srq = sr->session->s_srqueue;
245 smb_srqueue_waitq_to_runq(srq);
246 sr->sr_worker = curthread;
247 sr->sr_time_active = gethrtime();
248
249 /*
250 * Always dispatch to the work function, because cancelled
251 * requests need an error reply (NT_STATUS_CANCELLED).
252 */
253 mutex_enter(&sr->sr_mutex);
254 if (sr->sr_state == SMB_REQ_STATE_SUBMITTED)
255 sr->sr_state = SMB_REQ_STATE_ACTIVE;
256 mutex_exit(&sr->sr_mutex);
257
258 smb2sr_work(sr);
259
260 smb_srqueue_runq_exit(srq);
261 }
262
263 /*
264 * Wrapper to setup a new mchain for the plaintext request that will
265 * replace the encrypted one. Returns non-zero to drop the connection.
266 * Error return values here are just for visibility in dtrace.
267 */
268 static int
smb3_decrypt_msg(smb_request_t * sr)269 smb3_decrypt_msg(smb_request_t *sr)
270 {
271 struct mbuf_chain clear_mbc = {0};
272 struct mbuf_chain tmp_mbc;
273 mbuf_t *m;
274 int clearsize;
275 int rc;
276
277 if (sr->session->dialect < SMB_VERS_3_0) {
278 /* Encrypted message in SMB 2.x */
279 return (-1);
280 }
281 if ((sr->session->srv_cap & SMB2_CAP_ENCRYPTION) == 0) {
282 /* Should have srv_cap SMB2_CAP_ENCRYPTION flag set! */
283 return (-2);
284 }
285
286 sr->encrypted = B_TRUE;
287 if (sr->command.max_bytes <
288 (SMB3_TFORM_HDR_SIZE + SMB2_HDR_SIZE)) {
289 /* Short transform header */
290 return (-3);
291 }
292 clearsize = sr->command.max_bytes - SMB3_TFORM_HDR_SIZE;
293
294 clear_mbc.max_bytes = clearsize;
295 m = smb_mbuf_alloc_chain(clearsize);
296 MBC_ATTACH_MBUF(&clear_mbc, m);
297
298 rc = smb3_decrypt_sr(sr, &sr->command, &clear_mbc);
299 if (rc != 0) {
300 MBC_FLUSH(&clear_mbc);
301 return (rc);
302 }
303
304 /* Swap clear_mbc in place of command */
305 tmp_mbc = sr->command;
306 sr->command = clear_mbc;
307 MBC_FLUSH(&tmp_mbc); // free old sr->command
308
309 return (0);
310 }
311
312 /*
313 * SMB2 credits determine how many simultaneous commands the
314 * client may issue, and bounds the range of message IDs those
315 * commands may use. With multi-credit support, commands may
316 * use ranges of message IDs, where the credits used by each
317 * command are proportional to their data transfer size.
318 *
319 * Every command may request an increase or decrease of
320 * the currently granted credits, based on the difference
321 * between the credit request and the credit charge.
322 * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits
323 *
324 * Most commands have credit_request=1, credit_charge=1,
325 * which keeps the credit grant unchanged.
326 *
327 * All we're really doing here (for now) is reducing the
328 * credit_response if the client requests a credit increase
329 * that would take their credit over the maximum, and
330 * limiting the decrease so they don't run out of credits.
331 *
332 * Later, this could do something dynamic based on load.
333 *
334 * One other non-obvious bit about credits: We keep the
335 * session s_max_credits low until the 1st authentication,
336 * at which point we'll set the normal maximum_credits.
337 * Some clients ask for more credits with session setup,
338 * and we need to handle that requested increase _after_
339 * the command-specific handler returns so it won't be
340 * restricted to the lower (pre-auth) limit.
341 */
342 static inline void
smb2_credit_decrease(smb_request_t * sr)343 smb2_credit_decrease(smb_request_t *sr)
344 {
345 smb_session_t *session = sr->session;
346 uint16_t cur, d;
347
348 ASSERT3U(sr->smb2_credit_request, <, sr->smb2_credit_charge);
349
350 mutex_enter(&session->s_credits_mutex);
351 cur = session->s_cur_credits;
352 ASSERT(cur > 0);
353
354 /* Handle credit decrease. */
355 d = sr->smb2_credit_charge - sr->smb2_credit_request;
356
357 /*
358 * Prevent underflow of current credits, and
359 * enforce a minimum of one credit, per:
360 * [MS-SMB2] 3.3.1.2
361 */
362 if (d >= cur) {
363 /*
364 * Tried to give up more credits than we should.
365 * Reduce the decrement.
366 */
367 d = cur - 1;
368 cur = 1;
369 DTRACE_PROBE1(smb2__credit__neg, smb_request_t *, sr);
370 } else {
371 cur -= d;
372 }
373
374 ASSERT3U(d, <=, sr->smb2_credit_charge);
375 sr->smb2_credit_response = sr->smb2_credit_charge - d;
376
377 DTRACE_PROBE3(smb2__credit__decrease,
378 smb_request_t *, sr, int, (int)cur,
379 int, (int)session->s_cur_credits);
380
381 session->s_cur_credits = cur;
382 mutex_exit(&session->s_credits_mutex);
383 }
384
385 /*
386 * Second half of SMB2 credit handling (increases)
387 */
388 static inline void
smb2_credit_increase(smb_request_t * sr)389 smb2_credit_increase(smb_request_t *sr)
390 {
391 smb_session_t *session = sr->session;
392 uint16_t cur, d;
393
394 ASSERT3U(sr->smb2_credit_request, >, sr->smb2_credit_charge);
395
396 mutex_enter(&session->s_credits_mutex);
397 cur = session->s_cur_credits;
398
399 /* Handle credit increase. */
400 d = sr->smb2_credit_request - sr->smb2_credit_charge;
401
402 /*
403 * If new credits would be above max,
404 * reduce the credit grant.
405 */
406 if (d > (session->s_max_credits - cur)) {
407 d = session->s_max_credits - cur;
408 cur = session->s_max_credits;
409 DTRACE_PROBE1(smb2__credit__max, smb_request_t *, sr);
410 } else {
411 cur += d;
412 }
413 sr->smb2_credit_response = sr->smb2_credit_charge + d;
414
415 DTRACE_PROBE3(smb2__credit__increase,
416 smb_request_t *, sr, int, (int)cur,
417 int, (int)session->s_cur_credits);
418
419 session->s_cur_credits = cur;
420 mutex_exit(&session->s_credits_mutex);
421 }
422
423 /*
424 * Record some statistics: latency, rx bytes, tx bytes
425 * per: server, session & kshare.
426 */
427 inline void
smb2_record_stats(smb_request_t * sr,smb_disp_stats_t * sds,boolean_t need_lat)428 smb2_record_stats(smb_request_t *sr, smb_disp_stats_t *sds, boolean_t need_lat)
429 {
430 int64_t rxb;
431 int64_t txb;
432
433 rxb = (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr);
434 txb = (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr);
435 txb += sr->smb2_async_txb;
436 sr->smb2_async_txb = 0;
437
438 smb_server_inc_req(sr->sr_server);
439 atomic_add_64(&sds->sdt_rxb, rxb);
440 atomic_add_64(&sds->sdt_txb, txb);
441
442 if (need_lat) {
443 hrtime_t dt;
444 dt = gethrtime() - sr->sr_time_start;
445 smb_latency_add_sample(&sds->sdt_lat, dt);
446 }
447 }
448
449 /*
450 * smb2sr_work
451 *
452 * This function processes each SMB command in the current request
453 * (which may be a compound request) building a reply containing
454 * SMB reply messages, one-to-one with the SMB commands. Some SMB
455 * commands (change notify, blocking locks) may require both an
456 * "interim response" and a later "async response" at completion.
457 * In such cases, we'll encode the interim response in the reply
458 * compound we're building, and put the (now async) command on a
459 * list of commands that need further processing. After we've
460 * finished processing the commands in this compound and building
461 * the compound reply, we'll send the compound reply, and finally
462 * process the list of async commands.
463 *
464 * As we work our way through the compound request and reply,
465 * we need to keep track of the bounds of the current request
466 * and reply. For the request, this uses an MBC_SHADOW_CHAIN
467 * that begins at smb2_cmd_hdr. The reply is appended to the
468 * sr->reply chain starting at smb2_reply_hdr.
469 *
470 * This function must always free the smb request, or arrange
471 * for it to be completed and free'd later (if SDRC_SR_KEPT).
472 */
473 void
smb2sr_work(struct smb_request * sr)474 smb2sr_work(struct smb_request *sr)
475 {
476 const smb_disp_entry_t *sdd;
477 smb_disp_stats_t *sds;
478 smb_session_t *session;
479 uint32_t msg_len;
480 uint16_t cmd_idx;
481 int rc = 0;
482 boolean_t disconnect = B_FALSE;
483 boolean_t related;
484
485 session = sr->session;
486
487 ASSERT(sr->smb2_async_mode == B_FALSE);
488 ASSERT(sr->tid_tree == 0);
489 ASSERT(sr->uid_user == 0);
490 ASSERT(sr->fid_ofile == 0);
491 sr->smb_fid = (uint16_t)-1;
492 sr->smb2_status = 0;
493
494 /* temporary until we identify a user */
495 sr->user_cr = zone_kcred();
496
497 cmd_start:
498 /*
499 * Note that we don't check sr_state here and abort the
500 * compound if cancelled (etc.) because some SMB2 command
501 * handlers need to do work even when cancelled.
502 *
503 * We treat some status codes as if "sticky", meaning
504 * once they're set after some command handler returns,
505 * all remaining commands get this status without even
506 * calling the command-specific handler.
507 */
508 if (sr->smb2_status != NT_STATUS_CANCELLED &&
509 sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES)
510 sr->smb2_status = 0;
511
512 /*
513 * Decode the request header
514 *
515 * Most problems with decoding will result in the error
516 * STATUS_INVALID_PARAMETER. If the decoding problem
517 * prevents continuing, we'll close the connection.
518 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
519 */
520 sr->smb2_async_mode = B_FALSE;
521 sr->smb2_cmd_hdr = sr->command.chain_offset;
522 if ((rc = smb2_decode_header(sr)) != 0) {
523 cmn_err(CE_WARN, "clnt %s bad SMB2 header",
524 session->ip_addr_str);
525 disconnect = B_TRUE;
526 goto cleanup;
527 }
528
529 /*
530 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear
531 * in messages from the server back to the client.
532 */
533 if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) {
534 cmn_err(CE_WARN, "clnt %s bad SMB2 flags",
535 session->ip_addr_str);
536 disconnect = B_TRUE;
537 goto cleanup;
538 }
539 related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS);
540 sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR;
541 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
542 /* Probably an async cancel. */
543 DTRACE_PROBE1(smb2__dispatch__async, smb_request_t *, sr);
544 }
545
546 /*
547 * In case we bail out with an error before we get to the
548 * section that computes the credit grant, initialize the
549 * response header fields so that credits won't change.
550 * Note: SMB 2.02 clients may send credit charge zero.
551 */
552 if (sr->smb2_credit_charge == 0)
553 sr->smb2_credit_charge = 1;
554 sr->smb2_credit_response = sr->smb2_credit_charge;
555
556 /*
557 * Write a tentative reply header.
558 *
559 * We could just leave this blank, but if we're using the
560 * mdb module feature that extracts packets, it's useful
561 * to have the header mostly correct here.
562 *
563 * If we have already exhausted the output space, then the
564 * client is trying something funny. Log it and kill 'em.
565 */
566 sr->smb2_next_reply = 0;
567 ASSERT((sr->reply.chain_offset & 7) == 0);
568 sr->smb2_reply_hdr = sr->reply.chain_offset;
569 if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) {
570 cmn_err(CE_WARN, "clnt %s excessive reply",
571 session->ip_addr_str);
572 disconnect = B_TRUE;
573 goto cleanup;
574 }
575
576 /*
577 * Figure out the length of data following the SMB2 header.
578 * It ends at either the next SMB2 header if there is one
579 * (smb2_next_command != 0) or at the end of the message.
580 */
581 if (sr->smb2_next_command != 0) {
582 /* [MS-SMB2] says this is 8-byte aligned */
583 msg_len = sr->smb2_next_command;
584 if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
585 ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
586 cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
587 session->ip_addr_str);
588 disconnect = B_TRUE;
589 goto cleanup;
590 }
591 } else {
592 msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
593 }
594
595 /*
596 * Setup a shadow chain for this SMB2 command, starting
597 * with the header and ending at either the next command
598 * or the end of the message. The signing check below
599 * needs the entire SMB2 command. After that's done, we
600 * advance chain_offset to the end of the header where
601 * the command specific handlers continue decoding.
602 */
603 (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
604 sr->smb2_cmd_hdr, msg_len);
605
606 /*
607 * We will consume the data for this request from smb_data.
608 * That effectively consumes msg_len bytes from sr->command
609 * but doesn't update its chain_offset, so we need to update
610 * that here to make later received bytes accounting work.
611 */
612 sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len;
613 ASSERT(sr->command.chain_offset <= sr->command.max_bytes);
614
615 /*
616 * Validate the commmand code, get dispatch table entries.
617 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
618 *
619 * The last slot in the dispatch table is used to handle
620 * invalid commands. Same for statistics.
621 */
622 if (sr->smb2_cmd_code < SMB2_INVALID_CMD)
623 cmd_idx = sr->smb2_cmd_code;
624 else
625 cmd_idx = SMB2_INVALID_CMD;
626 sdd = &smb2_disp_table[cmd_idx];
627 sds = &session->s_server->sv_disp_stats2[cmd_idx];
628
629 /*
630 * If this command is NOT "related" to the previous,
631 * clear out the UID, TID, FID state that might be
632 * left over from the previous command.
633 *
634 * If the command IS related, any new IDs are ignored,
635 * and we simply continue with the previous user, tree,
636 * and open file.
637 */
638 if (!related) {
639 /*
640 * Drop user, tree, file; carefully ordered to
641 * avoid dangling references: file, tree, user
642 */
643 if (sr->fid_ofile != NULL) {
644 smb_ofile_release(sr->fid_ofile);
645 sr->fid_ofile = NULL;
646 }
647 if (sr->tid_tree != NULL) {
648 smb_tree_release(sr->tid_tree);
649 sr->tid_tree = NULL;
650 }
651 if (sr->uid_user != NULL) {
652 smb_user_release(sr->uid_user);
653 sr->uid_user = NULL;
654 sr->user_cr = zone_kcred();
655 }
656 }
657
658 /*
659 * Make sure we have a user and tree as needed
660 * according to the flags for the this command.
661 * Note that we may have inherited these.
662 */
663 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) {
664 /*
665 * This command requires a user session.
666 */
667 if (related) {
668 /*
669 * Previous command should have given us a user.
670 * [MS-SMB2] 3.3.5.2 Handling Related Requests
671 */
672 if (sr->uid_user == NULL) {
673 smb2sr_put_error(sr,
674 NT_STATUS_INVALID_PARAMETER);
675 goto cmd_done;
676 }
677 sr->smb2_ssnid = sr->uid_user->u_ssnid;
678 } else {
679 /*
680 * Lookup the UID
681 * [MS-SMB2] 3.3.5.2 Verifying the Session
682 */
683 ASSERT(sr->uid_user == NULL);
684 /*
685 * [MS-SMB2] 3.3.5.2.7 Handling Compounded Requests
686 *
687 * If this is an encrypted compound request,
688 * ensure that the ssnid in the request
689 * is the same as the tform ssnid if this
690 * message is not related.
691 *
692 * The reasons this is done seem to apply equally
693 * to uncompounded requests, so we apply it to all.
694 */
695
696 if (sr->encrypted &&
697 sr->smb2_ssnid != sr->th_ssnid) {
698 disconnect = B_TRUE;
699 goto cleanup; /* just do this for now */
700 }
701
702 sr->uid_user = smb_session_lookup_ssnid(session,
703 sr->smb2_ssnid);
704 if (sr->uid_user == NULL) {
705 smb2sr_put_error(sr,
706 NT_STATUS_USER_SESSION_DELETED);
707 goto cmd_done;
708 }
709
710 /*
711 * [MS-SMB2] 3.3.5.2.9 Verifying the Session
712 *
713 * If we're talking 3.x,
714 * RejectUnencryptedAccess is TRUE,
715 * Session.EncryptData is TRUE,
716 * and the message wasn't encrypted,
717 * return ACCESS_DENIED.
718 *
719 * Note that Session.EncryptData can only be TRUE when
720 * we're talking 3.x.
721 */
722 if (sr->uid_user->u_encrypt == SMB_CONFIG_REQUIRED &&
723 !sr->encrypted) {
724 smb2sr_put_error(sr,
725 NT_STATUS_ACCESS_DENIED);
726 goto cmd_done;
727 }
728
729 sr->user_cr = smb_user_getcred(sr->uid_user);
730 }
731 ASSERT(sr->uid_user != NULL);
732
733 /*
734 * Encrypt if:
735 * - The cmd is not SESSION_SETUP or NEGOTIATE; AND
736 * - Session.EncryptData is TRUE
737 *
738 * Those commands suppress UID, so they can't be the cmd here.
739 */
740 if (sr->uid_user->u_encrypt != SMB_CONFIG_DISABLED &&
741 sr->th_sid_user == NULL) {
742 smb_user_hold_internal(sr->uid_user);
743 sr->th_sid_user = sr->uid_user;
744 sr->th_ssnid = sr->smb2_ssnid;
745 }
746 }
747
748 if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) {
749 /*
750 * This command requires a tree connection.
751 */
752 if (related) {
753 /*
754 * Previous command should have given us a tree.
755 * [MS-SMB2] 3.3.5.2 Handling Related Requests
756 */
757 if (sr->tid_tree == NULL) {
758 smb2sr_put_error(sr,
759 NT_STATUS_INVALID_PARAMETER);
760 goto cmd_done;
761 }
762 sr->smb_tid = sr->tid_tree->t_tid;
763 } else {
764 /*
765 * Lookup the TID
766 * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect
767 */
768 ASSERT(sr->tid_tree == NULL);
769 sr->tid_tree = smb_session_lookup_tree(session,
770 sr->smb_tid);
771 if (sr->tid_tree == NULL) {
772 smb2sr_put_error(sr,
773 NT_STATUS_NETWORK_NAME_DELETED);
774 goto cmd_done;
775 }
776
777 /*
778 * [MS-SMB2] 3.3.5.2.11 Verifying the Tree Connect
779 *
780 * If we support 3.x, RejectUnencryptedAccess is TRUE,
781 * if Tcon.EncryptData is TRUE or
782 * global EncryptData is TRUE and
783 * the message wasn't encrypted, or
784 * if Tcon.EncryptData is TRUE or
785 * global EncryptData is TRUE or
786 * the request was encrypted and
787 * the connection doesn't support encryption,
788 * return ACCESS_DENIED.
789 *
790 * If RejectUnencryptedAccess is TRUE, we force
791 * max_protocol to at least 3.0. Additionally,
792 * if the tree requires encryption, we don't care
793 * what we support, we still enforce encryption.
794 * Since smb3_decrypt_msg() does check session->srv_cap,
795 * we only need to check sr->encrypted here.
796 */
797 if (sr->tid_tree->t_encrypt == SMB_CONFIG_REQUIRED &&
798 !sr->encrypted) {
799 smb2sr_put_error(sr,
800 NT_STATUS_ACCESS_DENIED);
801 goto cmd_done;
802 }
803 }
804 ASSERT(sr->tid_tree != NULL);
805
806 /*
807 * Encrypt if:
808 * - The cmd is not TREE_CONNECT; AND
809 * - Tree.EncryptData is TRUE
810 *
811 * TREE_CONNECT suppresses TID, so that can't be the cmd here.
812 * NOTE: assumes we can't have a tree without a user
813 */
814 if (sr->tid_tree->t_encrypt != SMB_CONFIG_DISABLED &&
815 sr->th_sid_user == NULL) {
816 smb_user_hold_internal(sr->uid_user);
817 sr->th_sid_user = sr->uid_user;
818 sr->th_ssnid = sr->smb2_ssnid;
819 }
820 }
821
822 /*
823 * SMB2 signature verification, two parts:
824 * (a) Require SMB2_FLAGS_SIGNED (for most request types)
825 * (b) If SMB2_FLAGS_SIGNED is set, check the signature.
826 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
827 */
828
829 /*
830 * No user session means no signature check. That's OK,
831 * i.e. for commands marked SDDF_SUPPRESS_UID above.
832 * Note, this also means we won't sign the reply.
833 */
834 if (sr->uid_user == NULL)
835 sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
836
837 /*
838 * The SDDF_SUPPRESS_UID dispatch is set for requests that
839 * don't need a UID (user). These also don't require a
840 * signature check here.
841 *
842 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
843 *
844 * If the packet was successfully decrypted, the message
845 * signature has already been verified, so we can skip this.
846 */
847 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
848 !sr->encrypted && sr->uid_user != NULL &&
849 (sr->uid_user->u_sign_flags & SMB_SIGNING_ENABLED) != 0) {
850 /*
851 * If the request is signed, check the signature.
852 * Otherwise, if signing is required, deny access.
853 */
854 if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) != 0) {
855 if (smb2_sign_check_request(sr) != 0) {
856 smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
857 DTRACE_PROBE1(smb2__sign__check,
858 smb_request_t *, sr);
859 goto cmd_done;
860 }
861 } else if (
862 (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) {
863 smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
864 goto cmd_done;
865 }
866 }
867
868 /*
869 * Now that the signing check is done with smb_data,
870 * advance past the SMB2 header we decoded earlier.
871 * This leaves sr->smb_data correctly positioned
872 * for command-specific decoding in the dispatch
873 * function called next.
874 */
875 sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE;
876
877 /*
878 * Credit adjustments (decrease)
879 */
880 if (sr->smb2_credit_request < sr->smb2_credit_charge)
881 smb2_credit_decrease(sr);
882
883 /*
884 * The real work: call the SMB2 command handler
885 * (except for "sticky" smb2_status - see above)
886 */
887 sr->sr_time_start = gethrtime();
888 rc = SDRC_SUCCESS;
889 if (sr->smb2_status == 0) {
890 /* NB: not using pre_op */
891 rc = (*sdd->sdt_function)(sr);
892 /* NB: not using post_op */
893 } else {
894 smb2sr_put_error(sr, sr->smb2_status);
895 }
896
897 /*
898 * When the sdt_function returns SDRC_SR_KEPT, it means
899 * this SR may have been passed to another thread so we
900 * MUST NOT touch it anymore.
901 */
902 if (rc == SDRC_SR_KEPT)
903 return;
904
905 MBC_FLUSH(&sr->raw_data);
906
907 /*
908 * Credit adjustments (increase)
909 *
910 * If we've gone async, credit adjustments were done
911 * when we sent the interim reply.
912 */
913 if (!sr->smb2_async_mode) {
914 if (sr->smb2_credit_request > sr->smb2_credit_charge) {
915 smb2_credit_increase(sr);
916 }
917 }
918
919 cmd_done:
920 switch (rc) {
921 case SDRC_SUCCESS:
922 break;
923 default:
924 /*
925 * SMB2 does not use the other dispatch return codes.
926 * If we see something else, log an event so we'll
927 * know something is returning bogus status codes.
928 * If you see these in the log, use dtrace to find
929 * the code returning something else.
930 */
931 #ifdef DEBUG
932 cmn_err(CE_NOTE, "handler for %u returned 0x%x",
933 sr->smb2_cmd_code, rc);
934 #endif
935 smb2sr_put_error(sr, NT_STATUS_INTERNAL_ERROR);
936 break;
937 case SDRC_ERROR:
938 /*
939 * Many command handlers return SDRC_ERROR for any
940 * problems decoding the request, and don't bother
941 * setting smb2_status. For those cases, the best
942 * status return would be "invalid parameter".
943 */
944 if (sr->smb2_status == 0)
945 sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
946 smb2sr_put_error(sr, sr->smb2_status);
947 break;
948 case SDRC_DROP_VC:
949 disconnect = B_TRUE;
950 goto cleanup;
951
952 case SDRC_NO_REPLY:
953 /* will free sr */
954 goto cleanup;
955 }
956
957 /*
958 * Pad the reply to align(8) if there will be another.
959 */
960 if (sr->smb2_next_command != 0)
961 (void) smb_mbc_put_align(&sr->reply, 8);
962
963 /*
964 * Record some statistics. Uses:
965 * rxb = command.chain_offset - smb2_cmd_hdr;
966 * txb = reply.chain_offset - smb2_reply_hdr;
967 * which at this point represent the current cmd/reply.
968 */
969 smb2_record_stats(sr, sds, B_TRUE);
970
971 /*
972 * If there's a next command, figure out where it starts,
973 * and fill in the next header offset for the reply.
974 * Note: We sanity checked smb2_next_command above.
975 */
976 if (sr->smb2_next_command != 0) {
977 sr->command.chain_offset =
978 sr->smb2_cmd_hdr + sr->smb2_next_command;
979 sr->smb2_next_reply =
980 sr->reply.chain_offset - sr->smb2_reply_hdr;
981 } else {
982 ASSERT(sr->smb2_next_reply == 0);
983 }
984
985 /*
986 * Overwrite the (now final) SMB2 header for this response.
987 */
988 (void) smb2_encode_header(sr, B_TRUE);
989
990 /*
991 * Cannot move this into smb2_session_setup() - encoded header required.
992 */
993 if (session->dialect >= SMB_VERS_3_11 &&
994 sr->smb2_cmd_code == SMB2_SESSION_SETUP &&
995 sr->smb2_status == NT_STATUS_MORE_PROCESSING_REQUIRED) {
996 if (smb31_preauth_sha512_calc(sr, &sr->reply,
997 sr->uid_user->u_preauth_hashval,
998 sr->uid_user->u_preauth_hashval) != 0)
999 cmn_err(CE_WARN, "(3) Preauth hash calculation "
1000 "failed");
1001 }
1002
1003 /* Don't sign if we're going to encrypt */
1004 if (sr->th_sid_user == NULL &&
1005 (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) != 0)
1006 smb2_sign_reply(sr);
1007
1008 if (sr->smb2_next_command != 0)
1009 goto cmd_start;
1010
1011 /*
1012 * If we have a durable handle, and this operation updated
1013 * the nvlist, write it out (before smb2_send_reply).
1014 */
1015 if (sr->dh_nvl_dirty) {
1016 sr->dh_nvl_dirty = B_FALSE;
1017 smb2_dh_update_nvfile(sr);
1018 }
1019
1020 smb2_send_reply(sr);
1021
1022 cleanup:
1023 if (disconnect)
1024 smb_session_disconnect(session);
1025
1026 /*
1027 * Do "postwork" for oplock (and maybe other things)
1028 */
1029 if (sr->sr_postwork != NULL)
1030 smb2sr_run_postwork(sr);
1031
1032 mutex_enter(&sr->sr_mutex);
1033 sr->sr_state = SMB_REQ_STATE_COMPLETED;
1034 mutex_exit(&sr->sr_mutex);
1035
1036 smb_request_free(sr);
1037 }
1038
1039 /*
1040 * Build an interim response for a command that needs to 'go async'.
1041 * This is used by requests that block for a relatively long time.
1042 * Leave the SR state so that smb2sr_work() can continue its
1043 * processing of this compound in "async mode".
1044 *
1045 * If we agree to "go async", this should return STATUS_SUCCESS.
1046 * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and
1047 * all requests following this request. (See the comments re.
1048 * "sticky" smb2_status values in smb2sr_work).
1049 *
1050 * Note: the Async ID we assign here is arbitrary, and need only
1051 * be unique among pending async responses on this connection, so
1052 * this just uses a modified messageID, which is already unique.
1053 *
1054 * Credits: All credit changes should happen via the interim
1055 * response, so we have to manage credits here. After this
1056 * returns to smb2sr_work, the final reply for this command
1057 * will have smb2_credit_response = 0
1058 * (meaning no further changes to the clients' credits).
1059 *
1060 * Compound responses:
1061 *
1062 * [MS-SMB2] 3.3.5.2.7.2 "Handling Compounded Related Requests" states:
1063 *
1064 * "When an operation requires asynchronous processing, all the subsequent
1065 * operations MUST also be processed asynchronously. The server MUST send an
1066 * interim response for all such operations as specified in section 3.3.4.2."
1067 *
1068 * One might expect that means we should also send interim responses for every
1069 * command following the current one. However, Windows does not do that, and at
1070 * least one client (mount.cifs) rejects such 'extra' responses as errors.
1071 * When asked for clarification, Microsoft Dochelp pointed to this part of
1072 * [MS-SMB2] 3.3.4.2 "Sending an Interim Response for an Asynchronous Operation"
1073 *
1074 * "The server MAY choose to send an interim response for any request..."
1075 *
1076 * Further experimentation on Windows reveals two separate behaviors, seemingly
1077 * distinguished by whether the request may block indefinitely:
1078 *
1079 * Bounded blocking (e.g. Oplock break):
1080 * - An interim response is sent before any part of the compound response.
1081 * - If multiple commands 'go async', they each send separate interim responses.
1082 * - A single compound response is sent when the final command completes.
1083 *
1084 * Indefinite blocking (e.g. Change Notify, Byte-Range Lock):
1085 * - If the command is not the last in the compound, it is rejected with
1086 * "NT_STATUS_INTERNAL_ERROR".
1087 * - All responses for commands prior to the one that 'goes async' are returned
1088 * immediately as a single compound response.
1089 * - An interim response is then sent separately from the compound response.
1090 *
1091 * We follow Windows behavior here for compatibility purposes, with one
1092 * exception: in the indefinite case, we deliver the interim response at the end
1093 * of the compound response, rather than separately, as we don't have the state
1094 * needed to modify the previous response's header to terminate the compound.
1095 * The command-specific handler chooses whether to allow this in the middle of
1096 * the compound.
1097 *
1098 * Errors here mean we failed to encode the interim response.
1099 * Rather than actually disconnect here, let's assume whatever problem we
1100 * encountered will be seen by the caller as they continue processing the
1101 * compound, and just restore everything and return an error.
1102 */
1103 static uint32_t
smb2sr_send_interim(smb_request_t * sr)1104 smb2sr_send_interim(smb_request_t *sr)
1105 {
1106 uint32_t saved_hdr_flags;
1107 boolean_t disconnect = B_FALSE;
1108
1109 sr->smb2_async_id = SMB2_ASYNCID(sr);
1110 sr->smb2_status = NT_STATUS_PENDING;
1111 sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
1112 saved_hdr_flags = sr->smb2_hdr_flags;
1113 sr->smb2_next_reply = 0;
1114
1115 if ((smb2_encode_header(sr, B_FALSE)) != 0) {
1116 cmn_err(CE_WARN, "clnt %s excessive reply",
1117 sr->session->ip_addr_str);
1118 disconnect = B_TRUE;
1119 goto cleanup;
1120 }
1121
1122 /*
1123 * Don't change (user, tree, file) because we want them
1124 * exactly as they were when we entered.
1125 * Interim responses are NOT signed; we already checked the signature
1126 * for this message, and will restore this flag on completion.
1127 */
1128 sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
1129
1130 smb2sr_put_error(sr, sr->smb2_status);
1131
1132 /*
1133 * Credit adjustments (increase)
1134 *
1135 * NOTE: interim responses are not signed.
1136 * Any attacker can modify the credit grant
1137 * in the response. Because of this property,
1138 * it is no worse to assume the credit charge and grant
1139 * are sane without verifying the signature,
1140 * and that saves us a whole lot of work.
1141 * If the credits WERE modified, we'll find out
1142 * when we verify the signature later,
1143 * which nullifies any changes caused here.
1144 */
1145 if (sr->smb2_credit_request > sr->smb2_credit_charge) {
1146 smb2_credit_increase(sr);
1147 }
1148
1149 DTRACE_PROBE1(go__async, smb_request_t *, sr);
1150
1151 /*
1152 * Overwrite the (now final) SMB2 header for this response.
1153 */
1154 (void) smb2_encode_header(sr, B_TRUE);
1155 sr->smb2_async_txb =
1156 (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr);
1157
1158 smb2_send_reply(sr);
1159
1160 /*
1161 * Now that we've responded (with credits granted),
1162 * the final response shouldn't grant any.
1163 */
1164 sr->smb2_credit_response = 0;
1165
1166 cleanup:
1167 sr->smb2_status = NT_STATUS_SUCCESS;
1168 sr->smb2_hdr_flags = saved_hdr_flags;
1169
1170 if (disconnect) {
1171 sr->smb2_async_mode = B_FALSE;
1172 sr->smb2_hdr_flags &= SMB2_FLAGS_ASYNC_COMMAND;
1173 return (NT_STATUS_INVALID_PARAMETER);
1174 }
1175
1176 return (NT_STATUS_SUCCESS);
1177 }
1178
1179 /*
1180 * This variant of 'go async' sends the interim response on its own,
1181 * saving/restoring the response compound currently under construction.
1182 * See the longer comment above smb2sr_send_interim().
1183 */
1184 uint32_t
smb2sr_go_async(smb_request_t * sr)1185 smb2sr_go_async(smb_request_t *sr)
1186 {
1187 struct mbuf_chain saved_reply;
1188 uint32_t saved_reply_hdr = sr->smb2_reply_hdr;
1189 uint32_t saved_next_reply = sr->smb2_next_reply;
1190 uint32_t status;
1191
1192 if (sr->smb2_async_mode) {
1193 /* cmd is already being processed async */
1194 return (NT_STATUS_SUCCESS);
1195 }
1196 sr->smb2_async_mode = B_TRUE;
1197
1198 /* The "server" session always runs async. */
1199 if (sr->session->sock == NULL)
1200 return (NT_STATUS_SUCCESS);
1201
1202 /*
1203 * All the encoding functions use sr and sr->reply.
1204 * Keep a copy of the old reply MBC around so we can send out our
1205 * interim response on its own, then restore the original.
1206 */
1207 saved_reply = sr->reply; /* struct copy */
1208 MBC_INIT(&sr->reply, sr->reply.max_bytes);
1209 sr->smb2_reply_hdr = 0;
1210
1211 status = smb2sr_send_interim(sr);
1212
1213 MBC_FLUSH(&sr->reply); /* clear our interim reply */
1214 sr->reply = saved_reply; /* struct copy - restore original */
1215 sr->smb2_next_reply = saved_next_reply;
1216 sr->smb2_reply_hdr = saved_reply_hdr;
1217
1218 return (status);
1219 }
1220
1221 /*
1222 * This variant of 'go async' appends the interim response
1223 * to the response compound currently under construction.
1224 * See the longer comment above smb2sr_send_interim().
1225 */
1226 uint32_t
smb2sr_go_async_indefinite(smb_request_t * sr)1227 smb2sr_go_async_indefinite(smb_request_t *sr)
1228 {
1229 uint32_t saved_reply_hdr = sr->smb2_reply_hdr;
1230 uint32_t status;
1231
1232 if (sr->smb2_async_mode) {
1233 /* cmd is already being processed async */
1234 return (NT_STATUS_SUCCESS);
1235 }
1236 sr->smb2_async_mode = B_TRUE;
1237
1238 /* The "server" session always runs async. */
1239 if (sr->session->sock == NULL)
1240 return (NT_STATUS_SUCCESS);
1241
1242 /* Rewind reply to start of header */
1243 sr->reply.chain_offset = saved_reply_hdr;
1244
1245 status = smb2sr_send_interim(sr);
1246 if (status != NT_STATUS_SUCCESS)
1247 return (status);
1248
1249 /* We've now consumed the reply; re-initialize it. */
1250 MBC_FLUSH(&sr->reply);
1251 ASSERT(sr->reply.max_bytes == sr->session->reply_max_bytes);
1252 ASSERT(sr->reply.chain_offset == 0);
1253 sr->smb2_reply_hdr = 0;
1254 (void) smb2_encode_header(sr, B_FALSE);
1255
1256 return (NT_STATUS_SUCCESS);
1257 }
1258
1259 int
smb2_decode_header(smb_request_t * sr)1260 smb2_decode_header(smb_request_t *sr)
1261 {
1262 uint32_t pid, tid;
1263 uint16_t hdr_len;
1264 int rc;
1265
1266 rc = smb_mbc_decodef(
1267 &sr->command, "Nwww..wwllqllq16c",
1268 &hdr_len, /* w */
1269 &sr->smb2_credit_charge, /* w */
1270 &sr->smb2_chan_seq, /* w */
1271 /* reserved .. */
1272 &sr->smb2_cmd_code, /* w */
1273 &sr->smb2_credit_request, /* w */
1274 &sr->smb2_hdr_flags, /* l */
1275 &sr->smb2_next_command, /* l */
1276 &sr->smb2_messageid, /* q */
1277 &pid, /* l */
1278 &tid, /* l */
1279 &sr->smb2_ssnid, /* q */
1280 sr->smb2_sig); /* 16c */
1281 if (rc)
1282 return (rc);
1283
1284 if (hdr_len != SMB2_HDR_SIZE)
1285 return (-1);
1286
1287 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1288 sr->smb2_async_id = pid |
1289 ((uint64_t)tid) << 32;
1290 sr->smb_pid = 0;
1291 sr->smb_tid = 0;
1292 } else {
1293 sr->smb2_async_id = 0;
1294 sr->smb_pid = pid;
1295 sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */
1296 }
1297
1298 return (rc);
1299 }
1300
1301 int
smb2_encode_header(smb_request_t * sr,boolean_t overwrite)1302 smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
1303 {
1304 uint64_t pid_tid_aid; /* pid+tid, or async id */
1305 int rc;
1306
1307 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1308 pid_tid_aid = sr->smb2_async_id;
1309 } else {
1310 pid_tid_aid = sr->smb_pid |
1311 ((uint64_t)sr->smb_tid) << 32;
1312 }
1313
1314 if (overwrite) {
1315 rc = smb_mbc_poke(&sr->reply,
1316 sr->smb2_reply_hdr,
1317 "Nwwlwwllqqq16c",
1318 SMB2_HDR_SIZE, /* w */
1319 sr->smb2_credit_charge, /* w */
1320 sr->smb2_status, /* l */
1321 sr->smb2_cmd_code, /* w */
1322 sr->smb2_credit_response, /* w */
1323 sr->smb2_hdr_flags, /* l */
1324 sr->smb2_next_reply, /* l */
1325 sr->smb2_messageid, /* q */
1326 pid_tid_aid, /* q */
1327 sr->smb2_ssnid, /* q */
1328 sr->smb2_sig); /* 16c */
1329 } else {
1330 rc = smb_mbc_encodef(&sr->reply,
1331 "Nwwlwwllqqq16c",
1332 SMB2_HDR_SIZE, /* w */
1333 sr->smb2_credit_charge, /* w */
1334 sr->smb2_status, /* l */
1335 sr->smb2_cmd_code, /* w */
1336 sr->smb2_credit_response, /* w */
1337 sr->smb2_hdr_flags, /* l */
1338 sr->smb2_next_reply, /* l */
1339 sr->smb2_messageid, /* q */
1340 pid_tid_aid, /* q */
1341 sr->smb2_ssnid, /* q */
1342 sr->smb2_sig); /* 16c */
1343 }
1344
1345 return (rc);
1346 }
1347
1348 void
smb2_send_reply(smb_request_t * sr)1349 smb2_send_reply(smb_request_t *sr)
1350 {
1351 struct mbuf_chain enc_reply;
1352 smb_session_t *session = sr->session;
1353 mbuf_t *m;
1354
1355 /*
1356 * [MS-SMB2] 3.3.4.1.4 Encrypting the Message
1357 *
1358 * When the connection supports encryption and the dialect
1359 * is 3.x, encrypt if:
1360 * - The request was encrypted OR
1361 * - The cmd is not SESSION_SETUP or NEGOTIATE AND
1362 * -- Session.EncryptData is TRUE OR
1363 * -- The cmd is not TREE_CONNECT AND
1364 * --- Tree.EncryptData is TRUE
1365 *
1366 * This boils down to sr->th_sid_user != NULL, and the rest
1367 * is enforced when th_sid_user is set.
1368 */
1369
1370 if ((session->capabilities & SMB2_CAP_ENCRYPTION) == 0 ||
1371 sr->th_sid_user == NULL) {
1372 (void) smb_session_send(sr->session, 0, &sr->reply);
1373 return;
1374 }
1375
1376 /*
1377 * Encrypted send
1378 *
1379 * Not doing in-place encryption because we may have
1380 * loaned buffers (eg. from ZFS) that are read-only.
1381 *
1382 * Setup the transform header in its own mblk,
1383 * with leading space for the netbios header.
1384 */
1385 MBC_INIT(&enc_reply, SMB3_TFORM_HDR_SIZE);
1386 m = enc_reply.chain;
1387 m->m_len = SMB3_TFORM_HDR_SIZE;
1388
1389 sr->th_msglen = sr->reply.chain_offset;
1390 m->m_next = smb_mbuf_alloc_chain(sr->th_msglen);
1391 enc_reply.max_bytes += sr->th_msglen;
1392
1393 if (smb3_encrypt_sr(sr, &sr->reply, &enc_reply) != 0) {
1394 cmn_err(CE_WARN, "smb3 encryption failed");
1395 smb_session_disconnect(sr->session);
1396 } else {
1397 (void) smb_session_send(sr->session, 0, &enc_reply);
1398 }
1399 MBC_FLUSH(&enc_reply);
1400 }
1401
1402 /*
1403 * This wrapper function exists to help catch calls to smbsr_status()
1404 * (which is SMB1-specific) in common code. See smbsr_status().
1405 * If the log message below is seen, put a dtrace probe on this
1406 * function with a stack() action to see who is calling the SMB1
1407 * "put error" from common code, and fix it.
1408 */
1409 void
smbsr_status_smb2(smb_request_t * sr,DWORD status)1410 smbsr_status_smb2(smb_request_t *sr, DWORD status)
1411 {
1412 const char *name;
1413
1414 if (sr->smb2_cmd_code < SMB2__NCMDS)
1415 name = smb2_disp_table[sr->smb2_cmd_code].sdt_name;
1416 else
1417 name = "<unknown>";
1418 #ifdef DEBUG
1419 cmn_err(CE_NOTE, "smbsr_status called for %s", name);
1420 #endif
1421
1422 smb2sr_put_error_data(sr, status, NULL);
1423 }
1424
1425 void
smb2sr_put_errno(struct smb_request * sr,int errnum)1426 smb2sr_put_errno(struct smb_request *sr, int errnum)
1427 {
1428 uint32_t status = smb_errno2status(errnum);
1429 smb2sr_put_error_data(sr, status, NULL);
1430 }
1431
1432 void
smb2sr_put_error(smb_request_t * sr,uint32_t status)1433 smb2sr_put_error(smb_request_t *sr, uint32_t status)
1434 {
1435 smb2sr_put_error_data(sr, status, NULL);
1436 }
1437
1438 /*
1439 * Build an SMB2 error response. [MS-SMB2] 2.2.2
1440 */
1441 void
smb2sr_put_error_data(smb_request_t * sr,uint32_t status,mbuf_chain_t * mbc)1442 smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
1443 {
1444 DWORD len;
1445
1446 /*
1447 * The common dispatch code writes this when it
1448 * updates the SMB2 header before sending.
1449 */
1450 sr->smb2_status = status;
1451
1452 /* Rewind to the end of the SMB header. */
1453 sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
1454
1455 /*
1456 * NB: Must provide at least one byte of error data,
1457 * per [MS-SMB2] 2.2.2
1458 */
1459 if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
1460 (void) smb_mbc_encodef(
1461 &sr->reply,
1462 "wwlC",
1463 9, /* StructSize */ /* w */
1464 0, /* reserved */ /* w */
1465 len, /* l */
1466 mbc); /* C */
1467 } else {
1468 (void) smb_mbc_encodef(
1469 &sr->reply,
1470 "wwl.",
1471 9, /* StructSize */ /* w */
1472 0, /* reserved */ /* w */
1473 0); /* l. */
1474 }
1475 }
1476
1477 /*
1478 * Build an SMB2 error context response (dialect 3.1.1).
1479 */
1480 void
smb2sr_put_error_ctx(smb_request_t * sr,uint32_t status,uint32_t errid,mbuf_chain_t * mbc)1481 smb2sr_put_error_ctx(smb_request_t *sr, uint32_t status, uint32_t errid,
1482 mbuf_chain_t *mbc)
1483 {
1484 DWORD len;
1485
1486 /*
1487 * The common dispatch code writes this when it
1488 * updates the SMB2 header before sending.
1489 */
1490 sr->smb2_status = status;
1491
1492 /* Rewind to the end of the SMB header. */
1493 sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
1494
1495 /*
1496 * Error Context is 8-byte header plus encaps. data (ErrorContextData),
1497 * which can be zero-length.
1498 */
1499 if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
1500 (void) smb_mbc_encodef(
1501 &sr->reply,
1502 "wbblllC",
1503 9, /* StructSize */ /* w */
1504 1, /* ErrorContextCount */ /* b */
1505 0, /* reserved */ /* b */
1506 8+len, /* ByteCount */ /* l */
1507 len, /* ErrorDataLength */ /* l */
1508 errid, /* ErrorId */ /* l */
1509 mbc); /* C */
1510 } else {
1511 (void) smb_mbc_encodef(
1512 &sr->reply,
1513 "wbblll",
1514 9, /* StructSize */ /* w */
1515 1, /* ErrorContextCount */ /* b */
1516 0, /* reserved */ /* b */
1517 8, /* ByteCount */ /* l */
1518 0, /* ErrorDataLength */ /* l */
1519 errid); /* ErrorId */ /* l */
1520 }
1521 }
1522
1523 /*
1524 * Build an SMB2 error context response with SMB2_ERROR_ID_DEFAULT ErrorId.
1525 *
1526 * This only handles the case we currently need, encapsulating a
1527 * single error data section inside an SMB2_ERROR_ID_DEFAULT
1528 * error context type (which is type zero, and that's what
1529 * the zero on the end of this function name refers to).
1530 */
1531 void
smb2sr_put_error_ctx0(smb_request_t * sr,uint32_t status,mbuf_chain_t * mbc)1532 smb2sr_put_error_ctx0(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
1533 {
1534 return (smb2sr_put_error_ctx(sr, status, SMB2_ERROR_ID_DEFAULT, mbc));
1535 }
1536
1537 /*
1538 * smb2sr_lookup_fid
1539 *
1540 * Setup sr->fid_ofile, either inherited from a related command,
1541 * or obtained via FID lookup. Similar inheritance logic as in
1542 * smb2sr_work.
1543 */
1544 uint32_t
smb2sr_lookup_fid(smb_request_t * sr,smb2fid_t * fid)1545 smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid)
1546 {
1547 boolean_t related = sr->smb2_hdr_flags &
1548 SMB2_FLAGS_RELATED_OPERATIONS;
1549
1550 if (related) {
1551 if (sr->fid_ofile == NULL)
1552 return (NT_STATUS_INVALID_PARAMETER);
1553 sr->smb_fid = sr->fid_ofile->f_fid;
1554 return (0);
1555 }
1556
1557 /*
1558 * If we could be sure this is called only once per cmd,
1559 * we could simply ASSERT(sr->fid_ofile == NULL) here.
1560 * However, there are cases where it can be called again
1561 * handling the same command, so let's tolerate that.
1562 */
1563 if (sr->fid_ofile == NULL) {
1564 sr->smb_fid = (uint16_t)fid->temporal;
1565 sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
1566 }
1567 if (sr->fid_ofile == NULL ||
1568 sr->fid_ofile->f_persistid != fid->persistent)
1569 return (NT_STATUS_FILE_CLOSED);
1570
1571 return (0);
1572 }
1573
1574 /*
1575 * smb2_dispatch_stats_init
1576 *
1577 * Initializes dispatch statistics for SMB2.
1578 * See also smb_dispatch_stats_init(), which fills in
1579 * the lower part of the statistics array, from zero
1580 * through SMB_COM_NUM;
1581 */
1582 void
smb2_dispatch_stats_init(smb_server_t * sv)1583 smb2_dispatch_stats_init(smb_server_t *sv)
1584 {
1585 smb_disp_stats_t *sds = sv->sv_disp_stats2;
1586 smb_kstat_req_t *ksr;
1587 int i;
1588
1589 ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2;
1590
1591 for (i = 0; i < SMB2__NCMDS; i++, ksr++) {
1592 smb_latency_init(&sds[i].sdt_lat);
1593 (void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name,
1594 sizeof (ksr->kr_name));
1595 }
1596 }
1597
1598 /*
1599 * smb2_dispatch_stats_fini
1600 *
1601 * Frees and destroyes the resources used for statistics.
1602 */
1603 void
smb2_dispatch_stats_fini(smb_server_t * sv)1604 smb2_dispatch_stats_fini(smb_server_t *sv)
1605 {
1606 smb_disp_stats_t *sds = sv->sv_disp_stats2;
1607 int i;
1608
1609 for (i = 0; i < SMB2__NCMDS; i++)
1610 smb_latency_destroy(&sds[i].sdt_lat);
1611 }
1612
1613 void
smb2_dispatch_stats_update(smb_server_t * sv,smb_kstat_req_t * ksr,int first,int nreq)1614 smb2_dispatch_stats_update(smb_server_t *sv,
1615 smb_kstat_req_t *ksr, int first, int nreq)
1616 {
1617 smb_disp_stats_t *sds = sv->sv_disp_stats2;
1618 int i;
1619 int last;
1620
1621 last = first + nreq - 1;
1622
1623 if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS)) {
1624 for (i = first; i <= last; i++, ksr++) {
1625 ksr->kr_rxb = sds[i].sdt_rxb;
1626 ksr->kr_txb = sds[i].sdt_txb;
1627 mutex_enter(&sds[i].sdt_lat.ly_mutex);
1628 ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq;
1629 ksr->kr_sum = sds[i].sdt_lat.ly_a_sum;
1630 ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean;
1631 ksr->kr_a_stddev =
1632 sds[i].sdt_lat.ly_a_stddev;
1633 ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean;
1634 ksr->kr_d_stddev =
1635 sds[i].sdt_lat.ly_d_stddev;
1636 sds[i].sdt_lat.ly_d_mean = 0;
1637 sds[i].sdt_lat.ly_d_nreq = 0;
1638 sds[i].sdt_lat.ly_d_stddev = 0;
1639 sds[i].sdt_lat.ly_d_sum = 0;
1640 mutex_exit(&sds[i].sdt_lat.ly_mutex);
1641 }
1642 }
1643 }
1644
1645 /*
1646 * Append new_sr to the postwork queue. sr->smb2_cmd_code encodes
1647 * the action that should be run by this sr.
1648 *
1649 * This queue is rarely used (and normally empty) so we're OK
1650 * using a simple "walk to tail and insert" here.
1651 */
1652 void
smb2sr_append_postwork(smb_request_t * top_sr,smb_request_t * new_sr)1653 smb2sr_append_postwork(smb_request_t *top_sr, smb_request_t *new_sr)
1654 {
1655 smb_request_t *last_sr;
1656
1657 ASSERT(top_sr->session->dialect >= SMB_VERS_2_BASE);
1658
1659 last_sr = top_sr;
1660 while (last_sr->sr_postwork != NULL)
1661 last_sr = last_sr->sr_postwork;
1662
1663 last_sr->sr_postwork = new_sr;
1664 }
1665
1666 /*
1667 * Run any "post work" that was appended to the main SR while it
1668 * was running. This is called after the request has been sent
1669 * for the main SR, and used in cases i.e. the oplock code, where
1670 * we need to send something to the client only _after_ the main
1671 * sr request has gone out.
1672 */
1673 static void
smb2sr_run_postwork(smb_request_t * top_sr)1674 smb2sr_run_postwork(smb_request_t *top_sr)
1675 {
1676 smb_request_t *post_sr; /* the one we're running */
1677 smb_request_t *next_sr;
1678
1679 while ((post_sr = top_sr->sr_postwork) != NULL) {
1680 next_sr = post_sr->sr_postwork;
1681 top_sr->sr_postwork = next_sr;
1682 post_sr->sr_postwork = NULL;
1683
1684 post_sr->sr_worker = top_sr->sr_worker;
1685 post_sr->sr_state = SMB_REQ_STATE_ACTIVE;
1686
1687 switch (post_sr->smb2_cmd_code) {
1688 case SMB2_OPLOCK_BREAK:
1689 smb_oplock_send_break(post_sr);
1690 break;
1691 default:
1692 ASSERT(0);
1693 }
1694
1695 /*
1696 * If we have a durable handle, and this operation
1697 * updated the nvlist, write it out.
1698 */
1699 if (post_sr->dh_nvl_dirty) {
1700 post_sr->dh_nvl_dirty = B_FALSE;
1701 smb2_dh_update_nvfile(post_sr);
1702 }
1703
1704 post_sr->sr_state = SMB_REQ_STATE_COMPLETED;
1705 smb_request_free(post_sr);
1706 }
1707 }
1708