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 2016 Nexenta Systems, Inc. All rights reserved. 14 */ 15 16 17 #include <smbsrv/smb2_kproto.h> 18 #include <smbsrv/smb_kstat.h> 19 #include <smbsrv/smb2.h> 20 21 /* 22 * Saved state for a command that "goes async". When a compound request 23 * contains a command that may block indefinitely, the compound reply is 24 * composed with an "interim response" for that command, and information 25 * needed to actually dispatch that command is saved on a list of "async" 26 * commands for this compound request. After the compound reply is sent, 27 * the list of async commands is processed, and those may block as long 28 * as they need to without affecting the initial compound request. 29 * 30 * Now interestingly, this "async" mechanism is not used with the full 31 * range of asynchrony that one might imagine. The design of async 32 * request processing can be drastically simplified if we can assume 33 * that there's no need to run more than one async command at a time. 34 * With that simplifying assumption, we can continue using the current 35 * "one worker thread per request message" model, which has very simple 36 * locking rules etc. The same worker thread that handles the initial 37 * compound request can handle the list of async requests. 38 * 39 * As it turns out, SMB2 clients do not try to use more than one "async" 40 * command in a compound. If they were to do so, the [MS-SMB2] spec. 41 * allows us to decline additional async requests with an error. 42 * 43 * smb_async_req_t is the struct used to save an "async" request on 44 * the list of requests that had an interim reply in the initial 45 * compound reply. This includes everything needed to restart 46 * processing at the async command. 47 */ 48 49 typedef struct smb2_async_req { 50 51 smb_sdrc_t (*ar_func)(smb_request_t *); 52 53 int ar_cmd_hdr; /* smb2_cmd_hdr offset */ 54 int ar_cmd_len; /* length from hdr */ 55 56 /* 57 * SMB2 header fields. 58 */ 59 uint16_t ar_cmd_code; 60 uint16_t ar_uid; 61 uint16_t ar_tid; 62 uint32_t ar_pid; 63 uint32_t ar_hdr_flags; 64 uint64_t ar_messageid; 65 } smb2_async_req_t; 66 67 void smb2sr_do_async(smb_request_t *); 68 smb_sdrc_t smb2_invalid_cmd(smb_request_t *); 69 static void smb2_tq_work(void *); 70 71 static const smb_disp_entry_t 72 smb2_disp_table[SMB2__NCMDS] = { 73 74 /* text-name, pre, func, post, cmd-code, dialect, flags */ 75 76 { "smb2_negotiate", NULL, 77 smb2_negotiate, NULL, 0, 0, 78 SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 79 80 { "smb2_session_setup", NULL, 81 smb2_session_setup, NULL, 0, 0, 82 SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 83 84 { "smb2_logoff", NULL, 85 smb2_logoff, NULL, 0, 0, 86 SDDF_SUPPRESS_TID }, 87 88 { "smb2_tree_connect", NULL, 89 smb2_tree_connect, NULL, 0, 0, 90 SDDF_SUPPRESS_TID }, 91 92 { "smb2_tree_disconn", NULL, 93 smb2_tree_disconn, NULL, 0, 0 }, 94 95 { "smb2_create", NULL, 96 smb2_create, NULL, 0, 0 }, 97 98 { "smb2_close", NULL, 99 smb2_close, NULL, 0, 0 }, 100 101 { "smb2_flush", NULL, 102 smb2_flush, NULL, 0, 0 }, 103 104 { "smb2_read", NULL, 105 smb2_read, NULL, 0, 0 }, 106 107 { "smb2_write", NULL, 108 smb2_write, NULL, 0, 0 }, 109 110 { "smb2_lock", NULL, 111 smb2_lock, NULL, 0, 0 }, 112 113 { "smb2_ioctl", NULL, 114 smb2_ioctl, NULL, 0, 0 }, 115 116 { "smb2_cancel", NULL, 117 smb2_cancel, NULL, 0, 0, 118 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 119 120 { "smb2_echo", NULL, 121 smb2_echo, NULL, 0, 0, 122 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 123 124 { "smb2_query_dir", NULL, 125 smb2_query_dir, NULL, 0, 0 }, 126 127 { "smb2_change_notify", NULL, 128 smb2_change_notify, NULL, 0, 0 }, 129 130 { "smb2_query_info", NULL, 131 smb2_query_info, NULL, 0, 0 }, 132 133 { "smb2_set_info", NULL, 134 smb2_set_info, NULL, 0, 0 }, 135 136 { "smb2_oplock_break_ack", NULL, 137 smb2_oplock_break_ack, NULL, 0, 0 }, 138 139 { "smb2_invalid_cmd", NULL, 140 smb2_invalid_cmd, NULL, 0, 0, 141 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 142 }; 143 144 smb_sdrc_t 145 smb2_invalid_cmd(smb_request_t *sr) 146 { 147 #ifdef DEBUG 148 cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code", 149 sr->session->ip_addr_str); 150 #endif 151 sr->smb2_status = NT_STATUS_INVALID_PARAMETER; 152 return (SDRC_DROP_VC); 153 } 154 155 /* 156 * This is the SMB2 handler for new smb requests, called from 157 * smb_session_reader after SMB negotiate is done. For most SMB2 158 * requests, we just enqueue them for the smb_session_worker to 159 * execute via the task queue, so they can block for resources 160 * without stopping the reader thread. A few protocol messages 161 * are special cases and are handled directly here in the reader 162 * thread so they don't wait for taskq scheduling. 163 * 164 * This function must either enqueue the new request for 165 * execution via the task queue, or execute it directly 166 * and then free it. If this returns non-zero, the caller 167 * will drop the session. 168 */ 169 int 170 smb2sr_newrq(smb_request_t *sr) 171 { 172 struct mbuf_chain *mbc = &sr->command; 173 uint32_t magic; 174 int rc, skip; 175 176 if (smb_mbc_peek(mbc, 0, "l", &magic) != 0) 177 goto drop; 178 179 if (magic != SMB2_PROTOCOL_MAGIC) 180 goto drop; 181 182 /* 183 * Walk the SMB2 commands in this compound message and 184 * keep track of the range of message IDs it uses. 185 */ 186 for (;;) { 187 if (smb2_decode_header(sr) != 0) 188 goto drop; 189 190 /* 191 * Cancel requests are special: They refer to 192 * an earlier message ID (or an async. ID), 193 * never a new ID, and are never compounded. 194 * This is intentionally not "goto drop" 195 * because rc may be zero (success). 196 */ 197 if (sr->smb2_cmd_code == SMB2_CANCEL) { 198 rc = smb2_newrq_cancel(sr); 199 smb_request_free(sr); 200 return (rc); 201 } 202 203 /* 204 * Keep track of the total credits in this compound 205 * and the first (real) message ID (not: 0, -1) 206 * While we're looking, verify that all (real) IDs 207 * are (first <= ID < (first + msg_credits)) 208 */ 209 if (sr->smb2_credit_charge == 0) 210 sr->smb2_credit_charge = 1; 211 sr->smb2_total_credits += sr->smb2_credit_charge; 212 213 if (sr->smb2_messageid != 0 && 214 sr->smb2_messageid != UINT64_MAX) { 215 216 if (sr->smb2_first_msgid == 0) 217 sr->smb2_first_msgid = sr->smb2_messageid; 218 219 if (sr->smb2_messageid < sr->smb2_first_msgid || 220 sr->smb2_messageid >= (sr->smb2_first_msgid + 221 sr->smb2_total_credits)) { 222 long long id = (long long) sr->smb2_messageid; 223 cmn_err(CE_WARN, "clnt %s msg ID 0x%llx " 224 "out of sequence in compound", 225 sr->session->ip_addr_str, id); 226 } 227 } 228 229 /* Normal loop exit on next == zero */ 230 if (sr->smb2_next_command == 0) 231 break; 232 233 /* Abundance of caution... */ 234 if (sr->smb2_next_command < SMB2_HDR_SIZE) 235 goto drop; 236 237 /* Advance to the next header. */ 238 skip = sr->smb2_next_command - SMB2_HDR_SIZE; 239 if (MBC_ROOM_FOR(mbc, skip) == 0) 240 goto drop; 241 mbc->chain_offset += skip; 242 } 243 /* Rewind back to the top. */ 244 mbc->chain_offset = 0; 245 246 /* 247 * Submit the request to the task queue, which calls 248 * smb2_tq_work when the workload permits. 249 */ 250 sr->sr_time_submitted = gethrtime(); 251 sr->sr_state = SMB_REQ_STATE_SUBMITTED; 252 smb_srqueue_waitq_enter(sr->session->s_srqueue); 253 (void) taskq_dispatch(sr->sr_server->sv_worker_pool, 254 smb2_tq_work, sr, TQ_SLEEP); 255 return (0); 256 257 drop: 258 smb_request_free(sr); 259 return (-1); 260 } 261 262 static void 263 smb2_tq_work(void *arg) 264 { 265 smb_request_t *sr; 266 smb_srqueue_t *srq; 267 268 sr = (smb_request_t *)arg; 269 SMB_REQ_VALID(sr); 270 271 srq = sr->session->s_srqueue; 272 smb_srqueue_waitq_to_runq(srq); 273 sr->sr_worker = curthread; 274 sr->sr_time_active = gethrtime(); 275 276 /* 277 * Always dispatch to the work function, because cancelled 278 * requests need an error reply (NT_STATUS_CANCELLED). 279 */ 280 mutex_enter(&sr->sr_mutex); 281 if (sr->sr_state == SMB_REQ_STATE_SUBMITTED) 282 sr->sr_state = SMB_REQ_STATE_ACTIVE; 283 mutex_exit(&sr->sr_mutex); 284 285 smb2sr_work(sr); 286 287 smb_srqueue_runq_exit(srq); 288 } 289 290 /* 291 * smb2sr_work 292 * 293 * This function processes each SMB command in the current request 294 * (which may be a compound request) building a reply containing 295 * SMB reply messages, one-to-one with the SMB commands. Some SMB 296 * commands (change notify, blocking locks) may require both an 297 * "interim response" and a later "async response" at completion. 298 * In such cases, we'll encode the interim response in the reply 299 * compound we're building, and put the (now async) command on a 300 * list of commands that need further processing. After we've 301 * finished processing the commands in this compound and building 302 * the compound reply, we'll send the compound reply, and finally 303 * process the list of async commands. 304 * 305 * As we work our way through the compound request and reply, 306 * we need to keep track of the bounds of the current request 307 * and reply. For the request, this uses an MBC_SHADOW_CHAIN 308 * that begins at smb2_cmd_hdr. The reply is appended to the 309 * sr->reply chain starting at smb2_reply_hdr. 310 * 311 * This function must always free the smb request. 312 */ 313 void 314 smb2sr_work(struct smb_request *sr) 315 { 316 const smb_disp_entry_t *sdd; 317 smb_disp_stats_t *sds; 318 smb_session_t *session; 319 uint32_t msg_len; 320 uint16_t cmd_idx; 321 int rc = 0; 322 boolean_t disconnect = B_FALSE; 323 boolean_t related; 324 325 session = sr->session; 326 327 ASSERT(sr->tid_tree == 0); 328 ASSERT(sr->uid_user == 0); 329 ASSERT(sr->fid_ofile == 0); 330 sr->smb_fid = (uint16_t)-1; 331 sr->smb2_status = 0; 332 333 /* temporary until we identify a user */ 334 sr->user_cr = zone_kcred(); 335 336 cmd_start: 337 /* 338 * Note that we don't check sr_state here and abort the 339 * compound if cancelled (etc.) because some SMB2 command 340 * handlers need to do work even when cancelled. 341 * 342 * We treat some status codes as if "sticky", meaning 343 * once they're set after some command handler returns, 344 * all remaining commands get this status without even 345 * calling the command-specific handler. 346 */ 347 if (sr->smb2_status != NT_STATUS_CANCELLED && 348 sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES) 349 sr->smb2_status = 0; 350 351 /* 352 * Decode the request header 353 * 354 * Most problems with decoding will result in the error 355 * STATUS_INVALID_PARAMETER. If the decoding problem 356 * prevents continuing, we'll close the connection. 357 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 358 */ 359 sr->smb2_cmd_hdr = sr->command.chain_offset; 360 if ((rc = smb2_decode_header(sr)) != 0) { 361 cmn_err(CE_WARN, "clnt %s bad SMB2 header", 362 session->ip_addr_str); 363 disconnect = B_TRUE; 364 goto cleanup; 365 } 366 367 /* 368 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear 369 * in messages from the server back to the client. 370 */ 371 if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) { 372 cmn_err(CE_WARN, "clnt %s bad SMB2 flags", 373 session->ip_addr_str); 374 disconnect = B_TRUE; 375 goto cleanup; 376 } 377 related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS); 378 379 /* 380 * In case we bail out with an error before we get to the 381 * section that computes the credit grant, initialize the 382 * response header fields so that credits won't change. 383 * Note: SMB 2.02 clients may send credit charge zero. 384 */ 385 if (sr->smb2_credit_charge == 0) 386 sr->smb2_credit_charge = 1; 387 sr->smb2_credit_response = sr->smb2_credit_charge; 388 389 /* 390 * Reserve space for the reply header, and save the offset. 391 * The reply header will be overwritten later. If we have 392 * already exhausted the output space, then this client is 393 * trying something funny. Log it and kill 'em. 394 */ 395 sr->smb2_reply_hdr = sr->reply.chain_offset; 396 if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) { 397 cmn_err(CE_WARN, "clnt %s excessive reply", 398 session->ip_addr_str); 399 disconnect = B_TRUE; 400 goto cleanup; 401 } 402 403 /* 404 * Figure out the length of data following the SMB2 header. 405 * It ends at either the next SMB2 header if there is one 406 * (smb2_next_command != 0) or at the end of the message. 407 */ 408 if (sr->smb2_next_command != 0) { 409 /* [MS-SMB2] says this is 8-byte aligned */ 410 msg_len = sr->smb2_next_command; 411 if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) || 412 ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) { 413 cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd", 414 session->ip_addr_str); 415 disconnect = B_TRUE; 416 goto cleanup; 417 } 418 } else { 419 msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr; 420 } 421 422 /* 423 * Setup a shadow chain for this SMB2 command, starting 424 * with the header and ending at either the next command 425 * or the end of the message. The signing check below 426 * needs the entire SMB2 command. After that's done, we 427 * advance chain_offset to the end of the header where 428 * the command specific handlers continue decoding. 429 */ 430 (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, 431 sr->smb2_cmd_hdr, msg_len); 432 433 /* 434 * We will consume the data for this request from smb_data. 435 * That effectively consumes msg_len bytes from sr->command 436 * but doesn't update its chain_offset, so we need to update 437 * that here to make later received bytes accounting work. 438 */ 439 sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len; 440 ASSERT(sr->command.chain_offset <= sr->command.max_bytes); 441 442 /* 443 * Validate the commmand code, get dispatch table entries. 444 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 445 * 446 * The last slot in the dispatch table is used to handle 447 * invalid commands. Same for statistics. 448 */ 449 if (sr->smb2_cmd_code < SMB2_INVALID_CMD) 450 cmd_idx = sr->smb2_cmd_code; 451 else 452 cmd_idx = SMB2_INVALID_CMD; 453 sdd = &smb2_disp_table[cmd_idx]; 454 sds = &session->s_server->sv_disp_stats2[cmd_idx]; 455 456 /* 457 * If this command is NOT "related" to the previous, 458 * clear out the UID, TID, FID state that might be 459 * left over from the previous command. 460 * 461 * If the command IS related, any new IDs are ignored, 462 * and we simply continue with the previous user, tree, 463 * and open file. 464 */ 465 if (!related) { 466 /* 467 * Drop user, tree, file; carefully ordered to 468 * avoid dangling references: file, tree, user 469 */ 470 if (sr->fid_ofile != NULL) { 471 smb_ofile_request_complete(sr->fid_ofile); 472 smb_ofile_release(sr->fid_ofile); 473 sr->fid_ofile = NULL; 474 } 475 if (sr->tid_tree != NULL) { 476 smb_tree_release(sr->tid_tree); 477 sr->tid_tree = NULL; 478 } 479 if (sr->uid_user != NULL) { 480 smb_user_release(sr->uid_user); 481 sr->uid_user = NULL; 482 sr->user_cr = zone_kcred(); 483 } 484 } 485 486 /* 487 * Make sure we have a user and tree as needed 488 * according to the flags for the this command. 489 * Note that we may have inherited these. 490 */ 491 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) { 492 /* 493 * This command requires a user session. 494 */ 495 if (related) { 496 /* 497 * Previous command should have given us a user. 498 * [MS-SMB2] 3.3.5.2 Handling Related Requests 499 */ 500 if (sr->uid_user == NULL) { 501 smb2sr_put_error(sr, 502 NT_STATUS_INVALID_PARAMETER); 503 goto cmd_done; 504 } 505 sr->smb_uid = sr->uid_user->u_uid; 506 } else { 507 /* 508 * Lookup the UID 509 * [MS-SMB2] 3.3.5.2 Verifying the Session 510 */ 511 ASSERT(sr->uid_user == NULL); 512 sr->uid_user = smb_session_lookup_uid(session, 513 sr->smb_uid); 514 if (sr->uid_user == NULL) { 515 smb2sr_put_error(sr, 516 NT_STATUS_USER_SESSION_DELETED); 517 goto cmd_done; 518 } 519 sr->user_cr = smb_user_getcred(sr->uid_user); 520 } 521 ASSERT(sr->uid_user != NULL); 522 } 523 524 if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) { 525 /* 526 * This command requires a tree connection. 527 */ 528 if (related) { 529 /* 530 * Previous command should have given us a tree. 531 * [MS-SMB2] 3.3.5.2 Handling Related Requests 532 */ 533 if (sr->tid_tree == NULL) { 534 smb2sr_put_error(sr, 535 NT_STATUS_INVALID_PARAMETER); 536 goto cmd_done; 537 } 538 sr->smb_tid = sr->tid_tree->t_tid; 539 } else { 540 /* 541 * Lookup the TID 542 * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect 543 */ 544 ASSERT(sr->tid_tree == NULL); 545 sr->tid_tree = smb_session_lookup_tree(session, 546 sr->smb_tid); 547 if (sr->tid_tree == NULL) { 548 smb2sr_put_error(sr, 549 NT_STATUS_NETWORK_NAME_DELETED); 550 goto cmd_done; 551 } 552 } 553 ASSERT(sr->tid_tree != NULL); 554 } 555 556 /* 557 * SMB2 signature verification, two parts: 558 * (a) Require SMB2_FLAGS_SIGNED (for most request types) 559 * (b) If SMB2_FLAGS_SIGNED is set, check the signature. 560 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature 561 */ 562 563 /* 564 * No user session means no signature check. That's OK, 565 * i.e. for commands marked SDDF_SUPPRESS_UID above. 566 * Note, this also means we won't sign the reply. 567 */ 568 if (sr->uid_user == NULL) 569 sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 570 571 /* 572 * The SDDF_SUPPRESS_UID dispatch is set for requests that 573 * don't need a UID (user). These also don't require a 574 * signature check here. 575 */ 576 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 && 577 sr->uid_user != NULL && 578 (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) { 579 /* 580 * This request type should be signed, and 581 * we're configured to require signatures. 582 */ 583 if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) { 584 smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 585 goto cmd_done; 586 } 587 rc = smb2_sign_check_request(sr); 588 if (rc != 0) { 589 DTRACE_PROBE1(smb2__sign__check, smb_request_t, sr); 590 smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 591 goto cmd_done; 592 } 593 } 594 595 /* 596 * Now that the signing check is done with smb_data, 597 * advance past the SMB2 header we decoded earlier. 598 * This leaves sr->smb_data correctly positioned 599 * for command-specific decoding in the dispatch 600 * function called next. 601 */ 602 sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE; 603 604 /* 605 * SMB2 credits determine how many simultaneous commands the 606 * client may issue, and bounds the range of message IDs those 607 * commands may use. With multi-credit support, commands may 608 * use ranges of message IDs, where the credits used by each 609 * command are proportional to their data transfer size. 610 * 611 * Every command may request an increase or decrease of 612 * the currently granted credits, based on the difference 613 * between the credit request and the credit charge. 614 * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits 615 * 616 * Most commands have credit_request=1, credit_charge=1, 617 * which keeps the credit grant unchanged. 618 * 619 * All we're really doing here (for now) is reducing the 620 * credit_response if the client requests a credit increase 621 * that would take their credit over the maximum, and 622 * limiting the decrease so they don't run out of credits. 623 * 624 * Later, this could do something dynamic based on load. 625 * 626 * One other non-obvious bit about credits: We keep the 627 * session s_max_credits low until the 1st authentication, 628 * at which point we'll set the normal maximum_credits. 629 * Some clients ask for more credits with session setup, 630 * and we need to handle that requested increase _after_ 631 * the command-specific handler returns so it won't be 632 * restricted to the lower (pre-auth) limit. 633 */ 634 sr->smb2_credit_response = sr->smb2_credit_request; 635 if (sr->smb2_credit_request < sr->smb2_credit_charge) { 636 uint16_t cur, d; 637 638 mutex_enter(&session->s_credits_mutex); 639 cur = session->s_cur_credits; 640 641 /* Handle credit decrease. */ 642 d = sr->smb2_credit_charge - sr->smb2_credit_request; 643 cur -= d; 644 if (cur & 0x8000) { 645 /* 646 * underflow (bad credit charge or request) 647 * leave credits unchanged (response=charge) 648 */ 649 cur = session->s_cur_credits; 650 sr->smb2_credit_response = sr->smb2_credit_charge; 651 DTRACE_PROBE1(smb2__credit__neg, smb_request_t, sr); 652 } 653 654 /* 655 * The server MUST ensure that the number of credits 656 * held by the client is never reduced to zero. 657 * [MS-SMB2] 3.3.1.2 658 */ 659 if (cur == 0) { 660 cur = 1; 661 sr->smb2_credit_response += 1; 662 DTRACE_PROBE1(smb2__credit__min, smb_request_t, sr); 663 } 664 665 DTRACE_PROBE3(smb2__credit__decrease, 666 smb_request_t, sr, int, (int)cur, 667 int, (int)session->s_cur_credits); 668 669 session->s_cur_credits = cur; 670 mutex_exit(&session->s_credits_mutex); 671 } 672 673 /* 674 * The real work: call the SMB2 command handler 675 * (except for "sticky" smb2_status - see above) 676 */ 677 sr->sr_time_start = gethrtime(); 678 rc = SDRC_SUCCESS; 679 if (sr->smb2_status == 0) { 680 /* NB: not using pre_op */ 681 rc = (*sdd->sdt_function)(sr); 682 /* NB: not using post_op */ 683 } else { 684 smb2sr_put_error(sr, sr->smb2_status); 685 } 686 687 MBC_FLUSH(&sr->raw_data); 688 689 /* 690 * Second half of SMB2 credit handling (increases) 691 */ 692 if (sr->smb2_credit_request > sr->smb2_credit_charge) { 693 uint16_t cur, d; 694 695 mutex_enter(&session->s_credits_mutex); 696 cur = session->s_cur_credits; 697 698 /* Handle credit increase. */ 699 d = sr->smb2_credit_request - sr->smb2_credit_charge; 700 cur += d; 701 702 /* 703 * If new credits would be above max, 704 * reduce the credit grant. 705 */ 706 if (cur > session->s_max_credits) { 707 d = cur - session->s_max_credits; 708 cur = session->s_max_credits; 709 sr->smb2_credit_response -= d; 710 DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr); 711 } 712 713 DTRACE_PROBE3(smb2__credit__increase, 714 smb_request_t, sr, int, (int)cur, 715 int, (int)session->s_cur_credits); 716 717 session->s_cur_credits = cur; 718 mutex_exit(&session->s_credits_mutex); 719 } 720 721 cmd_done: 722 /* 723 * Pad the reply to align(8) if necessary. 724 */ 725 if (sr->reply.chain_offset & 7) { 726 int padsz = 8 - (sr->reply.chain_offset & 7); 727 (void) smb_mbc_encodef(&sr->reply, "#.", padsz); 728 } 729 ASSERT((sr->reply.chain_offset & 7) == 0); 730 731 /* 732 * Record some statistics: latency, rx bytes, tx bytes. 733 */ 734 smb_server_inc_req(sr->sr_server); 735 smb_latency_add_sample(&sds->sdt_lat, 736 gethrtime() - sr->sr_time_start); 737 atomic_add_64(&sds->sdt_rxb, 738 (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr)); 739 atomic_add_64(&sds->sdt_txb, 740 (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr)); 741 742 switch (rc) { 743 case SDRC_SUCCESS: 744 break; 745 default: 746 /* 747 * SMB2 does not use the other dispatch return codes. 748 * If we see something else, log an event so we'll 749 * know something is returning bogus status codes. 750 * If you see these in the log, use dtrace to find 751 * the code returning something else. 752 */ 753 #ifdef DEBUG 754 cmn_err(CE_NOTE, "handler for %u returned 0x%x", 755 sr->smb2_cmd_code, rc); 756 #endif 757 /* FALLTHROUGH */ 758 case SDRC_ERROR: 759 if (sr->smb2_status == 0) 760 sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 761 break; 762 case SDRC_DROP_VC: 763 disconnect = B_TRUE; 764 goto cleanup; 765 766 case SDRC_NO_REPLY: 767 /* will free sr */ 768 goto cleanup; 769 } 770 771 /* 772 * If there's a next command, figure out where it starts, 773 * and fill in the next command offset for the reply. 774 * Note: We sanity checked smb2_next_command above 775 * (the offset to the next command). Similarly set 776 * smb2_next_reply as the offset to the next reply. 777 */ 778 if (sr->smb2_next_command != 0) { 779 sr->command.chain_offset = 780 sr->smb2_cmd_hdr + sr->smb2_next_command; 781 sr->smb2_next_reply = 782 sr->reply.chain_offset - sr->smb2_reply_hdr; 783 } else { 784 sr->smb2_next_reply = 0; 785 } 786 787 /* 788 * Overwrite the SMB2 header for the response of 789 * this command (possibly part of a compound). 790 * encode_header adds: SMB2_FLAGS_SERVER_TO_REDIR 791 */ 792 (void) smb2_encode_header(sr, B_TRUE); 793 794 if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) 795 smb2_sign_reply(sr); 796 797 if (sr->smb2_next_command != 0) 798 goto cmd_start; 799 800 /* 801 * We've done all the commands in this compound. 802 * Send it out. 803 */ 804 smb2_send_reply(sr); 805 806 /* 807 * If any of the requests "went async", process those now. 808 * The async. function "keeps" this sr, changing its state 809 * to completed and calling smb_request_free(). 810 */ 811 if (sr->sr_async_req != NULL) { 812 smb2sr_do_async(sr); 813 return; 814 } 815 816 cleanup: 817 if (disconnect) { 818 smb_rwx_rwenter(&session->s_lock, RW_WRITER); 819 switch (session->s_state) { 820 case SMB_SESSION_STATE_DISCONNECTED: 821 case SMB_SESSION_STATE_TERMINATED: 822 break; 823 default: 824 smb_soshutdown(session->sock); 825 session->s_state = SMB_SESSION_STATE_DISCONNECTED; 826 break; 827 } 828 smb_rwx_rwexit(&session->s_lock); 829 } 830 831 mutex_enter(&sr->sr_mutex); 832 sr->sr_state = SMB_REQ_STATE_COMPLETED; 833 mutex_exit(&sr->sr_mutex); 834 835 smb_request_free(sr); 836 } 837 838 /* 839 * Dispatch an async request using saved information. 840 * See smb2sr_save_async and [MS-SMB2] 3.3.4.2 841 * 842 * This is sort of a "lite" version of smb2sr_work. Initialize the 843 * command and reply areas as they were when the command-speicific 844 * handler started (in case it needs to decode anything again). 845 * Call the async function, which builds the command-specific part 846 * of the response. Finally, send the response and free the sr. 847 */ 848 void 849 smb2sr_do_async(smb_request_t *sr) 850 { 851 const smb_disp_entry_t *sdd; 852 smb_disp_stats_t *sds; 853 smb2_async_req_t *ar; 854 int rc = 0; 855 856 /* 857 * Restore what smb2_decode_header found. 858 * (In lieu of decoding it again.) 859 */ 860 ar = sr->sr_async_req; 861 sr->smb2_cmd_hdr = ar->ar_cmd_hdr; 862 sr->smb2_cmd_code = ar->ar_cmd_code; 863 sr->smb2_hdr_flags = ar->ar_hdr_flags; 864 sr->smb2_async_id = (uintptr_t)ar; 865 sr->smb2_messageid = ar->ar_messageid; 866 sr->smb_pid = ar->ar_pid; 867 sr->smb_tid = ar->ar_tid; 868 sr->smb_uid = ar->ar_uid; 869 sr->smb2_status = 0; 870 871 /* 872 * Async requests don't grant credits, because any credits 873 * should have gone out with the interim reply. 874 * An async reply goes alone (no next reply). 875 */ 876 sr->smb2_credit_response = 0; 877 sr->smb2_next_reply = 0; 878 879 /* 880 * Setup input mbuf_chain 881 */ 882 ASSERT(ar->ar_cmd_len >= SMB2_HDR_SIZE); 883 (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, 884 sr->smb2_cmd_hdr + SMB2_HDR_SIZE, 885 ar->ar_cmd_len - SMB2_HDR_SIZE); 886 887 /* 888 * Setup output mbuf_chain 889 */ 890 MBC_FLUSH(&sr->reply); 891 sr->smb2_reply_hdr = sr->reply.chain_offset; 892 (void) smb2_encode_header(sr, B_FALSE); 893 894 VERIFY3U(sr->smb2_cmd_code, <, SMB2_INVALID_CMD); 895 sdd = &smb2_disp_table[sr->smb2_cmd_code]; 896 sds = sr->session->s_server->sv_disp_stats2; 897 sds = &sds[sr->smb2_cmd_code]; 898 899 /* 900 * Keep the UID, TID, ofile we have. 901 */ 902 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 && 903 sr->uid_user == NULL) { 904 smb2sr_put_error(sr, NT_STATUS_USER_SESSION_DELETED); 905 goto cmd_done; 906 } 907 if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0 && 908 sr->tid_tree == NULL) { 909 smb2sr_put_error(sr, NT_STATUS_NETWORK_NAME_DELETED); 910 goto cmd_done; 911 } 912 913 /* 914 * Signature already verified 915 * Credits handled... 916 * 917 * Just call the async handler function. 918 */ 919 rc = ar->ar_func(sr); 920 if (rc != 0 && sr->smb2_status == 0) 921 sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 922 923 cmd_done: 924 /* 925 * Pad the reply to align(8) if necessary. 926 */ 927 if (sr->reply.chain_offset & 7) { 928 int padsz = 8 - (sr->reply.chain_offset & 7); 929 (void) smb_mbc_encodef(&sr->reply, "#.", padsz); 930 } 931 ASSERT((sr->reply.chain_offset & 7) == 0); 932 933 /* 934 * Record some statistics: (just tx bytes here) 935 */ 936 atomic_add_64(&sds->sdt_txb, 937 (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr)); 938 939 /* 940 * Overwrite the SMB2 header for the response of 941 * this command (possibly part of a compound). 942 * The call adds: SMB2_FLAGS_SERVER_TO_REDIR 943 */ 944 (void) smb2_encode_header(sr, B_TRUE); 945 946 if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) 947 smb2_sign_reply(sr); 948 949 smb2_send_reply(sr); 950 951 /* 952 * Done. Unlink and free. 953 */ 954 sr->sr_async_req = NULL; 955 kmem_free(ar, sizeof (*ar)); 956 957 mutex_enter(&sr->sr_mutex); 958 sr->sr_state = SMB_REQ_STATE_COMPLETED; 959 mutex_exit(&sr->sr_mutex); 960 961 smb_request_free(sr); 962 } 963 964 /* 965 * In preparation for sending an "interim response", save 966 * all the state we'll need to run an async command later, 967 * and assign an "async id" for this (now async) command. 968 * See [MS-SMB2] 3.3.4.2 969 * 970 * If more than one request in a compound request tries to 971 * "go async", we can "say no". See [MS-SMB2] 3.3.4.2 972 * If an operation would require asynchronous processing 973 * but resources are constrained, the server MAY choose to 974 * fail that operation with STATUS_INSUFFICIENT_RESOURCES. 975 * 976 * For simplicity, we further restrict the cases where we're 977 * willing to "go async", and only allow the last command in a 978 * compound to "go async". It happens that this is the only 979 * case where we're actually asked to go async anyway. This 980 * simplification also means there can be at most one command 981 * in a compound that "goes async" (the last one). 982 * 983 * If we agree to "go async", this should return STATUS_PENDING. 984 * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and 985 * all requests following this request. (See the comments re. 986 * "sticky" smb2_status values in smb2sr_work). 987 * 988 * Note: the Async ID we assign here is arbitrary, and need only 989 * be unique among pending async responses on this connection, so 990 * this just uses an object address as the Async ID. 991 * 992 * Also, the assigned worker is the ONLY thread using this 993 * async request object (sr_async_req) so no locking. 994 */ 995 uint32_t 996 smb2sr_go_async(smb_request_t *sr, 997 smb_sdrc_t (*async_func)(smb_request_t *)) 998 { 999 smb2_async_req_t *ar; 1000 1001 if (sr->smb2_next_command != 0) 1002 return (NT_STATUS_INSUFFICIENT_RESOURCES); 1003 1004 ASSERT(sr->sr_async_req == NULL); 1005 ar = kmem_zalloc(sizeof (*ar), KM_SLEEP); 1006 1007 /* 1008 * Place an interim response in the compound reply. 1009 * 1010 * Turn on the "async" flag for both the (synchronous) 1011 * interim response and the (later) async response, 1012 * by storing that in flags before coping into ar. 1013 */ 1014 sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND; 1015 sr->smb2_async_id = (uintptr_t)ar; 1016 1017 ar->ar_func = async_func; 1018 ar->ar_cmd_hdr = sr->smb2_cmd_hdr; 1019 ar->ar_cmd_len = sr->smb_data.max_bytes - sr->smb2_cmd_hdr; 1020 1021 ar->ar_cmd_code = sr->smb2_cmd_code; 1022 ar->ar_hdr_flags = sr->smb2_hdr_flags; 1023 ar->ar_messageid = sr->smb2_messageid; 1024 ar->ar_pid = sr->smb_pid; 1025 ar->ar_tid = sr->smb_tid; 1026 ar->ar_uid = sr->smb_uid; 1027 1028 sr->sr_async_req = ar; 1029 1030 /* Interim responses are NOT signed. */ 1031 sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 1032 1033 return (NT_STATUS_PENDING); 1034 } 1035 1036 int 1037 smb2_decode_header(smb_request_t *sr) 1038 { 1039 uint64_t ssnid; 1040 uint32_t pid, tid; 1041 uint16_t hdr_len; 1042 int rc; 1043 1044 rc = smb_mbc_decodef( 1045 &sr->command, "Nwww..wwllqllq16c", 1046 &hdr_len, /* w */ 1047 &sr->smb2_credit_charge, /* w */ 1048 &sr->smb2_chan_seq, /* w */ 1049 /* reserved .. */ 1050 &sr->smb2_cmd_code, /* w */ 1051 &sr->smb2_credit_request, /* w */ 1052 &sr->smb2_hdr_flags, /* l */ 1053 &sr->smb2_next_command, /* l */ 1054 &sr->smb2_messageid, /* q */ 1055 &pid, /* l */ 1056 &tid, /* l */ 1057 &ssnid, /* q */ 1058 sr->smb2_sig); /* 16c */ 1059 if (rc) 1060 return (rc); 1061 1062 if (hdr_len != SMB2_HDR_SIZE) 1063 return (-1); 1064 1065 sr->smb_uid = (uint16_t)ssnid; /* XXX wide UIDs */ 1066 1067 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1068 sr->smb2_async_id = pid | 1069 ((uint64_t)tid) << 32; 1070 } else { 1071 sr->smb_pid = pid; 1072 sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */ 1073 } 1074 1075 return (rc); 1076 } 1077 1078 int 1079 smb2_encode_header(smb_request_t *sr, boolean_t overwrite) 1080 { 1081 uint64_t ssnid = sr->smb_uid; 1082 uint64_t pid_tid_aid; /* pid+tid, or async id */ 1083 uint32_t reply_hdr_flags; 1084 int rc; 1085 1086 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1087 pid_tid_aid = sr->smb2_async_id; 1088 } else { 1089 pid_tid_aid = sr->smb_pid | 1090 ((uint64_t)sr->smb_tid) << 32; 1091 } 1092 reply_hdr_flags = sr->smb2_hdr_flags | SMB2_FLAGS_SERVER_TO_REDIR; 1093 1094 if (overwrite) { 1095 rc = smb_mbc_poke(&sr->reply, 1096 sr->smb2_reply_hdr, 1097 "Nwwlwwllqqq16c", 1098 SMB2_HDR_SIZE, /* w */ 1099 sr->smb2_credit_charge, /* w */ 1100 sr->smb2_status, /* l */ 1101 sr->smb2_cmd_code, /* w */ 1102 sr->smb2_credit_response, /* w */ 1103 reply_hdr_flags, /* l */ 1104 sr->smb2_next_reply, /* l */ 1105 sr->smb2_messageid, /* q */ 1106 pid_tid_aid, /* q */ 1107 ssnid, /* q */ 1108 sr->smb2_sig); /* 16c */ 1109 } else { 1110 rc = smb_mbc_encodef(&sr->reply, 1111 "Nwwlwwllqqq16c", 1112 SMB2_HDR_SIZE, /* w */ 1113 sr->smb2_credit_charge, /* w */ 1114 sr->smb2_status, /* l */ 1115 sr->smb2_cmd_code, /* w */ 1116 sr->smb2_credit_response, /* w */ 1117 reply_hdr_flags, /* l */ 1118 sr->smb2_next_reply, /* l */ 1119 sr->smb2_messageid, /* q */ 1120 pid_tid_aid, /* q */ 1121 ssnid, /* q */ 1122 sr->smb2_sig); /* 16c */ 1123 } 1124 1125 return (rc); 1126 } 1127 1128 void 1129 smb2_send_reply(smb_request_t *sr) 1130 { 1131 1132 if (smb_session_send(sr->session, 0, &sr->reply) == 0) 1133 sr->reply.chain = 0; 1134 } 1135 1136 /* 1137 * This wrapper function exists to help catch calls to smbsr_status() 1138 * (which is SMB1-specific) in common code. See smbsr_status(). 1139 * If the log message below is seen, put a dtrace probe on this 1140 * function with a stack() action to see who is calling the SMB1 1141 * "put error" from common code, and fix it. 1142 */ 1143 void 1144 smbsr_status_smb2(smb_request_t *sr, DWORD status) 1145 { 1146 const char *name; 1147 1148 if (sr->smb2_cmd_code < SMB2__NCMDS) 1149 name = smb2_disp_table[sr->smb2_cmd_code].sdt_name; 1150 else 1151 name = "<unknown>"; 1152 #ifdef DEBUG 1153 cmn_err(CE_NOTE, "smbsr_status called for %s", name); 1154 #endif 1155 1156 smb2sr_put_error_data(sr, status, NULL); 1157 } 1158 1159 void 1160 smb2sr_put_errno(struct smb_request *sr, int errnum) 1161 { 1162 uint32_t status = smb_errno2status(errnum); 1163 smb2sr_put_error_data(sr, status, NULL); 1164 } 1165 1166 void 1167 smb2sr_put_error(smb_request_t *sr, uint32_t status) 1168 { 1169 smb2sr_put_error_data(sr, status, NULL); 1170 } 1171 1172 /* 1173 * Build an SMB2 error response. [MS-SMB2] 2.2.2 1174 */ 1175 void 1176 smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc) 1177 { 1178 DWORD len; 1179 1180 /* 1181 * The common dispatch code writes this when it 1182 * updates the SMB2 header before sending. 1183 */ 1184 sr->smb2_status = status; 1185 1186 /* Rewind to the end of the SMB header. */ 1187 sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE; 1188 1189 /* 1190 * NB: Must provide at least one byte of error data, 1191 * per [MS-SMB2] 2.2.2 1192 */ 1193 if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) { 1194 (void) smb_mbc_encodef( 1195 &sr->reply, 1196 "wwlC", 1197 9, /* StructSize */ /* w */ 1198 0, /* reserved */ /* w */ 1199 len, /* l */ 1200 mbc); /* C */ 1201 } else { 1202 (void) smb_mbc_encodef( 1203 &sr->reply, 1204 "wwl.", 1205 9, /* StructSize */ /* w */ 1206 0, /* reserved */ /* w */ 1207 0); /* l. */ 1208 } 1209 } 1210 1211 /* 1212 * smb2sr_lookup_fid 1213 * 1214 * Setup sr->fid_ofile, either inherited from a related command, 1215 * or obtained via FID lookup. Similar inheritance logic as in 1216 * smb2sr_work. 1217 */ 1218 uint32_t 1219 smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid) 1220 { 1221 boolean_t related = sr->smb2_hdr_flags & 1222 SMB2_FLAGS_RELATED_OPERATIONS; 1223 1224 if (related) { 1225 if (sr->fid_ofile == NULL) 1226 return (NT_STATUS_INVALID_PARAMETER); 1227 sr->smb_fid = sr->fid_ofile->f_fid; 1228 return (0); 1229 } 1230 1231 /* 1232 * If we could be sure this is called only once per cmd, 1233 * we could simply ASSERT(sr->fid_ofile == NULL) here. 1234 * However, there are cases where it can be called again 1235 * handling the same command, so let's tolerate that. 1236 */ 1237 if (sr->fid_ofile == NULL) { 1238 sr->smb_fid = (uint16_t)fid->temporal; 1239 sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid); 1240 } 1241 if (sr->fid_ofile == NULL) 1242 return (NT_STATUS_FILE_CLOSED); 1243 1244 return (0); 1245 } 1246 1247 /* 1248 * smb2_dispatch_stats_init 1249 * 1250 * Initializes dispatch statistics for SMB2. 1251 * See also smb_dispatch_stats_init(), which fills in 1252 * the lower part of the statistics array, from zero 1253 * through SMB_COM_NUM; 1254 */ 1255 void 1256 smb2_dispatch_stats_init(smb_server_t *sv) 1257 { 1258 smb_disp_stats_t *sds = sv->sv_disp_stats2; 1259 smb_kstat_req_t *ksr; 1260 int i; 1261 1262 ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2; 1263 1264 for (i = 0; i < SMB2__NCMDS; i++, ksr++) { 1265 smb_latency_init(&sds[i].sdt_lat); 1266 (void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name, 1267 sizeof (ksr->kr_name)); 1268 } 1269 } 1270 1271 /* 1272 * smb2_dispatch_stats_fini 1273 * 1274 * Frees and destroyes the resources used for statistics. 1275 */ 1276 void 1277 smb2_dispatch_stats_fini(smb_server_t *sv) 1278 { 1279 smb_disp_stats_t *sds = sv->sv_disp_stats2; 1280 int i; 1281 1282 for (i = 0; i < SMB2__NCMDS; i++) 1283 smb_latency_destroy(&sds[i].sdt_lat); 1284 } 1285 1286 void 1287 smb2_dispatch_stats_update(smb_server_t *sv, 1288 smb_kstat_req_t *ksr, int first, int nreq) 1289 { 1290 smb_disp_stats_t *sds = sv->sv_disp_stats2; 1291 int i; 1292 int last; 1293 1294 last = first + nreq - 1; 1295 1296 if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS)) { 1297 for (i = first; i <= last; i++, ksr++) { 1298 ksr->kr_rxb = sds[i].sdt_rxb; 1299 ksr->kr_txb = sds[i].sdt_txb; 1300 mutex_enter(&sds[i].sdt_lat.ly_mutex); 1301 ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq; 1302 ksr->kr_sum = sds[i].sdt_lat.ly_a_sum; 1303 ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean; 1304 ksr->kr_a_stddev = 1305 sds[i].sdt_lat.ly_a_stddev; 1306 ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean; 1307 ksr->kr_d_stddev = 1308 sds[i].sdt_lat.ly_d_stddev; 1309 sds[i].sdt_lat.ly_d_mean = 0; 1310 sds[i].sdt_lat.ly_d_nreq = 0; 1311 sds[i].sdt_lat.ly_d_stddev = 0; 1312 sds[i].sdt_lat.ly_d_sum = 0; 1313 mutex_exit(&sds[i].sdt_lat.ly_mutex); 1314 } 1315 } 1316 } 1317