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 /* 1358ccc3dcSGordon Ross * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 14a90cf9f2SGordon Ross */ 15a90cf9f2SGordon Ross 16a90cf9f2SGordon Ross 17a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h> 18a90cf9f2SGordon Ross #include <smbsrv/smb_kstat.h> 19a90cf9f2SGordon Ross #include <smbsrv/smb2.h> 20a90cf9f2SGordon Ross 21*148d1a41SMatt Barden #define SMB2_ASYNCID(sr) (sr->smb2_messageid ^ (1ULL << 62)) 22a90cf9f2SGordon Ross 23a90cf9f2SGordon Ross smb_sdrc_t smb2_invalid_cmd(smb_request_t *); 24a90cf9f2SGordon Ross static void smb2_tq_work(void *); 25a90cf9f2SGordon Ross 26adb064afSToomas Soome static const smb_disp_entry_t 27a90cf9f2SGordon Ross smb2_disp_table[SMB2__NCMDS] = { 28a90cf9f2SGordon Ross 29a90cf9f2SGordon Ross /* text-name, pre, func, post, cmd-code, dialect, flags */ 30a90cf9f2SGordon Ross 31a90cf9f2SGordon Ross { "smb2_negotiate", NULL, 32a90cf9f2SGordon Ross smb2_negotiate, NULL, 0, 0, 33a90cf9f2SGordon Ross SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 34a90cf9f2SGordon Ross 35a90cf9f2SGordon Ross { "smb2_session_setup", NULL, 36a90cf9f2SGordon Ross smb2_session_setup, NULL, 0, 0, 37a90cf9f2SGordon Ross SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 38a90cf9f2SGordon Ross 39a90cf9f2SGordon Ross { "smb2_logoff", NULL, 40a90cf9f2SGordon Ross smb2_logoff, NULL, 0, 0, 41a90cf9f2SGordon Ross SDDF_SUPPRESS_TID }, 42a90cf9f2SGordon Ross 43a90cf9f2SGordon Ross { "smb2_tree_connect", NULL, 44a90cf9f2SGordon Ross smb2_tree_connect, NULL, 0, 0, 45a90cf9f2SGordon Ross SDDF_SUPPRESS_TID }, 46a90cf9f2SGordon Ross 47a90cf9f2SGordon Ross { "smb2_tree_disconn", NULL, 48a90cf9f2SGordon Ross smb2_tree_disconn, NULL, 0, 0 }, 49a90cf9f2SGordon Ross 50a90cf9f2SGordon Ross { "smb2_create", NULL, 51a90cf9f2SGordon Ross smb2_create, NULL, 0, 0 }, 52a90cf9f2SGordon Ross 53a90cf9f2SGordon Ross { "smb2_close", NULL, 54a90cf9f2SGordon Ross smb2_close, NULL, 0, 0 }, 55a90cf9f2SGordon Ross 56a90cf9f2SGordon Ross { "smb2_flush", NULL, 57a90cf9f2SGordon Ross smb2_flush, NULL, 0, 0 }, 58a90cf9f2SGordon Ross 59a90cf9f2SGordon Ross { "smb2_read", NULL, 60a90cf9f2SGordon Ross smb2_read, NULL, 0, 0 }, 61a90cf9f2SGordon Ross 62a90cf9f2SGordon Ross { "smb2_write", NULL, 63a90cf9f2SGordon Ross smb2_write, NULL, 0, 0 }, 64a90cf9f2SGordon Ross 65a90cf9f2SGordon Ross { "smb2_lock", NULL, 66a90cf9f2SGordon Ross smb2_lock, NULL, 0, 0 }, 67a90cf9f2SGordon Ross 68a90cf9f2SGordon Ross { "smb2_ioctl", NULL, 69a90cf9f2SGordon Ross smb2_ioctl, NULL, 0, 0 }, 70a90cf9f2SGordon Ross 71a90cf9f2SGordon Ross { "smb2_cancel", NULL, 725677e049SGordon Ross smb2_cancel, NULL, 0, 0, 735677e049SGordon Ross SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 74a90cf9f2SGordon Ross 75a90cf9f2SGordon Ross { "smb2_echo", NULL, 76a90cf9f2SGordon Ross smb2_echo, NULL, 0, 0, 77a90cf9f2SGordon Ross SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 78a90cf9f2SGordon Ross 79a90cf9f2SGordon Ross { "smb2_query_dir", NULL, 80a90cf9f2SGordon Ross smb2_query_dir, NULL, 0, 0 }, 81a90cf9f2SGordon Ross 82a90cf9f2SGordon Ross { "smb2_change_notify", NULL, 83a90cf9f2SGordon Ross smb2_change_notify, NULL, 0, 0 }, 84a90cf9f2SGordon Ross 85a90cf9f2SGordon Ross { "smb2_query_info", NULL, 86a90cf9f2SGordon Ross smb2_query_info, NULL, 0, 0 }, 87a90cf9f2SGordon Ross 88a90cf9f2SGordon Ross { "smb2_set_info", NULL, 89a90cf9f2SGordon Ross smb2_set_info, NULL, 0, 0 }, 90a90cf9f2SGordon Ross 91a90cf9f2SGordon Ross { "smb2_oplock_break_ack", NULL, 92a90cf9f2SGordon Ross smb2_oplock_break_ack, NULL, 0, 0 }, 93a90cf9f2SGordon Ross 94a90cf9f2SGordon Ross { "smb2_invalid_cmd", NULL, 95a90cf9f2SGordon Ross smb2_invalid_cmd, NULL, 0, 0, 96a90cf9f2SGordon Ross SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 97a90cf9f2SGordon Ross }; 98a90cf9f2SGordon Ross 99a90cf9f2SGordon Ross smb_sdrc_t 100a90cf9f2SGordon Ross smb2_invalid_cmd(smb_request_t *sr) 101a90cf9f2SGordon Ross { 102a90cf9f2SGordon Ross #ifdef DEBUG 103a90cf9f2SGordon Ross cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code", 104a90cf9f2SGordon Ross sr->session->ip_addr_str); 105a90cf9f2SGordon Ross #endif 106a90cf9f2SGordon Ross sr->smb2_status = NT_STATUS_INVALID_PARAMETER; 107a90cf9f2SGordon Ross return (SDRC_DROP_VC); 108a90cf9f2SGordon Ross } 109a90cf9f2SGordon Ross 110a90cf9f2SGordon Ross /* 111a90cf9f2SGordon Ross * This is the SMB2 handler for new smb requests, called from 112a90cf9f2SGordon Ross * smb_session_reader after SMB negotiate is done. For most SMB2 113a90cf9f2SGordon Ross * requests, we just enqueue them for the smb_session_worker to 114a90cf9f2SGordon Ross * execute via the task queue, so they can block for resources 115a90cf9f2SGordon Ross * without stopping the reader thread. A few protocol messages 116a90cf9f2SGordon Ross * are special cases and are handled directly here in the reader 117a90cf9f2SGordon Ross * thread so they don't wait for taskq scheduling. 118a90cf9f2SGordon Ross * 119a90cf9f2SGordon Ross * This function must either enqueue the new request for 120a90cf9f2SGordon Ross * execution via the task queue, or execute it directly 121a90cf9f2SGordon Ross * and then free it. If this returns non-zero, the caller 122a90cf9f2SGordon Ross * will drop the session. 123a90cf9f2SGordon Ross */ 124a90cf9f2SGordon Ross int 125a90cf9f2SGordon Ross smb2sr_newrq(smb_request_t *sr) 126a90cf9f2SGordon Ross { 1275677e049SGordon Ross struct mbuf_chain *mbc = &sr->command; 128a90cf9f2SGordon Ross uint32_t magic; 1295677e049SGordon Ross int rc, skip; 1305677e049SGordon Ross 1315677e049SGordon Ross if (smb_mbc_peek(mbc, 0, "l", &magic) != 0) 1325677e049SGordon Ross goto drop; 1335677e049SGordon Ross 1345677e049SGordon Ross if (magic != SMB2_PROTOCOL_MAGIC) 1355677e049SGordon Ross goto drop; 136a90cf9f2SGordon Ross 137a90cf9f2SGordon Ross /* 1385677e049SGordon Ross * Walk the SMB2 commands in this compound message and 1395677e049SGordon Ross * keep track of the range of message IDs it uses. 140a90cf9f2SGordon Ross */ 1415677e049SGordon Ross for (;;) { 1425677e049SGordon Ross if (smb2_decode_header(sr) != 0) 1435677e049SGordon Ross goto drop; 1445677e049SGordon Ross 1455677e049SGordon Ross /* 1465677e049SGordon Ross * Cancel requests are special: They refer to 1475677e049SGordon Ross * an earlier message ID (or an async. ID), 1485677e049SGordon Ross * never a new ID, and are never compounded. 1495677e049SGordon Ross * This is intentionally not "goto drop" 1505677e049SGordon Ross * because rc may be zero (success). 1515677e049SGordon Ross */ 1525677e049SGordon Ross if (sr->smb2_cmd_code == SMB2_CANCEL) { 1535677e049SGordon Ross rc = smb2_newrq_cancel(sr); 1545677e049SGordon Ross smb_request_free(sr); 1555677e049SGordon Ross return (rc); 1565677e049SGordon Ross } 1575677e049SGordon Ross 1585677e049SGordon Ross /* 1595677e049SGordon Ross * Keep track of the total credits in this compound 1605677e049SGordon Ross * and the first (real) message ID (not: 0, -1) 1615677e049SGordon Ross * While we're looking, verify that all (real) IDs 1625677e049SGordon Ross * are (first <= ID < (first + msg_credits)) 1635677e049SGordon Ross */ 1645677e049SGordon Ross if (sr->smb2_credit_charge == 0) 1655677e049SGordon Ross sr->smb2_credit_charge = 1; 1665677e049SGordon Ross sr->smb2_total_credits += sr->smb2_credit_charge; 1675677e049SGordon Ross 1685677e049SGordon Ross if (sr->smb2_messageid != 0 && 1695677e049SGordon Ross sr->smb2_messageid != UINT64_MAX) { 1705677e049SGordon Ross 1715677e049SGordon Ross if (sr->smb2_first_msgid == 0) 1725677e049SGordon Ross sr->smb2_first_msgid = sr->smb2_messageid; 1735677e049SGordon Ross 1745677e049SGordon Ross if (sr->smb2_messageid < sr->smb2_first_msgid || 1755677e049SGordon Ross sr->smb2_messageid >= (sr->smb2_first_msgid + 1765677e049SGordon Ross sr->smb2_total_credits)) { 1775677e049SGordon Ross long long id = (long long) sr->smb2_messageid; 1785677e049SGordon Ross cmn_err(CE_WARN, "clnt %s msg ID 0x%llx " 1795677e049SGordon Ross "out of sequence in compound", 1805677e049SGordon Ross sr->session->ip_addr_str, id); 1815677e049SGordon Ross } 1825677e049SGordon Ross } 1835677e049SGordon Ross 1845677e049SGordon Ross /* Normal loop exit on next == zero */ 1855677e049SGordon Ross if (sr->smb2_next_command == 0) 1865677e049SGordon Ross break; 1875677e049SGordon Ross 1885677e049SGordon Ross /* Abundance of caution... */ 1895677e049SGordon Ross if (sr->smb2_next_command < SMB2_HDR_SIZE) 1905677e049SGordon Ross goto drop; 1915677e049SGordon Ross 1925677e049SGordon Ross /* Advance to the next header. */ 1935677e049SGordon Ross skip = sr->smb2_next_command - SMB2_HDR_SIZE; 1945677e049SGordon Ross if (MBC_ROOM_FOR(mbc, skip) == 0) 1955677e049SGordon Ross goto drop; 1965677e049SGordon Ross mbc->chain_offset += skip; 197a90cf9f2SGordon Ross } 1985677e049SGordon Ross /* Rewind back to the top. */ 1995677e049SGordon Ross mbc->chain_offset = 0; 200a90cf9f2SGordon Ross 201a90cf9f2SGordon Ross /* 202a90cf9f2SGordon Ross * Submit the request to the task queue, which calls 203a90cf9f2SGordon Ross * smb2_tq_work when the workload permits. 204a90cf9f2SGordon Ross */ 205a90cf9f2SGordon Ross sr->sr_time_submitted = gethrtime(); 206a90cf9f2SGordon Ross sr->sr_state = SMB_REQ_STATE_SUBMITTED; 207a90cf9f2SGordon Ross smb_srqueue_waitq_enter(sr->session->s_srqueue); 208a90cf9f2SGordon Ross (void) taskq_dispatch(sr->sr_server->sv_worker_pool, 209a90cf9f2SGordon Ross smb2_tq_work, sr, TQ_SLEEP); 210a90cf9f2SGordon Ross return (0); 2115677e049SGordon Ross 2125677e049SGordon Ross drop: 2135677e049SGordon Ross smb_request_free(sr); 2145677e049SGordon Ross return (-1); 215a90cf9f2SGordon Ross } 216a90cf9f2SGordon Ross 217a90cf9f2SGordon Ross static void 218a90cf9f2SGordon Ross smb2_tq_work(void *arg) 219a90cf9f2SGordon Ross { 220a90cf9f2SGordon Ross smb_request_t *sr; 221a90cf9f2SGordon Ross smb_srqueue_t *srq; 222a90cf9f2SGordon Ross 223a90cf9f2SGordon Ross sr = (smb_request_t *)arg; 224a90cf9f2SGordon Ross SMB_REQ_VALID(sr); 225a90cf9f2SGordon Ross 226a90cf9f2SGordon Ross srq = sr->session->s_srqueue; 227a90cf9f2SGordon Ross smb_srqueue_waitq_to_runq(srq); 228a90cf9f2SGordon Ross sr->sr_worker = curthread; 229a90cf9f2SGordon Ross sr->sr_time_active = gethrtime(); 230a90cf9f2SGordon Ross 231a90cf9f2SGordon Ross /* 2325677e049SGordon Ross * Always dispatch to the work function, because cancelled 2335677e049SGordon Ross * requests need an error reply (NT_STATUS_CANCELLED). 234a90cf9f2SGordon Ross */ 2355677e049SGordon Ross mutex_enter(&sr->sr_mutex); 2365677e049SGordon Ross if (sr->sr_state == SMB_REQ_STATE_SUBMITTED) 2375677e049SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE; 2385677e049SGordon Ross mutex_exit(&sr->sr_mutex); 2395677e049SGordon Ross 240a90cf9f2SGordon Ross smb2sr_work(sr); 241a90cf9f2SGordon Ross 242a90cf9f2SGordon Ross smb_srqueue_runq_exit(srq); 243a90cf9f2SGordon Ross } 244a90cf9f2SGordon Ross 245*148d1a41SMatt Barden /* 246*148d1a41SMatt Barden * SMB2 credits determine how many simultaneous commands the 247*148d1a41SMatt Barden * client may issue, and bounds the range of message IDs those 248*148d1a41SMatt Barden * commands may use. With multi-credit support, commands may 249*148d1a41SMatt Barden * use ranges of message IDs, where the credits used by each 250*148d1a41SMatt Barden * command are proportional to their data transfer size. 251*148d1a41SMatt Barden * 252*148d1a41SMatt Barden * Every command may request an increase or decrease of 253*148d1a41SMatt Barden * the currently granted credits, based on the difference 254*148d1a41SMatt Barden * between the credit request and the credit charge. 255*148d1a41SMatt Barden * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits 256*148d1a41SMatt Barden * 257*148d1a41SMatt Barden * Most commands have credit_request=1, credit_charge=1, 258*148d1a41SMatt Barden * which keeps the credit grant unchanged. 259*148d1a41SMatt Barden * 260*148d1a41SMatt Barden * All we're really doing here (for now) is reducing the 261*148d1a41SMatt Barden * credit_response if the client requests a credit increase 262*148d1a41SMatt Barden * that would take their credit over the maximum, and 263*148d1a41SMatt Barden * limiting the decrease so they don't run out of credits. 264*148d1a41SMatt Barden * 265*148d1a41SMatt Barden * Later, this could do something dynamic based on load. 266*148d1a41SMatt Barden * 267*148d1a41SMatt Barden * One other non-obvious bit about credits: We keep the 268*148d1a41SMatt Barden * session s_max_credits low until the 1st authentication, 269*148d1a41SMatt Barden * at which point we'll set the normal maximum_credits. 270*148d1a41SMatt Barden * Some clients ask for more credits with session setup, 271*148d1a41SMatt Barden * and we need to handle that requested increase _after_ 272*148d1a41SMatt Barden * the command-specific handler returns so it won't be 273*148d1a41SMatt Barden * restricted to the lower (pre-auth) limit. 274*148d1a41SMatt Barden */ 275*148d1a41SMatt Barden static inline void 276*148d1a41SMatt Barden smb2_credit_decrease(smb_request_t *sr) 277*148d1a41SMatt Barden { 278*148d1a41SMatt Barden smb_session_t *session = sr->session; 279*148d1a41SMatt Barden uint16_t cur, d; 280*148d1a41SMatt Barden 281*148d1a41SMatt Barden mutex_enter(&session->s_credits_mutex); 282*148d1a41SMatt Barden cur = session->s_cur_credits; 283*148d1a41SMatt Barden 284*148d1a41SMatt Barden /* Handle credit decrease. */ 285*148d1a41SMatt Barden d = sr->smb2_credit_charge - sr->smb2_credit_request; 286*148d1a41SMatt Barden cur -= d; 287*148d1a41SMatt Barden if (cur & 0x8000) { 288*148d1a41SMatt Barden /* 289*148d1a41SMatt Barden * underflow (bad credit charge or request) 290*148d1a41SMatt Barden * leave credits unchanged (response=charge) 291*148d1a41SMatt Barden */ 292*148d1a41SMatt Barden cur = session->s_cur_credits; 293*148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_charge; 294*148d1a41SMatt Barden DTRACE_PROBE1(smb2__credit__neg, smb_request_t *, sr); 295*148d1a41SMatt Barden } 296*148d1a41SMatt Barden 297*148d1a41SMatt Barden /* 298*148d1a41SMatt Barden * The server MUST ensure that the number of credits 299*148d1a41SMatt Barden * held by the client is never reduced to zero. 300*148d1a41SMatt Barden * [MS-SMB2] 3.3.1.2 301*148d1a41SMatt Barden */ 302*148d1a41SMatt Barden if (cur == 0) { 303*148d1a41SMatt Barden cur = 1; 304*148d1a41SMatt Barden sr->smb2_credit_response += 1; 305*148d1a41SMatt Barden DTRACE_PROBE1(smb2__credit__min, smb_request_t *, sr); 306*148d1a41SMatt Barden } 307*148d1a41SMatt Barden 308*148d1a41SMatt Barden DTRACE_PROBE3(smb2__credit__decrease, 309*148d1a41SMatt Barden smb_request_t *, sr, int, (int)cur, 310*148d1a41SMatt Barden int, (int)session->s_cur_credits); 311*148d1a41SMatt Barden 312*148d1a41SMatt Barden session->s_cur_credits = cur; 313*148d1a41SMatt Barden mutex_exit(&session->s_credits_mutex); 314*148d1a41SMatt Barden } 315*148d1a41SMatt Barden 316*148d1a41SMatt Barden /* 317*148d1a41SMatt Barden * Second half of SMB2 credit handling (increases) 318*148d1a41SMatt Barden */ 319*148d1a41SMatt Barden static inline void 320*148d1a41SMatt Barden smb2_credit_increase(smb_request_t *sr) 321*148d1a41SMatt Barden { 322*148d1a41SMatt Barden smb_session_t *session = sr->session; 323*148d1a41SMatt Barden uint16_t cur, d; 324*148d1a41SMatt Barden 325*148d1a41SMatt Barden mutex_enter(&session->s_credits_mutex); 326*148d1a41SMatt Barden cur = session->s_cur_credits; 327*148d1a41SMatt Barden 328*148d1a41SMatt Barden /* Handle credit increase. */ 329*148d1a41SMatt Barden d = sr->smb2_credit_request - sr->smb2_credit_charge; 330*148d1a41SMatt Barden cur += d; 331*148d1a41SMatt Barden 332*148d1a41SMatt Barden /* 333*148d1a41SMatt Barden * If new credits would be above max, 334*148d1a41SMatt Barden * reduce the credit grant. 335*148d1a41SMatt Barden */ 336*148d1a41SMatt Barden if (cur > session->s_max_credits) { 337*148d1a41SMatt Barden d = cur - session->s_max_credits; 338*148d1a41SMatt Barden cur = session->s_max_credits; 339*148d1a41SMatt Barden sr->smb2_credit_response -= d; 340*148d1a41SMatt Barden DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr); 341*148d1a41SMatt Barden } 342*148d1a41SMatt Barden 343*148d1a41SMatt Barden DTRACE_PROBE3(smb2__credit__increase, 344*148d1a41SMatt Barden smb_request_t *, sr, int, (int)cur, 345*148d1a41SMatt Barden int, (int)session->s_cur_credits); 346*148d1a41SMatt Barden 347*148d1a41SMatt Barden session->s_cur_credits = cur; 348*148d1a41SMatt Barden mutex_exit(&session->s_credits_mutex); 349*148d1a41SMatt Barden } 350*148d1a41SMatt Barden 351*148d1a41SMatt Barden /* 352*148d1a41SMatt Barden * Record some statistics: latency, rx bytes, tx bytes 353*148d1a41SMatt Barden * per: server, session & kshare. 354*148d1a41SMatt Barden */ 355*148d1a41SMatt Barden static inline void 356*148d1a41SMatt Barden smb2_record_stats(smb_request_t *sr, smb_disp_stats_t *sds, boolean_t tx_only) 357*148d1a41SMatt Barden { 358*148d1a41SMatt Barden hrtime_t dt; 359*148d1a41SMatt Barden int64_t rxb; 360*148d1a41SMatt Barden int64_t txb; 361*148d1a41SMatt Barden 362*148d1a41SMatt Barden dt = gethrtime() - sr->sr_time_start; 363*148d1a41SMatt Barden rxb = (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr); 364*148d1a41SMatt Barden txb = (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr); 365*148d1a41SMatt Barden 366*148d1a41SMatt Barden if (!tx_only) { 367*148d1a41SMatt Barden smb_server_inc_req(sr->sr_server); 368*148d1a41SMatt Barden smb_latency_add_sample(&sds->sdt_lat, dt); 369*148d1a41SMatt Barden atomic_add_64(&sds->sdt_rxb, rxb); 370*148d1a41SMatt Barden } 371*148d1a41SMatt Barden atomic_add_64(&sds->sdt_txb, txb); 372*148d1a41SMatt Barden } 373*148d1a41SMatt Barden 374a90cf9f2SGordon Ross /* 375a90cf9f2SGordon Ross * smb2sr_work 376a90cf9f2SGordon Ross * 377a90cf9f2SGordon Ross * This function processes each SMB command in the current request 378a90cf9f2SGordon Ross * (which may be a compound request) building a reply containing 379a90cf9f2SGordon Ross * SMB reply messages, one-to-one with the SMB commands. Some SMB 380a90cf9f2SGordon Ross * commands (change notify, blocking locks) may require both an 381a90cf9f2SGordon Ross * "interim response" and a later "async response" at completion. 382a90cf9f2SGordon Ross * In such cases, we'll encode the interim response in the reply 383a90cf9f2SGordon Ross * compound we're building, and put the (now async) command on a 384a90cf9f2SGordon Ross * list of commands that need further processing. After we've 385a90cf9f2SGordon Ross * finished processing the commands in this compound and building 386a90cf9f2SGordon Ross * the compound reply, we'll send the compound reply, and finally 387a90cf9f2SGordon Ross * process the list of async commands. 388a90cf9f2SGordon Ross * 389a90cf9f2SGordon Ross * As we work our way through the compound request and reply, 390a90cf9f2SGordon Ross * we need to keep track of the bounds of the current request 391a90cf9f2SGordon Ross * and reply. For the request, this uses an MBC_SHADOW_CHAIN 392a90cf9f2SGordon Ross * that begins at smb2_cmd_hdr. The reply is appended to the 393a90cf9f2SGordon Ross * sr->reply chain starting at smb2_reply_hdr. 394a90cf9f2SGordon Ross * 395bfe5e737SGordon Ross * This function must always free the smb request, or arrange 396bfe5e737SGordon Ross * for it to be completed and free'd later (if SDRC_SR_KEPT). 397a90cf9f2SGordon Ross */ 398a90cf9f2SGordon Ross void 399a90cf9f2SGordon Ross smb2sr_work(struct smb_request *sr) 400a90cf9f2SGordon Ross { 401a90cf9f2SGordon Ross const smb_disp_entry_t *sdd; 402a90cf9f2SGordon Ross smb_disp_stats_t *sds; 403a90cf9f2SGordon Ross smb_session_t *session; 404a90cf9f2SGordon Ross uint32_t msg_len; 405a90cf9f2SGordon Ross uint16_t cmd_idx; 406a90cf9f2SGordon Ross int rc = 0; 407a90cf9f2SGordon Ross boolean_t disconnect = B_FALSE; 408a90cf9f2SGordon Ross boolean_t related; 409a90cf9f2SGordon Ross 410a90cf9f2SGordon Ross session = sr->session; 411a90cf9f2SGordon Ross 412*148d1a41SMatt Barden ASSERT(sr->smb2_async == B_FALSE); 413a90cf9f2SGordon Ross ASSERT(sr->tid_tree == 0); 414a90cf9f2SGordon Ross ASSERT(sr->uid_user == 0); 415a90cf9f2SGordon Ross ASSERT(sr->fid_ofile == 0); 416a90cf9f2SGordon Ross sr->smb_fid = (uint16_t)-1; 417a90cf9f2SGordon Ross sr->smb2_status = 0; 418a90cf9f2SGordon Ross 419a90cf9f2SGordon Ross /* temporary until we identify a user */ 420a90cf9f2SGordon Ross sr->user_cr = zone_kcred(); 421a90cf9f2SGordon Ross 422a90cf9f2SGordon Ross cmd_start: 423a90cf9f2SGordon Ross /* 4245677e049SGordon Ross * Note that we don't check sr_state here and abort the 4255677e049SGordon Ross * compound if cancelled (etc.) because some SMB2 command 4265677e049SGordon Ross * handlers need to do work even when cancelled. 427a90cf9f2SGordon Ross * 428a90cf9f2SGordon Ross * We treat some status codes as if "sticky", meaning 429a90cf9f2SGordon Ross * once they're set after some command handler returns, 430a90cf9f2SGordon Ross * all remaining commands get this status without even 4315677e049SGordon Ross * calling the command-specific handler. 432a90cf9f2SGordon Ross */ 433a90cf9f2SGordon Ross if (sr->smb2_status != NT_STATUS_CANCELLED && 434a90cf9f2SGordon Ross sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES) 435a90cf9f2SGordon Ross sr->smb2_status = 0; 436a90cf9f2SGordon Ross 4375677e049SGordon Ross /* 4385677e049SGordon Ross * Decode the request header 4395677e049SGordon Ross * 4405677e049SGordon Ross * Most problems with decoding will result in the error 4415677e049SGordon Ross * STATUS_INVALID_PARAMETER. If the decoding problem 4425677e049SGordon Ross * prevents continuing, we'll close the connection. 4435677e049SGordon Ross * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 4445677e049SGordon Ross */ 445a90cf9f2SGordon Ross sr->smb2_cmd_hdr = sr->command.chain_offset; 446a90cf9f2SGordon Ross if ((rc = smb2_decode_header(sr)) != 0) { 447a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s bad SMB2 header", 448a90cf9f2SGordon Ross session->ip_addr_str); 449a90cf9f2SGordon Ross disconnect = B_TRUE; 450a90cf9f2SGordon Ross goto cleanup; 451a90cf9f2SGordon Ross } 452a90cf9f2SGordon Ross 453a90cf9f2SGordon Ross /* 454a90cf9f2SGordon Ross * The SMB2_FLAGS_SERVER_TO_REDIR should only appear 455a90cf9f2SGordon Ross * in messages from the server back to the client. 456a90cf9f2SGordon Ross */ 457a90cf9f2SGordon Ross if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) { 458a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s bad SMB2 flags", 459a90cf9f2SGordon Ross session->ip_addr_str); 460a90cf9f2SGordon Ross disconnect = B_TRUE; 461a90cf9f2SGordon Ross goto cleanup; 462a90cf9f2SGordon Ross } 463a90cf9f2SGordon Ross related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS); 464*148d1a41SMatt Barden sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR; 465*148d1a41SMatt Barden if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 466*148d1a41SMatt Barden /* Probably an async cancel. */ 467*148d1a41SMatt Barden DTRACE_PROBE1(smb2__dispatch__async, smb_request_t *, sr); 468*148d1a41SMatt Barden } else if (sr->smb2_async) { 469*148d1a41SMatt Barden /* Previous command in compound went async. */ 470*148d1a41SMatt Barden sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND; 471*148d1a41SMatt Barden sr->smb2_async_id = SMB2_ASYNCID(sr); 472*148d1a41SMatt Barden } 473a90cf9f2SGordon Ross 474a90cf9f2SGordon Ross /* 475a90cf9f2SGordon Ross * In case we bail out with an error before we get to the 476a90cf9f2SGordon Ross * section that computes the credit grant, initialize the 477a90cf9f2SGordon Ross * response header fields so that credits won't change. 478a90cf9f2SGordon Ross * Note: SMB 2.02 clients may send credit charge zero. 479a90cf9f2SGordon Ross */ 480a90cf9f2SGordon Ross if (sr->smb2_credit_charge == 0) 481a90cf9f2SGordon Ross sr->smb2_credit_charge = 1; 482a90cf9f2SGordon Ross sr->smb2_credit_response = sr->smb2_credit_charge; 483a90cf9f2SGordon Ross 484a90cf9f2SGordon Ross /* 485*148d1a41SMatt Barden * Write a tentative reply header. 486*148d1a41SMatt Barden * 487*148d1a41SMatt Barden * We could just leave this blank, but if we're using the 488*148d1a41SMatt Barden * mdb module feature that extracts packets, it's useful 489*148d1a41SMatt Barden * to have the header mostly correct here. 490*148d1a41SMatt Barden * 491*148d1a41SMatt Barden * If we have already exhausted the output space, then the 492*148d1a41SMatt Barden * client is trying something funny. Log it and kill 'em. 493a90cf9f2SGordon Ross */ 494*148d1a41SMatt Barden sr->smb2_next_reply = 0; 495*148d1a41SMatt Barden ASSERT((sr->reply.chain_offset & 7) == 0); 496a90cf9f2SGordon Ross sr->smb2_reply_hdr = sr->reply.chain_offset; 497a90cf9f2SGordon Ross if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) { 498a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s excessive reply", 499a90cf9f2SGordon Ross session->ip_addr_str); 500a90cf9f2SGordon Ross disconnect = B_TRUE; 501a90cf9f2SGordon Ross goto cleanup; 502a90cf9f2SGordon Ross } 503a90cf9f2SGordon Ross 504a90cf9f2SGordon Ross /* 505a90cf9f2SGordon Ross * Figure out the length of data following the SMB2 header. 506a90cf9f2SGordon Ross * It ends at either the next SMB2 header if there is one 507a90cf9f2SGordon Ross * (smb2_next_command != 0) or at the end of the message. 508a90cf9f2SGordon Ross */ 509a90cf9f2SGordon Ross if (sr->smb2_next_command != 0) { 510a90cf9f2SGordon Ross /* [MS-SMB2] says this is 8-byte aligned */ 511a90cf9f2SGordon Ross msg_len = sr->smb2_next_command; 512a90cf9f2SGordon Ross if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) || 513a90cf9f2SGordon Ross ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) { 514a90cf9f2SGordon Ross cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd", 515a90cf9f2SGordon Ross session->ip_addr_str); 516a90cf9f2SGordon Ross disconnect = B_TRUE; 517a90cf9f2SGordon Ross goto cleanup; 518a90cf9f2SGordon Ross } 519a90cf9f2SGordon Ross } else { 520a90cf9f2SGordon Ross msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr; 521a90cf9f2SGordon Ross } 522a90cf9f2SGordon Ross 523a90cf9f2SGordon Ross /* 524a90cf9f2SGordon Ross * Setup a shadow chain for this SMB2 command, starting 525a90cf9f2SGordon Ross * with the header and ending at either the next command 526a90cf9f2SGordon Ross * or the end of the message. The signing check below 527a90cf9f2SGordon Ross * needs the entire SMB2 command. After that's done, we 528a90cf9f2SGordon Ross * advance chain_offset to the end of the header where 529a90cf9f2SGordon Ross * the command specific handlers continue decoding. 530a90cf9f2SGordon Ross */ 531a90cf9f2SGordon Ross (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, 532a90cf9f2SGordon Ross sr->smb2_cmd_hdr, msg_len); 533a90cf9f2SGordon Ross 534ac2bf314SMatt Barden /* 535ac2bf314SMatt Barden * We will consume the data for this request from smb_data. 536ac2bf314SMatt Barden * That effectively consumes msg_len bytes from sr->command 537ac2bf314SMatt Barden * but doesn't update its chain_offset, so we need to update 538ac2bf314SMatt Barden * that here to make later received bytes accounting work. 539ac2bf314SMatt Barden */ 540ac2bf314SMatt Barden sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len; 541ac2bf314SMatt Barden ASSERT(sr->command.chain_offset <= sr->command.max_bytes); 542ac2bf314SMatt Barden 543a90cf9f2SGordon Ross /* 544a90cf9f2SGordon Ross * Validate the commmand code, get dispatch table entries. 545a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 546a90cf9f2SGordon Ross * 547a90cf9f2SGordon Ross * The last slot in the dispatch table is used to handle 548a90cf9f2SGordon Ross * invalid commands. Same for statistics. 549a90cf9f2SGordon Ross */ 550a90cf9f2SGordon Ross if (sr->smb2_cmd_code < SMB2_INVALID_CMD) 551a90cf9f2SGordon Ross cmd_idx = sr->smb2_cmd_code; 552a90cf9f2SGordon Ross else 553a90cf9f2SGordon Ross cmd_idx = SMB2_INVALID_CMD; 554a90cf9f2SGordon Ross sdd = &smb2_disp_table[cmd_idx]; 555a90cf9f2SGordon Ross sds = &session->s_server->sv_disp_stats2[cmd_idx]; 556a90cf9f2SGordon Ross 557a90cf9f2SGordon Ross /* 558a90cf9f2SGordon Ross * If this command is NOT "related" to the previous, 559a90cf9f2SGordon Ross * clear out the UID, TID, FID state that might be 560a90cf9f2SGordon Ross * left over from the previous command. 561a90cf9f2SGordon Ross * 562a90cf9f2SGordon Ross * If the command IS related, any new IDs are ignored, 563a90cf9f2SGordon Ross * and we simply continue with the previous user, tree, 564a90cf9f2SGordon Ross * and open file. 565a90cf9f2SGordon Ross */ 566a90cf9f2SGordon Ross if (!related) { 567a90cf9f2SGordon Ross /* 568a90cf9f2SGordon Ross * Drop user, tree, file; carefully ordered to 569a90cf9f2SGordon Ross * avoid dangling references: file, tree, user 570a90cf9f2SGordon Ross */ 571a90cf9f2SGordon Ross if (sr->fid_ofile != NULL) { 572a90cf9f2SGordon Ross smb_ofile_request_complete(sr->fid_ofile); 573a90cf9f2SGordon Ross smb_ofile_release(sr->fid_ofile); 574a90cf9f2SGordon Ross sr->fid_ofile = NULL; 575a90cf9f2SGordon Ross } 576a90cf9f2SGordon Ross if (sr->tid_tree != NULL) { 577a90cf9f2SGordon Ross smb_tree_release(sr->tid_tree); 578a90cf9f2SGordon Ross sr->tid_tree = NULL; 579a90cf9f2SGordon Ross } 580a90cf9f2SGordon Ross if (sr->uid_user != NULL) { 581a90cf9f2SGordon Ross smb_user_release(sr->uid_user); 582a90cf9f2SGordon Ross sr->uid_user = NULL; 583a90cf9f2SGordon Ross sr->user_cr = zone_kcred(); 584a90cf9f2SGordon Ross } 585a90cf9f2SGordon Ross } 586a90cf9f2SGordon Ross 587a90cf9f2SGordon Ross /* 588a90cf9f2SGordon Ross * Make sure we have a user and tree as needed 589a90cf9f2SGordon Ross * according to the flags for the this command. 590a90cf9f2SGordon Ross * Note that we may have inherited these. 591a90cf9f2SGordon Ross */ 592a90cf9f2SGordon Ross if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) { 593a90cf9f2SGordon Ross /* 594a90cf9f2SGordon Ross * This command requires a user session. 595a90cf9f2SGordon Ross */ 596a90cf9f2SGordon Ross if (related) { 597a90cf9f2SGordon Ross /* 598a90cf9f2SGordon Ross * Previous command should have given us a user. 599a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Handling Related Requests 600a90cf9f2SGordon Ross */ 601a90cf9f2SGordon Ross if (sr->uid_user == NULL) { 602a90cf9f2SGordon Ross smb2sr_put_error(sr, 603a90cf9f2SGordon Ross NT_STATUS_INVALID_PARAMETER); 604a90cf9f2SGordon Ross goto cmd_done; 605a90cf9f2SGordon Ross } 606811599a4SMatt Barden sr->smb2_ssnid = sr->uid_user->u_ssnid; 607a90cf9f2SGordon Ross } else { 608a90cf9f2SGordon Ross /* 609a90cf9f2SGordon Ross * Lookup the UID 610a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Verifying the Session 611a90cf9f2SGordon Ross */ 612a90cf9f2SGordon Ross ASSERT(sr->uid_user == NULL); 613811599a4SMatt Barden sr->uid_user = smb_session_lookup_ssnid(session, 614811599a4SMatt Barden sr->smb2_ssnid); 615a90cf9f2SGordon Ross if (sr->uid_user == NULL) { 616a90cf9f2SGordon Ross smb2sr_put_error(sr, 617a90cf9f2SGordon Ross NT_STATUS_USER_SESSION_DELETED); 618a90cf9f2SGordon Ross goto cmd_done; 619a90cf9f2SGordon Ross } 620a90cf9f2SGordon Ross sr->user_cr = smb_user_getcred(sr->uid_user); 621a90cf9f2SGordon Ross } 622a90cf9f2SGordon Ross ASSERT(sr->uid_user != NULL); 623a90cf9f2SGordon Ross } 624a90cf9f2SGordon Ross 625a90cf9f2SGordon Ross if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) { 626a90cf9f2SGordon Ross /* 627a90cf9f2SGordon Ross * This command requires a tree connection. 628a90cf9f2SGordon Ross */ 629a90cf9f2SGordon Ross if (related) { 630a90cf9f2SGordon Ross /* 631a90cf9f2SGordon Ross * Previous command should have given us a tree. 632a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Handling Related Requests 633a90cf9f2SGordon Ross */ 634a90cf9f2SGordon Ross if (sr->tid_tree == NULL) { 635a90cf9f2SGordon Ross smb2sr_put_error(sr, 636a90cf9f2SGordon Ross NT_STATUS_INVALID_PARAMETER); 637a90cf9f2SGordon Ross goto cmd_done; 638a90cf9f2SGordon Ross } 639a90cf9f2SGordon Ross sr->smb_tid = sr->tid_tree->t_tid; 640a90cf9f2SGordon Ross } else { 641a90cf9f2SGordon Ross /* 642a90cf9f2SGordon Ross * Lookup the TID 643a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect 644a90cf9f2SGordon Ross */ 645a90cf9f2SGordon Ross ASSERT(sr->tid_tree == NULL); 646a90cf9f2SGordon Ross sr->tid_tree = smb_session_lookup_tree(session, 647a90cf9f2SGordon Ross sr->smb_tid); 648a90cf9f2SGordon Ross if (sr->tid_tree == NULL) { 649a90cf9f2SGordon Ross smb2sr_put_error(sr, 650a90cf9f2SGordon Ross NT_STATUS_NETWORK_NAME_DELETED); 651a90cf9f2SGordon Ross goto cmd_done; 652a90cf9f2SGordon Ross } 653a90cf9f2SGordon Ross } 654a90cf9f2SGordon Ross ASSERT(sr->tid_tree != NULL); 655a90cf9f2SGordon Ross } 656a90cf9f2SGordon Ross 657a90cf9f2SGordon Ross /* 658a90cf9f2SGordon Ross * SMB2 signature verification, two parts: 659a90cf9f2SGordon Ross * (a) Require SMB2_FLAGS_SIGNED (for most request types) 660a90cf9f2SGordon Ross * (b) If SMB2_FLAGS_SIGNED is set, check the signature. 661a90cf9f2SGordon Ross * [MS-SMB2] 3.3.5.2.4 Verifying the Signature 662a90cf9f2SGordon Ross */ 663a90cf9f2SGordon Ross 664a90cf9f2SGordon Ross /* 665a90cf9f2SGordon Ross * No user session means no signature check. That's OK, 666a90cf9f2SGordon Ross * i.e. for commands marked SDDF_SUPPRESS_UID above. 667a90cf9f2SGordon Ross * Note, this also means we won't sign the reply. 668a90cf9f2SGordon Ross */ 669a90cf9f2SGordon Ross if (sr->uid_user == NULL) 670a90cf9f2SGordon Ross sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 671a90cf9f2SGordon Ross 672a90cf9f2SGordon Ross /* 673a90cf9f2SGordon Ross * The SDDF_SUPPRESS_UID dispatch is set for requests that 674a90cf9f2SGordon Ross * don't need a UID (user). These also don't require a 675a90cf9f2SGordon Ross * signature check here. 676a90cf9f2SGordon Ross */ 677a90cf9f2SGordon Ross if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 && 678a90cf9f2SGordon Ross sr->uid_user != NULL && 679a90cf9f2SGordon Ross (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) { 680a90cf9f2SGordon Ross /* 681a90cf9f2SGordon Ross * This request type should be signed, and 682a90cf9f2SGordon Ross * we're configured to require signatures. 683a90cf9f2SGordon Ross */ 684a90cf9f2SGordon Ross if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) { 685a90cf9f2SGordon Ross smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 686a90cf9f2SGordon Ross goto cmd_done; 687a90cf9f2SGordon Ross } 688a90cf9f2SGordon Ross rc = smb2_sign_check_request(sr); 689a90cf9f2SGordon Ross if (rc != 0) { 690*148d1a41SMatt Barden DTRACE_PROBE1(smb2__sign__check, smb_request_t *, sr); 691a90cf9f2SGordon Ross smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 692a90cf9f2SGordon Ross goto cmd_done; 693a90cf9f2SGordon Ross } 694a90cf9f2SGordon Ross } 695a90cf9f2SGordon Ross 696a90cf9f2SGordon Ross /* 697a90cf9f2SGordon Ross * Now that the signing check is done with smb_data, 698a90cf9f2SGordon Ross * advance past the SMB2 header we decoded earlier. 699a90cf9f2SGordon Ross * This leaves sr->smb_data correctly positioned 700a90cf9f2SGordon Ross * for command-specific decoding in the dispatch 701a90cf9f2SGordon Ross * function called next. 702a90cf9f2SGordon Ross */ 703a90cf9f2SGordon Ross sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE; 704a90cf9f2SGordon Ross 705a90cf9f2SGordon Ross /* 706*148d1a41SMatt Barden * Credit adjustments (decrease) 707a90cf9f2SGordon Ross * 708*148d1a41SMatt Barden * If we've gone async, credit adjustments were done 709*148d1a41SMatt Barden * when we sent the interim reply. 710a90cf9f2SGordon Ross */ 711*148d1a41SMatt Barden if (!sr->smb2_async) { 712*148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_request; 713*148d1a41SMatt Barden if (sr->smb2_credit_request < sr->smb2_credit_charge) { 714*148d1a41SMatt Barden smb2_credit_decrease(sr); 715a90cf9f2SGordon Ross } 716a90cf9f2SGordon Ross } 717a90cf9f2SGordon Ross 718a90cf9f2SGordon Ross /* 719a90cf9f2SGordon Ross * The real work: call the SMB2 command handler 720a90cf9f2SGordon Ross * (except for "sticky" smb2_status - see above) 721a90cf9f2SGordon Ross */ 722a90cf9f2SGordon Ross sr->sr_time_start = gethrtime(); 723a90cf9f2SGordon Ross rc = SDRC_SUCCESS; 724a90cf9f2SGordon Ross if (sr->smb2_status == 0) { 725a90cf9f2SGordon Ross /* NB: not using pre_op */ 726a90cf9f2SGordon Ross rc = (*sdd->sdt_function)(sr); 727a90cf9f2SGordon Ross /* NB: not using post_op */ 7285677e049SGordon Ross } else { 7295677e049SGordon Ross smb2sr_put_error(sr, sr->smb2_status); 730a90cf9f2SGordon Ross } 731a90cf9f2SGordon Ross 732a90cf9f2SGordon Ross /* 733*148d1a41SMatt Barden * When the sdt_function returns SDRC_SR_KEPT, it means 734*148d1a41SMatt Barden * this SR may have been passed to another thread so we 735*148d1a41SMatt Barden * MUST NOT touch it anymore. 736a90cf9f2SGordon Ross */ 737*148d1a41SMatt Barden if (rc == SDRC_SR_KEPT) 738*148d1a41SMatt Barden return; 739a90cf9f2SGordon Ross 740*148d1a41SMatt Barden MBC_FLUSH(&sr->raw_data); 741a90cf9f2SGordon Ross 742*148d1a41SMatt Barden /* 743*148d1a41SMatt Barden * Credit adjustments (increase) 744*148d1a41SMatt Barden */ 745*148d1a41SMatt Barden if (!sr->smb2_async) { 746*148d1a41SMatt Barden if (sr->smb2_credit_request > sr->smb2_credit_charge) { 747*148d1a41SMatt Barden smb2_credit_increase(sr); 748a90cf9f2SGordon Ross } 749a90cf9f2SGordon Ross } 750a90cf9f2SGordon Ross 751a90cf9f2SGordon Ross cmd_done: 752a90cf9f2SGordon Ross switch (rc) { 753a90cf9f2SGordon Ross case SDRC_SUCCESS: 754a90cf9f2SGordon Ross break; 755a90cf9f2SGordon Ross default: 756a90cf9f2SGordon Ross /* 757a90cf9f2SGordon Ross * SMB2 does not use the other dispatch return codes. 758a90cf9f2SGordon Ross * If we see something else, log an event so we'll 759a90cf9f2SGordon Ross * know something is returning bogus status codes. 760a90cf9f2SGordon Ross * If you see these in the log, use dtrace to find 761a90cf9f2SGordon Ross * the code returning something else. 762a90cf9f2SGordon Ross */ 763a90cf9f2SGordon Ross #ifdef DEBUG 764a90cf9f2SGordon Ross cmn_err(CE_NOTE, "handler for %u returned 0x%x", 765a90cf9f2SGordon Ross sr->smb2_cmd_code, rc); 766a90cf9f2SGordon Ross #endif 76793bc28dbSGordon Ross sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 76893bc28dbSGordon Ross break; 769a90cf9f2SGordon Ross case SDRC_ERROR: 77093bc28dbSGordon Ross /* 77193bc28dbSGordon Ross * Many command handlers return SDRC_ERROR for any 77293bc28dbSGordon Ross * problems decoding the request, and don't bother 77393bc28dbSGordon Ross * setting smb2_status. For those cases, the best 77493bc28dbSGordon Ross * status return would be "invalid parameter". 77593bc28dbSGordon Ross */ 776a90cf9f2SGordon Ross if (sr->smb2_status == 0) 77793bc28dbSGordon Ross sr->smb2_status = NT_STATUS_INVALID_PARAMETER; 778a90cf9f2SGordon Ross break; 779a90cf9f2SGordon Ross case SDRC_DROP_VC: 780a90cf9f2SGordon Ross disconnect = B_TRUE; 781a90cf9f2SGordon Ross goto cleanup; 7825677e049SGordon Ross 7835677e049SGordon Ross case SDRC_NO_REPLY: 7845677e049SGordon Ross /* will free sr */ 7855677e049SGordon Ross goto cleanup; 786a90cf9f2SGordon Ross } 787a90cf9f2SGordon Ross 788*148d1a41SMatt Barden /* 789*148d1a41SMatt Barden * Pad the reply to align(8) if there will be another. 790*148d1a41SMatt Barden * (We don't compound async replies.) 791*148d1a41SMatt Barden */ 792*148d1a41SMatt Barden if (!sr->smb2_async && sr->smb2_next_command != 0) 793*148d1a41SMatt Barden (void) smb_mbc_put_align(&sr->reply, 8); 794*148d1a41SMatt Barden 795*148d1a41SMatt Barden /* 796*148d1a41SMatt Barden * Record some statistics. Uses: 797*148d1a41SMatt Barden * rxb = command.chain_offset - smb2_cmd_hdr; 798*148d1a41SMatt Barden * txb = reply.chain_offset - smb2_reply_hdr; 799*148d1a41SMatt Barden * which at this point represent the current cmd/reply. 800*148d1a41SMatt Barden * 801*148d1a41SMatt Barden * Note: If async, this does txb only, and 802*148d1a41SMatt Barden * skips the smb_latency_add_sample() calls. 803*148d1a41SMatt Barden */ 804*148d1a41SMatt Barden smb2_record_stats(sr, sds, sr->smb2_async); 805*148d1a41SMatt Barden 806a90cf9f2SGordon Ross /* 807a90cf9f2SGordon Ross * If there's a next command, figure out where it starts, 808*148d1a41SMatt Barden * and fill in the next header offset for the reply. 809*148d1a41SMatt Barden * Note: We sanity checked smb2_next_command above. 810a90cf9f2SGordon Ross */ 811a90cf9f2SGordon Ross if (sr->smb2_next_command != 0) { 812a90cf9f2SGordon Ross sr->command.chain_offset = 813a90cf9f2SGordon Ross sr->smb2_cmd_hdr + sr->smb2_next_command; 814a90cf9f2SGordon Ross sr->smb2_next_reply = 815a90cf9f2SGordon Ross sr->reply.chain_offset - sr->smb2_reply_hdr; 816a90cf9f2SGordon Ross } else { 817*148d1a41SMatt Barden ASSERT(sr->smb2_next_reply == 0); 818a90cf9f2SGordon Ross } 819a90cf9f2SGordon Ross 820a90cf9f2SGordon Ross /* 821*148d1a41SMatt Barden * Overwrite the (now final) SMB2 header for this response. 822a90cf9f2SGordon Ross */ 823a90cf9f2SGordon Ross (void) smb2_encode_header(sr, B_TRUE); 824a90cf9f2SGordon Ross 825a90cf9f2SGordon Ross if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) 826a90cf9f2SGordon Ross smb2_sign_reply(sr); 827a90cf9f2SGordon Ross 828a90cf9f2SGordon Ross /* 829*148d1a41SMatt Barden * Non-async runs the whole compound before send. 830*148d1a41SMatt Barden * When we've gone async, send each individually. 831a90cf9f2SGordon Ross */ 832*148d1a41SMatt Barden if (!sr->smb2_async && sr->smb2_next_command != 0) 833*148d1a41SMatt Barden goto cmd_start; 834a90cf9f2SGordon Ross smb2_send_reply(sr); 835*148d1a41SMatt Barden if (sr->smb2_async && sr->smb2_next_command != 0) { 836*148d1a41SMatt Barden MBC_FLUSH(&sr->reply); /* New reply buffer. */ 837*148d1a41SMatt Barden ASSERT(sr->reply.max_bytes == sr->session->reply_max_bytes); 838*148d1a41SMatt Barden goto cmd_start; 839a90cf9f2SGordon Ross } 840a90cf9f2SGordon Ross 841a90cf9f2SGordon Ross cleanup: 842*148d1a41SMatt Barden if (disconnect) 843*148d1a41SMatt Barden smb_session_disconnect(session); 844a90cf9f2SGordon Ross 845a90cf9f2SGordon Ross mutex_enter(&sr->sr_mutex); 846a90cf9f2SGordon Ross sr->sr_state = SMB_REQ_STATE_COMPLETED; 847a90cf9f2SGordon Ross mutex_exit(&sr->sr_mutex); 848a90cf9f2SGordon Ross 849a90cf9f2SGordon Ross smb_request_free(sr); 850a90cf9f2SGordon Ross } 851a90cf9f2SGordon Ross 852a90cf9f2SGordon Ross /* 853*148d1a41SMatt Barden * Build interim responses for the current and all following 854*148d1a41SMatt Barden * requests in this compound, then send the compound response, 855*148d1a41SMatt Barden * leaving the SR state so that smb2sr_work() can continue its 856*148d1a41SMatt Barden * processing of this compound in "async mode". 857a90cf9f2SGordon Ross * 858*148d1a41SMatt Barden * If we agree to "go async", this should return STATUS_SUCCESS. 859*148d1a41SMatt Barden * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and 860*148d1a41SMatt Barden * all requests following this request. (See the comments re. 861*148d1a41SMatt Barden * "sticky" smb2_status values in smb2sr_work). 862*148d1a41SMatt Barden * 863*148d1a41SMatt Barden * Note: the Async ID we assign here is arbitrary, and need only 864*148d1a41SMatt Barden * be unique among pending async responses on this connection, so 865*148d1a41SMatt Barden * this just uses a modified messageID, which is already unique. 866*148d1a41SMatt Barden * 867*148d1a41SMatt Barden * Credits: All credit changes should happen via the interim 868*148d1a41SMatt Barden * responses, so we have to manage credits here. After this 869*148d1a41SMatt Barden * returns to smb2sr_work, the final replies for all these 870*148d1a41SMatt Barden * commands will have smb2_credit_response = smb2_credit_charge 871*148d1a41SMatt Barden * (meaning no further changes to the clients' credits). 872a90cf9f2SGordon Ross */ 873*148d1a41SMatt Barden uint32_t 874*148d1a41SMatt Barden smb2sr_go_async(smb_request_t *sr) 875a90cf9f2SGordon Ross { 876*148d1a41SMatt Barden smb_session_t *session; 877*148d1a41SMatt Barden smb_disp_stats_t *sds; 878*148d1a41SMatt Barden uint16_t cmd_idx; 879*148d1a41SMatt Barden int32_t saved_com_offset; 880*148d1a41SMatt Barden uint32_t saved_cmd_hdr; 881*148d1a41SMatt Barden uint16_t saved_cred_resp; 882*148d1a41SMatt Barden uint32_t saved_hdr_flags; 883*148d1a41SMatt Barden uint32_t saved_reply_hdr; 884*148d1a41SMatt Barden uint32_t msg_len; 885*148d1a41SMatt Barden boolean_t disconnect = B_FALSE; 886*148d1a41SMatt Barden 887*148d1a41SMatt Barden if (sr->smb2_async) { 888*148d1a41SMatt Barden /* already went async in some previous cmd. */ 889*148d1a41SMatt Barden return (NT_STATUS_SUCCESS); 890*148d1a41SMatt Barden } 891*148d1a41SMatt Barden sr->smb2_async = B_TRUE; 892*148d1a41SMatt Barden 893*148d1a41SMatt Barden /* The "server" session always runs async. */ 894*148d1a41SMatt Barden session = sr->session; 895*148d1a41SMatt Barden if (session->sock == NULL) 896*148d1a41SMatt Barden return (NT_STATUS_SUCCESS); 897*148d1a41SMatt Barden 898*148d1a41SMatt Barden sds = NULL; 899*148d1a41SMatt Barden saved_com_offset = sr->command.chain_offset; 900*148d1a41SMatt Barden saved_cmd_hdr = sr->smb2_cmd_hdr; 901*148d1a41SMatt Barden saved_cred_resp = sr->smb2_credit_response; 902*148d1a41SMatt Barden saved_hdr_flags = sr->smb2_hdr_flags; 903*148d1a41SMatt Barden saved_reply_hdr = sr->smb2_reply_hdr; 904a90cf9f2SGordon Ross 905a90cf9f2SGordon Ross /* 906*148d1a41SMatt Barden * The command-specific handler should not yet have put any 907*148d1a41SMatt Barden * data in the reply except for the (place holder) header. 908a90cf9f2SGordon Ross */ 909*148d1a41SMatt Barden if (sr->reply.chain_offset != sr->smb2_reply_hdr + SMB2_HDR_SIZE) { 910*148d1a41SMatt Barden ASSERT3U(sr->reply.chain_offset, ==, 911*148d1a41SMatt Barden sr->smb2_reply_hdr + SMB2_HDR_SIZE); 912*148d1a41SMatt Barden return (NT_STATUS_INTERNAL_ERROR); 913*148d1a41SMatt Barden } 914a90cf9f2SGordon Ross 915a90cf9f2SGordon Ross /* 916*148d1a41SMatt Barden * Rewind to the start of the current header in both the 917*148d1a41SMatt Barden * command and reply bufers, so the loop below can just 918*148d1a41SMatt Barden * decode/encode just in every pass. This means the 919*148d1a41SMatt Barden * current command header is decoded again, but that 920*148d1a41SMatt Barden * avoids having to special-case the first loop pass. 921a90cf9f2SGordon Ross */ 922*148d1a41SMatt Barden sr->command.chain_offset = sr->smb2_cmd_hdr; 923*148d1a41SMatt Barden sr->reply.chain_offset = sr->smb2_reply_hdr; 924a90cf9f2SGordon Ross 925a90cf9f2SGordon Ross /* 926*148d1a41SMatt Barden * This command processing loop is a simplified version of 927*148d1a41SMatt Barden * smb2sr_work() that just puts an "interim response" for 928*148d1a41SMatt Barden * every command in the compound (NT_STATUS_PENDING). 929a90cf9f2SGordon Ross */ 930*148d1a41SMatt Barden cmd_start: 931*148d1a41SMatt Barden sr->smb2_status = NT_STATUS_PENDING; 932a90cf9f2SGordon Ross 933bfe5e737SGordon Ross /* 934*148d1a41SMatt Barden * Decode the request header 935bfe5e737SGordon Ross */ 936*148d1a41SMatt Barden sr->smb2_cmd_hdr = sr->command.chain_offset; 937*148d1a41SMatt Barden if ((smb2_decode_header(sr)) != 0) { 938*148d1a41SMatt Barden cmn_err(CE_WARN, "clnt %s bad SMB2 header", 939*148d1a41SMatt Barden session->ip_addr_str); 940*148d1a41SMatt Barden disconnect = B_TRUE; 941*148d1a41SMatt Barden goto cleanup; 942*148d1a41SMatt Barden } 943*148d1a41SMatt Barden sr->smb2_hdr_flags |= (SMB2_FLAGS_SERVER_TO_REDIR | 944*148d1a41SMatt Barden SMB2_FLAGS_ASYNC_COMMAND); 945*148d1a41SMatt Barden sr->smb2_async_id = SMB2_ASYNCID(sr); 946bfe5e737SGordon Ross 947a90cf9f2SGordon Ross /* 948*148d1a41SMatt Barden * In case we bail out... 949a90cf9f2SGordon Ross */ 950*148d1a41SMatt Barden if (sr->smb2_credit_charge == 0) 951*148d1a41SMatt Barden sr->smb2_credit_charge = 1; 952*148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_charge; 953a90cf9f2SGordon Ross 954a90cf9f2SGordon Ross /* 955*148d1a41SMatt Barden * Write a tentative reply header. 956a90cf9f2SGordon Ross */ 957*148d1a41SMatt Barden sr->smb2_next_reply = 0; 958*148d1a41SMatt Barden ASSERT((sr->reply.chain_offset & 7) == 0); 959*148d1a41SMatt Barden sr->smb2_reply_hdr = sr->reply.chain_offset; 960*148d1a41SMatt Barden if ((smb2_encode_header(sr, B_FALSE)) != 0) { 961*148d1a41SMatt Barden cmn_err(CE_WARN, "clnt %s excessive reply", 962*148d1a41SMatt Barden session->ip_addr_str); 963*148d1a41SMatt Barden disconnect = B_TRUE; 964*148d1a41SMatt Barden goto cleanup; 965a90cf9f2SGordon Ross } 966a90cf9f2SGordon Ross 967a90cf9f2SGordon Ross /* 968*148d1a41SMatt Barden * Figure out the length of data... 969a90cf9f2SGordon Ross */ 970*148d1a41SMatt Barden if (sr->smb2_next_command != 0) { 971*148d1a41SMatt Barden /* [MS-SMB2] says this is 8-byte aligned */ 972*148d1a41SMatt Barden msg_len = sr->smb2_next_command; 973*148d1a41SMatt Barden if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) || 974*148d1a41SMatt Barden ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) { 975*148d1a41SMatt Barden cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd", 976*148d1a41SMatt Barden session->ip_addr_str); 977*148d1a41SMatt Barden disconnect = B_TRUE; 978*148d1a41SMatt Barden goto cleanup; 979*148d1a41SMatt Barden } 980*148d1a41SMatt Barden } else { 981*148d1a41SMatt Barden msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr; 982bfe5e737SGordon Ross } 983a90cf9f2SGordon Ross 984*148d1a41SMatt Barden /* 985*148d1a41SMatt Barden * We just skip any data, so no shadow chain etc. 986*148d1a41SMatt Barden */ 987*148d1a41SMatt Barden sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len; 988*148d1a41SMatt Barden ASSERT(sr->command.chain_offset <= sr->command.max_bytes); 989bfe5e737SGordon Ross 990*148d1a41SMatt Barden /* 991*148d1a41SMatt Barden * Validate the commmand code... 992*148d1a41SMatt Barden */ 993*148d1a41SMatt Barden if (sr->smb2_cmd_code < SMB2_INVALID_CMD) 994*148d1a41SMatt Barden cmd_idx = sr->smb2_cmd_code; 995*148d1a41SMatt Barden else 996*148d1a41SMatt Barden cmd_idx = SMB2_INVALID_CMD; 997*148d1a41SMatt Barden sds = &session->s_server->sv_disp_stats2[cmd_idx]; 998bfe5e737SGordon Ross 999a90cf9f2SGordon Ross /* 1000*148d1a41SMatt Barden * Don't change (user, tree, file) because we want them 1001*148d1a41SMatt Barden * exactly as they were when we entered. That also means 1002*148d1a41SMatt Barden * we may not have the right user in sr->uid_user for 1003*148d1a41SMatt Barden * signature checks, so leave that until smb2sr_work 1004*148d1a41SMatt Barden * runs these commands "for real". Therefore, here 1005*148d1a41SMatt Barden * we behave as if: (sr->uid_user == NULL) 1006a90cf9f2SGordon Ross */ 1007*148d1a41SMatt Barden sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 1008a90cf9f2SGordon Ross 1009a90cf9f2SGordon Ross /* 1010*148d1a41SMatt Barden * Credit adjustments (decrease) 1011*148d1a41SMatt Barden * 1012*148d1a41SMatt Barden * NOTE: interim responses are not signed. 1013*148d1a41SMatt Barden * Any attacker can modify the credit grant 1014*148d1a41SMatt Barden * in the response. Because of this property, 1015*148d1a41SMatt Barden * it is no worse to assume the credit charge and grant 1016*148d1a41SMatt Barden * are sane without verifying the signature, 1017*148d1a41SMatt Barden * and that saves us a whole lot of work. 1018*148d1a41SMatt Barden * If the credits WERE modified, we'll find out 1019*148d1a41SMatt Barden * when we verify the signature later, 1020*148d1a41SMatt Barden * which nullifies any changes caused here. 1021*148d1a41SMatt Barden * 1022*148d1a41SMatt Barden * Skip this on the first command, because the 1023*148d1a41SMatt Barden * credit decrease was done by the caller. 1024a90cf9f2SGordon Ross */ 1025*148d1a41SMatt Barden if (sr->smb2_cmd_hdr != saved_cmd_hdr) { 1026*148d1a41SMatt Barden sr->smb2_credit_response = sr->smb2_credit_request; 1027*148d1a41SMatt Barden if (sr->smb2_credit_request < sr->smb2_credit_charge) { 1028*148d1a41SMatt Barden smb2_credit_decrease(sr); 1029*148d1a41SMatt Barden } 1030*148d1a41SMatt Barden } 1031a90cf9f2SGordon Ross 1032a90cf9f2SGordon Ross /* 1033*148d1a41SMatt Barden * The real work: ... (would be here) 1034a90cf9f2SGordon Ross */ 1035*148d1a41SMatt Barden smb2sr_put_error(sr, sr->smb2_status); 1036a90cf9f2SGordon Ross 1037*148d1a41SMatt Barden /* 1038*148d1a41SMatt Barden * Credit adjustments (increase) 1039*148d1a41SMatt Barden */ 1040*148d1a41SMatt Barden if (sr->smb2_credit_request > sr->smb2_credit_charge) { 1041*148d1a41SMatt Barden smb2_credit_increase(sr); 1042*148d1a41SMatt Barden } 1043a90cf9f2SGordon Ross 1044*148d1a41SMatt Barden /* cmd_done: label */ 1045a90cf9f2SGordon Ross 1046a90cf9f2SGordon Ross /* 1047*148d1a41SMatt Barden * Pad the reply to align(8) if there will be another. 1048*148d1a41SMatt Barden * This (interim) reply uses compounding. 1049a90cf9f2SGordon Ross */ 1050*148d1a41SMatt Barden if (sr->smb2_next_command != 0) 1051*148d1a41SMatt Barden (void) smb_mbc_put_align(&sr->reply, 8); 1052a90cf9f2SGordon Ross 1053*148d1a41SMatt Barden /* 1054*148d1a41SMatt Barden * Record some statistics. Uses: 1055*148d1a41SMatt Barden * rxb = command.chain_offset - smb2_cmd_hdr; 1056*148d1a41SMatt Barden * txb = reply.chain_offset - smb2_reply_hdr; 1057*148d1a41SMatt Barden * which at this point represent the current cmd/reply. 1058*148d1a41SMatt Barden * 1059*148d1a41SMatt Barden * Note: We're doing smb_latency_add_sample() for all 1060*148d1a41SMatt Barden * remaining commands NOW, which means we won't include 1061*148d1a41SMatt Barden * the async part of their work in latency statistics. 1062*148d1a41SMatt Barden * That's intentional, as the async part of a command 1063*148d1a41SMatt Barden * would otherwise skew our latency statistics. 1064*148d1a41SMatt Barden */ 1065*148d1a41SMatt Barden smb2_record_stats(sr, sds, B_FALSE); 1066a90cf9f2SGordon Ross 1067*148d1a41SMatt Barden /* 1068*148d1a41SMatt Barden * If there's a next command, figure out where it starts, 1069*148d1a41SMatt Barden * and fill in the next header offset for the reply. 1070*148d1a41SMatt Barden * Note: We sanity checked smb2_next_command above. 1071*148d1a41SMatt Barden */ 1072*148d1a41SMatt Barden if (sr->smb2_next_command != 0) { 1073*148d1a41SMatt Barden sr->command.chain_offset = 1074*148d1a41SMatt Barden sr->smb2_cmd_hdr + sr->smb2_next_command; 1075*148d1a41SMatt Barden sr->smb2_next_reply = 1076*148d1a41SMatt Barden sr->reply.chain_offset - sr->smb2_reply_hdr; 1077*148d1a41SMatt Barden } else { 1078*148d1a41SMatt Barden ASSERT(sr->smb2_next_reply == 0); 1079*148d1a41SMatt Barden } 1080a90cf9f2SGordon Ross 1081*148d1a41SMatt Barden /* 1082*148d1a41SMatt Barden * Overwrite the (now final) SMB2 header for this response. 1083*148d1a41SMatt Barden */ 1084*148d1a41SMatt Barden (void) smb2_encode_header(sr, B_TRUE); 1085a90cf9f2SGordon Ross 1086*148d1a41SMatt Barden /* 1087*148d1a41SMatt Barden * Process whole compound before sending. 1088*148d1a41SMatt Barden */ 1089a90cf9f2SGordon Ross if (sr->smb2_next_command != 0) 1090*148d1a41SMatt Barden goto cmd_start; 1091*148d1a41SMatt Barden smb2_send_reply(sr); 1092a90cf9f2SGordon Ross 1093*148d1a41SMatt Barden ASSERT(!disconnect); 1094a90cf9f2SGordon Ross 1095*148d1a41SMatt Barden cleanup: 1096a90cf9f2SGordon Ross /* 1097*148d1a41SMatt Barden * Restore caller's command processing state. 1098a90cf9f2SGordon Ross */ 1099*148d1a41SMatt Barden sr->smb2_cmd_hdr = saved_cmd_hdr; 1100*148d1a41SMatt Barden sr->command.chain_offset = saved_cmd_hdr; 1101*148d1a41SMatt Barden (void) smb2_decode_header(sr); 1102*148d1a41SMatt Barden sr->command.chain_offset = saved_com_offset; 1103a90cf9f2SGordon Ross 1104*148d1a41SMatt Barden sr->smb2_credit_response = saved_cred_resp; 1105*148d1a41SMatt Barden sr->smb2_hdr_flags = saved_hdr_flags; 1106*148d1a41SMatt Barden sr->smb2_status = NT_STATUS_SUCCESS; 1107a90cf9f2SGordon Ross 1108*148d1a41SMatt Barden /* 1109*148d1a41SMatt Barden * In here, the "disconnect" flag just means we had an 1110*148d1a41SMatt Barden * error decoding or encoding something. Rather than 1111*148d1a41SMatt Barden * actually disconnect here, let's assume whatever 1112*148d1a41SMatt Barden * problem we encountered will be seen by the caller 1113*148d1a41SMatt Barden * as they continue processing the compound, and just 1114*148d1a41SMatt Barden * restore everything and return an error. 1115*148d1a41SMatt Barden */ 1116*148d1a41SMatt Barden if (disconnect) { 1117*148d1a41SMatt Barden sr->smb2_async = B_FALSE; 1118*148d1a41SMatt Barden sr->smb2_reply_hdr = saved_reply_hdr; 1119*148d1a41SMatt Barden sr->reply.chain_offset = sr->smb2_reply_hdr; 1120*148d1a41SMatt Barden (void) smb2_encode_header(sr, B_FALSE); 1121*148d1a41SMatt Barden return (NT_STATUS_INVALID_PARAMETER); 1122*148d1a41SMatt Barden } 1123a90cf9f2SGordon Ross 1124*148d1a41SMatt Barden /* 1125*148d1a41SMatt Barden * The compound reply buffer we sent is now gone. 1126*148d1a41SMatt Barden * Setup a new reply buffer for the caller. 1127*148d1a41SMatt Barden */ 1128*148d1a41SMatt Barden sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND; 1129*148d1a41SMatt Barden sr->smb2_async_id = SMB2_ASYNCID(sr); 1130*148d1a41SMatt Barden sr->smb2_next_reply = 0; 1131*148d1a41SMatt Barden MBC_FLUSH(&sr->reply); 1132*148d1a41SMatt Barden ASSERT(sr->reply.max_bytes == sr->session->reply_max_bytes); 1133*148d1a41SMatt Barden ASSERT(sr->reply.chain_offset == 0); 1134*148d1a41SMatt Barden sr->smb2_reply_hdr = 0; 1135*148d1a41SMatt Barden (void) smb2_encode_header(sr, B_FALSE); 1136a90cf9f2SGordon Ross 1137*148d1a41SMatt Barden return (NT_STATUS_SUCCESS); 1138a90cf9f2SGordon Ross } 1139a90cf9f2SGordon Ross 1140a90cf9f2SGordon Ross int 1141a90cf9f2SGordon Ross smb2_decode_header(smb_request_t *sr) 1142a90cf9f2SGordon Ross { 1143a90cf9f2SGordon Ross uint32_t pid, tid; 1144a90cf9f2SGordon Ross uint16_t hdr_len; 1145a90cf9f2SGordon Ross int rc; 1146a90cf9f2SGordon Ross 1147a90cf9f2SGordon Ross rc = smb_mbc_decodef( 1148a90cf9f2SGordon Ross &sr->command, "Nwww..wwllqllq16c", 1149a90cf9f2SGordon Ross &hdr_len, /* w */ 1150a90cf9f2SGordon Ross &sr->smb2_credit_charge, /* w */ 1151a90cf9f2SGordon Ross &sr->smb2_chan_seq, /* w */ 1152a90cf9f2SGordon Ross /* reserved .. */ 1153a90cf9f2SGordon Ross &sr->smb2_cmd_code, /* w */ 1154a90cf9f2SGordon Ross &sr->smb2_credit_request, /* w */ 1155a90cf9f2SGordon Ross &sr->smb2_hdr_flags, /* l */ 1156a90cf9f2SGordon Ross &sr->smb2_next_command, /* l */ 1157a90cf9f2SGordon Ross &sr->smb2_messageid, /* q */ 1158a90cf9f2SGordon Ross &pid, /* l */ 1159a90cf9f2SGordon Ross &tid, /* l */ 1160*148d1a41SMatt Barden &sr->smb2_ssnid, /* q */ 1161a90cf9f2SGordon Ross sr->smb2_sig); /* 16c */ 1162a90cf9f2SGordon Ross if (rc) 1163a90cf9f2SGordon Ross return (rc); 1164a90cf9f2SGordon Ross 1165a90cf9f2SGordon Ross if (hdr_len != SMB2_HDR_SIZE) 1166a90cf9f2SGordon Ross return (-1); 1167a90cf9f2SGordon Ross 1168a90cf9f2SGordon Ross if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1169a90cf9f2SGordon Ross sr->smb2_async_id = pid | 1170a90cf9f2SGordon Ross ((uint64_t)tid) << 32; 1171*148d1a41SMatt Barden sr->smb_pid = 0; 1172*148d1a41SMatt Barden sr->smb_tid = 0; 1173a90cf9f2SGordon Ross } else { 1174*148d1a41SMatt Barden sr->smb2_async_id = 0; 1175a90cf9f2SGordon Ross sr->smb_pid = pid; 1176a90cf9f2SGordon Ross sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */ 1177a90cf9f2SGordon Ross } 1178a90cf9f2SGordon Ross 1179a90cf9f2SGordon Ross return (rc); 1180a90cf9f2SGordon Ross } 1181a90cf9f2SGordon Ross 1182a90cf9f2SGordon Ross int 1183a90cf9f2SGordon Ross smb2_encode_header(smb_request_t *sr, boolean_t overwrite) 1184a90cf9f2SGordon Ross { 1185a90cf9f2SGordon Ross uint64_t pid_tid_aid; /* pid+tid, or async id */ 1186a90cf9f2SGordon Ross int rc; 1187a90cf9f2SGordon Ross 1188a90cf9f2SGordon Ross if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1189a90cf9f2SGordon Ross pid_tid_aid = sr->smb2_async_id; 1190a90cf9f2SGordon Ross } else { 1191a90cf9f2SGordon Ross pid_tid_aid = sr->smb_pid | 1192a90cf9f2SGordon Ross ((uint64_t)sr->smb_tid) << 32; 1193a90cf9f2SGordon Ross } 1194a90cf9f2SGordon Ross 1195a90cf9f2SGordon Ross if (overwrite) { 1196a90cf9f2SGordon Ross rc = smb_mbc_poke(&sr->reply, 1197a90cf9f2SGordon Ross sr->smb2_reply_hdr, 1198a90cf9f2SGordon Ross "Nwwlwwllqqq16c", 1199a90cf9f2SGordon Ross SMB2_HDR_SIZE, /* w */ 1200a90cf9f2SGordon Ross sr->smb2_credit_charge, /* w */ 1201a90cf9f2SGordon Ross sr->smb2_status, /* l */ 1202a90cf9f2SGordon Ross sr->smb2_cmd_code, /* w */ 1203a90cf9f2SGordon Ross sr->smb2_credit_response, /* w */ 1204*148d1a41SMatt Barden sr->smb2_hdr_flags, /* l */ 1205a90cf9f2SGordon Ross sr->smb2_next_reply, /* l */ 1206a90cf9f2SGordon Ross sr->smb2_messageid, /* q */ 1207a90cf9f2SGordon Ross pid_tid_aid, /* q */ 1208811599a4SMatt Barden sr->smb2_ssnid, /* q */ 1209a90cf9f2SGordon Ross sr->smb2_sig); /* 16c */ 1210a90cf9f2SGordon Ross } else { 1211a90cf9f2SGordon Ross rc = smb_mbc_encodef(&sr->reply, 1212a90cf9f2SGordon Ross "Nwwlwwllqqq16c", 1213a90cf9f2SGordon Ross SMB2_HDR_SIZE, /* w */ 1214a90cf9f2SGordon Ross sr->smb2_credit_charge, /* w */ 1215a90cf9f2SGordon Ross sr->smb2_status, /* l */ 1216a90cf9f2SGordon Ross sr->smb2_cmd_code, /* w */ 1217a90cf9f2SGordon Ross sr->smb2_credit_response, /* w */ 1218*148d1a41SMatt Barden sr->smb2_hdr_flags, /* l */ 1219a90cf9f2SGordon Ross sr->smb2_next_reply, /* l */ 1220a90cf9f2SGordon Ross sr->smb2_messageid, /* q */ 1221a90cf9f2SGordon Ross pid_tid_aid, /* q */ 1222811599a4SMatt Barden sr->smb2_ssnid, /* q */ 1223a90cf9f2SGordon Ross sr->smb2_sig); /* 16c */ 1224a90cf9f2SGordon Ross } 1225a90cf9f2SGordon Ross 1226a90cf9f2SGordon Ross return (rc); 1227a90cf9f2SGordon Ross } 1228a90cf9f2SGordon Ross 1229a90cf9f2SGordon Ross void 1230a90cf9f2SGordon Ross smb2_send_reply(smb_request_t *sr) 1231a90cf9f2SGordon Ross { 1232a90cf9f2SGordon Ross 1233a90cf9f2SGordon Ross if (smb_session_send(sr->session, 0, &sr->reply) == 0) 1234a90cf9f2SGordon Ross sr->reply.chain = 0; 1235a90cf9f2SGordon Ross } 1236a90cf9f2SGordon Ross 1237a90cf9f2SGordon Ross /* 1238a90cf9f2SGordon Ross * This wrapper function exists to help catch calls to smbsr_status() 1239a90cf9f2SGordon Ross * (which is SMB1-specific) in common code. See smbsr_status(). 1240a90cf9f2SGordon Ross * If the log message below is seen, put a dtrace probe on this 1241a90cf9f2SGordon Ross * function with a stack() action to see who is calling the SMB1 1242a90cf9f2SGordon Ross * "put error" from common code, and fix it. 1243a90cf9f2SGordon Ross */ 1244a90cf9f2SGordon Ross void 1245a90cf9f2SGordon Ross smbsr_status_smb2(smb_request_t *sr, DWORD status) 1246a90cf9f2SGordon Ross { 1247a90cf9f2SGordon Ross const char *name; 1248a90cf9f2SGordon Ross 1249a90cf9f2SGordon Ross if (sr->smb2_cmd_code < SMB2__NCMDS) 1250a90cf9f2SGordon Ross name = smb2_disp_table[sr->smb2_cmd_code].sdt_name; 1251a90cf9f2SGordon Ross else 1252a90cf9f2SGordon Ross name = "<unknown>"; 1253a90cf9f2SGordon Ross #ifdef DEBUG 1254a90cf9f2SGordon Ross cmn_err(CE_NOTE, "smbsr_status called for %s", name); 1255a90cf9f2SGordon Ross #endif 1256a90cf9f2SGordon Ross 1257a90cf9f2SGordon Ross smb2sr_put_error_data(sr, status, NULL); 1258a90cf9f2SGordon Ross } 1259a90cf9f2SGordon Ross 1260a90cf9f2SGordon Ross void 1261a90cf9f2SGordon Ross smb2sr_put_errno(struct smb_request *sr, int errnum) 1262a90cf9f2SGordon Ross { 1263a90cf9f2SGordon Ross uint32_t status = smb_errno2status(errnum); 1264a90cf9f2SGordon Ross smb2sr_put_error_data(sr, status, NULL); 1265a90cf9f2SGordon Ross } 1266a90cf9f2SGordon Ross 1267a90cf9f2SGordon Ross void 1268a90cf9f2SGordon Ross smb2sr_put_error(smb_request_t *sr, uint32_t status) 1269a90cf9f2SGordon Ross { 1270a90cf9f2SGordon Ross smb2sr_put_error_data(sr, status, NULL); 1271a90cf9f2SGordon Ross } 1272a90cf9f2SGordon Ross 1273a90cf9f2SGordon Ross /* 1274a90cf9f2SGordon Ross * Build an SMB2 error response. [MS-SMB2] 2.2.2 1275a90cf9f2SGordon Ross */ 1276a90cf9f2SGordon Ross void 1277a90cf9f2SGordon Ross smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc) 1278a90cf9f2SGordon Ross { 1279a90cf9f2SGordon Ross DWORD len; 1280a90cf9f2SGordon Ross 1281a90cf9f2SGordon Ross /* 1282a90cf9f2SGordon Ross * The common dispatch code writes this when it 1283a90cf9f2SGordon Ross * updates the SMB2 header before sending. 1284a90cf9f2SGordon Ross */ 1285a90cf9f2SGordon Ross sr->smb2_status = status; 1286a90cf9f2SGordon Ross 1287a90cf9f2SGordon Ross /* Rewind to the end of the SMB header. */ 1288a90cf9f2SGordon Ross sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE; 1289a90cf9f2SGordon Ross 1290a90cf9f2SGordon Ross /* 1291a90cf9f2SGordon Ross * NB: Must provide at least one byte of error data, 1292a90cf9f2SGordon Ross * per [MS-SMB2] 2.2.2 1293a90cf9f2SGordon Ross */ 1294a90cf9f2SGordon Ross if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) { 1295a90cf9f2SGordon Ross (void) smb_mbc_encodef( 1296a90cf9f2SGordon Ross &sr->reply, 1297a90cf9f2SGordon Ross "wwlC", 1298a90cf9f2SGordon Ross 9, /* StructSize */ /* w */ 1299a90cf9f2SGordon Ross 0, /* reserved */ /* w */ 1300a90cf9f2SGordon Ross len, /* l */ 1301a90cf9f2SGordon Ross mbc); /* C */ 1302a90cf9f2SGordon Ross } else { 1303a90cf9f2SGordon Ross (void) smb_mbc_encodef( 1304a90cf9f2SGordon Ross &sr->reply, 1305a90cf9f2SGordon Ross "wwl.", 1306a90cf9f2SGordon Ross 9, /* StructSize */ /* w */ 1307a90cf9f2SGordon Ross 0, /* reserved */ /* w */ 1308a90cf9f2SGordon Ross 0); /* l. */ 1309a90cf9f2SGordon Ross } 1310a90cf9f2SGordon Ross } 1311a90cf9f2SGordon Ross 1312a90cf9f2SGordon Ross /* 1313a90cf9f2SGordon Ross * smb2sr_lookup_fid 1314a90cf9f2SGordon Ross * 1315a90cf9f2SGordon Ross * Setup sr->fid_ofile, either inherited from a related command, 1316a90cf9f2SGordon Ross * or obtained via FID lookup. Similar inheritance logic as in 1317a90cf9f2SGordon Ross * smb2sr_work. 1318a90cf9f2SGordon Ross */ 1319a90cf9f2SGordon Ross uint32_t 1320a90cf9f2SGordon Ross smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid) 1321a90cf9f2SGordon Ross { 1322a90cf9f2SGordon Ross boolean_t related = sr->smb2_hdr_flags & 1323a90cf9f2SGordon Ross SMB2_FLAGS_RELATED_OPERATIONS; 1324a90cf9f2SGordon Ross 1325a90cf9f2SGordon Ross if (related) { 1326a90cf9f2SGordon Ross if (sr->fid_ofile == NULL) 1327a90cf9f2SGordon Ross return (NT_STATUS_INVALID_PARAMETER); 1328a90cf9f2SGordon Ross sr->smb_fid = sr->fid_ofile->f_fid; 1329a90cf9f2SGordon Ross return (0); 1330a90cf9f2SGordon Ross } 1331a90cf9f2SGordon Ross 1332a90cf9f2SGordon Ross /* 1333a90cf9f2SGordon Ross * If we could be sure this is called only once per cmd, 1334a90cf9f2SGordon Ross * we could simply ASSERT(sr->fid_ofile == NULL) here. 1335a90cf9f2SGordon Ross * However, there are cases where it can be called again 1336a90cf9f2SGordon Ross * handling the same command, so let's tolerate that. 1337a90cf9f2SGordon Ross */ 1338a90cf9f2SGordon Ross if (sr->fid_ofile == NULL) { 1339a90cf9f2SGordon Ross sr->smb_fid = (uint16_t)fid->temporal; 1340a90cf9f2SGordon Ross sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid); 1341a90cf9f2SGordon Ross } 1342811599a4SMatt Barden if (sr->fid_ofile == NULL || 1343811599a4SMatt Barden sr->fid_ofile->f_persistid != fid->persistent) 1344a90cf9f2SGordon Ross return (NT_STATUS_FILE_CLOSED); 1345a90cf9f2SGordon Ross 1346a90cf9f2SGordon Ross return (0); 1347a90cf9f2SGordon Ross } 1348a90cf9f2SGordon Ross 1349a90cf9f2SGordon Ross /* 1350a90cf9f2SGordon Ross * smb2_dispatch_stats_init 1351a90cf9f2SGordon Ross * 1352a90cf9f2SGordon Ross * Initializes dispatch statistics for SMB2. 1353a90cf9f2SGordon Ross * See also smb_dispatch_stats_init(), which fills in 1354a90cf9f2SGordon Ross * the lower part of the statistics array, from zero 1355a90cf9f2SGordon Ross * through SMB_COM_NUM; 1356a90cf9f2SGordon Ross */ 1357a90cf9f2SGordon Ross void 1358a90cf9f2SGordon Ross smb2_dispatch_stats_init(smb_server_t *sv) 1359a90cf9f2SGordon Ross { 1360a90cf9f2SGordon Ross smb_disp_stats_t *sds = sv->sv_disp_stats2; 1361a90cf9f2SGordon Ross smb_kstat_req_t *ksr; 1362a90cf9f2SGordon Ross int i; 1363a90cf9f2SGordon Ross 1364a90cf9f2SGordon Ross ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2; 1365a90cf9f2SGordon Ross 1366a90cf9f2SGordon Ross for (i = 0; i < SMB2__NCMDS; i++, ksr++) { 1367a90cf9f2SGordon Ross smb_latency_init(&sds[i].sdt_lat); 1368a90cf9f2SGordon Ross (void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name, 1369a90cf9f2SGordon Ross sizeof (ksr->kr_name)); 1370a90cf9f2SGordon Ross } 1371a90cf9f2SGordon Ross } 1372a90cf9f2SGordon Ross 1373a90cf9f2SGordon Ross /* 1374a90cf9f2SGordon Ross * smb2_dispatch_stats_fini 1375a90cf9f2SGordon Ross * 1376a90cf9f2SGordon Ross * Frees and destroyes the resources used for statistics. 1377a90cf9f2SGordon Ross */ 1378a90cf9f2SGordon Ross void 1379a90cf9f2SGordon Ross smb2_dispatch_stats_fini(smb_server_t *sv) 1380a90cf9f2SGordon Ross { 1381a90cf9f2SGordon Ross smb_disp_stats_t *sds = sv->sv_disp_stats2; 1382a90cf9f2SGordon Ross int i; 1383a90cf9f2SGordon Ross 1384a90cf9f2SGordon Ross for (i = 0; i < SMB2__NCMDS; i++) 1385a90cf9f2SGordon Ross smb_latency_destroy(&sds[i].sdt_lat); 1386a90cf9f2SGordon Ross } 1387a90cf9f2SGordon Ross 1388a90cf9f2SGordon Ross void 1389a90cf9f2SGordon Ross smb2_dispatch_stats_update(smb_server_t *sv, 1390a90cf9f2SGordon Ross smb_kstat_req_t *ksr, int first, int nreq) 1391a90cf9f2SGordon Ross { 1392a90cf9f2SGordon Ross smb_disp_stats_t *sds = sv->sv_disp_stats2; 1393a90cf9f2SGordon Ross int i; 1394a90cf9f2SGordon Ross int last; 1395a90cf9f2SGordon Ross 1396a90cf9f2SGordon Ross last = first + nreq - 1; 1397a90cf9f2SGordon Ross 1398a90cf9f2SGordon Ross if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS)) { 1399a90cf9f2SGordon Ross for (i = first; i <= last; i++, ksr++) { 1400a90cf9f2SGordon Ross ksr->kr_rxb = sds[i].sdt_rxb; 1401a90cf9f2SGordon Ross ksr->kr_txb = sds[i].sdt_txb; 1402a90cf9f2SGordon Ross mutex_enter(&sds[i].sdt_lat.ly_mutex); 1403a90cf9f2SGordon Ross ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq; 1404a90cf9f2SGordon Ross ksr->kr_sum = sds[i].sdt_lat.ly_a_sum; 1405a90cf9f2SGordon Ross ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean; 1406a90cf9f2SGordon Ross ksr->kr_a_stddev = 1407a90cf9f2SGordon Ross sds[i].sdt_lat.ly_a_stddev; 1408a90cf9f2SGordon Ross ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean; 1409a90cf9f2SGordon Ross ksr->kr_d_stddev = 1410a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_stddev; 1411a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_mean = 0; 1412a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_nreq = 0; 1413a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_stddev = 0; 1414a90cf9f2SGordon Ross sds[i].sdt_lat.ly_d_sum = 0; 1415a90cf9f2SGordon Ross mutex_exit(&sds[i].sdt_lat.ly_mutex); 1416a90cf9f2SGordon Ross } 1417a90cf9f2SGordon Ross } 1418a90cf9f2SGordon Ross } 1419