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 2022 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 static inline void
smb2_record_stats(smb_request_t * sr,smb_disp_stats_t * sds,boolean_t tx_only)428 smb2_record_stats(smb_request_t *sr, smb_disp_stats_t *sds, boolean_t tx_only)
429 {
430 hrtime_t dt;
431 int64_t rxb;
432 int64_t txb;
433
434 dt = gethrtime() - sr->sr_time_start;
435 rxb = (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr);
436 txb = (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr);
437
438 if (!tx_only) {
439 smb_server_inc_req(sr->sr_server);
440 smb_latency_add_sample(&sds->sdt_lat, dt);
441 atomic_add_64(&sds->sdt_rxb, rxb);
442 }
443 atomic_add_64(&sds->sdt_txb, txb);
444 }
445
446 /*
447 * smb2sr_work
448 *
449 * This function processes each SMB command in the current request
450 * (which may be a compound request) building a reply containing
451 * SMB reply messages, one-to-one with the SMB commands. Some SMB
452 * commands (change notify, blocking locks) may require both an
453 * "interim response" and a later "async response" at completion.
454 * In such cases, we'll encode the interim response in the reply
455 * compound we're building, and put the (now async) command on a
456 * list of commands that need further processing. After we've
457 * finished processing the commands in this compound and building
458 * the compound reply, we'll send the compound reply, and finally
459 * process the list of async commands.
460 *
461 * As we work our way through the compound request and reply,
462 * we need to keep track of the bounds of the current request
463 * and reply. For the request, this uses an MBC_SHADOW_CHAIN
464 * that begins at smb2_cmd_hdr. The reply is appended to the
465 * sr->reply chain starting at smb2_reply_hdr.
466 *
467 * This function must always free the smb request, or arrange
468 * for it to be completed and free'd later (if SDRC_SR_KEPT).
469 */
470 void
smb2sr_work(struct smb_request * sr)471 smb2sr_work(struct smb_request *sr)
472 {
473 const smb_disp_entry_t *sdd;
474 smb_disp_stats_t *sds;
475 smb_session_t *session;
476 uint32_t msg_len;
477 uint16_t cmd_idx;
478 int rc = 0;
479 boolean_t disconnect = B_FALSE;
480 boolean_t related;
481
482 session = sr->session;
483
484 ASSERT(sr->smb2_async == B_FALSE);
485 ASSERT(sr->tid_tree == 0);
486 ASSERT(sr->uid_user == 0);
487 ASSERT(sr->fid_ofile == 0);
488 sr->smb_fid = (uint16_t)-1;
489 sr->smb2_status = 0;
490
491 /* temporary until we identify a user */
492 sr->user_cr = zone_kcred();
493
494 cmd_start:
495 /*
496 * Note that we don't check sr_state here and abort the
497 * compound if cancelled (etc.) because some SMB2 command
498 * handlers need to do work even when cancelled.
499 *
500 * We treat some status codes as if "sticky", meaning
501 * once they're set after some command handler returns,
502 * all remaining commands get this status without even
503 * calling the command-specific handler.
504 */
505 if (sr->smb2_status != NT_STATUS_CANCELLED &&
506 sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES)
507 sr->smb2_status = 0;
508
509 /*
510 * Decode the request header
511 *
512 * Most problems with decoding will result in the error
513 * STATUS_INVALID_PARAMETER. If the decoding problem
514 * prevents continuing, we'll close the connection.
515 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
516 */
517 sr->smb2_cmd_hdr = sr->command.chain_offset;
518 if ((rc = smb2_decode_header(sr)) != 0) {
519 cmn_err(CE_WARN, "clnt %s bad SMB2 header",
520 session->ip_addr_str);
521 disconnect = B_TRUE;
522 goto cleanup;
523 }
524
525 /*
526 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear
527 * in messages from the server back to the client.
528 */
529 if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) {
530 cmn_err(CE_WARN, "clnt %s bad SMB2 flags",
531 session->ip_addr_str);
532 disconnect = B_TRUE;
533 goto cleanup;
534 }
535 related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS);
536 sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR;
537 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
538 /* Probably an async cancel. */
539 DTRACE_PROBE1(smb2__dispatch__async, smb_request_t *, sr);
540 } else if (sr->smb2_async) {
541 /* Previous command in compound went async. */
542 sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
543 sr->smb2_async_id = SMB2_ASYNCID(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 we've gone async, credit adjustments were done
881 * when we sent the interim reply.
882 */
883 if (!sr->smb2_async) {
884 if (sr->smb2_credit_request < sr->smb2_credit_charge) {
885 smb2_credit_decrease(sr);
886 }
887 }
888
889 /*
890 * The real work: call the SMB2 command handler
891 * (except for "sticky" smb2_status - see above)
892 */
893 sr->sr_time_start = gethrtime();
894 rc = SDRC_SUCCESS;
895 if (sr->smb2_status == 0) {
896 /* NB: not using pre_op */
897 rc = (*sdd->sdt_function)(sr);
898 /* NB: not using post_op */
899 } else {
900 smb2sr_put_error(sr, sr->smb2_status);
901 }
902
903 /*
904 * When the sdt_function returns SDRC_SR_KEPT, it means
905 * this SR may have been passed to another thread so we
906 * MUST NOT touch it anymore.
907 */
908 if (rc == SDRC_SR_KEPT)
909 return;
910
911 MBC_FLUSH(&sr->raw_data);
912
913 /*
914 * Credit adjustments (increase)
915 */
916 if (!sr->smb2_async) {
917 if (sr->smb2_credit_request > sr->smb2_credit_charge) {
918 smb2_credit_increase(sr);
919 }
920 }
921
922 cmd_done:
923 switch (rc) {
924 case SDRC_SUCCESS:
925 break;
926 default:
927 /*
928 * SMB2 does not use the other dispatch return codes.
929 * If we see something else, log an event so we'll
930 * know something is returning bogus status codes.
931 * If you see these in the log, use dtrace to find
932 * the code returning something else.
933 */
934 #ifdef DEBUG
935 cmn_err(CE_NOTE, "handler for %u returned 0x%x",
936 sr->smb2_cmd_code, rc);
937 #endif
938 smb2sr_put_error(sr, NT_STATUS_INTERNAL_ERROR);
939 break;
940 case SDRC_ERROR:
941 /*
942 * Many command handlers return SDRC_ERROR for any
943 * problems decoding the request, and don't bother
944 * setting smb2_status. For those cases, the best
945 * status return would be "invalid parameter".
946 */
947 if (sr->smb2_status == 0)
948 sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
949 smb2sr_put_error(sr, sr->smb2_status);
950 break;
951 case SDRC_DROP_VC:
952 disconnect = B_TRUE;
953 goto cleanup;
954
955 case SDRC_NO_REPLY:
956 /* will free sr */
957 goto cleanup;
958 }
959
960 /*
961 * Pad the reply to align(8) if there will be another.
962 * (We don't compound async replies.)
963 */
964 if (!sr->smb2_async && sr->smb2_next_command != 0)
965 (void) smb_mbc_put_align(&sr->reply, 8);
966
967 /*
968 * Record some statistics. Uses:
969 * rxb = command.chain_offset - smb2_cmd_hdr;
970 * txb = reply.chain_offset - smb2_reply_hdr;
971 * which at this point represent the current cmd/reply.
972 *
973 * Note: If async, this does txb only, and
974 * skips the smb_latency_add_sample() calls.
975 */
976 smb2_record_stats(sr, sds, sr->smb2_async);
977
978 /*
979 * If there's a next command, figure out where it starts,
980 * and fill in the next header offset for the reply.
981 * Note: We sanity checked smb2_next_command above.
982 * Once we've gone async, replies are not compounded.
983 */
984 if (sr->smb2_next_command != 0) {
985 sr->command.chain_offset =
986 sr->smb2_cmd_hdr + sr->smb2_next_command;
987 }
988 if (sr->smb2_next_command != 0 && !sr->smb2_async) {
989 sr->smb2_next_reply =
990 sr->reply.chain_offset - sr->smb2_reply_hdr;
991 } else {
992 ASSERT(sr->smb2_next_reply == 0);
993 }
994
995 /*
996 * Overwrite the (now final) SMB2 header for this response.
997 */
998 (void) smb2_encode_header(sr, B_TRUE);
999
1000 /*
1001 * Cannot move this into smb2_session_setup() - encoded header required.
1002 */
1003 if (session->dialect >= SMB_VERS_3_11 &&
1004 sr->smb2_cmd_code == SMB2_SESSION_SETUP &&
1005 sr->smb2_status == NT_STATUS_MORE_PROCESSING_REQUIRED) {
1006 if (smb31_preauth_sha512_calc(sr, &sr->reply,
1007 sr->uid_user->u_preauth_hashval,
1008 sr->uid_user->u_preauth_hashval) != 0)
1009 cmn_err(CE_WARN, "(3) Preauth hash calculation "
1010 "failed");
1011 }
1012
1013 /* Don't sign if we're going to encrypt */
1014 if (sr->th_sid_user == NULL &&
1015 (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) != 0)
1016 smb2_sign_reply(sr);
1017
1018 /*
1019 * Non-async runs the whole compound before send.
1020 * When we've gone async, send each individually.
1021 */
1022 if (!sr->smb2_async && sr->smb2_next_command != 0)
1023 goto cmd_start;
1024
1025 /*
1026 * If we have a durable handle, and this operation updated
1027 * the nvlist, write it out (before smb2_send_reply).
1028 */
1029 if (sr->dh_nvl_dirty) {
1030 sr->dh_nvl_dirty = B_FALSE;
1031 smb2_dh_update_nvfile(sr);
1032 }
1033
1034 smb2_send_reply(sr);
1035 if (sr->smb2_async && sr->smb2_next_command != 0) {
1036 MBC_FLUSH(&sr->reply); /* New reply buffer. */
1037 ASSERT(sr->reply.max_bytes == sr->session->reply_max_bytes);
1038 goto cmd_start;
1039 }
1040
1041 cleanup:
1042 if (disconnect)
1043 smb_session_disconnect(session);
1044
1045 /*
1046 * Do "postwork" for oplock (and maybe other things)
1047 */
1048 if (sr->sr_postwork != NULL)
1049 smb2sr_run_postwork(sr);
1050
1051 mutex_enter(&sr->sr_mutex);
1052 sr->sr_state = SMB_REQ_STATE_COMPLETED;
1053 mutex_exit(&sr->sr_mutex);
1054
1055 smb_request_free(sr);
1056 }
1057
1058 /*
1059 * Build interim responses for the current and all following
1060 * requests in this compound, then send the compound response,
1061 * leaving the SR state so that smb2sr_work() can continue its
1062 * processing of this compound in "async mode".
1063 *
1064 * If we agree to "go async", this should return STATUS_SUCCESS.
1065 * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and
1066 * all requests following this request. (See the comments re.
1067 * "sticky" smb2_status values in smb2sr_work).
1068 *
1069 * Note: the Async ID we assign here is arbitrary, and need only
1070 * be unique among pending async responses on this connection, so
1071 * this just uses a modified messageID, which is already unique.
1072 *
1073 * Credits: All credit changes should happen via the interim
1074 * responses, so we have to manage credits here. After this
1075 * returns to smb2sr_work, the final replies for all these
1076 * commands will have smb2_credit_response = smb2_credit_charge
1077 * (meaning no further changes to the clients' credits).
1078 */
1079 uint32_t
smb2sr_go_async(smb_request_t * sr)1080 smb2sr_go_async(smb_request_t *sr)
1081 {
1082 smb_session_t *session;
1083 smb_disp_stats_t *sds;
1084 uint16_t cmd_idx;
1085 int32_t saved_com_offset;
1086 uint32_t saved_cmd_hdr;
1087 uint16_t saved_cred_resp;
1088 uint32_t saved_hdr_flags;
1089 uint32_t saved_reply_hdr;
1090 uint32_t msg_len;
1091 boolean_t disconnect = B_FALSE;
1092
1093 if (sr->smb2_async) {
1094 /* already went async in some previous cmd. */
1095 return (NT_STATUS_SUCCESS);
1096 }
1097 sr->smb2_async = B_TRUE;
1098
1099 /* The "server" session always runs async. */
1100 session = sr->session;
1101 if (session->sock == NULL)
1102 return (NT_STATUS_SUCCESS);
1103
1104 sds = NULL;
1105 saved_com_offset = sr->command.chain_offset;
1106 saved_cmd_hdr = sr->smb2_cmd_hdr;
1107 saved_cred_resp = sr->smb2_credit_response;
1108 saved_hdr_flags = sr->smb2_hdr_flags;
1109 saved_reply_hdr = sr->smb2_reply_hdr;
1110
1111 /*
1112 * The command-specific handler should not yet have put any
1113 * data in the reply except for the (place holder) header.
1114 */
1115 if (sr->reply.chain_offset != sr->smb2_reply_hdr + SMB2_HDR_SIZE) {
1116 ASSERT3U(sr->reply.chain_offset, ==,
1117 sr->smb2_reply_hdr + SMB2_HDR_SIZE);
1118 return (NT_STATUS_INTERNAL_ERROR);
1119 }
1120
1121 /*
1122 * Rewind to the start of the current header in both the
1123 * command and reply bufers, so the loop below can just
1124 * decode/encode just in every pass. This means the
1125 * current command header is decoded again, but that
1126 * avoids having to special-case the first loop pass.
1127 */
1128 sr->command.chain_offset = sr->smb2_cmd_hdr;
1129 sr->reply.chain_offset = sr->smb2_reply_hdr;
1130
1131 /*
1132 * This command processing loop is a simplified version of
1133 * smb2sr_work() that just puts an "interim response" for
1134 * every command in the compound (NT_STATUS_PENDING).
1135 */
1136 cmd_start:
1137 sr->smb2_status = NT_STATUS_PENDING;
1138
1139 /*
1140 * Decode the request header
1141 */
1142 sr->smb2_cmd_hdr = sr->command.chain_offset;
1143 if ((smb2_decode_header(sr)) != 0) {
1144 cmn_err(CE_WARN, "clnt %s bad SMB2 header",
1145 session->ip_addr_str);
1146 disconnect = B_TRUE;
1147 goto cleanup;
1148 }
1149 sr->smb2_hdr_flags |= (SMB2_FLAGS_SERVER_TO_REDIR |
1150 SMB2_FLAGS_ASYNC_COMMAND);
1151 sr->smb2_async_id = SMB2_ASYNCID(sr);
1152
1153 /*
1154 * In case we bail out...
1155 */
1156 if (sr->smb2_credit_charge == 0)
1157 sr->smb2_credit_charge = 1;
1158 sr->smb2_credit_response = sr->smb2_credit_charge;
1159
1160 /*
1161 * Write a tentative reply header.
1162 */
1163 sr->smb2_next_reply = 0;
1164 ASSERT((sr->reply.chain_offset & 7) == 0);
1165 sr->smb2_reply_hdr = sr->reply.chain_offset;
1166 if ((smb2_encode_header(sr, B_FALSE)) != 0) {
1167 cmn_err(CE_WARN, "clnt %s excessive reply",
1168 session->ip_addr_str);
1169 disconnect = B_TRUE;
1170 goto cleanup;
1171 }
1172
1173 /*
1174 * Figure out the length of data...
1175 */
1176 if (sr->smb2_next_command != 0) {
1177 /* [MS-SMB2] says this is 8-byte aligned */
1178 msg_len = sr->smb2_next_command;
1179 if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
1180 ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
1181 cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
1182 session->ip_addr_str);
1183 disconnect = B_TRUE;
1184 goto cleanup;
1185 }
1186 } else {
1187 msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
1188 }
1189
1190 /*
1191 * We just skip any data, so no shadow chain etc.
1192 */
1193 sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len;
1194 ASSERT(sr->command.chain_offset <= sr->command.max_bytes);
1195
1196 /*
1197 * Validate the commmand code...
1198 */
1199 if (sr->smb2_cmd_code < SMB2_INVALID_CMD)
1200 cmd_idx = sr->smb2_cmd_code;
1201 else
1202 cmd_idx = SMB2_INVALID_CMD;
1203 sds = &session->s_server->sv_disp_stats2[cmd_idx];
1204
1205 /*
1206 * Don't change (user, tree, file) because we want them
1207 * exactly as they were when we entered. That also means
1208 * we may not have the right user in sr->uid_user for
1209 * signature checks, so leave that until smb2sr_work
1210 * runs these commands "for real". Therefore, here
1211 * we behave as if: (sr->uid_user == NULL)
1212 */
1213 sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
1214
1215 /*
1216 * Credit adjustments (decrease)
1217 *
1218 * NOTE: interim responses are not signed.
1219 * Any attacker can modify the credit grant
1220 * in the response. Because of this property,
1221 * it is no worse to assume the credit charge and grant
1222 * are sane without verifying the signature,
1223 * and that saves us a whole lot of work.
1224 * If the credits WERE modified, we'll find out
1225 * when we verify the signature later,
1226 * which nullifies any changes caused here.
1227 *
1228 * Skip this on the first command, because the
1229 * credit decrease was done by the caller.
1230 */
1231 if (sr->smb2_cmd_hdr != saved_cmd_hdr) {
1232 if (sr->smb2_credit_request < sr->smb2_credit_charge) {
1233 smb2_credit_decrease(sr);
1234 }
1235 }
1236
1237 /*
1238 * The real work: ... (would be here)
1239 */
1240 smb2sr_put_error(sr, sr->smb2_status);
1241
1242 /*
1243 * Credit adjustments (increase)
1244 */
1245 if (sr->smb2_credit_request > sr->smb2_credit_charge) {
1246 smb2_credit_increase(sr);
1247 }
1248
1249 /* cmd_done: label */
1250
1251 /*
1252 * Pad the reply to align(8) if there will be another.
1253 * This (interim) reply uses compounding.
1254 */
1255 if (sr->smb2_next_command != 0)
1256 (void) smb_mbc_put_align(&sr->reply, 8);
1257
1258 /*
1259 * Record some statistics. Uses:
1260 * rxb = command.chain_offset - smb2_cmd_hdr;
1261 * txb = reply.chain_offset - smb2_reply_hdr;
1262 * which at this point represent the current cmd/reply.
1263 *
1264 * Note: We're doing smb_latency_add_sample() for all
1265 * remaining commands NOW, which means we won't include
1266 * the async part of their work in latency statistics.
1267 * That's intentional, as the async part of a command
1268 * would otherwise skew our latency statistics.
1269 */
1270 smb2_record_stats(sr, sds, B_FALSE);
1271
1272 /*
1273 * If there's a next command, figure out where it starts,
1274 * and fill in the next header offset for the reply.
1275 * Note: We sanity checked smb2_next_command above.
1276 */
1277 if (sr->smb2_next_command != 0) {
1278 sr->command.chain_offset =
1279 sr->smb2_cmd_hdr + sr->smb2_next_command;
1280 sr->smb2_next_reply =
1281 sr->reply.chain_offset - sr->smb2_reply_hdr;
1282 } else {
1283 ASSERT(sr->smb2_next_reply == 0);
1284 }
1285
1286 /*
1287 * Overwrite the (now final) SMB2 header for this response.
1288 */
1289 (void) smb2_encode_header(sr, B_TRUE);
1290
1291 /*
1292 * Process whole compound before sending.
1293 */
1294 if (sr->smb2_next_command != 0)
1295 goto cmd_start;
1296 smb2_send_reply(sr);
1297
1298 ASSERT(!disconnect);
1299
1300 cleanup:
1301 /*
1302 * Restore caller's command processing state.
1303 */
1304 sr->smb2_cmd_hdr = saved_cmd_hdr;
1305 sr->command.chain_offset = saved_cmd_hdr;
1306 (void) smb2_decode_header(sr);
1307 sr->command.chain_offset = saved_com_offset;
1308
1309 sr->smb2_credit_response = saved_cred_resp;
1310 sr->smb2_hdr_flags = saved_hdr_flags;
1311 sr->smb2_status = NT_STATUS_SUCCESS;
1312
1313 /*
1314 * In here, the "disconnect" flag just means we had an
1315 * error decoding or encoding something. Rather than
1316 * actually disconnect here, let's assume whatever
1317 * problem we encountered will be seen by the caller
1318 * as they continue processing the compound, and just
1319 * restore everything and return an error.
1320 */
1321 if (disconnect) {
1322 sr->smb2_async = B_FALSE;
1323 sr->smb2_reply_hdr = saved_reply_hdr;
1324 sr->reply.chain_offset = sr->smb2_reply_hdr;
1325 (void) smb2_encode_header(sr, B_FALSE);
1326 return (NT_STATUS_INVALID_PARAMETER);
1327 }
1328
1329 /*
1330 * The compound reply buffer we sent is now gone.
1331 * Setup a new reply buffer for the caller.
1332 */
1333 sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
1334 sr->smb2_async_id = SMB2_ASYNCID(sr);
1335 sr->smb2_next_reply = 0;
1336 MBC_FLUSH(&sr->reply);
1337 ASSERT(sr->reply.max_bytes == sr->session->reply_max_bytes);
1338 ASSERT(sr->reply.chain_offset == 0);
1339 sr->smb2_reply_hdr = 0;
1340 (void) smb2_encode_header(sr, B_FALSE);
1341
1342 return (NT_STATUS_SUCCESS);
1343 }
1344
1345 int
smb2_decode_header(smb_request_t * sr)1346 smb2_decode_header(smb_request_t *sr)
1347 {
1348 uint32_t pid, tid;
1349 uint16_t hdr_len;
1350 int rc;
1351
1352 rc = smb_mbc_decodef(
1353 &sr->command, "Nwww..wwllqllq16c",
1354 &hdr_len, /* w */
1355 &sr->smb2_credit_charge, /* w */
1356 &sr->smb2_chan_seq, /* w */
1357 /* reserved .. */
1358 &sr->smb2_cmd_code, /* w */
1359 &sr->smb2_credit_request, /* w */
1360 &sr->smb2_hdr_flags, /* l */
1361 &sr->smb2_next_command, /* l */
1362 &sr->smb2_messageid, /* q */
1363 &pid, /* l */
1364 &tid, /* l */
1365 &sr->smb2_ssnid, /* q */
1366 sr->smb2_sig); /* 16c */
1367 if (rc)
1368 return (rc);
1369
1370 if (hdr_len != SMB2_HDR_SIZE)
1371 return (-1);
1372
1373 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1374 sr->smb2_async_id = pid |
1375 ((uint64_t)tid) << 32;
1376 sr->smb_pid = 0;
1377 sr->smb_tid = 0;
1378 } else {
1379 sr->smb2_async_id = 0;
1380 sr->smb_pid = pid;
1381 sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */
1382 }
1383
1384 return (rc);
1385 }
1386
1387 int
smb2_encode_header(smb_request_t * sr,boolean_t overwrite)1388 smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
1389 {
1390 uint64_t pid_tid_aid; /* pid+tid, or async id */
1391 int rc;
1392
1393 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1394 pid_tid_aid = sr->smb2_async_id;
1395 } else {
1396 pid_tid_aid = sr->smb_pid |
1397 ((uint64_t)sr->smb_tid) << 32;
1398 }
1399
1400 if (overwrite) {
1401 rc = smb_mbc_poke(&sr->reply,
1402 sr->smb2_reply_hdr,
1403 "Nwwlwwllqqq16c",
1404 SMB2_HDR_SIZE, /* w */
1405 sr->smb2_credit_charge, /* w */
1406 sr->smb2_status, /* l */
1407 sr->smb2_cmd_code, /* w */
1408 sr->smb2_credit_response, /* w */
1409 sr->smb2_hdr_flags, /* l */
1410 sr->smb2_next_reply, /* l */
1411 sr->smb2_messageid, /* q */
1412 pid_tid_aid, /* q */
1413 sr->smb2_ssnid, /* q */
1414 sr->smb2_sig); /* 16c */
1415 } else {
1416 rc = smb_mbc_encodef(&sr->reply,
1417 "Nwwlwwllqqq16c",
1418 SMB2_HDR_SIZE, /* w */
1419 sr->smb2_credit_charge, /* w */
1420 sr->smb2_status, /* l */
1421 sr->smb2_cmd_code, /* w */
1422 sr->smb2_credit_response, /* w */
1423 sr->smb2_hdr_flags, /* l */
1424 sr->smb2_next_reply, /* l */
1425 sr->smb2_messageid, /* q */
1426 pid_tid_aid, /* q */
1427 sr->smb2_ssnid, /* q */
1428 sr->smb2_sig); /* 16c */
1429 }
1430
1431 return (rc);
1432 }
1433
1434 void
smb2_send_reply(smb_request_t * sr)1435 smb2_send_reply(smb_request_t *sr)
1436 {
1437 struct mbuf_chain enc_reply;
1438 smb_session_t *session = sr->session;
1439 mbuf_t *m;
1440
1441 /*
1442 * [MS-SMB2] 3.3.4.1.4 Encrypting the Message
1443 *
1444 * When the connection supports encryption and the dialect
1445 * is 3.x, encrypt if:
1446 * - The request was encrypted OR
1447 * - The cmd is not SESSION_SETUP or NEGOTIATE AND
1448 * -- Session.EncryptData is TRUE OR
1449 * -- The cmd is not TREE_CONNECT AND
1450 * --- Tree.EncryptData is TRUE
1451 *
1452 * This boils down to sr->th_sid_user != NULL, and the rest
1453 * is enforced when th_sid_user is set.
1454 */
1455
1456 if ((session->capabilities & SMB2_CAP_ENCRYPTION) == 0 ||
1457 sr->th_sid_user == NULL) {
1458 (void) smb_session_send(sr->session, 0, &sr->reply);
1459 return;
1460 }
1461
1462 /*
1463 * Encrypted send
1464 *
1465 * Not doing in-place encryption because we may have
1466 * loaned buffers (eg. from ZFS) that are read-only.
1467 *
1468 * Setup the transform header in its own mblk,
1469 * with leading space for the netbios header.
1470 */
1471 MBC_INIT(&enc_reply, SMB3_TFORM_HDR_SIZE);
1472 m = enc_reply.chain;
1473 m->m_len = SMB3_TFORM_HDR_SIZE;
1474
1475 sr->th_msglen = sr->reply.chain_offset;
1476 m->m_next = smb_mbuf_alloc_chain(sr->th_msglen);
1477 enc_reply.max_bytes += sr->th_msglen;
1478
1479 if (smb3_encrypt_sr(sr, &sr->reply, &enc_reply) != 0) {
1480 cmn_err(CE_WARN, "smb3 encryption failed");
1481 smb_session_disconnect(sr->session);
1482 } else {
1483 (void) smb_session_send(sr->session, 0, &enc_reply);
1484 }
1485 MBC_FLUSH(&enc_reply);
1486 }
1487
1488 /*
1489 * This wrapper function exists to help catch calls to smbsr_status()
1490 * (which is SMB1-specific) in common code. See smbsr_status().
1491 * If the log message below is seen, put a dtrace probe on this
1492 * function with a stack() action to see who is calling the SMB1
1493 * "put error" from common code, and fix it.
1494 */
1495 void
smbsr_status_smb2(smb_request_t * sr,DWORD status)1496 smbsr_status_smb2(smb_request_t *sr, DWORD status)
1497 {
1498 const char *name;
1499
1500 if (sr->smb2_cmd_code < SMB2__NCMDS)
1501 name = smb2_disp_table[sr->smb2_cmd_code].sdt_name;
1502 else
1503 name = "<unknown>";
1504 #ifdef DEBUG
1505 cmn_err(CE_NOTE, "smbsr_status called for %s", name);
1506 #endif
1507
1508 smb2sr_put_error_data(sr, status, NULL);
1509 }
1510
1511 void
smb2sr_put_errno(struct smb_request * sr,int errnum)1512 smb2sr_put_errno(struct smb_request *sr, int errnum)
1513 {
1514 uint32_t status = smb_errno2status(errnum);
1515 smb2sr_put_error_data(sr, status, NULL);
1516 }
1517
1518 void
smb2sr_put_error(smb_request_t * sr,uint32_t status)1519 smb2sr_put_error(smb_request_t *sr, uint32_t status)
1520 {
1521 smb2sr_put_error_data(sr, status, NULL);
1522 }
1523
1524 /*
1525 * Build an SMB2 error response. [MS-SMB2] 2.2.2
1526 */
1527 void
smb2sr_put_error_data(smb_request_t * sr,uint32_t status,mbuf_chain_t * mbc)1528 smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
1529 {
1530 DWORD len;
1531
1532 /*
1533 * The common dispatch code writes this when it
1534 * updates the SMB2 header before sending.
1535 */
1536 sr->smb2_status = status;
1537
1538 /* Rewind to the end of the SMB header. */
1539 sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
1540
1541 /*
1542 * NB: Must provide at least one byte of error data,
1543 * per [MS-SMB2] 2.2.2
1544 */
1545 if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
1546 (void) smb_mbc_encodef(
1547 &sr->reply,
1548 "wwlC",
1549 9, /* StructSize */ /* w */
1550 0, /* reserved */ /* w */
1551 len, /* l */
1552 mbc); /* C */
1553 } else {
1554 (void) smb_mbc_encodef(
1555 &sr->reply,
1556 "wwl.",
1557 9, /* StructSize */ /* w */
1558 0, /* reserved */ /* w */
1559 0); /* l. */
1560 }
1561 }
1562
1563 /*
1564 * Build an SMB2 error context response (dialect 3.1.1).
1565 */
1566 void
smb2sr_put_error_ctx(smb_request_t * sr,uint32_t status,uint32_t errid,mbuf_chain_t * mbc)1567 smb2sr_put_error_ctx(smb_request_t *sr, uint32_t status, uint32_t errid,
1568 mbuf_chain_t *mbc)
1569 {
1570 DWORD len;
1571
1572 /*
1573 * The common dispatch code writes this when it
1574 * updates the SMB2 header before sending.
1575 */
1576 sr->smb2_status = status;
1577
1578 /* Rewind to the end of the SMB header. */
1579 sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
1580
1581 /*
1582 * Error Context is 8-byte header plus encaps. data (ErrorContextData),
1583 * which can be zero-length.
1584 */
1585 if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
1586 (void) smb_mbc_encodef(
1587 &sr->reply,
1588 "wbblllC",
1589 9, /* StructSize */ /* w */
1590 1, /* ErrorContextCount */ /* b */
1591 0, /* reserved */ /* b */
1592 8+len, /* ByteCount */ /* l */
1593 len, /* ErrorDataLength */ /* l */
1594 errid, /* ErrorId */ /* l */
1595 mbc); /* C */
1596 } else {
1597 (void) smb_mbc_encodef(
1598 &sr->reply,
1599 "wbblll",
1600 9, /* StructSize */ /* w */
1601 1, /* ErrorContextCount */ /* b */
1602 0, /* reserved */ /* b */
1603 8, /* ByteCount */ /* l */
1604 0, /* ErrorDataLength */ /* l */
1605 errid); /* ErrorId */ /* l */
1606 }
1607 }
1608
1609 /*
1610 * Build an SMB2 error context response with SMB2_ERROR_ID_DEFAULT ErrorId.
1611 *
1612 * This only handles the case we currently need, encapsulating a
1613 * single error data section inside an SMB2_ERROR_ID_DEFAULT
1614 * error context type (which is type zero, and that's what
1615 * the zero on the end of this function name refers to).
1616 */
1617 void
smb2sr_put_error_ctx0(smb_request_t * sr,uint32_t status,mbuf_chain_t * mbc)1618 smb2sr_put_error_ctx0(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
1619 {
1620 return (smb2sr_put_error_ctx(sr, status, SMB2_ERROR_ID_DEFAULT, mbc));
1621 }
1622
1623 /*
1624 * smb2sr_lookup_fid
1625 *
1626 * Setup sr->fid_ofile, either inherited from a related command,
1627 * or obtained via FID lookup. Similar inheritance logic as in
1628 * smb2sr_work.
1629 */
1630 uint32_t
smb2sr_lookup_fid(smb_request_t * sr,smb2fid_t * fid)1631 smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid)
1632 {
1633 boolean_t related = sr->smb2_hdr_flags &
1634 SMB2_FLAGS_RELATED_OPERATIONS;
1635
1636 if (related) {
1637 if (sr->fid_ofile == NULL)
1638 return (NT_STATUS_INVALID_PARAMETER);
1639 sr->smb_fid = sr->fid_ofile->f_fid;
1640 return (0);
1641 }
1642
1643 /*
1644 * If we could be sure this is called only once per cmd,
1645 * we could simply ASSERT(sr->fid_ofile == NULL) here.
1646 * However, there are cases where it can be called again
1647 * handling the same command, so let's tolerate that.
1648 */
1649 if (sr->fid_ofile == NULL) {
1650 sr->smb_fid = (uint16_t)fid->temporal;
1651 sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
1652 }
1653 if (sr->fid_ofile == NULL ||
1654 sr->fid_ofile->f_persistid != fid->persistent)
1655 return (NT_STATUS_FILE_CLOSED);
1656
1657 return (0);
1658 }
1659
1660 /*
1661 * smb2_dispatch_stats_init
1662 *
1663 * Initializes dispatch statistics for SMB2.
1664 * See also smb_dispatch_stats_init(), which fills in
1665 * the lower part of the statistics array, from zero
1666 * through SMB_COM_NUM;
1667 */
1668 void
smb2_dispatch_stats_init(smb_server_t * sv)1669 smb2_dispatch_stats_init(smb_server_t *sv)
1670 {
1671 smb_disp_stats_t *sds = sv->sv_disp_stats2;
1672 smb_kstat_req_t *ksr;
1673 int i;
1674
1675 ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2;
1676
1677 for (i = 0; i < SMB2__NCMDS; i++, ksr++) {
1678 smb_latency_init(&sds[i].sdt_lat);
1679 (void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name,
1680 sizeof (ksr->kr_name));
1681 }
1682 }
1683
1684 /*
1685 * smb2_dispatch_stats_fini
1686 *
1687 * Frees and destroyes the resources used for statistics.
1688 */
1689 void
smb2_dispatch_stats_fini(smb_server_t * sv)1690 smb2_dispatch_stats_fini(smb_server_t *sv)
1691 {
1692 smb_disp_stats_t *sds = sv->sv_disp_stats2;
1693 int i;
1694
1695 for (i = 0; i < SMB2__NCMDS; i++)
1696 smb_latency_destroy(&sds[i].sdt_lat);
1697 }
1698
1699 void
smb2_dispatch_stats_update(smb_server_t * sv,smb_kstat_req_t * ksr,int first,int nreq)1700 smb2_dispatch_stats_update(smb_server_t *sv,
1701 smb_kstat_req_t *ksr, int first, int nreq)
1702 {
1703 smb_disp_stats_t *sds = sv->sv_disp_stats2;
1704 int i;
1705 int last;
1706
1707 last = first + nreq - 1;
1708
1709 if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS)) {
1710 for (i = first; i <= last; i++, ksr++) {
1711 ksr->kr_rxb = sds[i].sdt_rxb;
1712 ksr->kr_txb = sds[i].sdt_txb;
1713 mutex_enter(&sds[i].sdt_lat.ly_mutex);
1714 ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq;
1715 ksr->kr_sum = sds[i].sdt_lat.ly_a_sum;
1716 ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean;
1717 ksr->kr_a_stddev =
1718 sds[i].sdt_lat.ly_a_stddev;
1719 ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean;
1720 ksr->kr_d_stddev =
1721 sds[i].sdt_lat.ly_d_stddev;
1722 sds[i].sdt_lat.ly_d_mean = 0;
1723 sds[i].sdt_lat.ly_d_nreq = 0;
1724 sds[i].sdt_lat.ly_d_stddev = 0;
1725 sds[i].sdt_lat.ly_d_sum = 0;
1726 mutex_exit(&sds[i].sdt_lat.ly_mutex);
1727 }
1728 }
1729 }
1730
1731 /*
1732 * Append new_sr to the postwork queue. sr->smb2_cmd_code encodes
1733 * the action that should be run by this sr.
1734 *
1735 * This queue is rarely used (and normally empty) so we're OK
1736 * using a simple "walk to tail and insert" here.
1737 */
1738 void
smb2sr_append_postwork(smb_request_t * top_sr,smb_request_t * new_sr)1739 smb2sr_append_postwork(smb_request_t *top_sr, smb_request_t *new_sr)
1740 {
1741 smb_request_t *last_sr;
1742
1743 ASSERT(top_sr->session->dialect >= SMB_VERS_2_BASE);
1744
1745 last_sr = top_sr;
1746 while (last_sr->sr_postwork != NULL)
1747 last_sr = last_sr->sr_postwork;
1748
1749 last_sr->sr_postwork = new_sr;
1750 }
1751
1752 /*
1753 * Run any "post work" that was appended to the main SR while it
1754 * was running. This is called after the request has been sent
1755 * for the main SR, and used in cases i.e. the oplock code, where
1756 * we need to send something to the client only _after_ the main
1757 * sr request has gone out.
1758 */
1759 static void
smb2sr_run_postwork(smb_request_t * top_sr)1760 smb2sr_run_postwork(smb_request_t *top_sr)
1761 {
1762 smb_request_t *post_sr; /* the one we're running */
1763 smb_request_t *next_sr;
1764
1765 while ((post_sr = top_sr->sr_postwork) != NULL) {
1766 next_sr = post_sr->sr_postwork;
1767 top_sr->sr_postwork = next_sr;
1768 post_sr->sr_postwork = NULL;
1769
1770 post_sr->sr_worker = top_sr->sr_worker;
1771 post_sr->sr_state = SMB_REQ_STATE_ACTIVE;
1772
1773 switch (post_sr->smb2_cmd_code) {
1774 case SMB2_OPLOCK_BREAK:
1775 smb_oplock_send_break(post_sr);
1776 break;
1777 default:
1778 ASSERT(0);
1779 }
1780
1781 /*
1782 * If we have a durable handle, and this operation
1783 * updated the nvlist, write it out.
1784 */
1785 if (post_sr->dh_nvl_dirty) {
1786 post_sr->dh_nvl_dirty = B_FALSE;
1787 smb2_dh_update_nvfile(post_sr);
1788 }
1789
1790 post_sr->sr_state = SMB_REQ_STATE_COMPLETED;
1791 smb_request_free(post_sr);
1792 }
1793 }
1794