1a90cf9f2SGordon Ross /* 2a90cf9f2SGordon Ross * This file and its contents are supplied under the terms of the 3a90cf9f2SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0. 4a90cf9f2SGordon Ross * You may only use this file in accordance with the terms of version 5a90cf9f2SGordon Ross * 1.0 of the CDDL. 6a90cf9f2SGordon Ross * 7a90cf9f2SGordon Ross * A full copy of the text of the CDDL should have accompanied this 8a90cf9f2SGordon Ross * source. A copy of the CDDL is also available via the Internet at 9a90cf9f2SGordon Ross * http://www.illumos.org/license/CDDL. 10a90cf9f2SGordon Ross */ 11a90cf9f2SGordon Ross 12a90cf9f2SGordon Ross /* 131160dcf7SMatt Barden * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 14*541826a8SAndrew Stormont * Copyright 2019 RackTop Systems. 15a90cf9f2SGordon Ross */ 16a90cf9f2SGordon Ross 17a90cf9f2SGordon Ross 18a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h> 19a90cf9f2SGordon Ross #include <smbsrv/smb_kstat.h> 20a90cf9f2SGordon Ross #include <smbsrv/smb2.h> 21a90cf9f2SGordon Ross 22148d1a41SMatt Barden #define SMB2_ASYNCID(sr) (sr->smb2_messageid ^ (1ULL << 62)) 23a90cf9f2SGordon Ross 24a90cf9f2SGordon Ross smb_sdrc_t smb2_invalid_cmd(smb_request_t *); 25a90cf9f2SGordon Ross static void smb2_tq_work(void *); 2694047d49SGordon Ross static void smb2sr_run_postwork(smb_request_t *); 271160dcf7SMatt Barden static int smb3_decrypt_msg(smb_request_t *); 28a90cf9f2SGordon Ross 29adb064afSToomas Soome static const smb_disp_entry_t 30a90cf9f2SGordon Ross smb2_disp_table[SMB2__NCMDS] = { 31a90cf9f2SGordon Ross 32a90cf9f2SGordon Ross /* text-name, pre, func, post, cmd-code, dialect, flags */ 33a90cf9f2SGordon Ross 34a90cf9f2SGordon Ross { "smb2_negotiate", NULL, 35a90cf9f2SGordon Ross smb2_negotiate, NULL, 0, 0, 36a90cf9f2SGordon Ross SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 37a90cf9f2SGordon Ross 38a90cf9f2SGordon Ross { "smb2_session_setup", NULL, 39a90cf9f2SGordon Ross smb2_session_setup, NULL, 0, 0, 40a90cf9f2SGordon Ross SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 41a90cf9f2SGordon Ross 42a90cf9f2SGordon Ross { "smb2_logoff", NULL, 43a90cf9f2SGordon Ross smb2_logoff, NULL, 0, 0, 44a90cf9f2SGordon Ross SDDF_SUPPRESS_TID }, 45a90cf9f2SGordon Ross 46a90cf9f2SGordon Ross { "smb2_tree_connect", NULL, 47a90cf9f2SGordon Ross smb2_tree_connect, NULL, 0, 0, 48a90cf9f2SGordon Ross SDDF_SUPPRESS_TID }, 49a90cf9f2SGordon Ross 50a90cf9f2SGordon Ross { "smb2_tree_disconn", NULL, 51a90cf9f2SGordon Ross smb2_tree_disconn, NULL, 0, 0 }, 52a90cf9f2SGordon Ross 53a90cf9f2SGordon Ross { "smb2_create", NULL, 54a90cf9f2SGordon Ross smb2_create, NULL, 0, 0 }, 55a90cf9f2SGordon Ross 56a90cf9f2SGordon Ross { "smb2_close", NULL, 57a90cf9f2SGordon Ross smb2_close, NULL, 0, 0 }, 58a90cf9f2SGordon Ross 59a90cf9f2SGordon Ross { "smb2_flush", NULL, 60a90cf9f2SGordon Ross smb2_flush, NULL, 0, 0 }, 61a90cf9f2SGordon Ross 62a90cf9f2SGordon Ross { "smb2_read", NULL, 63a90cf9f2SGordon Ross smb2_read, NULL, 0, 0 }, 64a90cf9f2SGordon Ross 65a90cf9f2SGordon Ross { "smb2_write", NULL, 66a90cf9f2SGordon Ross smb2_write, NULL, 0, 0 }, 67a90cf9f2SGordon Ross 68a90cf9f2SGordon Ross { "smb2_lock", NULL, 69a90cf9f2SGordon Ross smb2_lock, NULL, 0, 0 }, 70a90cf9f2SGordon Ross 71a90cf9f2SGordon Ross { "smb2_ioctl", NULL, 72a90cf9f2SGordon Ross smb2_ioctl, NULL, 0, 0 }, 73a90cf9f2SGordon Ross 74a90cf9f2SGordon Ross { "smb2_cancel", NULL, 755677e049SGordon Ross smb2_cancel, NULL, 0, 0, 765677e049SGordon Ross SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 77a90cf9f2SGordon Ross 78a90cf9f2SGordon Ross { "smb2_echo", NULL, 79a90cf9f2SGordon Ross smb2_echo, NULL, 0, 0, 80a90cf9f2SGordon Ross SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 81a90cf9f2SGordon Ross 82a90cf9f2SGordon Ross { "smb2_query_dir", NULL, 83a90cf9f2SGordon Ross smb2_query_dir, NULL, 0, 0 }, 84a90cf9f2SGordon Ross 85a90cf9f2SGordon Ross { "smb2_change_notify", NULL, 86a90cf9f2SGordon Ross smb2_change_notify, NULL, 0, 0 }, 87a90cf9f2SGordon Ross 88a90cf9f2SGordon Ross { "smb2_query_info", NULL, 89a90cf9f2SGordon Ross smb2_query_info, NULL, 0, 0 }, 90a90cf9f2SGordon Ross 91a90cf9f2SGordon Ross { "smb2_set_info", NULL, 92a90cf9f2SGordon Ross smb2_set_info, NULL, 0, 0 }, 93a90cf9f2SGordon Ross 94a90cf9f2SGordon Ross { "smb2_oplock_break_ack", NULL, 95a90cf9f2SGordon Ross smb2_oplock_break_ack, NULL, 0, 0 }, 96a90cf9f2SGordon Ross 97a90cf9f2SGordon Ross { "smb2_invalid_cmd", NULL, 98a90cf9f2SGordon Ross smb2_invalid_cmd, NULL, 0, 0, 99a90cf9f2SGordon Ross SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 100a90cf9f2SGordon Ross }; 101a90cf9f2SGordon Ross 102a90cf9f2SGordon Ross smb_sdrc_t 103a90cf9f2SGordon Ross smb2_invalid_cmd(smb_request_t *sr) 104a90cf9f2SGordon Ross { 105a90cf9f2SGordon Ross #ifdef DEBUG 106a90cf9f2SGordon Ross cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code", 107a90cf9f2SGordon Ross sr->session->ip_addr_str); 108a90cf9f2SGordon Ross #endif 109a90cf9f2SGordon Ross sr->smb2_status = NT_STATUS_INVALID_PARAMETER; 110a90cf9f2SGordon Ross return (SDRC_DROP_VC); 111a90cf9f2SGordon Ross } 112a90cf9f2SGordon Ross 113a90cf9f2SGordon Ross /* 114a90cf9f2SGordon Ross * This is the SMB2 handler for new smb requests, called from 115a90cf9f2SGordon Ross * smb_session_reader after SMB negotiate is done. For most SMB2 116a90cf9f2SGordon Ross * requests, we just enqueue them for the smb_session_worker to 117a90cf9f2SGordon Ross * execute via the task queue, so they can block for resources 118a90cf9f2SGordon Ross * without stopping the reader thread. A few protocol messages 119a90cf9f2SGordon Ross * are special cases and are handled directly here in the reader 120a90cf9f2SGordon Ross * thread so they don't wait for taskq scheduling. 121a90cf9f2SGordon Ross * 122a90cf9f2SGordon Ross * This function must either enqueue the new request for 123a90cf9f2SGordon Ross * execution via the task queue, or execute it directly 124a90cf9f2SGordon Ross * and then free it. If this returns non-zero, the caller 125a90cf9f2SGordon Ross * will drop the session. 126a90cf9f2SGordon Ross */ 127a90cf9f2SGordon Ross int 128a90cf9f2SGordon Ross smb2sr_newrq(smb_request_t *sr) 129a90cf9f2SGordon Ross { 1305677e049SGordon Ross struct mbuf_chain *mbc = &sr->command; 131a90cf9f2SGordon Ross uint32_t magic; 1325677e049SGordon Ross int rc, skip; 1335677e049SGordon Ross 1345677e049SGordon Ross if (smb_mbc_peek(mbc, 0, "l", &magic) != 0) 1355677e049SGordon Ross goto drop; 1365677e049SGordon Ross 1371160dcf7SMatt Barden /* 0xFD S M B */ 1381160dcf7SMatt Barden if (magic == SMB3_ENCRYPTED_MAGIC) { 1391160dcf7SMatt Barden if (smb3_decrypt_msg(sr) != 0) 1401160dcf7SMatt Barden goto drop; 1411160dcf7SMatt Barden /* 1421160dcf7SMatt Barden * Should now be looking at an un-encrypted 1431160dcf7SMatt Barden * SMB2 message header. 1441160dcf7SMatt Barden */ 1451160dcf7SMatt Barden if (smb_mbc_peek(mbc, 0, "l", &magic) != 0) 1461160dcf7SMatt Barden goto drop; 1471160dcf7SMatt Barden } 1481160dcf7SMatt Barden 1495677e049SGordon Ross if (magic != SMB2_PROTOCOL_MAGIC) 1505677e049SGordon Ross goto drop; 151a90cf9f2SGordon Ross 152a90cf9f2SGordon Ross /* 1535677e049SGordon Ross * Walk the SMB2 commands in this compound message and 1545677e049SGordon Ross * keep track of the range of message IDs it uses. 155a90cf9f2SGordon Ross */ 1565677e049SGordon Ross for (;;) { 1575677e049SGordon Ross if (smb2_decode_header(sr) != 0) 1585677e049SGordon Ross goto drop; 1595677e049SGordon Ross 1605677e049SGordon Ross /* 1615677e049SGordon Ross * Cancel requests are special: They refer to 1625677e049SGordon Ross * an earlier message ID (or an async. ID), 1635677e049SGordon Ross * never a new ID, and are never compounded. 1645677e049SGordon Ross * This is intentionally not "goto drop" 1655677e049SGordon Ross * because rc may be zero (success). 1665677e049SGordon Ross */ 1675677e049SGordon Ross if (sr->smb2_cmd_code == SMB2_CANCEL) { 1685677e049SGordon Ross rc = smb2_newrq_cancel(sr); 1695677e049SGordon Ross smb_request_free(sr); 1705677e049SGordon Ross return (rc); 1715677e049SGordon Ross } 1725677e049SGordon Ross 1735677e049SGordon Ross /* 1745677e049SGordon Ross * Keep track of the total credits in this compound 1755677e049SGordon Ross * and the first (real) message ID (not: 0, -1) 1765677e049SGordon Ross * While we're looking, verify that all (real) IDs 1775677e049SGordon Ross * are (first <= ID < (first + msg_credits)) 1785677e049SGordon Ross */ 1795677e049SGordon Ross if (sr->smb2_credit_charge == 0) 1805677e049SGordon Ross sr->smb2_credit_charge = 1; 1815677e049SGordon Ross sr->smb2_total_credits += sr->smb2_credit_charge; 1825677e049SGordon Ross 1835677e049SGordon Ross if (sr->smb2_messageid != 0 && 1845677e049SGordon Ross sr->smb2_messageid != UINT64_MAX) { 1855677e049SGordon Ross 1865677e049SGordon Ross if (sr->smb2_first_msgid == 0) 1875677e049SGordon Ross sr->smb2_first_msgid = sr->smb2_messageid; 1885677e049SGordon Ross 1895677e049SGordon Ross if (sr->smb2_messageid < sr->smb2_first_msgid || 1905677e049SGordon Ross sr->smb2_messageid >= (sr->smb2_first_msgid + 1915677e049SGordon Ross sr->smb2_total_credits)) { 1925677e049SGordon Ross long long id = (long long) sr->smb2_messageid; 1935677e049SGordon Ross cmn_err(CE_WARN, "clnt %s msg ID 0x%llx " 1945677e049SGordon Ross "out of sequence in compound", 1955677e049SGordon Ross sr->session->ip_addr_str, id); 1965677e049SGordon Ross } 1975677e049SGordon Ross } 1985677e049SGordon Ross 1995677e049SGordon Ross /* Normal loop exit on next == zero */ 2005677e049SGordon Ross if (sr->smb2_next_command == 0) 2015677e049SGordon Ross break; 2025677e049SGordon Ross 2035677e049SGordon Ross /* Abundance of caution... */ 2045677e049SGordon Ross if (sr->smb2_next_command < SMB2_HDR_SIZE) 2055677e049SGordon Ross goto drop; 2065677e049SGordon Ross 2075677e049SGordon Ross /* Advance to the next header. */ 2085677e049SGordon Ross skip = sr->smb2_next_command - SMB2_HDR_SIZE; 2095677e049SGordon Ross if (MBC_ROOM_FOR(mbc, skip) == 0) 2105677e049SGordon Ross goto drop; 2115677e049SGordon Ross mbc->chain_offset += skip; 212a90cf9f2SGordon Ross } 2135677e049SGordon Ross /* Rewind back to the top. */ 2145677e049SGordon Ross mbc->chain_offset = 0; 215a90cf9f2SGordon Ross 216a90cf9f2SGordon Ross /* 217a90cf9f2SGordon Ross * Submit the request to the task queue, which calls 218a90cf9f2SGordon Ross * smb2_tq_work when the workload permits. 219a90cf9f2SGordon Ross */ 220a90cf9f2SGordon Ross sr->sr_time_submitted = gethrtime(); 221a90cf9f2SGordon Ross sr->sr_state = SMB_REQ_STATE_SUBMITTED; 222a90cf9f2SGordon Ross smb_srqueue_waitq_enter(sr->session->s_srqueue); 223a90cf9f2SGordon Ross (void) taskq_dispatch(sr->sr_server->sv_worker_pool, 224a90cf9f2SGordon Ross smb2_tq_work, sr, TQ_SLEEP); 225a90cf9f2SGordon Ross return (0); 2265677e049SGordon Ross 2275677e049SGordon Ross drop: 2285677e049SGordon Ross smb_request_free(sr); 2295677e049SGordon Ross return (-1); 230a90cf9f2SGordon Ross } 231a90cf9f2SGordon Ross 232a90cf9f2SGordon Ross static void 233a90cf9f2SGordon Ross smb2_tq_work(void *arg) 234a90cf9f2SGordon Ross { 235a90cf9f2SGordon Ross smb_request_t *sr; 236a90cf9f2SGordon Ross smb_srqueue_t *srq; 237a90cf9f2SGordon Ross 238a90cf9f2SGordon Ross sr = (smb_request_t *)arg; 239a90cf9f2SGordon Ross SMB_REQ_VALID(sr); 240a90cf9f2SGordon Ross 241a90cf9f2SGordon Ross srq = sr->session->s_srqueue; 242a90cf9f2SGordon Ross smb_srqueue_waitq_to_runq(srq); 243a90cf9f2SGordon Ross sr->sr_worker = curthread; 244a90cf9f2SGordon Ross sr->sr_time_active = gethrtime(); 245a90cf9f2SGordon Ross 246a90cf9f2SGordon Ross /* 2475677e049SGordon Ross * Always dispatch to the work function, because cancelled 2485677e049SGordon Ross * requests need an error reply (NT_STATUS_CANCELLED). 249a90cf9f2SGordon Ross */ 2505677e049SGordon Ross mutex_enter(&sr->sr_mutex); 2515677e049SGordon Ross if (sr->sr_state == SMB_REQ_STATE_SUBMITTED) 2525677e049SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE; 2535677e049SGordon Ross mutex_exit(&sr->sr_mutex); 2545677e049SGordon Ross 255a90cf9f2SGordon Ross smb2sr_work(sr); 256a90cf9f2SGordon Ross 257a90cf9f2SGordon Ross smb_srqueue_runq_exit(srq); 258a90cf9f2SGordon Ross } 259a90cf9f2SGordon Ross 2601160dcf7SMatt Barden static int 2611160dcf7SMatt Barden smb3_decrypt_msg(smb_request_t *sr) 2621160dcf7SMatt Barden { 2631160dcf7SMatt Barden int save_offset; 2641160dcf7SMatt Barden 2651160dcf7SMatt Barden if (sr->session->dialect < SMB_VERS_3_0) { 2661160dcf7SMatt Barden cmn_err(CE_WARN, "encrypted message in SMB 2.x"); 2671160dcf7SMatt Barden return (-1); 2681160dcf7SMatt Barden } 2691160dcf7SMatt Barden 2701160dcf7SMatt Barden sr->encrypted = B_TRUE; 2711160dcf7SMatt Barden save_offset = sr->command.chain_offset; 2721160dcf7SMatt Barden if (smb3_decode_tform_header(sr) != 0) { 2731160dcf7SMatt Barden cmn_err(CE_WARN, "bad transform header"); 2741160dcf7SMatt Barden return (-1); 2751160dcf7SMatt Barden } 2761160dcf7SMatt Barden sr->command.chain_offset = save_offset; 2771160dcf7SMatt Barden 2781160dcf7SMatt Barden sr->tform_ssn = smb_session_lookup_ssnid(sr->session, 2791160dcf7SMatt Barden sr->smb3_tform_ssnid); 2801160dcf7SMatt Barden if (sr->tform_ssn == NULL) { 2811160dcf7SMatt Barden cmn_err(CE_WARN, "transform header: session not found"); 2821160dcf7SMatt Barden return (-1); 2831160dcf7SMatt Barden } 2841160dcf7SMatt Barden 2851160dcf7SMatt Barden if (smb3_decrypt_sr(sr) != 0) { 2861160dcf7SMatt Barden cmn_err(CE_WARN, "smb3 decryption failed"); 2871160dcf7SMatt Barden return (-1); 2881160dcf7SMatt Barden } 2891160dcf7SMatt Barden 2901160dcf7SMatt Barden return (0); 2911160dcf7SMatt Barden } 2921160dcf7SMatt Barden 293148d1a41SMatt Barden /* 294148d1a41SMatt Barden * SMB2 credits determine how many simultaneous commands the 295148d1a41SMatt Barden * client may issue, and bounds the range of message IDs those 296148d1a41SMatt Barden * commands may use. With multi-credit support, commands may 297148d1a41SMatt Barden * use ranges of message IDs, where the credits used by each 298148d1a41SMatt Barden * command are proportional to their data transfer size. 299148d1a41SMatt Barden * 300148d1a41SMatt Barden * Every command may request an increase or decrease of 301148d1a41SMatt Barden * the currently granted credits, based on the difference 302148d1a41SMatt Barden * between the credit request and the credit charge. 303148d1a41SMatt Barden * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits 304148d1a41SMatt Barden * 305148d1a41SMatt Barden * Most commands have credit_request=1, credit_charge=1, 306148d1a41SMatt Barden * which keeps the credit grant unchanged. 307148d1a41SMatt Barden * 308148d1a41SMatt Barden * All we're really doing here (for now) is reducing the 309148d1a41SMatt Barden * credit_response if the client requests a credit increase 310148d1a41SMatt Barden * that would take their credit over the maximum, and 311148d1a41SMatt Barden * limiting the decrease so they don't run out of credits. 312148d1a41SMatt Barden * 313148d1a41SMatt Barden * Later, this could do something dynamic based on load. 314148d1a41SMatt Barden * 315148d1a41SMatt Barden * One other non-obvious bit about credits: We keep the 316148d1a41SMatt Barden * session s_max_credits low until the 1st authentication, 317148d1a41SMatt Barden * at which point we'll set the normal maximum_credits. 318148d1a41SMatt Barden * Some clients ask for more credits with session setup, 319148d1a41SMatt Barden * and we need to handle that requested increase _after_ 320148d1a41SMatt Barden * the command-specific handler returns so it won't be 321148d1a41SMatt Barden * restricted to the lower (pre-auth) limit. 322148d1a41SMatt Barden */ 323148d1a41SMatt Barden static inline void 324148d1a41SMatt Barden smb2_credit_decrease(smb_request_t *sr) 325148d1a41SMatt Barden { 326148d1a41SMatt Barden smb_session_t *session = sr->session; 327148d1a41SMatt Barden uint16_t cur, d; 328148d1a41SMatt Barden 329148d1a41SMatt Barden mutex_enter(&session->s_credits_mutex); 330148d1a41SMatt Barden cur = session->s_cur_credits; 331148d1a41SMatt Barden 332148d1a41SMatt Barden /* Handle credit decrease. */ 333148d1a41SMatt Barden d = sr->smb2_credit_charge - sr->smb2_credit_request; 334148d1a41SMatt Barden cur -= d; 335148d1a41SMatt Barden if (cur & 0x8000) { 336148d1a41SMatt Barden /* 337148d1a41SMatt Barden * underflow (bad credit charge or request) 338148d1a41SMatt Barden * leave credits unchanged (response=charge) 339148d1a41SMatt Barden */ 340148d1a41SMatt Barden cur = session->s_cur_credits; 341148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_charge; 342148d1a41SMatt Barden DTRACE_PROBE1(smb2__credit__neg, smb_request_t *, sr); 343148d1a41SMatt Barden } 344148d1a41SMatt Barden 345148d1a41SMatt Barden /* 346148d1a41SMatt Barden * The server MUST ensure that the number of credits 347148d1a41SMatt Barden * held by the client is never reduced to zero. 348148d1a41SMatt Barden * [MS-SMB2] 3.3.1.2 349148d1a41SMatt Barden */ 350148d1a41SMatt Barden if (cur == 0) { 351148d1a41SMatt Barden cur = 1; 352148d1a41SMatt Barden sr->smb2_credit_response += 1; 353148d1a41SMatt Barden DTRACE_PROBE1(smb2__credit__min, smb_request_t *, sr); 354148d1a41SMatt Barden } 355148d1a41SMatt Barden 356148d1a41SMatt Barden DTRACE_PROBE3(smb2__credit__decrease, 357148d1a41SMatt Barden smb_request_t *, sr, int, (int)cur, 358148d1a41SMatt Barden int, (int)session->s_cur_credits); 359148d1a41SMatt Barden 360148d1a41SMatt Barden session->s_cur_credits = cur; 361148d1a41SMatt Barden mutex_exit(&session->s_credits_mutex); 362148d1a41SMatt Barden } 363148d1a41SMatt Barden 364148d1a41SMatt Barden /* 365148d1a41SMatt Barden * Second half of SMB2 credit handling (increases) 366148d1a41SMatt Barden */ 367148d1a41SMatt Barden static inline void 368148d1a41SMatt Barden smb2_credit_increase(smb_request_t *sr) 369148d1a41SMatt Barden { 370148d1a41SMatt Barden smb_session_t *session = sr->session; 371148d1a41SMatt Barden uint16_t cur, d; 372148d1a41SMatt Barden 373148d1a41SMatt Barden mutex_enter(&session->s_credits_mutex); 374148d1a41SMatt Barden cur = session->s_cur_credits; 375148d1a41SMatt Barden 376148d1a41SMatt Barden /* Handle credit increase. */ 377148d1a41SMatt Barden d = sr->smb2_credit_request - sr->smb2_credit_charge; 378148d1a41SMatt Barden cur += d; 379148d1a41SMatt Barden 380148d1a41SMatt Barden /* 381148d1a41SMatt Barden * If new credits would be above max, 382148d1a41SMatt Barden * reduce the credit grant. 383148d1a41SMatt Barden */ 384148d1a41SMatt Barden if (cur > session->s_max_credits) { 385148d1a41SMatt Barden d = cur - session->s_max_credits; 386148d1a41SMatt Barden cur = session->s_max_credits; 387148d1a41SMatt Barden sr->smb2_credit_response -= d; 388148d1a41SMatt Barden DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr); 389148d1a41SMatt Barden } 390148d1a41SMatt Barden 391148d1a41SMatt Barden DTRACE_PROBE3(smb2__credit__increase, 392148d1a41SMatt Barden smb_request_t *, sr, int, (int)cur, 393148d1a41SMatt Barden int, (int)session->s_cur_credits); 394148d1a41SMatt Barden 395148d1a41SMatt Barden session->s_cur_credits = cur; 396148d1a41SMatt Barden mutex_exit(&session->s_credits_mutex); 397148d1a41SMatt Barden } 398148d1a41SMatt Barden 399148d1a41SMatt Barden /* 400148d1a41SMatt Barden * Record some statistics: latency, rx bytes, tx bytes 401148d1a41SMatt Barden * per: server, session & kshare. 402148d1a41SMatt Barden */ 403148d1a41SMatt Barden static inline void 404148d1a41SMatt Barden smb2_record_stats(smb_request_t *sr, smb_disp_stats_t *sds, boolean_t tx_only) 405148d1a41SMatt Barden { 406148d1a41SMatt Barden hrtime_t dt; 407148d1a41SMatt Barden int64_t rxb; 408148d1a41SMatt Barden int64_t txb; 409148d1a41SMatt Barden 410148d1a41SMatt Barden dt = gethrtime() - sr->sr_time_start; 411148d1a41SMatt Barden rxb = (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr); 412148d1a41SMatt Barden txb = (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr); 413148d1a41SMatt Barden 414148d1a41SMatt Barden if (!tx_only) { 415148d1a41SMatt Barden smb_server_inc_req(sr->sr_server); 416148d1a41SMatt Barden smb_latency_add_sample(&sds->sdt_lat, dt); 417148d1a41SMatt Barden atomic_add_64(&sds->sdt_rxb, rxb); 418148d1a41SMatt Barden } 419148d1a41SMatt Barden atomic_add_64(&sds->sdt_txb, txb); 420148d1a41SMatt Barden } 421148d1a41SMatt Barden 422a90cf9f2SGordon Ross /* 423a90cf9f2SGordon Ross * smb2sr_work 424a90cf9f2SGordon Ross * 425a90cf9f2SGordon Ross * This function processes each SMB command in the current request 426a90cf9f2SGordon Ross * (which may be a compound request) building a reply containing 427a90cf9f2SGordon Ross * SMB reply messages, one-to-one with the SMB commands. Some SMB 428a90cf9f2SGordon Ross * commands (change notify, blocking locks) may require both an 429a90cf9f2SGordon Ross * "interim response" and a later "async response" at completion. 430a90cf9f2SGordon Ross * In such cases, we'll encode the interim response in the reply 431a90cf9f2SGordon Ross * compound we're building, and put the (now async) command on a 432a90cf9f2SGordon Ross * list of commands that need further processing. After we've 433a90cf9f2SGordon Ross * finished processing the commands in this compound and building 434a90cf9f2SGordon Ross * the compound reply, we'll send the compound reply, and finally 435a90cf9f2SGordon Ross * process the list of async commands. 436a90cf9f2SGordon Ross * 437a90cf9f2SGordon Ross * As we work our way through the compound request and reply, 438a90cf9f2SGordon Ross * we need to keep track of the bounds of the current request 439a90cf9f2SGordon Ross * and reply. For the request, this uses an MBC_SHADOW_CHAIN 440a90cf9f2SGordon Ross * that begins at smb2_cmd_hdr. The reply is appended to the 441a90cf9f2SGordon Ross * sr->reply chain starting at smb2_reply_hdr. 442a90cf9f2SGordon Ross * 443bfe5e737SGordon Ross * This function must always free the smb request, or arrange 444bfe5e737SGordon Ross * for it to be completed and free'd later (if SDRC_SR_KEPT). 445a90cf9f2SGordon Ross */ 446a90cf9f2SGordon Ross void 447a90cf9f2SGordon Ross smb2sr_work(struct smb_request *sr) 448a90cf9f2SGordon Ross { 449a90cf9f2SGordon Ross const smb_disp_entry_t *sdd; 450a90cf9f2SGordon Ross smb_disp_stats_t *sds; 451a90cf9f2SGordon Ross smb_session_t *session; 452a90cf9f2SGordon Ross uint32_t msg_len; 453a90cf9f2SGordon Ross uint16_t cmd_idx; 454a90cf9f2SGordon Ross int rc = 0; 455a90cf9f2SGordon Ross boolean_t disconnect = B_FALSE; 456a90cf9f2SGordon Ross boolean_t related; 457a90cf9f2SGordon Ross 458a90cf9f2SGordon Ross session = sr->session; 459a90cf9f2SGordon Ross 460148d1a41SMatt Barden ASSERT(sr->smb2_async == B_FALSE); 461a90cf9f2SGordon Ross ASSERT(sr->tid_tree == 0); 462a90cf9f2SGordon Ross ASSERT(sr->uid_user == 0); 463a90cf9f2SGordon Ross ASSERT(sr->fid_ofile == 0); 464a90cf9f2SGordon Ross sr->smb_fid = (uint16_t)-1; 465a90cf9f2SGordon Ross sr->smb2_status = 0; 466a90cf9f2SGordon Ross 467a90cf9f2SGordon Ross /* temporary until we identify a user */ 468a90cf9f2SGordon Ross sr->user_cr = zone_kcred(); 469a90cf9f2SGordon Ross 470a90cf9f2SGordon Ross cmd_start: 471a90cf9f2SGordon Ross /* 4725677e049SGordon Ross * Note that we don't check sr_state here and abort the 4735677e049SGordon Ross * compound if cancelled (etc.) because some SMB2 command 4745677e049SGordon Ross * handlers need to do work even when cancelled. 475a90cf9f2SGordon Ross * 476a90cf9f2SGordon Ross * We treat some status codes as if "sticky", meaning 477a90cf9f2SGordon Ross * once they're set after some command handler returns, 478a90cf9f2SGordon Ross * all remaining commands get this status without even 4795677e049SGordon Ross * calling the command-specific handler. 480a90cf9f2SGordon Ross */ 481a90cf9f2SGordon Ross if (sr->smb2_status != NT_STATUS_CANCELLED && 482a90cf9f2SGordon Ross sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES) 483a90cf9f2SGordon Ross sr->smb2_status = 0; 484a90cf9f2SGordon Ross 4855677e049SGordon Ross /* 4865677e049SGordon Ross * Decode the request header 4875677e049SGordon Ross * 4885677e049SGordon Ross * Most problems with decoding will result in the error 4895677e049SGordon Ross * STATUS_INVALID_PARAMETER. If the decoding problem 4905677e049SGordon Ross * prevents continuing, we'll close the connection. 4915677e049SGordon Ross * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 4925677e049SGordon Ross */ 493a90cf9f2SGordon Ross sr->smb2_cmd_hdr = sr->command.chain_offset; 494a90cf9f2SGordon Ross if ((rc = smb2_decode_header(sr)) != 0) { 495a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s bad SMB2 header", 496a90cf9f2SGordon Ross session->ip_addr_str); 497a90cf9f2SGordon Ross disconnect = B_TRUE; 498a90cf9f2SGordon Ross goto cleanup; 499a90cf9f2SGordon Ross } 500a90cf9f2SGordon Ross 501a90cf9f2SGordon Ross /* 502a90cf9f2SGordon Ross * The SMB2_FLAGS_SERVER_TO_REDIR should only appear 503a90cf9f2SGordon Ross * in messages from the server back to the client. 504a90cf9f2SGordon Ross */ 505a90cf9f2SGordon Ross if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) { 506a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s bad SMB2 flags", 507a90cf9f2SGordon Ross session->ip_addr_str); 508a90cf9f2SGordon Ross disconnect = B_TRUE; 509a90cf9f2SGordon Ross goto cleanup; 510a90cf9f2SGordon Ross } 511a90cf9f2SGordon Ross related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS); 512148d1a41SMatt Barden sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR; 513148d1a41SMatt Barden if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 514148d1a41SMatt Barden /* Probably an async cancel. */ 515148d1a41SMatt Barden DTRACE_PROBE1(smb2__dispatch__async, smb_request_t *, sr); 516148d1a41SMatt Barden } else if (sr->smb2_async) { 517148d1a41SMatt Barden /* Previous command in compound went async. */ 518148d1a41SMatt Barden sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND; 519148d1a41SMatt Barden sr->smb2_async_id = SMB2_ASYNCID(sr); 520148d1a41SMatt Barden } 521a90cf9f2SGordon Ross 522a90cf9f2SGordon Ross /* 523a90cf9f2SGordon Ross * In case we bail out with an error before we get to the 524a90cf9f2SGordon Ross * section that computes the credit grant, initialize the 525a90cf9f2SGordon Ross * response header fields so that credits won't change. 526a90cf9f2SGordon Ross * Note: SMB 2.02 clients may send credit charge zero. 527a90cf9f2SGordon Ross */ 528a90cf9f2SGordon Ross if (sr->smb2_credit_charge == 0) 529a90cf9f2SGordon Ross sr->smb2_credit_charge = 1; 530a90cf9f2SGordon Ross sr->smb2_credit_response = sr->smb2_credit_charge; 531a90cf9f2SGordon Ross 532a90cf9f2SGordon Ross /* 533148d1a41SMatt Barden * Write a tentative reply header. 534148d1a41SMatt Barden * 535148d1a41SMatt Barden * We could just leave this blank, but if we're using the 536148d1a41SMatt Barden * mdb module feature that extracts packets, it's useful 537148d1a41SMatt Barden * to have the header mostly correct here. 538148d1a41SMatt Barden * 539148d1a41SMatt Barden * If we have already exhausted the output space, then the 540148d1a41SMatt Barden * client is trying something funny. Log it and kill 'em. 541a90cf9f2SGordon Ross */ 542148d1a41SMatt Barden sr->smb2_next_reply = 0; 543148d1a41SMatt Barden ASSERT((sr->reply.chain_offset & 7) == 0); 544a90cf9f2SGordon Ross sr->smb2_reply_hdr = sr->reply.chain_offset; 545a90cf9f2SGordon Ross if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) { 546a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s excessive reply", 547a90cf9f2SGordon Ross session->ip_addr_str); 548a90cf9f2SGordon Ross disconnect = B_TRUE; 549a90cf9f2SGordon Ross goto cleanup; 550a90cf9f2SGordon Ross } 551a90cf9f2SGordon Ross 552a90cf9f2SGordon Ross /* 553a90cf9f2SGordon Ross * Figure out the length of data following the SMB2 header. 554a90cf9f2SGordon Ross * It ends at either the next SMB2 header if there is one 555a90cf9f2SGordon Ross * (smb2_next_command != 0) or at the end of the message. 556a90cf9f2SGordon Ross */ 557a90cf9f2SGordon Ross if (sr->smb2_next_command != 0) { 558a90cf9f2SGordon Ross /* [MS-SMB2] says this is 8-byte aligned */ 559a90cf9f2SGordon Ross msg_len = sr->smb2_next_command; 560a90cf9f2SGordon Ross if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) || 561a90cf9f2SGordon Ross ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) { 562a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd", 563a90cf9f2SGordon Ross session->ip_addr_str); 564a90cf9f2SGordon Ross disconnect = B_TRUE; 565a90cf9f2SGordon Ross goto cleanup; 566a90cf9f2SGordon Ross } 567a90cf9f2SGordon Ross } else { 568a90cf9f2SGordon Ross msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr; 569a90cf9f2SGordon Ross } 570a90cf9f2SGordon Ross 571a90cf9f2SGordon Ross /* 572a90cf9f2SGordon Ross * Setup a shadow chain for this SMB2 command, starting 573a90cf9f2SGordon Ross * with the header and ending at either the next command 574a90cf9f2SGordon Ross * or the end of the message. The signing check below 575a90cf9f2SGordon Ross * needs the entire SMB2 command. After that's done, we 576a90cf9f2SGordon Ross * advance chain_offset to the end of the header where 577a90cf9f2SGordon Ross * the command specific handlers continue decoding. 578a90cf9f2SGordon Ross */ 579a90cf9f2SGordon Ross (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, 580a90cf9f2SGordon Ross sr->smb2_cmd_hdr, msg_len); 581a90cf9f2SGordon Ross 582ac2bf314SMatt Barden /* 583ac2bf314SMatt Barden * We will consume the data for this request from smb_data. 584ac2bf314SMatt Barden * That effectively consumes msg_len bytes from sr->command 585ac2bf314SMatt Barden * but doesn't update its chain_offset, so we need to update 586ac2bf314SMatt Barden * that here to make later received bytes accounting work. 587ac2bf314SMatt Barden */ 588ac2bf314SMatt Barden sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len; 589ac2bf314SMatt Barden ASSERT(sr->command.chain_offset <= sr->command.max_bytes); 590ac2bf314SMatt Barden 591a90cf9f2SGordon Ross /* 592a90cf9f2SGordon Ross * Validate the commmand code, get dispatch table entries. 593a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 594a90cf9f2SGordon Ross * 595a90cf9f2SGordon Ross * The last slot in the dispatch table is used to handle 596a90cf9f2SGordon Ross * invalid commands. Same for statistics. 597a90cf9f2SGordon Ross */ 598a90cf9f2SGordon Ross if (sr->smb2_cmd_code < SMB2_INVALID_CMD) 599a90cf9f2SGordon Ross cmd_idx = sr->smb2_cmd_code; 600a90cf9f2SGordon Ross else 601a90cf9f2SGordon Ross cmd_idx = SMB2_INVALID_CMD; 602a90cf9f2SGordon Ross sdd = &smb2_disp_table[cmd_idx]; 603a90cf9f2SGordon Ross sds = &session->s_server->sv_disp_stats2[cmd_idx]; 604a90cf9f2SGordon Ross 605a90cf9f2SGordon Ross /* 606a90cf9f2SGordon Ross * If this command is NOT "related" to the previous, 607a90cf9f2SGordon Ross * clear out the UID, TID, FID state that might be 608a90cf9f2SGordon Ross * left over from the previous command. 609a90cf9f2SGordon Ross * 610a90cf9f2SGordon Ross * If the command IS related, any new IDs are ignored, 611a90cf9f2SGordon Ross * and we simply continue with the previous user, tree, 612a90cf9f2SGordon Ross * and open file. 613a90cf9f2SGordon Ross */ 614a90cf9f2SGordon Ross if (!related) { 615a90cf9f2SGordon Ross /* 616a90cf9f2SGordon Ross * Drop user, tree, file; carefully ordered to 617a90cf9f2SGordon Ross * avoid dangling references: file, tree, user 618a90cf9f2SGordon Ross */ 619a90cf9f2SGordon Ross if (sr->fid_ofile != NULL) { 620a90cf9f2SGordon Ross smb_ofile_release(sr->fid_ofile); 621a90cf9f2SGordon Ross sr->fid_ofile = NULL; 622a90cf9f2SGordon Ross } 623a90cf9f2SGordon Ross if (sr->tid_tree != NULL) { 624a90cf9f2SGordon Ross smb_tree_release(sr->tid_tree); 625a90cf9f2SGordon Ross sr->tid_tree = NULL; 626a90cf9f2SGordon Ross } 627a90cf9f2SGordon Ross if (sr->uid_user != NULL) { 628a90cf9f2SGordon Ross smb_user_release(sr->uid_user); 629a90cf9f2SGordon Ross sr->uid_user = NULL; 630a90cf9f2SGordon Ross sr->user_cr = zone_kcred(); 631a90cf9f2SGordon Ross } 632a90cf9f2SGordon Ross } 633a90cf9f2SGordon Ross 634a90cf9f2SGordon Ross /* 635a90cf9f2SGordon Ross * Make sure we have a user and tree as needed 636a90cf9f2SGordon Ross * according to the flags for the this command. 637a90cf9f2SGordon Ross * Note that we may have inherited these. 638a90cf9f2SGordon Ross */ 639a90cf9f2SGordon Ross if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) { 640a90cf9f2SGordon Ross /* 641a90cf9f2SGordon Ross * This command requires a user session. 642a90cf9f2SGordon Ross */ 643a90cf9f2SGordon Ross if (related) { 644a90cf9f2SGordon Ross /* 645a90cf9f2SGordon Ross * Previous command should have given us a user. 646a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Handling Related Requests 647a90cf9f2SGordon Ross */ 648a90cf9f2SGordon Ross if (sr->uid_user == NULL) { 649a90cf9f2SGordon Ross smb2sr_put_error(sr, 650a90cf9f2SGordon Ross NT_STATUS_INVALID_PARAMETER); 651a90cf9f2SGordon Ross goto cmd_done; 652a90cf9f2SGordon Ross } 653811599a4SMatt Barden sr->smb2_ssnid = sr->uid_user->u_ssnid; 654a90cf9f2SGordon Ross } else { 655a90cf9f2SGordon Ross /* 656a90cf9f2SGordon Ross * Lookup the UID 657a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Verifying the Session 658a90cf9f2SGordon Ross */ 659a90cf9f2SGordon Ross ASSERT(sr->uid_user == NULL); 6601160dcf7SMatt Barden /* 6611160dcf7SMatt Barden * [MS-SMB2] 3.3.5.2.7 Handling Compounded Requests 6621160dcf7SMatt Barden * 6631160dcf7SMatt Barden * If this is an encrypted compound request, 6641160dcf7SMatt Barden * ensure that the ssnid in the request 6651160dcf7SMatt Barden * is the same as the tform ssnid if this 6661160dcf7SMatt Barden * message is not related. 6671160dcf7SMatt Barden * 6681160dcf7SMatt Barden * The reasons this is done seem to apply equally 6691160dcf7SMatt Barden * to uncompounded requests, so we apply it to all. 6701160dcf7SMatt Barden */ 6711160dcf7SMatt Barden 6721160dcf7SMatt Barden if (sr->encrypted && 6731160dcf7SMatt Barden sr->smb2_ssnid != sr->smb3_tform_ssnid) { 6741160dcf7SMatt Barden disconnect = B_TRUE; 6751160dcf7SMatt Barden goto cleanup; /* just do this for now */ 6761160dcf7SMatt Barden } 6771160dcf7SMatt Barden 678811599a4SMatt Barden sr->uid_user = smb_session_lookup_ssnid(session, 679811599a4SMatt Barden sr->smb2_ssnid); 680a90cf9f2SGordon Ross if (sr->uid_user == NULL) { 681a90cf9f2SGordon Ross smb2sr_put_error(sr, 682a90cf9f2SGordon Ross NT_STATUS_USER_SESSION_DELETED); 683a90cf9f2SGordon Ross goto cmd_done; 684a90cf9f2SGordon Ross } 6851160dcf7SMatt Barden 6861160dcf7SMatt Barden /* 6871160dcf7SMatt Barden * [MS-SMB2] 3.3.5.2.9 Verifying the Session 6881160dcf7SMatt Barden * 6891160dcf7SMatt Barden * If we're talking 3.x, 6901160dcf7SMatt Barden * RejectUnencryptedAccess is TRUE, 6911160dcf7SMatt Barden * Session.EncryptData is TRUE, 6921160dcf7SMatt Barden * and the message wasn't encrypted, 6931160dcf7SMatt Barden * return ACCESS_DENIED. 6941160dcf7SMatt Barden * 6951160dcf7SMatt Barden * Note that Session.EncryptData can only be TRUE when 6961160dcf7SMatt Barden * we're talking 3.x. 6971160dcf7SMatt Barden */ 6981160dcf7SMatt Barden 6991160dcf7SMatt Barden if (sr->uid_user->u_encrypt == 7001160dcf7SMatt Barden SMB_CONFIG_REQUIRED && 7011160dcf7SMatt Barden !sr->encrypted) { 7021160dcf7SMatt Barden smb2sr_put_error(sr, 7031160dcf7SMatt Barden NT_STATUS_ACCESS_DENIED); 7041160dcf7SMatt Barden goto cmd_done; 7051160dcf7SMatt Barden } 7061160dcf7SMatt Barden 707a90cf9f2SGordon Ross sr->user_cr = smb_user_getcred(sr->uid_user); 708a90cf9f2SGordon Ross } 709a90cf9f2SGordon Ross ASSERT(sr->uid_user != NULL); 7101160dcf7SMatt Barden 7111160dcf7SMatt Barden /* 7121160dcf7SMatt Barden * Encrypt if: 7131160dcf7SMatt Barden * - The cmd is not SESSION_SETUP or NEGOTIATE; AND 7141160dcf7SMatt Barden * - Session.EncryptData is TRUE 7151160dcf7SMatt Barden * 7161160dcf7SMatt Barden * Those commands suppress UID, so they can't be the cmd here. 7171160dcf7SMatt Barden */ 7181160dcf7SMatt Barden if (sr->uid_user->u_encrypt != SMB_CONFIG_DISABLED && 7191160dcf7SMatt Barden sr->tform_ssn == NULL) { 7201160dcf7SMatt Barden smb_user_hold_internal(sr->uid_user); 7211160dcf7SMatt Barden sr->tform_ssn = sr->uid_user; 7221160dcf7SMatt Barden sr->smb3_tform_ssnid = sr->smb2_ssnid; 7231160dcf7SMatt Barden } 724a90cf9f2SGordon Ross } 725a90cf9f2SGordon Ross 726a90cf9f2SGordon Ross if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) { 727a90cf9f2SGordon Ross /* 728a90cf9f2SGordon Ross * This command requires a tree connection. 729a90cf9f2SGordon Ross */ 730a90cf9f2SGordon Ross if (related) { 731a90cf9f2SGordon Ross /* 732a90cf9f2SGordon Ross * Previous command should have given us a tree. 733a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Handling Related Requests 734a90cf9f2SGordon Ross */ 735a90cf9f2SGordon Ross if (sr->tid_tree == NULL) { 736a90cf9f2SGordon Ross smb2sr_put_error(sr, 737a90cf9f2SGordon Ross NT_STATUS_INVALID_PARAMETER); 738a90cf9f2SGordon Ross goto cmd_done; 739a90cf9f2SGordon Ross } 740a90cf9f2SGordon Ross sr->smb_tid = sr->tid_tree->t_tid; 741a90cf9f2SGordon Ross } else { 742a90cf9f2SGordon Ross /* 743a90cf9f2SGordon Ross * Lookup the TID 744a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect 745a90cf9f2SGordon Ross */ 746a90cf9f2SGordon Ross ASSERT(sr->tid_tree == NULL); 747a90cf9f2SGordon Ross sr->tid_tree = smb_session_lookup_tree(session, 748a90cf9f2SGordon Ross sr->smb_tid); 749a90cf9f2SGordon Ross if (sr->tid_tree == NULL) { 750a90cf9f2SGordon Ross smb2sr_put_error(sr, 751a90cf9f2SGordon Ross NT_STATUS_NETWORK_NAME_DELETED); 752a90cf9f2SGordon Ross goto cmd_done; 753a90cf9f2SGordon Ross } 7541160dcf7SMatt Barden 7551160dcf7SMatt Barden /* 7561160dcf7SMatt Barden * [MS-SMB2] 3.3.5.2.11 Verifying the Tree Connect 7571160dcf7SMatt Barden * 7581160dcf7SMatt Barden * If we support 3.x, RejectUnencryptedAccess is TRUE, 7591160dcf7SMatt Barden * if Tcon.EncryptData is TRUE or 7601160dcf7SMatt Barden * global EncryptData is TRUE and 7611160dcf7SMatt Barden * the message wasn't encrypted, or 7621160dcf7SMatt Barden * if Tcon.EncryptData is TRUE or 7631160dcf7SMatt Barden * global EncryptData is TRUE or 7641160dcf7SMatt Barden * the request was encrypted and 7651160dcf7SMatt Barden * the connection doesn't support encryption, 7661160dcf7SMatt Barden * return ACCESS_DENIED. 7671160dcf7SMatt Barden * 7681160dcf7SMatt Barden * If RejectUnencryptedAccess is TRUE, we force 7691160dcf7SMatt Barden * max_protocol to at least 3.0. Additionally, 7701160dcf7SMatt Barden * if the tree requires encryption, we don't care 7711160dcf7SMatt Barden * what we support, we still enforce encryption. 7721160dcf7SMatt Barden */ 7731160dcf7SMatt Barden if (sr->tid_tree->t_encrypt == SMB_CONFIG_REQUIRED && 7741160dcf7SMatt Barden (!sr->encrypted || 7751160dcf7SMatt Barden (session->srv_cap & SMB2_CAP_ENCRYPTION) == 0)) { 7761160dcf7SMatt Barden smb2sr_put_error(sr, 7771160dcf7SMatt Barden NT_STATUS_ACCESS_DENIED); 7781160dcf7SMatt Barden goto cmd_done; 7791160dcf7SMatt Barden } 780a90cf9f2SGordon Ross } 781a90cf9f2SGordon Ross ASSERT(sr->tid_tree != NULL); 7821160dcf7SMatt Barden 7831160dcf7SMatt Barden /* 7841160dcf7SMatt Barden * Encrypt if: 7851160dcf7SMatt Barden * - The cmd is not TREE_CONNECT; AND 7861160dcf7SMatt Barden * - Tree.EncryptData is TRUE 7871160dcf7SMatt Barden * 7881160dcf7SMatt Barden * TREE_CONNECT suppresses TID, so that can't be the cmd here. 7891160dcf7SMatt Barden * NOTE: assumes we can't have a tree without a user 7901160dcf7SMatt Barden */ 7911160dcf7SMatt Barden if (sr->tid_tree->t_encrypt != SMB_CONFIG_DISABLED && 7921160dcf7SMatt Barden sr->tform_ssn == NULL) { 7931160dcf7SMatt Barden smb_user_hold_internal(sr->uid_user); 7941160dcf7SMatt Barden sr->tform_ssn = sr->uid_user; 7951160dcf7SMatt Barden sr->smb3_tform_ssnid = sr->smb2_ssnid; 7961160dcf7SMatt Barden } 797a90cf9f2SGordon Ross } 798a90cf9f2SGordon Ross 799a90cf9f2SGordon Ross /* 800a90cf9f2SGordon Ross * SMB2 signature verification, two parts: 801a90cf9f2SGordon Ross * (a) Require SMB2_FLAGS_SIGNED (for most request types) 802a90cf9f2SGordon Ross * (b) If SMB2_FLAGS_SIGNED is set, check the signature. 803a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2.4 Verifying the Signature 804a90cf9f2SGordon Ross */ 805a90cf9f2SGordon Ross 806a90cf9f2SGordon Ross /* 807a90cf9f2SGordon Ross * No user session means no signature check. That's OK, 808a90cf9f2SGordon Ross * i.e. for commands marked SDDF_SUPPRESS_UID above. 809a90cf9f2SGordon Ross * Note, this also means we won't sign the reply. 810a90cf9f2SGordon Ross */ 811a90cf9f2SGordon Ross if (sr->uid_user == NULL) 812a90cf9f2SGordon Ross sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 813a90cf9f2SGordon Ross 814a90cf9f2SGordon Ross /* 815a90cf9f2SGordon Ross * The SDDF_SUPPRESS_UID dispatch is set for requests that 816a90cf9f2SGordon Ross * don't need a UID (user). These also don't require a 817a90cf9f2SGordon Ross * signature check here. 8181160dcf7SMatt Barden * 8191160dcf7SMatt Barden * [MS-SMB2] 3.3.5.2.4 Verifying the Signature 8201160dcf7SMatt Barden * 8211160dcf7SMatt Barden * If the packet was successfully decrypted, the message 8221160dcf7SMatt Barden * signature has already been verified, so we can skip this. 823a90cf9f2SGordon Ross */ 824a90cf9f2SGordon Ross if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 && 8251160dcf7SMatt Barden !sr->encrypted && sr->uid_user != NULL && 826a90cf9f2SGordon Ross (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) { 827a90cf9f2SGordon Ross /* 828a90cf9f2SGordon Ross * This request type should be signed, and 829a90cf9f2SGordon Ross * we're configured to require signatures. 830a90cf9f2SGordon Ross */ 831a90cf9f2SGordon Ross if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) { 832a90cf9f2SGordon Ross smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 833a90cf9f2SGordon Ross goto cmd_done; 834a90cf9f2SGordon Ross } 835a90cf9f2SGordon Ross rc = smb2_sign_check_request(sr); 836a90cf9f2SGordon Ross if (rc != 0) { 837148d1a41SMatt Barden DTRACE_PROBE1(smb2__sign__check, smb_request_t *, sr); 838a90cf9f2SGordon Ross smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 839a90cf9f2SGordon Ross goto cmd_done; 840a90cf9f2SGordon Ross } 841a90cf9f2SGordon Ross } 842a90cf9f2SGordon Ross 843a90cf9f2SGordon Ross /* 844a90cf9f2SGordon Ross * Now that the signing check is done with smb_data, 845a90cf9f2SGordon Ross * advance past the SMB2 header we decoded earlier. 846a90cf9f2SGordon Ross * This leaves sr->smb_data correctly positioned 847a90cf9f2SGordon Ross * for command-specific decoding in the dispatch 848a90cf9f2SGordon Ross * function called next. 849a90cf9f2SGordon Ross */ 850a90cf9f2SGordon Ross sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE; 851a90cf9f2SGordon Ross 852a90cf9f2SGordon Ross /* 853148d1a41SMatt Barden * Credit adjustments (decrease) 854a90cf9f2SGordon Ross * 855148d1a41SMatt Barden * If we've gone async, credit adjustments were done 856148d1a41SMatt Barden * when we sent the interim reply. 857a90cf9f2SGordon Ross */ 858148d1a41SMatt Barden if (!sr->smb2_async) { 859148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_request; 860148d1a41SMatt Barden if (sr->smb2_credit_request < sr->smb2_credit_charge) { 861148d1a41SMatt Barden smb2_credit_decrease(sr); 862a90cf9f2SGordon Ross } 863a90cf9f2SGordon Ross } 864a90cf9f2SGordon Ross 865a90cf9f2SGordon Ross /* 866a90cf9f2SGordon Ross * The real work: call the SMB2 command handler 867a90cf9f2SGordon Ross * (except for "sticky" smb2_status - see above) 868a90cf9f2SGordon Ross */ 869a90cf9f2SGordon Ross sr->sr_time_start = gethrtime(); 870a90cf9f2SGordon Ross rc = SDRC_SUCCESS; 871a90cf9f2SGordon Ross if (sr->smb2_status == 0) { 872a90cf9f2SGordon Ross /* NB: not using pre_op */ 873a90cf9f2SGordon Ross rc = (*sdd->sdt_function)(sr); 874a90cf9f2SGordon Ross /* NB: not using post_op */ 8755677e049SGordon Ross } else { 8765677e049SGordon Ross smb2sr_put_error(sr, sr->smb2_status); 877a90cf9f2SGordon Ross } 878a90cf9f2SGordon Ross 879a90cf9f2SGordon Ross /* 880148d1a41SMatt Barden * When the sdt_function returns SDRC_SR_KEPT, it means 881148d1a41SMatt Barden * this SR may have been passed to another thread so we 882148d1a41SMatt Barden * MUST NOT touch it anymore. 883a90cf9f2SGordon Ross */ 884148d1a41SMatt Barden if (rc == SDRC_SR_KEPT) 885148d1a41SMatt Barden return; 886a90cf9f2SGordon Ross 887148d1a41SMatt Barden MBC_FLUSH(&sr->raw_data); 888a90cf9f2SGordon Ross 889148d1a41SMatt Barden /* 890148d1a41SMatt Barden * Credit adjustments (increase) 891148d1a41SMatt Barden */ 892148d1a41SMatt Barden if (!sr->smb2_async) { 893148d1a41SMatt Barden if (sr->smb2_credit_request > sr->smb2_credit_charge) { 894148d1a41SMatt Barden smb2_credit_increase(sr); 895a90cf9f2SGordon Ross } 896a90cf9f2SGordon Ross } 897a90cf9f2SGordon Ross 898a90cf9f2SGordon Ross cmd_done: 899a90cf9f2SGordon Ross switch (rc) { 900a90cf9f2SGordon Ross case SDRC_SUCCESS: 901a90cf9f2SGordon Ross break; 902a90cf9f2SGordon Ross default: 903a90cf9f2SGordon Ross /* 904a90cf9f2SGordon Ross * SMB2 does not use the other dispatch return codes. 905a90cf9f2SGordon Ross * If we see something else, log an event so we'll 906a90cf9f2SGordon Ross * know something is returning bogus status codes. 907a90cf9f2SGordon Ross * If you see these in the log, use dtrace to find 908a90cf9f2SGordon Ross * the code returning something else. 909a90cf9f2SGordon Ross */ 910a90cf9f2SGordon Ross #ifdef DEBUG 911a90cf9f2SGordon Ross cmn_err(CE_NOTE, "handler for %u returned 0x%x", 912a90cf9f2SGordon Ross sr->smb2_cmd_code, rc); 913a90cf9f2SGordon Ross #endif 914*541826a8SAndrew Stormont smb2sr_put_error(sr, NT_STATUS_INTERNAL_ERROR); 91593bc28dbSGordon Ross break; 916a90cf9f2SGordon Ross case SDRC_ERROR: 91793bc28dbSGordon Ross /* 91893bc28dbSGordon Ross * Many command handlers return SDRC_ERROR for any 91993bc28dbSGordon Ross * problems decoding the request, and don't bother 92093bc28dbSGordon Ross * setting smb2_status. For those cases, the best 92193bc28dbSGordon Ross * status return would be "invalid parameter". 92293bc28dbSGordon Ross */ 923a90cf9f2SGordon Ross if (sr->smb2_status == 0) 92493bc28dbSGordon Ross sr->smb2_status = NT_STATUS_INVALID_PARAMETER; 925*541826a8SAndrew Stormont smb2sr_put_error(sr, sr->smb2_status); 926a90cf9f2SGordon Ross break; 927a90cf9f2SGordon Ross case SDRC_DROP_VC: 928a90cf9f2SGordon Ross disconnect = B_TRUE; 929a90cf9f2SGordon Ross goto cleanup; 9305677e049SGordon Ross 9315677e049SGordon Ross case SDRC_NO_REPLY: 9325677e049SGordon Ross /* will free sr */ 9335677e049SGordon Ross goto cleanup; 934a90cf9f2SGordon Ross } 935a90cf9f2SGordon Ross 936148d1a41SMatt Barden /* 937148d1a41SMatt Barden * Pad the reply to align(8) if there will be another. 938148d1a41SMatt Barden * (We don't compound async replies.) 939148d1a41SMatt Barden */ 940148d1a41SMatt Barden if (!sr->smb2_async && sr->smb2_next_command != 0) 941148d1a41SMatt Barden (void) smb_mbc_put_align(&sr->reply, 8); 942148d1a41SMatt Barden 943148d1a41SMatt Barden /* 944148d1a41SMatt Barden * Record some statistics. Uses: 945148d1a41SMatt Barden * rxb = command.chain_offset - smb2_cmd_hdr; 946148d1a41SMatt Barden * txb = reply.chain_offset - smb2_reply_hdr; 947148d1a41SMatt Barden * which at this point represent the current cmd/reply. 948148d1a41SMatt Barden * 949148d1a41SMatt Barden * Note: If async, this does txb only, and 950148d1a41SMatt Barden * skips the smb_latency_add_sample() calls. 951148d1a41SMatt Barden */ 952148d1a41SMatt Barden smb2_record_stats(sr, sds, sr->smb2_async); 953148d1a41SMatt Barden 954a90cf9f2SGordon Ross /* 955a90cf9f2SGordon Ross * If there's a next command, figure out where it starts, 956148d1a41SMatt Barden * and fill in the next header offset for the reply. 957148d1a41SMatt Barden * Note: We sanity checked smb2_next_command above. 958a90cf9f2SGordon Ross */ 959a90cf9f2SGordon Ross if (sr->smb2_next_command != 0) { 960a90cf9f2SGordon Ross sr->command.chain_offset = 961a90cf9f2SGordon Ross sr->smb2_cmd_hdr + sr->smb2_next_command; 962a90cf9f2SGordon Ross sr->smb2_next_reply = 963a90cf9f2SGordon Ross sr->reply.chain_offset - sr->smb2_reply_hdr; 964a90cf9f2SGordon Ross } else { 965148d1a41SMatt Barden ASSERT(sr->smb2_next_reply == 0); 966a90cf9f2SGordon Ross } 967a90cf9f2SGordon Ross 968a90cf9f2SGordon Ross /* 969148d1a41SMatt Barden * Overwrite the (now final) SMB2 header for this response. 970a90cf9f2SGordon Ross */ 971a90cf9f2SGordon Ross (void) smb2_encode_header(sr, B_TRUE); 972a90cf9f2SGordon Ross 9731160dcf7SMatt Barden /* Don't sign if we're going to encrypt */ 9741160dcf7SMatt Barden if (sr->tform_ssn == NULL && 9751160dcf7SMatt Barden (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) != 0) 976a90cf9f2SGordon Ross smb2_sign_reply(sr); 977a90cf9f2SGordon Ross 978a90cf9f2SGordon Ross /* 979148d1a41SMatt Barden * Non-async runs the whole compound before send. 980148d1a41SMatt Barden * When we've gone async, send each individually. 981a90cf9f2SGordon Ross */ 982148d1a41SMatt Barden if (!sr->smb2_async && sr->smb2_next_command != 0) 983148d1a41SMatt Barden goto cmd_start; 9848d94f651SGordon Ross 9858d94f651SGordon Ross /* 9868d94f651SGordon Ross * If we have a durable handle, and this operation updated 9878d94f651SGordon Ross * the nvlist, write it out (before smb2_send_reply). 9888d94f651SGordon Ross */ 9898d94f651SGordon Ross if (sr->dh_nvl_dirty) { 9908d94f651SGordon Ross sr->dh_nvl_dirty = B_FALSE; 9918d94f651SGordon Ross smb2_dh_update_nvfile(sr); 9928d94f651SGordon Ross } 9938d94f651SGordon Ross 994a90cf9f2SGordon Ross smb2_send_reply(sr); 995148d1a41SMatt Barden if (sr->smb2_async && sr->smb2_next_command != 0) { 996148d1a41SMatt Barden MBC_FLUSH(&sr->reply); /* New reply buffer. */ 997148d1a41SMatt Barden ASSERT(sr->reply.max_bytes == sr->session->reply_max_bytes); 998148d1a41SMatt Barden goto cmd_start; 999a90cf9f2SGordon Ross } 1000a90cf9f2SGordon Ross 1001a90cf9f2SGordon Ross cleanup: 1002148d1a41SMatt Barden if (disconnect) 1003148d1a41SMatt Barden smb_session_disconnect(session); 1004a90cf9f2SGordon Ross 10058d94f651SGordon Ross /* 10068d94f651SGordon Ross * Do "postwork" for oplock (and maybe other things) 10078d94f651SGordon Ross */ 100894047d49SGordon Ross if (sr->sr_postwork != NULL) 100994047d49SGordon Ross smb2sr_run_postwork(sr); 101094047d49SGordon Ross 1011a90cf9f2SGordon Ross mutex_enter(&sr->sr_mutex); 1012a90cf9f2SGordon Ross sr->sr_state = SMB_REQ_STATE_COMPLETED; 1013a90cf9f2SGordon Ross mutex_exit(&sr->sr_mutex); 1014a90cf9f2SGordon Ross 1015a90cf9f2SGordon Ross smb_request_free(sr); 1016a90cf9f2SGordon Ross } 1017a90cf9f2SGordon Ross 1018a90cf9f2SGordon Ross /* 1019148d1a41SMatt Barden * Build interim responses for the current and all following 1020148d1a41SMatt Barden * requests in this compound, then send the compound response, 1021148d1a41SMatt Barden * leaving the SR state so that smb2sr_work() can continue its 1022148d1a41SMatt Barden * processing of this compound in "async mode". 1023a90cf9f2SGordon Ross * 1024148d1a41SMatt Barden * If we agree to "go async", this should return STATUS_SUCCESS. 1025148d1a41SMatt Barden * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and 1026148d1a41SMatt Barden * all requests following this request. (See the comments re. 1027148d1a41SMatt Barden * "sticky" smb2_status values in smb2sr_work). 1028148d1a41SMatt Barden * 1029148d1a41SMatt Barden * Note: the Async ID we assign here is arbitrary, and need only 1030148d1a41SMatt Barden * be unique among pending async responses on this connection, so 1031148d1a41SMatt Barden * this just uses a modified messageID, which is already unique. 1032148d1a41SMatt Barden * 1033148d1a41SMatt Barden * Credits: All credit changes should happen via the interim 1034148d1a41SMatt Barden * responses, so we have to manage credits here. After this 1035148d1a41SMatt Barden * returns to smb2sr_work, the final replies for all these 1036148d1a41SMatt Barden * commands will have smb2_credit_response = smb2_credit_charge 1037148d1a41SMatt Barden * (meaning no further changes to the clients' credits). 1038a90cf9f2SGordon Ross */ 1039148d1a41SMatt Barden uint32_t 1040148d1a41SMatt Barden smb2sr_go_async(smb_request_t *sr) 1041a90cf9f2SGordon Ross { 1042148d1a41SMatt Barden smb_session_t *session; 1043148d1a41SMatt Barden smb_disp_stats_t *sds; 1044148d1a41SMatt Barden uint16_t cmd_idx; 1045148d1a41SMatt Barden int32_t saved_com_offset; 1046148d1a41SMatt Barden uint32_t saved_cmd_hdr; 1047148d1a41SMatt Barden uint16_t saved_cred_resp; 1048148d1a41SMatt Barden uint32_t saved_hdr_flags; 1049148d1a41SMatt Barden uint32_t saved_reply_hdr; 1050148d1a41SMatt Barden uint32_t msg_len; 1051148d1a41SMatt Barden boolean_t disconnect = B_FALSE; 1052148d1a41SMatt Barden 1053148d1a41SMatt Barden if (sr->smb2_async) { 1054148d1a41SMatt Barden /* already went async in some previous cmd. */ 1055148d1a41SMatt Barden return (NT_STATUS_SUCCESS); 1056148d1a41SMatt Barden } 1057148d1a41SMatt Barden sr->smb2_async = B_TRUE; 1058148d1a41SMatt Barden 1059148d1a41SMatt Barden /* The "server" session always runs async. */ 1060148d1a41SMatt Barden session = sr->session; 1061148d1a41SMatt Barden if (session->sock == NULL) 1062148d1a41SMatt Barden return (NT_STATUS_SUCCESS); 1063148d1a41SMatt Barden 1064148d1a41SMatt Barden sds = NULL; 1065148d1a41SMatt Barden saved_com_offset = sr->command.chain_offset; 1066148d1a41SMatt Barden saved_cmd_hdr = sr->smb2_cmd_hdr; 1067148d1a41SMatt Barden saved_cred_resp = sr->smb2_credit_response; 1068148d1a41SMatt Barden saved_hdr_flags = sr->smb2_hdr_flags; 1069148d1a41SMatt Barden saved_reply_hdr = sr->smb2_reply_hdr; 1070a90cf9f2SGordon Ross 1071a90cf9f2SGordon Ross /* 1072148d1a41SMatt Barden * The command-specific handler should not yet have put any 1073148d1a41SMatt Barden * data in the reply except for the (place holder) header. 1074a90cf9f2SGordon Ross */ 1075148d1a41SMatt Barden if (sr->reply.chain_offset != sr->smb2_reply_hdr + SMB2_HDR_SIZE) { 1076148d1a41SMatt Barden ASSERT3U(sr->reply.chain_offset, ==, 1077148d1a41SMatt Barden sr->smb2_reply_hdr + SMB2_HDR_SIZE); 1078148d1a41SMatt Barden return (NT_STATUS_INTERNAL_ERROR); 1079148d1a41SMatt Barden } 1080a90cf9f2SGordon Ross 1081a90cf9f2SGordon Ross /* 1082148d1a41SMatt Barden * Rewind to the start of the current header in both the 1083148d1a41SMatt Barden * command and reply bufers, so the loop below can just 1084148d1a41SMatt Barden * decode/encode just in every pass. This means the 1085148d1a41SMatt Barden * current command header is decoded again, but that 1086148d1a41SMatt Barden * avoids having to special-case the first loop pass. 1087a90cf9f2SGordon Ross */ 1088148d1a41SMatt Barden sr->command.chain_offset = sr->smb2_cmd_hdr; 1089148d1a41SMatt Barden sr->reply.chain_offset = sr->smb2_reply_hdr; 1090a90cf9f2SGordon Ross 1091a90cf9f2SGordon Ross /* 1092148d1a41SMatt Barden * This command processing loop is a simplified version of 1093148d1a41SMatt Barden * smb2sr_work() that just puts an "interim response" for 1094148d1a41SMatt Barden * every command in the compound (NT_STATUS_PENDING). 1095a90cf9f2SGordon Ross */ 1096148d1a41SMatt Barden cmd_start: 1097148d1a41SMatt Barden sr->smb2_status = NT_STATUS_PENDING; 1098a90cf9f2SGordon Ross 1099bfe5e737SGordon Ross /* 1100148d1a41SMatt Barden * Decode the request header 1101bfe5e737SGordon Ross */ 1102148d1a41SMatt Barden sr->smb2_cmd_hdr = sr->command.chain_offset; 1103148d1a41SMatt Barden if ((smb2_decode_header(sr)) != 0) { 1104148d1a41SMatt Barden cmn_err(CE_WARN, "clnt %s bad SMB2 header", 1105148d1a41SMatt Barden session->ip_addr_str); 1106148d1a41SMatt Barden disconnect = B_TRUE; 1107148d1a41SMatt Barden goto cleanup; 1108148d1a41SMatt Barden } 1109148d1a41SMatt Barden sr->smb2_hdr_flags |= (SMB2_FLAGS_SERVER_TO_REDIR | 1110148d1a41SMatt Barden SMB2_FLAGS_ASYNC_COMMAND); 1111148d1a41SMatt Barden sr->smb2_async_id = SMB2_ASYNCID(sr); 1112bfe5e737SGordon Ross 1113a90cf9f2SGordon Ross /* 1114148d1a41SMatt Barden * In case we bail out... 1115a90cf9f2SGordon Ross */ 1116148d1a41SMatt Barden if (sr->smb2_credit_charge == 0) 1117148d1a41SMatt Barden sr->smb2_credit_charge = 1; 1118148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_charge; 1119a90cf9f2SGordon Ross 1120a90cf9f2SGordon Ross /* 1121148d1a41SMatt Barden * Write a tentative reply header. 1122a90cf9f2SGordon Ross */ 1123148d1a41SMatt Barden sr->smb2_next_reply = 0; 1124148d1a41SMatt Barden ASSERT((sr->reply.chain_offset & 7) == 0); 1125148d1a41SMatt Barden sr->smb2_reply_hdr = sr->reply.chain_offset; 1126148d1a41SMatt Barden if ((smb2_encode_header(sr, B_FALSE)) != 0) { 1127148d1a41SMatt Barden cmn_err(CE_WARN, "clnt %s excessive reply", 1128148d1a41SMatt Barden session->ip_addr_str); 1129148d1a41SMatt Barden disconnect = B_TRUE; 1130148d1a41SMatt Barden goto cleanup; 1131a90cf9f2SGordon Ross } 1132a90cf9f2SGordon Ross 1133a90cf9f2SGordon Ross /* 1134148d1a41SMatt Barden * Figure out the length of data... 1135a90cf9f2SGordon Ross */ 1136148d1a41SMatt Barden if (sr->smb2_next_command != 0) { 1137148d1a41SMatt Barden /* [MS-SMB2] says this is 8-byte aligned */ 1138148d1a41SMatt Barden msg_len = sr->smb2_next_command; 1139148d1a41SMatt Barden if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) || 1140148d1a41SMatt Barden ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) { 1141148d1a41SMatt Barden cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd", 1142148d1a41SMatt Barden session->ip_addr_str); 1143148d1a41SMatt Barden disconnect = B_TRUE; 1144148d1a41SMatt Barden goto cleanup; 1145148d1a41SMatt Barden } 1146148d1a41SMatt Barden } else { 1147148d1a41SMatt Barden msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr; 1148bfe5e737SGordon Ross } 1149a90cf9f2SGordon Ross 1150148d1a41SMatt Barden /* 1151148d1a41SMatt Barden * We just skip any data, so no shadow chain etc. 1152148d1a41SMatt Barden */ 1153148d1a41SMatt Barden sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len; 1154148d1a41SMatt Barden ASSERT(sr->command.chain_offset <= sr->command.max_bytes); 1155bfe5e737SGordon Ross 1156148d1a41SMatt Barden /* 1157148d1a41SMatt Barden * Validate the commmand code... 1158148d1a41SMatt Barden */ 1159148d1a41SMatt Barden if (sr->smb2_cmd_code < SMB2_INVALID_CMD) 1160148d1a41SMatt Barden cmd_idx = sr->smb2_cmd_code; 1161148d1a41SMatt Barden else 1162148d1a41SMatt Barden cmd_idx = SMB2_INVALID_CMD; 1163148d1a41SMatt Barden sds = &session->s_server->sv_disp_stats2[cmd_idx]; 1164bfe5e737SGordon Ross 1165a90cf9f2SGordon Ross /* 1166148d1a41SMatt Barden * Don't change (user, tree, file) because we want them 1167148d1a41SMatt Barden * exactly as they were when we entered. That also means 1168148d1a41SMatt Barden * we may not have the right user in sr->uid_user for 1169148d1a41SMatt Barden * signature checks, so leave that until smb2sr_work 1170148d1a41SMatt Barden * runs these commands "for real". Therefore, here 1171148d1a41SMatt Barden * we behave as if: (sr->uid_user == NULL) 1172a90cf9f2SGordon Ross */ 1173148d1a41SMatt Barden sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 1174a90cf9f2SGordon Ross 1175a90cf9f2SGordon Ross /* 1176148d1a41SMatt Barden * Credit adjustments (decrease) 1177148d1a41SMatt Barden * 1178148d1a41SMatt Barden * NOTE: interim responses are not signed. 1179148d1a41SMatt Barden * Any attacker can modify the credit grant 1180148d1a41SMatt Barden * in the response. Because of this property, 1181148d1a41SMatt Barden * it is no worse to assume the credit charge and grant 1182148d1a41SMatt Barden * are sane without verifying the signature, 1183148d1a41SMatt Barden * and that saves us a whole lot of work. 1184148d1a41SMatt Barden * If the credits WERE modified, we'll find out 1185148d1a41SMatt Barden * when we verify the signature later, 1186148d1a41SMatt Barden * which nullifies any changes caused here. 1187148d1a41SMatt Barden * 1188148d1a41SMatt Barden * Skip this on the first command, because the 1189148d1a41SMatt Barden * credit decrease was done by the caller. 1190a90cf9f2SGordon Ross */ 1191148d1a41SMatt Barden if (sr->smb2_cmd_hdr != saved_cmd_hdr) { 1192148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_request; 1193148d1a41SMatt Barden if (sr->smb2_credit_request < sr->smb2_credit_charge) { 1194148d1a41SMatt Barden smb2_credit_decrease(sr); 1195148d1a41SMatt Barden } 1196148d1a41SMatt Barden } 1197a90cf9f2SGordon Ross 1198a90cf9f2SGordon Ross /* 1199148d1a41SMatt Barden * The real work: ... (would be here) 1200a90cf9f2SGordon Ross */ 1201148d1a41SMatt Barden smb2sr_put_error(sr, sr->smb2_status); 1202a90cf9f2SGordon Ross 1203148d1a41SMatt Barden /* 1204148d1a41SMatt Barden * Credit adjustments (increase) 1205148d1a41SMatt Barden */ 1206148d1a41SMatt Barden if (sr->smb2_credit_request > sr->smb2_credit_charge) { 1207148d1a41SMatt Barden smb2_credit_increase(sr); 1208148d1a41SMatt Barden } 1209a90cf9f2SGordon Ross 1210148d1a41SMatt Barden /* cmd_done: label */ 1211a90cf9f2SGordon Ross 1212a90cf9f2SGordon Ross /* 1213148d1a41SMatt Barden * Pad the reply to align(8) if there will be another. 1214148d1a41SMatt Barden * This (interim) reply uses compounding. 1215a90cf9f2SGordon Ross */ 1216148d1a41SMatt Barden if (sr->smb2_next_command != 0) 1217148d1a41SMatt Barden (void) smb_mbc_put_align(&sr->reply, 8); 1218a90cf9f2SGordon Ross 1219148d1a41SMatt Barden /* 1220148d1a41SMatt Barden * Record some statistics. Uses: 1221148d1a41SMatt Barden * rxb = command.chain_offset - smb2_cmd_hdr; 1222148d1a41SMatt Barden * txb = reply.chain_offset - smb2_reply_hdr; 1223148d1a41SMatt Barden * which at this point represent the current cmd/reply. 1224148d1a41SMatt Barden * 1225148d1a41SMatt Barden * Note: We're doing smb_latency_add_sample() for all 1226148d1a41SMatt Barden * remaining commands NOW, which means we won't include 1227148d1a41SMatt Barden * the async part of their work in latency statistics. 1228148d1a41SMatt Barden * That's intentional, as the async part of a command 1229148d1a41SMatt Barden * would otherwise skew our latency statistics. 1230148d1a41SMatt Barden */ 1231148d1a41SMatt Barden smb2_record_stats(sr, sds, B_FALSE); 1232a90cf9f2SGordon Ross 1233148d1a41SMatt Barden /* 1234148d1a41SMatt Barden * If there's a next command, figure out where it starts, 1235148d1a41SMatt Barden * and fill in the next header offset for the reply. 1236148d1a41SMatt Barden * Note: We sanity checked smb2_next_command above. 1237148d1a41SMatt Barden */ 1238148d1a41SMatt Barden if (sr->smb2_next_command != 0) { 1239148d1a41SMatt Barden sr->command.chain_offset = 1240148d1a41SMatt Barden sr->smb2_cmd_hdr + sr->smb2_next_command; 1241148d1a41SMatt Barden sr->smb2_next_reply = 1242148d1a41SMatt Barden sr->reply.chain_offset - sr->smb2_reply_hdr; 1243148d1a41SMatt Barden } else { 1244148d1a41SMatt Barden ASSERT(sr->smb2_next_reply == 0); 1245148d1a41SMatt Barden } 1246a90cf9f2SGordon Ross 1247148d1a41SMatt Barden /* 1248148d1a41SMatt Barden * Overwrite the (now final) SMB2 header for this response. 1249148d1a41SMatt Barden */ 1250148d1a41SMatt Barden (void) smb2_encode_header(sr, B_TRUE); 1251a90cf9f2SGordon Ross 1252148d1a41SMatt Barden /* 1253148d1a41SMatt Barden * Process whole compound before sending. 1254148d1a41SMatt Barden */ 1255a90cf9f2SGordon Ross if (sr->smb2_next_command != 0) 1256148d1a41SMatt Barden goto cmd_start; 1257148d1a41SMatt Barden smb2_send_reply(sr); 1258a90cf9f2SGordon Ross 1259148d1a41SMatt Barden ASSERT(!disconnect); 1260a90cf9f2SGordon Ross 1261148d1a41SMatt Barden cleanup: 1262a90cf9f2SGordon Ross /* 1263148d1a41SMatt Barden * Restore caller's command processing state. 1264a90cf9f2SGordon Ross */ 1265148d1a41SMatt Barden sr->smb2_cmd_hdr = saved_cmd_hdr; 1266148d1a41SMatt Barden sr->command.chain_offset = saved_cmd_hdr; 1267148d1a41SMatt Barden (void) smb2_decode_header(sr); 1268148d1a41SMatt Barden sr->command.chain_offset = saved_com_offset; 1269a90cf9f2SGordon Ross 1270148d1a41SMatt Barden sr->smb2_credit_response = saved_cred_resp; 1271148d1a41SMatt Barden sr->smb2_hdr_flags = saved_hdr_flags; 1272148d1a41SMatt Barden sr->smb2_status = NT_STATUS_SUCCESS; 1273a90cf9f2SGordon Ross 1274148d1a41SMatt Barden /* 1275148d1a41SMatt Barden * In here, the "disconnect" flag just means we had an 1276148d1a41SMatt Barden * error decoding or encoding something. Rather than 1277148d1a41SMatt Barden * actually disconnect here, let's assume whatever 1278148d1a41SMatt Barden * problem we encountered will be seen by the caller 1279148d1a41SMatt Barden * as they continue processing the compound, and just 1280148d1a41SMatt Barden * restore everything and return an error. 1281148d1a41SMatt Barden */ 1282148d1a41SMatt Barden if (disconnect) { 1283148d1a41SMatt Barden sr->smb2_async = B_FALSE; 1284148d1a41SMatt Barden sr->smb2_reply_hdr = saved_reply_hdr; 1285148d1a41SMatt Barden sr->reply.chain_offset = sr->smb2_reply_hdr; 1286148d1a41SMatt Barden (void) smb2_encode_header(sr, B_FALSE); 1287148d1a41SMatt Barden return (NT_STATUS_INVALID_PARAMETER); 1288148d1a41SMatt Barden } 1289a90cf9f2SGordon Ross 1290148d1a41SMatt Barden /* 1291148d1a41SMatt Barden * The compound reply buffer we sent is now gone. 1292148d1a41SMatt Barden * Setup a new reply buffer for the caller. 1293148d1a41SMatt Barden */ 1294148d1a41SMatt Barden sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND; 1295148d1a41SMatt Barden sr->smb2_async_id = SMB2_ASYNCID(sr); 1296148d1a41SMatt Barden sr->smb2_next_reply = 0; 1297148d1a41SMatt Barden MBC_FLUSH(&sr->reply); 1298148d1a41SMatt Barden ASSERT(sr->reply.max_bytes == sr->session->reply_max_bytes); 1299148d1a41SMatt Barden ASSERT(sr->reply.chain_offset == 0); 1300148d1a41SMatt Barden sr->smb2_reply_hdr = 0; 1301148d1a41SMatt Barden (void) smb2_encode_header(sr, B_FALSE); 1302a90cf9f2SGordon Ross 1303148d1a41SMatt Barden return (NT_STATUS_SUCCESS); 1304a90cf9f2SGordon Ross } 1305a90cf9f2SGordon Ross 13061160dcf7SMatt Barden int 13071160dcf7SMatt Barden smb3_decode_tform_header(smb_request_t *sr) 13081160dcf7SMatt Barden { 13091160dcf7SMatt Barden uint16_t flags; 13101160dcf7SMatt Barden int rc; 13111160dcf7SMatt Barden uint32_t protocolid; 13121160dcf7SMatt Barden 13131160dcf7SMatt Barden rc = smb_mbc_decodef( 13141160dcf7SMatt Barden &sr->command, "l16c16cl..wq", 13151160dcf7SMatt Barden &protocolid, /* l */ 13161160dcf7SMatt Barden sr->smb2_sig, /* 16c */ 13171160dcf7SMatt Barden sr->nonce, /* 16c */ 13181160dcf7SMatt Barden &sr->msgsize, /* l */ 13191160dcf7SMatt Barden /* reserved .. */ 13201160dcf7SMatt Barden &flags, /* w */ 13211160dcf7SMatt Barden &sr->smb3_tform_ssnid); /* q */ 13221160dcf7SMatt Barden if (rc) 13231160dcf7SMatt Barden return (rc); 13241160dcf7SMatt Barden 13251160dcf7SMatt Barden ASSERT3U(protocolid, ==, SMB3_ENCRYPTED_MAGIC); 13261160dcf7SMatt Barden 13271160dcf7SMatt Barden if (flags != 1) { 13281160dcf7SMatt Barden #ifdef DEBUG 13291160dcf7SMatt Barden cmn_err(CE_NOTE, "flags field not 1: %x", flags); 13301160dcf7SMatt Barden #endif 13311160dcf7SMatt Barden return (-1); 13321160dcf7SMatt Barden } 13331160dcf7SMatt Barden 13341160dcf7SMatt Barden /* 13351160dcf7SMatt Barden * MsgSize is the amount of data the client tell us to decrypt. 13361160dcf7SMatt Barden * Make sure this value is not too big and not too small. 13371160dcf7SMatt Barden */ 13381160dcf7SMatt Barden if (sr->msgsize < SMB2_HDR_SIZE || 13391160dcf7SMatt Barden sr->msgsize > sr->session->cmd_max_bytes || 13401160dcf7SMatt Barden sr->msgsize > sr->command.max_bytes - SMB3_TFORM_HDR_SIZE) 13411160dcf7SMatt Barden return (-1); 13421160dcf7SMatt Barden 13431160dcf7SMatt Barden return (rc); 13441160dcf7SMatt Barden } 13451160dcf7SMatt Barden 13461160dcf7SMatt Barden int 13471160dcf7SMatt Barden smb3_encode_tform_header(smb_request_t *sr, struct mbuf_chain *mbc) 13481160dcf7SMatt Barden { 13491160dcf7SMatt Barden int rc; 13501160dcf7SMatt Barden 13511160dcf7SMatt Barden /* Signature and Nonce are added in smb3_encrypt_sr */ 13521160dcf7SMatt Barden rc = smb_mbc_encodef( 13531160dcf7SMatt Barden mbc, "l32.lwwq", 13541160dcf7SMatt Barden SMB3_ENCRYPTED_MAGIC, /* l */ 13551160dcf7SMatt Barden /* signature(16), nonce(16) 32. */ 13561160dcf7SMatt Barden sr->msgsize, /* l */ 13571160dcf7SMatt Barden 0, /* reserved w */ 13581160dcf7SMatt Barden 1, /* flags w */ 13591160dcf7SMatt Barden sr->smb3_tform_ssnid); /* q */ 13601160dcf7SMatt Barden 13611160dcf7SMatt Barden return (rc); 13621160dcf7SMatt Barden } 13631160dcf7SMatt Barden 1364a90cf9f2SGordon Ross int 1365a90cf9f2SGordon Ross smb2_decode_header(smb_request_t *sr) 1366a90cf9f2SGordon Ross { 1367a90cf9f2SGordon Ross uint32_t pid, tid; 1368a90cf9f2SGordon Ross uint16_t hdr_len; 1369a90cf9f2SGordon Ross int rc; 1370a90cf9f2SGordon Ross 1371a90cf9f2SGordon Ross rc = smb_mbc_decodef( 1372a90cf9f2SGordon Ross &sr->command, "Nwww..wwllqllq16c", 1373a90cf9f2SGordon Ross &hdr_len, /* w */ 1374a90cf9f2SGordon Ross &sr->smb2_credit_charge, /* w */ 1375a90cf9f2SGordon Ross &sr->smb2_chan_seq, /* w */ 1376a90cf9f2SGordon Ross /* reserved .. */ 1377a90cf9f2SGordon Ross &sr->smb2_cmd_code, /* w */ 1378a90cf9f2SGordon Ross &sr->smb2_credit_request, /* w */ 1379a90cf9f2SGordon Ross &sr->smb2_hdr_flags, /* l */ 1380a90cf9f2SGordon Ross &sr->smb2_next_command, /* l */ 1381a90cf9f2SGordon Ross &sr->smb2_messageid, /* q */ 1382a90cf9f2SGordon Ross &pid, /* l */ 1383a90cf9f2SGordon Ross &tid, /* l */ 1384148d1a41SMatt Barden &sr->smb2_ssnid, /* q */ 1385a90cf9f2SGordon Ross sr->smb2_sig); /* 16c */ 1386a90cf9f2SGordon Ross if (rc) 1387a90cf9f2SGordon Ross return (rc); 1388a90cf9f2SGordon Ross 1389a90cf9f2SGordon Ross if (hdr_len != SMB2_HDR_SIZE) 1390a90cf9f2SGordon Ross return (-1); 1391a90cf9f2SGordon Ross 1392a90cf9f2SGordon Ross if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1393a90cf9f2SGordon Ross sr->smb2_async_id = pid | 1394a90cf9f2SGordon Ross ((uint64_t)tid) << 32; 1395148d1a41SMatt Barden sr->smb_pid = 0; 1396148d1a41SMatt Barden sr->smb_tid = 0; 1397a90cf9f2SGordon Ross } else { 1398148d1a41SMatt Barden sr->smb2_async_id = 0; 1399a90cf9f2SGordon Ross sr->smb_pid = pid; 1400a90cf9f2SGordon Ross sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */ 1401a90cf9f2SGordon Ross } 1402a90cf9f2SGordon Ross 1403a90cf9f2SGordon Ross return (rc); 1404a90cf9f2SGordon Ross } 1405a90cf9f2SGordon Ross 1406a90cf9f2SGordon Ross int 1407a90cf9f2SGordon Ross smb2_encode_header(smb_request_t *sr, boolean_t overwrite) 1408a90cf9f2SGordon Ross { 1409a90cf9f2SGordon Ross uint64_t pid_tid_aid; /* pid+tid, or async id */ 1410a90cf9f2SGordon Ross int rc; 1411a90cf9f2SGordon Ross 1412a90cf9f2SGordon Ross if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1413a90cf9f2SGordon Ross pid_tid_aid = sr->smb2_async_id; 1414a90cf9f2SGordon Ross } else { 1415a90cf9f2SGordon Ross pid_tid_aid = sr->smb_pid | 1416a90cf9f2SGordon Ross ((uint64_t)sr->smb_tid) << 32; 1417a90cf9f2SGordon Ross } 1418a90cf9f2SGordon Ross 1419a90cf9f2SGordon Ross if (overwrite) { 1420a90cf9f2SGordon Ross rc = smb_mbc_poke(&sr->reply, 1421a90cf9f2SGordon Ross sr->smb2_reply_hdr, 1422a90cf9f2SGordon Ross "Nwwlwwllqqq16c", 1423a90cf9f2SGordon Ross SMB2_HDR_SIZE, /* w */ 1424a90cf9f2SGordon Ross sr->smb2_credit_charge, /* w */ 1425a90cf9f2SGordon Ross sr->smb2_status, /* l */ 1426a90cf9f2SGordon Ross sr->smb2_cmd_code, /* w */ 1427a90cf9f2SGordon Ross sr->smb2_credit_response, /* w */ 1428148d1a41SMatt Barden sr->smb2_hdr_flags, /* l */ 1429a90cf9f2SGordon Ross sr->smb2_next_reply, /* l */ 1430a90cf9f2SGordon Ross sr->smb2_messageid, /* q */ 1431a90cf9f2SGordon Ross pid_tid_aid, /* q */ 1432811599a4SMatt Barden sr->smb2_ssnid, /* q */ 1433a90cf9f2SGordon Ross sr->smb2_sig); /* 16c */ 1434a90cf9f2SGordon Ross } else { 1435a90cf9f2SGordon Ross rc = smb_mbc_encodef(&sr->reply, 1436a90cf9f2SGordon Ross "Nwwlwwllqqq16c", 1437a90cf9f2SGordon Ross SMB2_HDR_SIZE, /* w */ 1438a90cf9f2SGordon Ross sr->smb2_credit_charge, /* w */ 1439a90cf9f2SGordon Ross sr->smb2_status, /* l */ 1440a90cf9f2SGordon Ross sr->smb2_cmd_code, /* w */ 1441a90cf9f2SGordon Ross sr->smb2_credit_response, /* w */ 1442148d1a41SMatt Barden sr->smb2_hdr_flags, /* l */ 1443a90cf9f2SGordon Ross sr->smb2_next_reply, /* l */ 1444a90cf9f2SGordon Ross sr->smb2_messageid, /* q */ 1445a90cf9f2SGordon Ross pid_tid_aid, /* q */ 1446811599a4SMatt Barden sr->smb2_ssnid, /* q */ 1447a90cf9f2SGordon Ross sr->smb2_sig); /* 16c */ 1448a90cf9f2SGordon Ross } 1449a90cf9f2SGordon Ross 1450a90cf9f2SGordon Ross return (rc); 1451a90cf9f2SGordon Ross } 1452a90cf9f2SGordon Ross 1453a90cf9f2SGordon Ross void 1454a90cf9f2SGordon Ross smb2_send_reply(smb_request_t *sr) 1455a90cf9f2SGordon Ross { 14561160dcf7SMatt Barden struct mbuf_chain enc_reply; 14571160dcf7SMatt Barden smb_session_t *session = sr->session; 14581160dcf7SMatt Barden void *tmpbuf; 14591160dcf7SMatt Barden size_t buflen; 14601160dcf7SMatt Barden struct mbuf_chain tmp; 14611160dcf7SMatt Barden 14621160dcf7SMatt Barden /* 14631160dcf7SMatt Barden * [MS-SMB2] 3.3.4.1.4 Encrypting the Message 14641160dcf7SMatt Barden * 14651160dcf7SMatt Barden * When the connection supports encryption and the dialect 14661160dcf7SMatt Barden * is 3.x, encrypt if: 14671160dcf7SMatt Barden * - The request was encrypted OR 14681160dcf7SMatt Barden * - The cmd is not SESSION_SETUP or NEGOTIATE AND 14691160dcf7SMatt Barden * -- Session.EncryptData is TRUE OR 14701160dcf7SMatt Barden * -- The cmd is not TREE_CONNECT AND 14711160dcf7SMatt Barden * --- Tree.EncryptData is TRUE 14721160dcf7SMatt Barden * 14731160dcf7SMatt Barden * This boils down to sr->tform_ssn != NULL, and the rest 14741160dcf7SMatt Barden * is enforced when tform_ssn is set. 14751160dcf7SMatt Barden */ 14761160dcf7SMatt Barden 14771160dcf7SMatt Barden if ((session->capabilities & SMB2_CAP_ENCRYPTION) == 0 || 14781160dcf7SMatt Barden sr->tform_ssn == NULL) { 14791160dcf7SMatt Barden if (smb_session_send(sr->session, 0, &sr->reply) == 0) 14801160dcf7SMatt Barden sr->reply.chain = 0; 14811160dcf7SMatt Barden return; 14821160dcf7SMatt Barden } 14831160dcf7SMatt Barden 14841160dcf7SMatt Barden sr->msgsize = sr->reply.chain_offset; 14851160dcf7SMatt Barden (void) MBC_SHADOW_CHAIN(&tmp, &sr->reply, 14861160dcf7SMatt Barden 0, sr->msgsize); 14871160dcf7SMatt Barden 14881160dcf7SMatt Barden buflen = SMB3_TFORM_HDR_SIZE + sr->msgsize; 14891160dcf7SMatt Barden 14901160dcf7SMatt Barden /* taken from smb_request_init_command_mbuf */ 14911160dcf7SMatt Barden tmpbuf = kmem_alloc(buflen, KM_SLEEP); 14921160dcf7SMatt Barden MBC_ATTACH_BUF(&enc_reply, tmpbuf, buflen); 14931160dcf7SMatt Barden enc_reply.flags = 0; 14941160dcf7SMatt Barden enc_reply.shadow_of = NULL; 14951160dcf7SMatt Barden 14961160dcf7SMatt Barden if (smb3_encode_tform_header(sr, &enc_reply) != 0) { 14971160dcf7SMatt Barden cmn_err(CE_WARN, "couldn't encode transform header"); 14981160dcf7SMatt Barden goto errout; 14991160dcf7SMatt Barden } 15001160dcf7SMatt Barden if (smb3_encrypt_sr(sr, &tmp, &enc_reply) != 0) { 15011160dcf7SMatt Barden cmn_err(CE_WARN, "smb3 encryption failed"); 15021160dcf7SMatt Barden goto errout; 15031160dcf7SMatt Barden } 15041160dcf7SMatt Barden 15051160dcf7SMatt Barden if (smb_session_send(sr->session, 0, &enc_reply) == 0) 15061160dcf7SMatt Barden enc_reply.chain = 0; 15071160dcf7SMatt Barden return; 1508a90cf9f2SGordon Ross 15091160dcf7SMatt Barden errout: 15101160dcf7SMatt Barden kmem_free(tmpbuf, buflen); 15111160dcf7SMatt Barden smb_session_disconnect(sr->session); 1512a90cf9f2SGordon Ross } 1513a90cf9f2SGordon Ross 1514a90cf9f2SGordon Ross /* 1515a90cf9f2SGordon Ross * This wrapper function exists to help catch calls to smbsr_status() 1516a90cf9f2SGordon Ross * (which is SMB1-specific) in common code. See smbsr_status(). 1517a90cf9f2SGordon Ross * If the log message below is seen, put a dtrace probe on this 1518a90cf9f2SGordon Ross * function with a stack() action to see who is calling the SMB1 1519a90cf9f2SGordon Ross * "put error" from common code, and fix it. 1520a90cf9f2SGordon Ross */ 1521a90cf9f2SGordon Ross void 1522a90cf9f2SGordon Ross smbsr_status_smb2(smb_request_t *sr, DWORD status) 1523a90cf9f2SGordon Ross { 1524a90cf9f2SGordon Ross const char *name; 1525a90cf9f2SGordon Ross 1526a90cf9f2SGordon Ross if (sr->smb2_cmd_code < SMB2__NCMDS) 1527a90cf9f2SGordon Ross name = smb2_disp_table[sr->smb2_cmd_code].sdt_name; 1528a90cf9f2SGordon Ross else 1529a90cf9f2SGordon Ross name = "<unknown>"; 1530a90cf9f2SGordon Ross #ifdef DEBUG 1531a90cf9f2SGordon Ross cmn_err(CE_NOTE, "smbsr_status called for %s", name); 1532a90cf9f2SGordon Ross #endif 1533a90cf9f2SGordon Ross 1534a90cf9f2SGordon Ross smb2sr_put_error_data(sr, status, NULL); 1535a90cf9f2SGordon Ross } 1536a90cf9f2SGordon Ross 1537a90cf9f2SGordon Ross void 1538a90cf9f2SGordon Ross smb2sr_put_errno(struct smb_request *sr, int errnum) 1539a90cf9f2SGordon Ross { 1540a90cf9f2SGordon Ross uint32_t status = smb_errno2status(errnum); 1541a90cf9f2SGordon Ross smb2sr_put_error_data(sr, status, NULL); 1542a90cf9f2SGordon Ross } 1543a90cf9f2SGordon Ross 1544a90cf9f2SGordon Ross void 1545a90cf9f2SGordon Ross smb2sr_put_error(smb_request_t *sr, uint32_t status) 1546a90cf9f2SGordon Ross { 1547a90cf9f2SGordon Ross smb2sr_put_error_data(sr, status, NULL); 1548a90cf9f2SGordon Ross } 1549a90cf9f2SGordon Ross 1550a90cf9f2SGordon Ross /* 1551a90cf9f2SGordon Ross * Build an SMB2 error response. [MS-SMB2] 2.2.2 1552a90cf9f2SGordon Ross */ 1553a90cf9f2SGordon Ross void 1554a90cf9f2SGordon Ross smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc) 1555a90cf9f2SGordon Ross { 1556a90cf9f2SGordon Ross DWORD len; 1557a90cf9f2SGordon Ross 1558a90cf9f2SGordon Ross /* 1559a90cf9f2SGordon Ross * The common dispatch code writes this when it 1560a90cf9f2SGordon Ross * updates the SMB2 header before sending. 1561a90cf9f2SGordon Ross */ 1562a90cf9f2SGordon Ross sr->smb2_status = status; 1563a90cf9f2SGordon Ross 1564a90cf9f2SGordon Ross /* Rewind to the end of the SMB header. */ 1565a90cf9f2SGordon Ross sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE; 1566a90cf9f2SGordon Ross 1567a90cf9f2SGordon Ross /* 1568a90cf9f2SGordon Ross * NB: Must provide at least one byte of error data, 1569a90cf9f2SGordon Ross * per [MS-SMB2] 2.2.2 1570a90cf9f2SGordon Ross */ 1571a90cf9f2SGordon Ross if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) { 1572a90cf9f2SGordon Ross (void) smb_mbc_encodef( 1573a90cf9f2SGordon Ross &sr->reply, 1574a90cf9f2SGordon Ross "wwlC", 1575a90cf9f2SGordon Ross 9, /* StructSize */ /* w */ 1576a90cf9f2SGordon Ross 0, /* reserved */ /* w */ 1577a90cf9f2SGordon Ross len, /* l */ 1578a90cf9f2SGordon Ross mbc); /* C */ 1579a90cf9f2SGordon Ross } else { 1580a90cf9f2SGordon Ross (void) smb_mbc_encodef( 1581a90cf9f2SGordon Ross &sr->reply, 1582a90cf9f2SGordon Ross "wwl.", 1583a90cf9f2SGordon Ross 9, /* StructSize */ /* w */ 1584a90cf9f2SGordon Ross 0, /* reserved */ /* w */ 1585a90cf9f2SGordon Ross 0); /* l. */ 1586a90cf9f2SGordon Ross } 1587a90cf9f2SGordon Ross } 1588a90cf9f2SGordon Ross 1589a90cf9f2SGordon Ross /* 1590a90cf9f2SGordon Ross * smb2sr_lookup_fid 1591a90cf9f2SGordon Ross * 1592a90cf9f2SGordon Ross * Setup sr->fid_ofile, either inherited from a related command, 1593a90cf9f2SGordon Ross * or obtained via FID lookup. Similar inheritance logic as in 1594a90cf9f2SGordon Ross * smb2sr_work. 1595a90cf9f2SGordon Ross */ 1596a90cf9f2SGordon Ross uint32_t 1597a90cf9f2SGordon Ross smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid) 1598a90cf9f2SGordon Ross { 1599a90cf9f2SGordon Ross boolean_t related = sr->smb2_hdr_flags & 1600a90cf9f2SGordon Ross SMB2_FLAGS_RELATED_OPERATIONS; 1601a90cf9f2SGordon Ross 1602a90cf9f2SGordon Ross if (related) { 1603a90cf9f2SGordon Ross if (sr->fid_ofile == NULL) 1604a90cf9f2SGordon Ross return (NT_STATUS_INVALID_PARAMETER); 1605a90cf9f2SGordon Ross sr->smb_fid = sr->fid_ofile->f_fid; 1606a90cf9f2SGordon Ross return (0); 1607a90cf9f2SGordon Ross } 1608a90cf9f2SGordon Ross 1609a90cf9f2SGordon Ross /* 1610a90cf9f2SGordon Ross * If we could be sure this is called only once per cmd, 1611a90cf9f2SGordon Ross * we could simply ASSERT(sr->fid_ofile == NULL) here. 1612a90cf9f2SGordon Ross * However, there are cases where it can be called again 1613a90cf9f2SGordon Ross * handling the same command, so let's tolerate that. 1614a90cf9f2SGordon Ross */ 1615a90cf9f2SGordon Ross if (sr->fid_ofile == NULL) { 1616a90cf9f2SGordon Ross sr->smb_fid = (uint16_t)fid->temporal; 1617a90cf9f2SGordon Ross sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid); 1618a90cf9f2SGordon Ross } 1619811599a4SMatt Barden if (sr->fid_ofile == NULL || 1620811599a4SMatt Barden sr->fid_ofile->f_persistid != fid->persistent) 1621a90cf9f2SGordon Ross return (NT_STATUS_FILE_CLOSED); 1622a90cf9f2SGordon Ross 1623a90cf9f2SGordon Ross return (0); 1624a90cf9f2SGordon Ross } 1625a90cf9f2SGordon Ross 1626a90cf9f2SGordon Ross /* 1627a90cf9f2SGordon Ross * smb2_dispatch_stats_init 1628a90cf9f2SGordon Ross * 1629a90cf9f2SGordon Ross * Initializes dispatch statistics for SMB2. 1630a90cf9f2SGordon Ross * See also smb_dispatch_stats_init(), which fills in 1631a90cf9f2SGordon Ross * the lower part of the statistics array, from zero 1632a90cf9f2SGordon Ross * through SMB_COM_NUM; 1633a90cf9f2SGordon Ross */ 1634a90cf9f2SGordon Ross void 1635a90cf9f2SGordon Ross smb2_dispatch_stats_init(smb_server_t *sv) 1636a90cf9f2SGordon Ross { 1637a90cf9f2SGordon Ross smb_disp_stats_t *sds = sv->sv_disp_stats2; 1638a90cf9f2SGordon Ross smb_kstat_req_t *ksr; 1639a90cf9f2SGordon Ross int i; 1640a90cf9f2SGordon Ross 1641a90cf9f2SGordon Ross ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2; 1642a90cf9f2SGordon Ross 1643a90cf9f2SGordon Ross for (i = 0; i < SMB2__NCMDS; i++, ksr++) { 1644a90cf9f2SGordon Ross smb_latency_init(&sds[i].sdt_lat); 1645a90cf9f2SGordon Ross (void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name, 1646a90cf9f2SGordon Ross sizeof (ksr->kr_name)); 1647a90cf9f2SGordon Ross } 1648a90cf9f2SGordon Ross } 1649a90cf9f2SGordon Ross 1650a90cf9f2SGordon Ross /* 1651a90cf9f2SGordon Ross * smb2_dispatch_stats_fini 1652a90cf9f2SGordon Ross * 1653a90cf9f2SGordon Ross * Frees and destroyes the resources used for statistics. 1654a90cf9f2SGordon Ross */ 1655a90cf9f2SGordon Ross void 1656a90cf9f2SGordon Ross smb2_dispatch_stats_fini(smb_server_t *sv) 1657a90cf9f2SGordon Ross { 1658a90cf9f2SGordon Ross smb_disp_stats_t *sds = sv->sv_disp_stats2; 1659a90cf9f2SGordon Ross int i; 1660a90cf9f2SGordon Ross 1661a90cf9f2SGordon Ross for (i = 0; i < SMB2__NCMDS; i++) 1662a90cf9f2SGordon Ross smb_latency_destroy(&sds[i].sdt_lat); 1663a90cf9f2SGordon Ross } 1664a90cf9f2SGordon Ross 1665a90cf9f2SGordon Ross void 1666a90cf9f2SGordon Ross smb2_dispatch_stats_update(smb_server_t *sv, 1667a90cf9f2SGordon Ross smb_kstat_req_t *ksr, int first, int nreq) 1668a90cf9f2SGordon Ross { 1669a90cf9f2SGordon Ross smb_disp_stats_t *sds = sv->sv_disp_stats2; 1670a90cf9f2SGordon Ross int i; 1671a90cf9f2SGordon Ross int last; 1672a90cf9f2SGordon Ross 1673a90cf9f2SGordon Ross last = first + nreq - 1; 1674a90cf9f2SGordon Ross 1675a90cf9f2SGordon Ross if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS)) { 1676a90cf9f2SGordon Ross for (i = first; i <= last; i++, ksr++) { 1677a90cf9f2SGordon Ross ksr->kr_rxb = sds[i].sdt_rxb; 1678a90cf9f2SGordon Ross ksr->kr_txb = sds[i].sdt_txb; 1679a90cf9f2SGordon Ross mutex_enter(&sds[i].sdt_lat.ly_mutex); 1680a90cf9f2SGordon Ross ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq; 1681a90cf9f2SGordon Ross ksr->kr_sum = sds[i].sdt_lat.ly_a_sum; 1682a90cf9f2SGordon Ross ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean; 1683a90cf9f2SGordon Ross ksr->kr_a_stddev = 1684a90cf9f2SGordon Ross sds[i].sdt_lat.ly_a_stddev; 1685a90cf9f2SGordon Ross ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean; 1686a90cf9f2SGordon Ross ksr->kr_d_stddev = 1687a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_stddev; 1688a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_mean = 0; 1689a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_nreq = 0; 1690a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_stddev = 0; 1691a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_sum = 0; 1692a90cf9f2SGordon Ross mutex_exit(&sds[i].sdt_lat.ly_mutex); 1693a90cf9f2SGordon Ross } 1694a90cf9f2SGordon Ross } 1695a90cf9f2SGordon Ross } 169694047d49SGordon Ross 169794047d49SGordon Ross /* 169894047d49SGordon Ross * Append new_sr to the postwork queue. sr->smb2_cmd_code encodes 169994047d49SGordon Ross * the action that should be run by this sr. 170094047d49SGordon Ross * 170194047d49SGordon Ross * This queue is rarely used (and normally empty) so we're OK 170294047d49SGordon Ross * using a simple "walk to tail and insert" here. 170394047d49SGordon Ross */ 170494047d49SGordon Ross void 170594047d49SGordon Ross smb2sr_append_postwork(smb_request_t *top_sr, smb_request_t *new_sr) 170694047d49SGordon Ross { 170794047d49SGordon Ross smb_request_t *last_sr; 170894047d49SGordon Ross 170994047d49SGordon Ross ASSERT(top_sr->session->dialect >= SMB_VERS_2_BASE); 171094047d49SGordon Ross 171194047d49SGordon Ross last_sr = top_sr; 171294047d49SGordon Ross while (last_sr->sr_postwork != NULL) 171394047d49SGordon Ross last_sr = last_sr->sr_postwork; 171494047d49SGordon Ross 171594047d49SGordon Ross last_sr->sr_postwork = new_sr; 171694047d49SGordon Ross } 171794047d49SGordon Ross 171894047d49SGordon Ross /* 171994047d49SGordon Ross * Run any "post work" that was appended to the main SR while it 172094047d49SGordon Ross * was running. This is called after the request has been sent 172194047d49SGordon Ross * for the main SR, and used in cases i.e. the oplock code, where 172294047d49SGordon Ross * we need to send something to the client only _after_ the main 172394047d49SGordon Ross * sr request has gone out. 172494047d49SGordon Ross */ 172594047d49SGordon Ross static void 172694047d49SGordon Ross smb2sr_run_postwork(smb_request_t *top_sr) 172794047d49SGordon Ross { 172894047d49SGordon Ross smb_request_t *post_sr; /* the one we're running */ 172994047d49SGordon Ross smb_request_t *next_sr; 173094047d49SGordon Ross 173194047d49SGordon Ross while ((post_sr = top_sr->sr_postwork) != NULL) { 173294047d49SGordon Ross next_sr = post_sr->sr_postwork; 173394047d49SGordon Ross top_sr->sr_postwork = next_sr; 173494047d49SGordon Ross post_sr->sr_postwork = NULL; 173594047d49SGordon Ross 173694047d49SGordon Ross post_sr->sr_worker = top_sr->sr_worker; 173794047d49SGordon Ross post_sr->sr_state = SMB_REQ_STATE_ACTIVE; 173894047d49SGordon Ross 173994047d49SGordon Ross switch (post_sr->smb2_cmd_code) { 174094047d49SGordon Ross case SMB2_OPLOCK_BREAK: 174194047d49SGordon Ross smb_oplock_send_brk(post_sr); 174294047d49SGordon Ross break; 174394047d49SGordon Ross default: 174494047d49SGordon Ross ASSERT(0); 174594047d49SGordon Ross } 17468d94f651SGordon Ross 17478d94f651SGordon Ross /* 17488d94f651SGordon Ross * If we have a durable handle, and this operation 17498d94f651SGordon Ross * updated the nvlist, write it out. 17508d94f651SGordon Ross */ 17518d94f651SGordon Ross if (post_sr->dh_nvl_dirty) { 17528d94f651SGordon Ross post_sr->dh_nvl_dirty = B_FALSE; 17538d94f651SGordon Ross smb2_dh_update_nvfile(post_sr); 17548d94f651SGordon Ross } 17558d94f651SGordon Ross 175694047d49SGordon Ross post_sr->sr_state = SMB_REQ_STATE_COMPLETED; 175794047d49SGordon Ross smb_request_free(post_sr); 175894047d49SGordon Ross } 175994047d49SGordon Ross } 1760