1a90cf9fGordon Ross/*
2a90cf9fGordon Ross * This file and its contents are supplied under the terms of the
3a90cf9fGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0.
4a90cf9fGordon Ross * You may only use this file in accordance with the terms of version
5a90cf9fGordon Ross * 1.0 of the CDDL.
6a90cf9fGordon Ross *
7a90cf9fGordon Ross * A full copy of the text of the CDDL should have accompanied this
8a90cf9fGordon Ross * source.  A copy of the CDDL is also available via the Internet at
9a90cf9fGordon Ross * http://www.illumos.org/license/CDDL.
10a90cf9fGordon Ross */
11a90cf9fGordon Ross
12a90cf9fGordon Ross/*
134ad35faMatt Barden * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
14541826aAndrew Stormont * Copyright 2019 RackTop Systems.
15a90cf9fGordon Ross */
16a90cf9fGordon Ross
17a90cf9fGordon Ross
18a90cf9fGordon Ross#include <smbsrv/smb2_kproto.h>
19a90cf9fGordon Ross#include <smbsrv/smb_kstat.h>
20a90cf9fGordon Ross#include <smbsrv/smb2.h>
21a90cf9fGordon Ross
22148d1a4Matt Barden#define	SMB2_ASYNCID(sr) (sr->smb2_messageid ^ (1ULL << 62))
23a90cf9fGordon Ross
24a90cf9fGordon Rosssmb_sdrc_t smb2_invalid_cmd(smb_request_t *);
25a90cf9fGordon Rossstatic void smb2_tq_work(void *);
2694047d4Gordon Rossstatic void smb2sr_run_postwork(smb_request_t *);
271160dcfMatt Bardenstatic int smb3_decrypt_msg(smb_request_t *);
28a90cf9fGordon Ross
29adb064aToomas Soomestatic const smb_disp_entry_t
30a90cf9fGordon Rosssmb2_disp_table[SMB2__NCMDS] = {
31a90cf9fGordon Ross
32a90cf9fGordon Ross	/* text-name, pre, func, post, cmd-code, dialect, flags */
33a90cf9fGordon Ross
34a90cf9fGordon Ross	{  "smb2_negotiate", NULL,
35a90cf9fGordon Ross	    smb2_negotiate, NULL, 0, 0,
36a90cf9fGordon Ross	    SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
37a90cf9fGordon Ross
38a90cf9fGordon Ross	{  "smb2_session_setup", NULL,
39a90cf9fGordon Ross	    smb2_session_setup, NULL, 0, 0,
40a90cf9fGordon Ross	    SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
41a90cf9fGordon Ross
42a90cf9fGordon Ross	{  "smb2_logoff", NULL,
43a90cf9fGordon Ross	    smb2_logoff, NULL, 0, 0,
44a90cf9fGordon Ross	    SDDF_SUPPRESS_TID },
45a90cf9fGordon Ross
46a90cf9fGordon Ross	{  "smb2_tree_connect", NULL,
47a90cf9fGordon Ross	    smb2_tree_connect, NULL, 0, 0,
48a90cf9fGordon Ross	    SDDF_SUPPRESS_TID },
49a90cf9fGordon Ross
50a90cf9fGordon Ross	{  "smb2_tree_disconn", NULL,
51a90cf9fGordon Ross	    smb2_tree_disconn, NULL, 0, 0 },
52a90cf9fGordon Ross
53a90cf9fGordon Ross	{  "smb2_create", NULL,
54a90cf9fGordon Ross	    smb2_create, NULL, 0, 0 },
55a90cf9fGordon Ross
56a90cf9fGordon Ross	{  "smb2_close", NULL,
57a90cf9fGordon Ross	    smb2_close, NULL, 0, 0 },
58a90cf9fGordon Ross
59a90cf9fGordon Ross	{  "smb2_flush", NULL,
60a90cf9fGordon Ross	    smb2_flush, NULL, 0, 0 },
61a90cf9fGordon Ross
62a90cf9fGordon Ross	{  "smb2_read", NULL,
63a90cf9fGordon Ross	    smb2_read, NULL, 0, 0 },
64a90cf9fGordon Ross
65a90cf9fGordon Ross	{  "smb2_write", NULL,
66a90cf9fGordon Ross	    smb2_write, NULL, 0, 0 },
67a90cf9fGordon Ross
68a90cf9fGordon Ross	{  "smb2_lock", NULL,
69a90cf9fGordon Ross	    smb2_lock, NULL, 0, 0 },
70a90cf9fGordon Ross
71a90cf9fGordon Ross	{  "smb2_ioctl", NULL,
72a90cf9fGordon Ross	    smb2_ioctl, NULL, 0, 0 },
73a90cf9fGordon Ross
74a90cf9fGordon Ross	{  "smb2_cancel", NULL,
755677e04Gordon Ross	    smb2_cancel, NULL, 0, 0,
765677e04Gordon Ross	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
77a90cf9fGordon Ross
78a90cf9fGordon Ross	{  "smb2_echo", NULL,
79a90cf9fGordon Ross	    smb2_echo, NULL, 0, 0,
80a90cf9fGordon Ross	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
81a90cf9fGordon Ross
82a90cf9fGordon Ross	{  "smb2_query_dir", NULL,
83a90cf9fGordon Ross	    smb2_query_dir, NULL, 0, 0 },
84a90cf9fGordon Ross
85a90cf9fGordon Ross	{  "smb2_change_notify", NULL,
86a90cf9fGordon Ross	    smb2_change_notify, NULL, 0, 0 },
87a90cf9fGordon Ross
88a90cf9fGordon Ross	{  "smb2_query_info", NULL,
89a90cf9fGordon Ross	    smb2_query_info, NULL, 0, 0 },
90a90cf9fGordon Ross
91a90cf9fGordon Ross	{  "smb2_set_info", NULL,
92a90cf9fGordon Ross	    smb2_set_info, NULL, 0, 0 },
93a90cf9fGordon Ross
94a90cf9fGordon Ross	{  "smb2_oplock_break_ack", NULL,
95a90cf9fGordon Ross	    smb2_oplock_break_ack, NULL, 0, 0 },
96a90cf9fGordon Ross
97a90cf9fGordon Ross	{  "smb2_invalid_cmd", NULL,
98a90cf9fGordon Ross	    smb2_invalid_cmd, NULL, 0, 0,
99a90cf9fGordon Ross	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
100a90cf9fGordon Ross};
101a90cf9fGordon Ross
102a90cf9fGordon Rosssmb_sdrc_t
103a90cf9fGordon Rosssmb2_invalid_cmd(smb_request_t *sr)
104a90cf9fGordon Ross{
105a90cf9fGordon Ross#ifdef	DEBUG
106a90cf9fGordon Ross	cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code",
107a90cf9fGordon Ross	    sr->session->ip_addr_str);
108a90cf9fGordon Ross#endif
109a90cf9fGordon Ross	sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
110a90cf9fGordon Ross	return (SDRC_DROP_VC);
111a90cf9fGordon Ross}
112a90cf9fGordon Ross
113a90cf9fGordon Ross/*
114a90cf9fGordon Ross * This is the SMB2 handler for new smb requests, called from
115a90cf9fGordon Ross * smb_session_reader after SMB negotiate is done.  For most SMB2
116a90cf9fGordon Ross * requests, we just enqueue them for the smb_session_worker to
117a90cf9fGordon Ross * execute via the task queue, so they can block for resources
118a90cf9fGordon Ross * without stopping the reader thread.  A few protocol messages
119a90cf9fGordon Ross * are special cases and are handled directly here in the reader
120a90cf9fGordon Ross * thread so they don't wait for taskq scheduling.
121a90cf9fGordon Ross *
122a90cf9fGordon Ross * This function must either enqueue the new request for
123a90cf9fGordon Ross * execution via the task queue, or execute it directly
124a90cf9fGordon Ross * and then free it.  If this returns non-zero, the caller
125a90cf9fGordon Ross * will drop the session.
126a90cf9fGordon Ross */
127a90cf9fGordon Rossint
128a90cf9fGordon Rosssmb2sr_newrq(smb_request_t *sr)
129a90cf9fGordon Ross{
1305677e04Gordon Ross	struct mbuf_chain *mbc = &sr->command;
131a90cf9fGordon Ross	uint32_t magic;
1325677e04Gordon Ross	int rc, skip;
1335677e04Gordon Ross
1345677e04Gordon Ross	if (smb_mbc_peek(mbc, 0, "l", &magic) != 0)
1355677e04Gordon Ross		goto drop;
1365677e04Gordon Ross
1371160dcfMatt Barden	/* 0xFD S M B */
1381160dcfMatt Barden	if (magic == SMB3_ENCRYPTED_MAGIC) {
1391160dcfMatt Barden		if (smb3_decrypt_msg(sr) != 0)
1401160dcfMatt Barden			goto drop;
1411160dcfMatt Barden		/*
1421160dcfMatt Barden		 * Should now be looking at an un-encrypted
1431160dcfMatt Barden		 * SMB2 message header.
1441160dcfMatt Barden		 */
1451160dcfMatt Barden		if (smb_mbc_peek(mbc, 0, "l", &magic) != 0)
1461160dcfMatt Barden			goto drop;
1471160dcfMatt Barden	}
1481160dcfMatt Barden
1495677e04Gordon Ross	if (magic != SMB2_PROTOCOL_MAGIC)
1505677e04Gordon Ross		goto drop;
151a90cf9fGordon Ross
152a90cf9fGordon Ross	/*
1535677e04Gordon Ross	 * Walk the SMB2 commands in this compound message and
1545677e04Gordon Ross	 * keep track of the range of message IDs it uses.
155a90cf9fGordon Ross	 */
1565677e04Gordon Ross	for (;;) {
1575677e04Gordon Ross		if (smb2_decode_header(sr) != 0)
1585677e04Gordon Ross			goto drop;
1595677e04Gordon Ross
1605677e04Gordon Ross		/*
1615677e04Gordon Ross		 * Cancel requests are special:  They refer to
1625677e04Gordon Ross		 * an earlier message ID (or an async. ID),
1635677e04Gordon Ross		 * never a new ID, and are never compounded.
1645677e04Gordon Ross		 * This is intentionally not "goto drop"
1655677e04Gordon Ross		 * because rc may be zero (success).
1665677e04Gordon Ross		 */
1675677e04Gordon Ross		if (sr->smb2_cmd_code == SMB2_CANCEL) {
1685677e04Gordon Ross			rc = smb2_newrq_cancel(sr);
1695677e04Gordon Ross			smb_request_free(sr);
1705677e04Gordon Ross			return (rc);
1715677e04Gordon Ross		}
1725677e04Gordon Ross
1735677e04Gordon Ross		/*
1745677e04Gordon Ross		 * Keep track of the total credits in this compound
1755677e04Gordon Ross		 * and the first (real) message ID (not: 0, -1)
1765677e04Gordon Ross		 * While we're looking, verify that all (real) IDs
1775677e04Gordon Ross		 * are (first <= ID < (first + msg_credits))
1785677e04Gordon Ross		 */
1795677e04Gordon Ross		if (sr->smb2_credit_charge == 0)
1805677e04Gordon Ross			sr->smb2_credit_charge = 1;
1815677e04Gordon Ross		sr->smb2_total_credits += sr->smb2_credit_charge;
1825677e04Gordon Ross
1835677e04Gordon Ross		if (sr->smb2_messageid != 0 &&
1845677e04Gordon Ross		    sr->smb2_messageid != UINT64_MAX) {
1855677e04Gordon Ross
1865677e04Gordon Ross			if (sr->smb2_first_msgid == 0)
1875677e04Gordon Ross				sr->smb2_first_msgid = sr->smb2_messageid;
1885677e04Gordon Ross
1895677e04Gordon Ross			if (sr->smb2_messageid < sr->smb2_first_msgid ||
1905677e04Gordon Ross			    sr->smb2_messageid >= (sr->smb2_first_msgid +
1915677e04Gordon Ross			    sr->smb2_total_credits)) {
1925677e04Gordon Ross				long long id = (long long) sr->smb2_messageid;
1935677e04Gordon Ross				cmn_err(CE_WARN, "clnt %s msg ID 0x%llx "
1945677e04Gordon Ross				    "out of sequence in compound",
1955677e04Gordon Ross				    sr->session->ip_addr_str, id);
1965677e04Gordon Ross			}
1975677e04Gordon Ross		}
1985677e04Gordon Ross
1995677e04Gordon Ross		/* Normal loop exit on next == zero */
2005677e04Gordon Ross		if (sr->smb2_next_command == 0)
2015677e04Gordon Ross			break;
2025677e04Gordon Ross
2035677e04Gordon Ross		/* Abundance of caution... */
2045677e04Gordon Ross		if (sr->smb2_next_command < SMB2_HDR_SIZE)
2055677e04Gordon Ross			goto drop;
2065677e04Gordon Ross
2075677e04Gordon Ross		/* Advance to the next header. */
2085677e04Gordon Ross		skip = sr->smb2_next_command - SMB2_HDR_SIZE;
2095677e04Gordon Ross		if (MBC_ROOM_FOR(mbc, skip) == 0)
2105677e04Gordon Ross			goto drop;
2115677e04Gordon Ross		mbc->chain_offset += skip;
212a90cf9fGordon Ross	}
2135677e04Gordon Ross	/* Rewind back to the top. */
2145677e04Gordon Ross	mbc->chain_offset = 0;
215a90cf9fGordon Ross
216a90cf9fGordon Ross	/*
217a90cf9fGordon Ross	 * Submit the request to the task queue, which calls
218a90cf9fGordon Ross	 * smb2_tq_work when the workload permits.
219a90cf9fGordon Ross	 */
220a90cf9fGordon Ross	sr->sr_time_submitted = gethrtime();
221a90cf9fGordon Ross	sr->sr_state = SMB_REQ_STATE_SUBMITTED;
222a90cf9fGordon Ross	smb_srqueue_waitq_enter(sr->session->s_srqueue);
223a90cf9fGordon Ross	(void) taskq_dispatch(sr->sr_server->sv_worker_pool,
224a90cf9fGordon Ross	    smb2_tq_work, sr, TQ_SLEEP);
225a90cf9fGordon Ross	return (0);
2265677e04Gordon Ross
2275677e04Gordon Rossdrop:
2285677e04Gordon Ross	smb_request_free(sr);
2295677e04Gordon Ross	return (-1);
230a90cf9fGordon Ross}
231a90cf9fGordon Ross
232a90cf9fGordon Rossstatic void
233a90cf9fGordon Rosssmb2_tq_work(void *arg)
234a90cf9fGordon Ross{
235a90cf9fGordon Ross	smb_request_t	*sr;
236a90cf9fGordon Ross	smb_srqueue_t	*srq;
237a90cf9fGordon Ross
238a90cf9fGordon Ross	sr = (smb_request_t *)arg;
239a90cf9fGordon Ross	SMB_REQ_VALID(sr);
240a90cf9fGordon Ross
241a90cf9fGordon Ross	srq = sr->session->s_srqueue;
242a90cf9fGordon Ross	smb_srqueue_waitq_to_runq(srq);
243a90cf9fGordon Ross	sr->sr_worker = curthread;
244a90cf9fGordon Ross	sr->sr_time_active = gethrtime();
245a90cf9fGordon Ross
246a90cf9fGordon Ross	/*
2475677e04Gordon Ross	 * Always dispatch to the work function, because cancelled
2485677e04Gordon Ross	 * requests need an error reply (NT_STATUS_CANCELLED).
249a90cf9fGordon Ross	 */
2505677e04Gordon Ross	mutex_enter(&sr->sr_mutex);
2515677e04Gordon Ross	if (sr->sr_state == SMB_REQ_STATE_SUBMITTED)
2525677e04Gordon Ross		sr->sr_state = SMB_REQ_STATE_ACTIVE;
2535677e04Gordon Ross	mutex_exit(&sr->sr_mutex);
2545677e04Gordon Ross
255a90cf9fGordon Ross	smb2sr_work(sr);
256a90cf9fGordon Ross
257a90cf9fGordon Ross	smb_srqueue_runq_exit(srq);
258a90cf9fGordon Ross}
259a90cf9fGordon Ross
2601160dcfMatt Bardenstatic int
2611160dcfMatt Bardensmb3_decrypt_msg(smb_request_t *sr)
2621160dcfMatt Barden{
2631160dcfMatt Barden	int save_offset;
2641160dcfMatt Barden
2651160dcfMatt Barden	if (sr->session->dialect < SMB_VERS_3_0) {
2661160dcfMatt Barden		cmn_err(CE_WARN, "encrypted message in SMB 2.x");
2671160dcfMatt Barden		return (-1);
2681160dcfMatt Barden	}
2691160dcfMatt Barden
2701160dcfMatt Barden	sr->encrypted = B_TRUE;
2711160dcfMatt Barden	save_offset = sr->command.chain_offset;
2721160dcfMatt Barden	if (smb3_decode_tform_header(sr) != 0) {
2731160dcfMatt Barden		cmn_err(CE_WARN, "bad transform header");
2741160dcfMatt Barden		return (-1);
2751160dcfMatt Barden	}
2761160dcfMatt Barden	sr->command.chain_offset = save_offset;
2771160dcfMatt Barden
2781160dcfMatt Barden	sr->tform_ssn = smb_session_lookup_ssnid(sr->session,
2791160dcfMatt Barden	    sr->smb3_tform_ssnid);
2801160dcfMatt Barden	if (sr->tform_ssn == NULL) {
2811160dcfMatt Barden		cmn_err(CE_WARN, "transform header: session not found");
2821160dcfMatt Barden		return (-1);
2831160dcfMatt Barden	}
2841160dcfMatt Barden
2851160dcfMatt Barden	if (smb3_decrypt_sr(sr) != 0) {
2861160dcfMatt Barden		cmn_err(CE_WARN, "smb3 decryption failed");
2871160dcfMatt Barden		return (-1);
2881160dcfMatt Barden	}
2891160dcfMatt Barden
2901160dcfMatt Barden	return (0);
2911160dcfMatt Barden}
2921160dcfMatt Barden
293a90cf9fGordon Ross/*
294148d1a4Matt Barden * SMB2 credits determine how many simultaneous commands the
295148d1a4Matt Barden * client may issue, and bounds the range of message IDs those
296148d1a4Matt Barden * commands may use.  With multi-credit support, commands may
297148d1a4Matt Barden * use ranges of message IDs, where the credits used by each
298148d1a4Matt Barden * command are proportional to their data transfer size.
299148d1a4Matt Barden *
300148d1a4Matt Barden * Every command may request an increase or decrease of
301148d1a4Matt Barden * the currently granted credits, based on the difference
302148d1a4Matt Barden * between the credit request and the credit charge.
303148d1a4Matt Barden * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits
304148d1a4Matt Barden *
305148d1a4Matt Barden * Most commands have credit_request=1, credit_charge=1,
306148d1a4Matt Barden * which keeps the credit grant unchanged.
307148d1a4Matt Barden *
308148d1a4Matt Barden * All we're really doing here (for now) is reducing the
309148d1a4Matt Barden * credit_response if the client requests a credit increase
310148d1a4Matt Barden * that would take their credit over the maximum, and
311148d1a4Matt Barden * limiting the decrease so they don't run out of credits.
312148d1a4Matt Barden *
313148d1a4Matt Barden * Later, this could do something dynamic based on load.
314148d1a4Matt Barden *
315148d1a4Matt Barden * One other non-obvious bit about credits: We keep the
316148d1a4Matt Barden * session s_max_credits low until the 1st authentication,
317148d1a4Matt Barden * at which point we'll set the normal maximum_credits.
318148d1a4Matt Barden * Some clients ask for more credits with session setup,
319148d1a4Matt Barden * and we need to handle that requested increase _after_
320148d1a4Matt Barden * the command-specific handler returns so it won't be
321148d1a4Matt Barden * restricted to the lower (pre-auth) limit.
322148d1a4Matt Barden */
323148d1a4Matt Bardenstatic inline void
324148d1a4Matt Bardensmb2_credit_decrease(smb_request_t *sr)
325148d1a4Matt Barden{
326148d1a4Matt Barden	smb_session_t *session = sr->session;
327148d1a4Matt Barden	uint16_t cur, d;
328148d1a4Matt Barden
329148d1a4Matt Barden	mutex_enter(&session->s_credits_mutex);
330148d1a4Matt Barden	cur = session->s_cur_credits;
331148d1a4Matt Barden
332148d1a4Matt Barden	/* Handle credit decrease. */
333148d1a4Matt Barden	d = sr->smb2_credit_charge - sr->smb2_credit_request;
334148d1a4Matt Barden	cur -= d;
335148d1a4Matt Barden	if (cur & 0x8000) {
336148d1a4Matt Barden		/*
337148d1a4Matt Barden		 * underflow (bad credit charge or request)
338148d1a4Matt Barden		 * leave credits unchanged (response=charge)
339148d1a4Matt Barden		 */
340148d1a4Matt Barden		cur = session->s_cur_credits;
341148d1a4Matt Barden		sr->smb2_credit_response = sr->smb2_credit_charge;
342148d1a4Matt Barden		DTRACE_PROBE1(smb2__credit__neg, smb_request_t *, sr);
343148d1a4Matt Barden	}
344148d1a4Matt Barden
345148d1a4Matt Barden	/*
346148d1a4Matt Barden	 * The server MUST ensure that the number of credits
347148d1a4Matt Barden	 * held by the client is never reduced to zero.
348148d1a4Matt Barden	 * [MS-SMB2] 3.3.1.2
349148d1a4Matt Barden	 */
350148d1a4Matt Barden	if (cur == 0) {
351148d1a4Matt Barden		cur = 1;
352148d1a4Matt Barden		sr->smb2_credit_response += 1;
353148d1a4Matt Barden		DTRACE_PROBE1(smb2__credit__min, smb_request_t *, sr);
354148d1a4Matt Barden	}
355148d1a4Matt Barden
356148d1a4Matt Barden	DTRACE_PROBE3(smb2__credit__decrease,
357148d1a4Matt Barden	    smb_request_t *, sr, int, (int)cur,
358148d1a4Matt Barden	    int, (int)session->s_cur_credits);
359148d1a4Matt Barden
360148d1a4Matt Barden	session->s_cur_credits = cur;
361148d1a4Matt Barden	mutex_exit(&session->s_credits_mutex);
362148d1a4Matt Barden}
363148d1a4Matt Barden
364148d1a4Matt Barden/*
365148d1a4Matt Barden * Second half of SMB2 credit handling (increases)
366148d1a4Matt Barden */
367148d1a4Matt Bardenstatic inline void
368148d1a4Matt Bardensmb2_credit_increase(smb_request_t *sr)
369148d1a4Matt Barden{
370148d1a4Matt Barden	smb_session_t *session = sr->session;
371148d1a4Matt Barden	uint16_t cur, d;
372148d1a4Matt Barden
373148d1a4Matt Barden	mutex_enter(&session->s_credits_mutex);
374148d1a4Matt Barden	cur = session->s_cur_credits;
375148d1a4Matt Barden
376148d1a4Matt Barden	/* Handle credit increase. */
377148d1a4Matt Barden	d = sr->smb2_credit_request - sr->smb2_credit_charge;
378148d1a4Matt Barden	cur += d;
379148d1a4Matt Barden
380148d1a4Matt Barden	/*
381148d1a4Matt Barden	 * If new credits would be above max,
382148d1a4Matt Barden	 * reduce the credit grant.
383148d1a4Matt Barden	 */
384148d1a4Matt Barden	if (cur > session->s_max_credits) {
385148d1a4Matt Barden		d = cur - session->s_max_credits;
386148d1a4Matt Barden		cur = session->s_max_credits;
387148d1a4Matt Barden		sr->smb2_credit_response -= d;
388148d1a4Matt Barden		DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr);
389148d1a4Matt Barden	}
390148d1a4Matt Barden
391148d1a4Matt Barden	DTRACE_PROBE3(smb2__credit__increase,
392148d1a4Matt Barden	    smb_request_t *, sr, int, (int)cur,
393148d1a4Matt Barden	    int, (int)session->s_cur_credits);
394148d1a4Matt Barden
395148d1a4Matt Barden	session->s_cur_credits = cur;
396148d1a4Matt Barden	mutex_exit(&session->s_credits_mutex);
397148d1a4Matt Barden}
398148d1a4Matt Barden
399148d1a4Matt Barden/*
400148d1a4Matt Barden * Record some statistics:  latency, rx bytes, tx bytes
401148d1a4Matt Barden * per:  server, session & kshare.
402148d1a4Matt Barden */
403148d1a4Matt Bardenstatic inline void
404148d1a4Matt Bardensmb2_record_stats(smb_request_t *sr, smb_disp_stats_t *sds, boolean_t tx_only)
405148d1a4Matt Barden{
406148d1a4Matt Barden	hrtime_t	dt;
407148d1a4Matt Barden	int64_t		rxb;
408148d1a4Matt Barden	int64_t		txb;
409148d1a4Matt Barden
410148d1a4Matt Barden	dt = gethrtime() - sr->sr_time_start;
411148d1a4Matt Barden	rxb = (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr);
412148d1a4Matt Barden	txb = (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr);
413148d1a4Matt Barden
414148d1a4Matt Barden	if (!tx_only) {
415148d1a4Matt Barden		smb_server_inc_req(sr->sr_server);
416148d1a4Matt Barden		smb_latency_add_sample(&sds->sdt_lat, dt);
417148d1a4Matt Barden		atomic_add_64(&sds->sdt_rxb, rxb);
418148d1a4Matt Barden	}
419148d1a4Matt Barden	atomic_add_64(&sds->sdt_txb, txb);
420148d1a4Matt Barden}
421148d1a4Matt Barden
422148d1a4Matt Barden/*
423a90cf9fGordon Ross * smb2sr_work
424a90cf9fGordon Ross *
425a90cf9fGordon Ross * This function processes each SMB command in the current request
426a90cf9fGordon Ross * (which may be a compound request) building a reply containing
427a90cf9fGordon Ross * SMB reply messages, one-to-one with the SMB commands.  Some SMB
428a90cf9fGordon Ross * commands (change notify, blocking locks) may require both an
429a90cf9fGordon Ross * "interim response" and a later "async response" at completion.
430a90cf9fGordon Ross * In such cases, we'll encode the interim response in the reply
431a90cf9fGordon Ross * compound we're building, and put the (now async) command on a
432a90cf9fGordon Ross * list of commands that need further processing.  After we've
433a90cf9fGordon Ross * finished processing the commands in this compound and building
434a90cf9fGordon Ross * the compound reply, we'll send the compound reply, and finally
435a90cf9fGordon Ross * process the list of async commands.
436a90cf9fGordon Ross *
437a90cf9fGordon Ross * As we work our way through the compound request and reply,
438a90cf9fGordon Ross * we need to keep track of the bounds of the current request
439a90cf9fGordon Ross * and reply.  For the request, this uses an MBC_SHADOW_CHAIN
440a90cf9fGordon Ross * that begins at smb2_cmd_hdr.  The reply is appended to the
441a90cf9fGordon Ross * sr->reply chain starting at smb2_reply_hdr.
442a90cf9fGordon Ross *
443bfe5e73Gordon Ross * This function must always free the smb request, or arrange
444bfe5e73Gordon Ross * for it to be completed and free'd later (if SDRC_SR_KEPT).
445a90cf9fGordon Ross */
446a90cf9fGordon Rossvoid
447a90cf9fGordon Rosssmb2sr_work(struct smb_request *sr)
448a90cf9fGordon Ross{
449a90cf9fGordon Ross	const smb_disp_entry_t	*sdd;
450a90cf9fGordon Ross	smb_disp_stats_t	*sds;
451a90cf9fGordon Ross	smb_session_t		*session;
452a90cf9fGordon Ross	uint32_t		msg_len;
453a90cf9fGordon Ross	uint16_t		cmd_idx;
454a90cf9fGordon Ross	int			rc = 0;
455a90cf9fGordon Ross	boolean_t		disconnect = B_FALSE;
456a90cf9fGordon Ross	boolean_t		related;
457a90cf9fGordon Ross
458a90cf9fGordon Ross	session = sr->session;
459a90cf9fGordon Ross
460148d1a4Matt Barden	ASSERT(sr->smb2_async == B_FALSE);
461a90cf9fGordon Ross	ASSERT(sr->tid_tree == 0);
462a90cf9fGordon Ross	ASSERT(sr->uid_user == 0);
463a90cf9fGordon Ross	ASSERT(sr->fid_ofile == 0);
464a90cf9fGordon Ross	sr->smb_fid = (uint16_t)-1;
465a90cf9fGordon Ross	sr->smb2_status = 0;
466a90cf9fGordon Ross
467a90cf9fGordon Ross	/* temporary until we identify a user */
468a90cf9fGordon Ross	sr->user_cr = zone_kcred();
469a90cf9fGordon Ross
470a90cf9fGordon Rosscmd_start:
471a90cf9fGordon Ross	/*
4725677e04Gordon Ross	 * Note that we don't check sr_state here and abort the
4735677e04Gordon Ross	 * compound if cancelled (etc.) because some SMB2 command
4745677e04Gordon Ross	 * handlers need to do work even when cancelled.
475a90cf9fGordon Ross	 *
476a90cf9fGordon Ross	 * We treat some status codes as if "sticky", meaning
477a90cf9fGordon Ross	 * once they're set after some command handler returns,
478a90cf9fGordon Ross	 * all remaining commands get this status without even
4795677e04Gordon Ross	 * calling the command-specific handler.
480a90cf9fGordon Ross	 */
481a90cf9fGordon Ross	if (sr->smb2_status != NT_STATUS_CANCELLED &&
482a90cf9fGordon Ross	    sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES)
483a90cf9fGordon Ross		sr->smb2_status = 0;
484a90cf9fGordon Ross
4855677e04Gordon Ross	/*
4865677e04Gordon Ross	 * Decode the request header
4875677e04Gordon Ross	 *
4885677e04Gordon Ross	 * Most problems with decoding will result in the error
4895677e04Gordon Ross	 * STATUS_INVALID_PARAMETER.  If the decoding problem
4905677e04Gordon Ross	 * prevents continuing, we'll close the connection.
4915677e04Gordon Ross	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
4925677e04Gordon Ross	 */
493a90cf9fGordon Ross	sr->smb2_cmd_hdr = sr->command.chain_offset;
494a90cf9fGordon Ross	if ((rc = smb2_decode_header(sr)) != 0) {
495a90cf9fGordon Ross		cmn_err(CE_WARN, "clnt %s bad SMB2 header",
496a90cf9fGordon Ross		    session->ip_addr_str);
497a90cf9fGordon Ross		disconnect = B_TRUE;
498a90cf9fGordon Ross		goto cleanup;
499a90cf9fGordon Ross	}
500a90cf9fGordon Ross
501a90cf9fGordon Ross	/*
502a90cf9fGordon Ross	 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear
503a90cf9fGordon Ross	 * in messages from the server back to the client.
504a90cf9fGordon Ross	 */
505a90cf9fGordon Ross	if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) {
506a90cf9fGordon Ross		cmn_err(CE_WARN, "clnt %s bad SMB2 flags",
507a90cf9fGordon Ross		    session->ip_addr_str);
508a90cf9fGordon Ross		disconnect = B_TRUE;
509a90cf9fGordon Ross		goto cleanup;
510a90cf9fGordon Ross	}
511a90cf9fGordon Ross	related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS);
512148d1a4Matt Barden	sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR;
513148d1a4Matt Barden	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
514148d1a4Matt Barden		/* Probably an async cancel. */
515148d1a4Matt Barden		DTRACE_PROBE1(smb2__dispatch__async, smb_request_t *, sr);
516148d1a4Matt Barden	} else if (sr->smb2_async) {
517148d1a4Matt Barden		/* Previous command in compound went async. */
518148d1a4Matt Barden		sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
519148d1a4Matt Barden		sr->smb2_async_id = SMB2_ASYNCID(sr);
520148d1a4Matt Barden	}
521a90cf9fGordon Ross
522a90cf9fGordon Ross	/*
523a90cf9fGordon Ross	 * In case we bail out with an error before we get to the
524a90cf9fGordon Ross	 * section that computes the credit grant, initialize the
525a90cf9fGordon Ross	 * response header fields so that credits won't change.
526a90cf9fGordon Ross	 * Note: SMB 2.02 clients may send credit charge zero.
527a90cf9fGordon Ross	 */
528a90cf9fGordon Ross	if (sr->smb2_credit_charge == 0)
529a90cf9fGordon Ross		sr->smb2_credit_charge = 1;
530a90cf9fGordon Ross	sr->smb2_credit_response = sr->smb2_credit_charge;
531a90cf9fGordon Ross
532a90cf9fGordon Ross	/*
533148d1a4Matt Barden	 * Write a tentative reply header.
534148d1a4Matt Barden	 *
535148d1a4Matt Barden	 * We could just leave this blank, but if we're using the
536148d1a4Matt Barden	 * mdb module feature that extracts packets, it's useful
537148d1a4Matt Barden	 * to have the header mostly correct here.
538148d1a4Matt Barden	 *
539148d1a4Matt Barden	 * If we have already exhausted the output space, then the
540148d1a4Matt Barden	 * client is trying something funny.  Log it and kill 'em.
541a90cf9fGordon Ross	 */
542148d1a4Matt Barden	sr->smb2_next_reply = 0;
543148d1a4Matt Barden	ASSERT((sr->reply.chain_offset & 7) == 0);
544a90cf9fGordon Ross	sr->smb2_reply_hdr = sr->reply.chain_offset;
545a90cf9fGordon Ross	if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) {
546a90cf9fGordon Ross		cmn_err(CE_WARN, "clnt %s excessive reply",
547a90cf9fGordon Ross		    session->ip_addr_str);
548a90cf9fGordon Ross		disconnect = B_TRUE;
549a90cf9fGordon Ross		goto cleanup;
550a90cf9fGordon Ross	}
551a90cf9fGordon Ross
552a90cf9fGordon Ross	/*
553a90cf9fGordon Ross	 * Figure out the length of data following the SMB2 header.
554a90cf9fGordon Ross	 * It ends at either the next SMB2 header if there is one
555a90cf9fGordon Ross	 * (smb2_next_command != 0) or at the end of the message.
556a90cf9fGordon Ross	 */
557a90cf9fGordon Ross	if (sr->smb2_next_command != 0) {
558a90cf9fGordon Ross		/* [MS-SMB2] says this is 8-byte aligned */
559a90cf9fGordon Ross		msg_len = sr->smb2_next_command;
560a90cf9fGordon Ross		if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
561a90cf9fGordon Ross		    ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
562a90cf9fGordon Ross			cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
563a90cf9fGordon Ross			    session->ip_addr_str);
564a90cf9fGordon Ross			disconnect = B_TRUE;
565a90cf9fGordon Ross			goto cleanup;
566a90cf9fGordon Ross		}
567a90cf9fGordon Ross	} else {
568a90cf9fGordon Ross		msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
569a90cf9fGordon Ross	}
570a90cf9fGordon Ross
571a90cf9fGordon Ross	/*
572a90cf9fGordon Ross	 * Setup a shadow chain for this SMB2 command, starting
573a90cf9fGordon Ross	 * with the header and ending at either the next command
574a90cf9fGordon Ross	 * or the end of the message.  The signing check below
575a90cf9fGordon Ross	 * needs the entire SMB2 command.  After that's done, we
576a90cf9fGordon Ross	 * advance chain_offset to the end of the header where
577a90cf9fGordon Ross	 * the command specific handlers continue decoding.
578a90cf9fGordon Ross	 */
579a90cf9fGordon Ross	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
580a90cf9fGordon Ross	    sr->smb2_cmd_hdr, msg_len);
581a90cf9fGordon Ross
582a90cf9fGordon Ross	/*
583ac2bf31Matt Barden	 * We will consume the data for this request from smb_data.
584ac2bf31Matt Barden	 * That effectively consumes msg_len bytes from sr->command
585ac2bf31Matt Barden	 * but doesn't update its chain_offset, so we need to update
586ac2bf31Matt Barden	 * that here to make later received bytes accounting work.
587ac2bf31Matt Barden	 */
588ac2bf31Matt Barden	sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len;
589ac2bf31Matt Barden	ASSERT(sr->command.chain_offset <= sr->command.max_bytes);
590ac2bf31Matt Barden
591ac2bf31Matt Barden	/*
592a90cf9fGordon Ross	 * Validate the commmand code, get dispatch table entries.
593a90cf9fGordon Ross	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
594a90cf9fGordon Ross	 *
595a90cf9fGordon Ross	 * The last slot in the dispatch table is used to handle
596a90cf9fGordon Ross	 * invalid commands.  Same for statistics.
597a90cf9fGordon Ross	 */
598a90cf9fGordon Ross	if (sr->smb2_cmd_code < SMB2_INVALID_CMD)
599a90cf9fGordon Ross		cmd_idx = sr->smb2_cmd_code;
600a90cf9fGordon Ross	else
601a90cf9fGordon Ross		cmd_idx = SMB2_INVALID_CMD;
602a90cf9fGordon Ross	sdd = &smb2_disp_table[cmd_idx];
603a90cf9fGordon Ross	sds = &session->s_server->sv_disp_stats2[cmd_idx];
604a90cf9fGordon Ross
605a90cf9fGordon Ross	/*
606a90cf9fGordon Ross	 * If this command is NOT "related" to the previous,
607a90cf9fGordon Ross	 * clear out the UID, TID, FID state that might be
608a90cf9fGordon Ross	 * left over from the previous command.
609a90cf9fGordon Ross	 *
610a90cf9fGordon Ross	 * If the command IS related, any new IDs are ignored,
611a90cf9fGordon Ross	 * and we simply continue with the previous user, tree,
612a90cf9fGordon Ross	 * and open file.
613a90cf9fGordon Ross	 */
614a90cf9fGordon Ross	if (!related) {
615a90cf9fGordon Ross		/*
616a90cf9fGordon Ross		 * Drop user, tree, file; carefully ordered to
617a90cf9fGordon Ross		 * avoid dangling references: file, tree, user
618a90cf9fGordon Ross		 */
619a90cf9fGordon Ross		if (sr->fid_ofile != NULL) {
620a90cf9fGordon Ross			smb_ofile_release(sr->fid_ofile);
621a90cf9fGordon Ross			sr->fid_ofile = NULL;
622a90cf9fGordon Ross		}
623a90cf9fGordon Ross		if (sr->tid_tree != NULL) {
624a90cf9fGordon Ross			smb_tree_release(sr->tid_tree);
625a90cf9fGordon Ross			sr->tid_tree = NULL;
626a90cf9fGordon Ross		}
627a90cf9fGordon Ross		if (sr->uid_user != NULL) {
628a90cf9fGordon Ross			smb_user_release(sr->uid_user);
629a90cf9fGordon Ross			sr->uid_user = NULL;
630a90cf9fGordon Ross			sr->user_cr = zone_kcred();
631a90cf9fGordon Ross		}
632a90cf9fGordon Ross	}
633a90cf9fGordon Ross
634a90cf9fGordon Ross	/*
635a90cf9fGordon Ross	 * Make sure we have a user and tree as needed
636a90cf9fGordon Ross	 * according to the flags for the this command.
637a90cf9fGordon Ross	 * Note that we may have inherited these.
638a90cf9fGordon Ross	 */
639a90cf9fGordon Ross	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) {
640a90cf9fGordon Ross		/*
641a90cf9fGordon Ross		 * This command requires a user session.
642a90cf9fGordon Ross		 */
643a90cf9fGordon Ross		if (related) {
644a90cf9fGordon Ross			/*
645a90cf9fGordon Ross			 * Previous command should have given us a user.
646a90cf9fGordon Ross			 * [MS-SMB2] 3.3.5.2 Handling Related Requests
647a90cf9fGordon Ross			 */
648a90cf9fGordon Ross			if (sr->uid_user == NULL) {
649