xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_session.c (revision 817fa55f6c07cd26e2de797b63ac4695d57108ab)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
22148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
238d94f651SGordon Ross  * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
24da6c28aaSamw  */
25b819cea2SGordon Ross 
26da6c28aaSamw #include <sys/atomic.h>
27da6c28aaSamw #include <sys/synch.h>
28da6c28aaSamw #include <sys/types.h>
29da6c28aaSamw #include <sys/sdt.h>
30f9bc6dadSDmitry.Savitsky@nexenta.com #include <sys/random.h>
31da6c28aaSamw #include <smbsrv/netbios.h>
32a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h>
33bbf6f00cSJordan Brown #include <smbsrv/string.h>
34b819cea2SGordon Ross #include <netinet/tcp.h>
35b819cea2SGordon Ross 
36a90cf9f2SGordon Ross /* How many iovec we'll handle as a local array (no allocation) */
37a90cf9f2SGordon Ross #define	SMB_LOCAL_IOV_MAX	16
38a90cf9f2SGordon Ross 
39b819cea2SGordon Ross #define	SMB_NEW_KID()	atomic_inc_64_nv(&smb_kids)
40da6c28aaSamw 
41faa1795aSjb150015 static volatile uint64_t smb_kids;
42da6c28aaSamw 
43b819cea2SGordon Ross /*
44b819cea2SGordon Ross  * We track the keepalive in minutes, but this constant
45b819cea2SGordon Ross  * specifies it in seconds, so convert to minutes.
46b819cea2SGordon Ross  */
47b819cea2SGordon Ross uint32_t smb_keep_alive = SMB_PI_KEEP_ALIVE_MIN / 60;
48da6c28aaSamw 
490897f7fbSGordon Ross /*
50*817fa55fSGordon Ross  * This is the maximum time we'll allow a "session" to exist with no
51*817fa55fSGordon Ross  * authenticated smb_user_t objects on it.  This allows a client to
52*817fa55fSGordon Ross  * logoff their "one and only" user session and then logon as some
53*817fa55fSGordon Ross  * different user.  (There are some tests that do that.)  The same
54*817fa55fSGordon Ross  * timeout mechanism also reduces the impact of clients that might
55*817fa55fSGordon Ross  * open TCP connections but never authenticate.
56*817fa55fSGordon Ross  */
57*817fa55fSGordon Ross int smb_session_auth_tmo = 30; /* sec. */
58*817fa55fSGordon Ross 
59*817fa55fSGordon Ross /*
600897f7fbSGordon Ross  * There are many smbtorture test cases that send
610897f7fbSGordon Ross  * racing requests, and where the tests fail if we
620897f7fbSGordon Ross  * don't execute them in exactly the order sent.
630897f7fbSGordon Ross  * These are test bugs.  The protocol makes no
640897f7fbSGordon Ross  * guarantees about execution order of requests
650897f7fbSGordon Ross  * that are concurrently active.
660897f7fbSGordon Ross  *
670897f7fbSGordon Ross  * Nonetheless, smbtorture has many useful tests,
680897f7fbSGordon Ross  * so we have this work-around we can enable to
690897f7fbSGordon Ross  * basically force sequential execution.  When
700897f7fbSGordon Ross  * enabled, insert a delay after each request is
710897f7fbSGordon Ross  * issued a taskq job.  Enable this with mdb by
720897f7fbSGordon Ross  * setting smb_reader_delay to 10.  Don't make it
730897f7fbSGordon Ross  * more than 500 or so or the server will appear
740897f7fbSGordon Ross  * to be so slow that tests may time out.
750897f7fbSGordon Ross  */
760897f7fbSGordon Ross int smb_reader_delay = 0;  /* mSec. */
770897f7fbSGordon Ross 
78a90cf9f2SGordon Ross static int  smbsr_newrq_initial(smb_request_t *);
79a90cf9f2SGordon Ross 
802c2961f8Sjose borrego static void smb_session_cancel(smb_session_t *);
81a90cf9f2SGordon Ross static int smb_session_reader(smb_session_t *);
82a90cf9f2SGordon Ross static int smb_session_xprt_puthdr(smb_session_t *,
83a90cf9f2SGordon Ross     uint8_t msg_type, uint32_t msg_len,
84a90cf9f2SGordon Ross     uint8_t *dst, size_t dstlen);
85811599a4SMatt Barden static void smb_session_disconnect_trees(smb_session_t	*);
86faa1795aSjb150015 static void smb_request_init_command_mbuf(smb_request_t *sr);
87f9bc6dadSDmitry.Savitsky@nexenta.com static void smb_session_genkey(smb_session_t *);
88da6c28aaSamw 
89811599a4SMatt Barden /*
90811599a4SMatt Barden  * This (legacy) code is in support of an "idle timeout" feature,
91811599a4SMatt Barden  * which is apparently incomplete.  To complete it, we should:
92811599a4SMatt Barden  * when the keep_alive timer expires, check whether the client
93811599a4SMatt Barden  * has any open files, and if not then kill their session.
94811599a4SMatt Barden  * Right now the timers are there, but nothing happens when
95811599a4SMatt Barden  * a timer expires.
96811599a4SMatt Barden  *
97811599a4SMatt Barden  * Todo: complete logic to kill idle sessions.
98811599a4SMatt Barden  *
99811599a4SMatt Barden  * Only called when sv_cfg.skc_keepalive != 0
100811599a4SMatt Barden  */
101da6c28aaSamw void
102811599a4SMatt Barden smb_session_timers(smb_server_t *sv)
103da6c28aaSamw {
104faa1795aSjb150015 	smb_session_t	*session;
105811599a4SMatt Barden 	smb_llist_t	*ll;
106da6c28aaSamw 
107811599a4SMatt Barden 	ll = &sv->sv_session_list;
1084163af6aSjose borrego 	smb_llist_enter(ll, RW_READER);
1094163af6aSjose borrego 	session = smb_llist_head(ll);
1104163af6aSjose borrego 	while (session != NULL) {
111da6c28aaSamw 		/*
112da6c28aaSamw 		 * Walk through the table and decrement each keep_alive
113da6c28aaSamw 		 * timer that has not timed out yet. (keepalive > 0)
114da6c28aaSamw 		 */
1154163af6aSjose borrego 		SMB_SESSION_VALID(session);
116faa1795aSjb150015 		if (session->keep_alive &&
117faa1795aSjb150015 		    (session->keep_alive != (uint32_t)-1))
118faa1795aSjb150015 			session->keep_alive--;
119811599a4SMatt Barden 
1204163af6aSjose borrego 		session = smb_llist_next(ll, session);
121faa1795aSjb150015 	}
1224163af6aSjose borrego 	smb_llist_exit(ll);
123faa1795aSjb150015 }
124da6c28aaSamw 
125da6c28aaSamw /*
126da6c28aaSamw  * Send a session message - supports SMB-over-NBT and SMB-over-TCP.
127a90cf9f2SGordon Ross  * If an mbuf chain is provided (optional), it will be freed and
128a90cf9f2SGordon Ross  * set to NULL -- unconditionally!  (error or not)
129da6c28aaSamw  *
130a90cf9f2SGordon Ross  * Builds a I/O vector (uio/iov) to do the send from mbufs, plus one
131a90cf9f2SGordon Ross  * segment for the 4-byte NBT header.
132da6c28aaSamw  */
133da6c28aaSamw int
134a90cf9f2SGordon Ross smb_session_send(smb_session_t *session, uint8_t nbt_type, mbuf_chain_t *mbc)
135da6c28aaSamw {
136a90cf9f2SGordon Ross 	uio_t		uio;
137a90cf9f2SGordon Ross 	iovec_t		local_iov[SMB_LOCAL_IOV_MAX];
138a90cf9f2SGordon Ross 	iovec_t		*alloc_iov = NULL;
139a90cf9f2SGordon Ross 	int		alloc_sz = 0;
140a90cf9f2SGordon Ross 	mbuf_t		*m;
141a90cf9f2SGordon Ross 	uint8_t		nbt_hdr[NETBIOS_HDR_SZ];
142a90cf9f2SGordon Ross 	uint32_t	nbt_len;
143a90cf9f2SGordon Ross 	int		i, nseg;
144da6c28aaSamw 	int		rc;
145da6c28aaSamw 
146da6c28aaSamw 	switch (session->s_state) {
147da6c28aaSamw 	case SMB_SESSION_STATE_DISCONNECTED:
148da6c28aaSamw 	case SMB_SESSION_STATE_TERMINATED:
149a90cf9f2SGordon Ross 		rc = ENOTCONN;
150a90cf9f2SGordon Ross 		goto out;
151a90cf9f2SGordon Ross 	default:
152a90cf9f2SGordon Ross 		break;
153a90cf9f2SGordon Ross 	}
154a90cf9f2SGordon Ross 
155a90cf9f2SGordon Ross 	/*
156a90cf9f2SGordon Ross 	 * Setup the IOV.  First, count the number of IOV segments
157a90cf9f2SGordon Ross 	 * (plus one for the NBT header) and decide whether we
158a90cf9f2SGordon Ross 	 * need to allocate an iovec or can use local_iov;
159a90cf9f2SGordon Ross 	 */
160a90cf9f2SGordon Ross 	bzero(&uio, sizeof (uio));
161a90cf9f2SGordon Ross 	nseg = 1;
162a90cf9f2SGordon Ross 	m = (mbc != NULL) ? mbc->chain : NULL;
163a90cf9f2SGordon Ross 	while (m != NULL) {
164a90cf9f2SGordon Ross 		nseg++;
165a90cf9f2SGordon Ross 		m = m->m_next;
166a90cf9f2SGordon Ross 	}
167a90cf9f2SGordon Ross 	if (nseg <= SMB_LOCAL_IOV_MAX) {
168a90cf9f2SGordon Ross 		uio.uio_iov = local_iov;
169a90cf9f2SGordon Ross 	} else {
170a90cf9f2SGordon Ross 		alloc_sz = nseg * sizeof (iovec_t);
171a90cf9f2SGordon Ross 		alloc_iov = kmem_alloc(alloc_sz, KM_SLEEP);
172a90cf9f2SGordon Ross 		uio.uio_iov = alloc_iov;
173a90cf9f2SGordon Ross 	}
174a90cf9f2SGordon Ross 	uio.uio_iovcnt = nseg;
175a90cf9f2SGordon Ross 	uio.uio_segflg = UIO_SYSSPACE;
176a90cf9f2SGordon Ross 	uio.uio_extflg = UIO_COPY_DEFAULT;
177a90cf9f2SGordon Ross 
178a90cf9f2SGordon Ross 	/*
179a90cf9f2SGordon Ross 	 * Build the iov list, meanwhile computing the length of
180a90cf9f2SGordon Ross 	 * the SMB payload (to put in the NBT header).
181a90cf9f2SGordon Ross 	 */
182a90cf9f2SGordon Ross 	uio.uio_iov[0].iov_base = (void *)nbt_hdr;
183a90cf9f2SGordon Ross 	uio.uio_iov[0].iov_len = sizeof (nbt_hdr);
184a90cf9f2SGordon Ross 	i = 1;
185a90cf9f2SGordon Ross 	nbt_len = 0;
186a90cf9f2SGordon Ross 	m = (mbc != NULL) ? mbc->chain : NULL;
187a90cf9f2SGordon Ross 	while (m != NULL) {
188a90cf9f2SGordon Ross 		uio.uio_iov[i].iov_base = m->m_data;
189a90cf9f2SGordon Ross 		uio.uio_iov[i++].iov_len = m->m_len;
190a90cf9f2SGordon Ross 		nbt_len += m->m_len;
191a90cf9f2SGordon Ross 		m = m->m_next;
192a90cf9f2SGordon Ross 	}
193a90cf9f2SGordon Ross 	ASSERT3S(i, ==, nseg);
194a90cf9f2SGordon Ross 
195a90cf9f2SGordon Ross 	/*
196a90cf9f2SGordon Ross 	 * Set the NBT header, set uio_resid
197a90cf9f2SGordon Ross 	 */
198a90cf9f2SGordon Ross 	uio.uio_resid = nbt_len + NETBIOS_HDR_SZ;
199a90cf9f2SGordon Ross 	rc = smb_session_xprt_puthdr(session, nbt_type, nbt_len,
200a90cf9f2SGordon Ross 	    nbt_hdr, NETBIOS_HDR_SZ);
201a90cf9f2SGordon Ross 	if (rc != 0)
202a90cf9f2SGordon Ross 		goto out;
203a90cf9f2SGordon Ross 
204a90cf9f2SGordon Ross 	smb_server_add_txb(session->s_server, (int64_t)uio.uio_resid);
205a90cf9f2SGordon Ross 	rc = smb_net_send_uio(session, &uio);
206a90cf9f2SGordon Ross 
207a90cf9f2SGordon Ross out:
208a90cf9f2SGordon Ross 	if (alloc_iov != NULL)
209a90cf9f2SGordon Ross 		kmem_free(alloc_iov, alloc_sz);
210da6c28aaSamw 	if ((mbc != NULL) && (mbc->chain != NULL)) {
211da6c28aaSamw 		m_freem(mbc->chain);
212da6c28aaSamw 		mbc->chain = NULL;
213da6c28aaSamw 		mbc->flags = 0;
214da6c28aaSamw 	}
21521b7895dSjb150015 	return (rc);
216da6c28aaSamw }
217da6c28aaSamw 
218da6c28aaSamw /*
219da6c28aaSamw  * Read, process and respond to a NetBIOS session request.
220da6c28aaSamw  *
221da6c28aaSamw  * A NetBIOS session must be established for SMB-over-NetBIOS.  Validate
222da6c28aaSamw  * the calling and called name format and save the client NetBIOS name,
223da6c28aaSamw  * which is used when a NetBIOS session is established to check for and
224da6c28aaSamw  * cleanup leftover state from a previous session.
225da6c28aaSamw  *
226da6c28aaSamw  * Session requests are not valid for SMB-over-TCP, which is unfortunate
227da6c28aaSamw  * because without the client name leftover state cannot be cleaned up
228da6c28aaSamw  * if the client is behind a NAT server.
229da6c28aaSamw  */
230da6c28aaSamw static int
231a90cf9f2SGordon Ross smb_netbios_session_request(struct smb_session *session)
232da6c28aaSamw {
233da6c28aaSamw 	int			rc;
234da6c28aaSamw 	char			*calling_name;
235da6c28aaSamw 	char			*called_name;
236da6c28aaSamw 	char			client_name[NETBIOS_NAME_SZ];
237da6c28aaSamw 	struct mbuf_chain	mbc;
238da6c28aaSamw 	char			*names = NULL;
239bbf6f00cSJordan Brown 	smb_wchar_t		*wbuf = NULL;
240da6c28aaSamw 	smb_xprt_t		hdr;
241da6c28aaSamw 	char *p;
242da6c28aaSamw 	int rc1, rc2;
243da6c28aaSamw 
244da6c28aaSamw 	session->keep_alive = smb_keep_alive;
245da6c28aaSamw 
246faa1795aSjb150015 	if ((rc = smb_session_xprt_gethdr(session, &hdr)) != 0)
247faa1795aSjb150015 		return (rc);
248da6c28aaSamw 
249da6c28aaSamw 	DTRACE_PROBE2(receive__session__req__xprthdr, struct session *, session,
250da6c28aaSamw 	    smb_xprt_t *, &hdr);
251da6c28aaSamw 
252da6c28aaSamw 	if ((hdr.xh_type != SESSION_REQUEST) ||
253da6c28aaSamw 	    (hdr.xh_length != NETBIOS_SESSION_REQUEST_DATA_LENGTH)) {
254da6c28aaSamw 		DTRACE_PROBE1(receive__session__req__failed,
255da6c28aaSamw 		    struct session *, session);
256da6c28aaSamw 		return (EINVAL);
257da6c28aaSamw 	}
258da6c28aaSamw 
259da6c28aaSamw 	names = kmem_alloc(hdr.xh_length, KM_SLEEP);
260da6c28aaSamw 
261da6c28aaSamw 	if ((rc = smb_sorecv(session->sock, names, hdr.xh_length)) != 0) {
262da6c28aaSamw 		kmem_free(names, hdr.xh_length);
263da6c28aaSamw 		DTRACE_PROBE1(receive__session__req__failed,
264da6c28aaSamw 		    struct session *, session);
265da6c28aaSamw 		return (rc);
266da6c28aaSamw 	}
267da6c28aaSamw 
268da6c28aaSamw 	DTRACE_PROBE3(receive__session__req__data, struct session *, session,
269da6c28aaSamw 	    char *, names, uint32_t, hdr.xh_length);
270da6c28aaSamw 
271da6c28aaSamw 	called_name = &names[0];
272da6c28aaSamw 	calling_name = &names[NETBIOS_ENCODED_NAME_SZ + 2];
273da6c28aaSamw 
274da6c28aaSamw 	rc1 = netbios_name_isvalid(called_name, 0);
275da6c28aaSamw 	rc2 = netbios_name_isvalid(calling_name, client_name);
276da6c28aaSamw 
277da6c28aaSamw 	if (rc1 == 0 || rc2 == 0) {
278da6c28aaSamw 
279da6c28aaSamw 		DTRACE_PROBE3(receive__invalid__session__req,
280da6c28aaSamw 		    struct session *, session, char *, names,
281da6c28aaSamw 		    uint32_t, hdr.xh_length);
282da6c28aaSamw 
283da6c28aaSamw 		kmem_free(names, hdr.xh_length);
284da6c28aaSamw 		MBC_INIT(&mbc, MAX_DATAGRAM_LENGTH);
2853db3f65cSamw 		(void) smb_mbc_encodef(&mbc, "b",
286da6c28aaSamw 		    DATAGRAM_INVALID_SOURCE_NAME_FORMAT);
287da6c28aaSamw 		(void) smb_session_send(session, NEGATIVE_SESSION_RESPONSE,
288da6c28aaSamw 		    &mbc);
289da6c28aaSamw 		return (EINVAL);
290da6c28aaSamw 	}
291da6c28aaSamw 
292da6c28aaSamw 	DTRACE_PROBE3(receive__session__req__calling__decoded,
293da6c28aaSamw 	    struct session *, session,
294da6c28aaSamw 	    char *, calling_name, char *, client_name);
295da6c28aaSamw 
296da6c28aaSamw 	/*
297da6c28aaSamw 	 * The client NetBIOS name is in oem codepage format.
298da6c28aaSamw 	 * We need to convert it to unicode and store it in
299da6c28aaSamw 	 * multi-byte format.  We also need to strip off any
300da6c28aaSamw 	 * spaces added as part of the NetBIOS name encoding.
301da6c28aaSamw 	 */
302bbf6f00cSJordan Brown 	wbuf = kmem_alloc((SMB_PI_MAX_HOST * sizeof (smb_wchar_t)), KM_SLEEP);
303bbf6f00cSJordan Brown 	(void) oemtoucs(wbuf, client_name, SMB_PI_MAX_HOST, OEM_CPG_850);
304bbf6f00cSJordan Brown 	(void) smb_wcstombs(session->workstation, wbuf, SMB_PI_MAX_HOST);
305bbf6f00cSJordan Brown 	kmem_free(wbuf, (SMB_PI_MAX_HOST * sizeof (smb_wchar_t)));
306da6c28aaSamw 
307da6c28aaSamw 	if ((p = strchr(session->workstation, ' ')) != 0)
308da6c28aaSamw 		*p = '\0';
309da6c28aaSamw 
310da6c28aaSamw 	kmem_free(names, hdr.xh_length);
311da6c28aaSamw 	return (smb_session_send(session, POSITIVE_SESSION_RESPONSE, NULL));
312da6c28aaSamw }
313da6c28aaSamw 
314da6c28aaSamw /*
315da6c28aaSamw  * Read 4-byte header from the session socket and build an in-memory
316da6c28aaSamw  * session transport header.  See smb_xprt_t definition for header
317da6c28aaSamw  * format information.
318da6c28aaSamw  *
319da6c28aaSamw  * Direct hosted NetBIOS-less SMB (SMB-over-TCP) uses port 445.  The
320da6c28aaSamw  * first byte of the four-byte header must be 0 and the next three
321da6c28aaSamw  * bytes contain the length of the remaining data.
322da6c28aaSamw  */
323da6c28aaSamw int
324da6c28aaSamw smb_session_xprt_gethdr(smb_session_t *session, smb_xprt_t *ret_hdr)
325da6c28aaSamw {
326faa1795aSjb150015 	int		rc;
327da6c28aaSamw 	unsigned char	buf[NETBIOS_HDR_SZ];
328da6c28aaSamw 
329faa1795aSjb150015 	if ((rc = smb_sorecv(session->sock, buf, NETBIOS_HDR_SZ)) != 0)
330faa1795aSjb150015 		return (rc);
331da6c28aaSamw 
332da6c28aaSamw 	switch (session->s_local_port) {
333a0aa776eSAlan Wright 	case IPPORT_NETBIOS_SSN:
334da6c28aaSamw 		ret_hdr->xh_type = buf[0];
335da6c28aaSamw 		ret_hdr->xh_length = (((uint32_t)buf[1] & 1) << 16) |
336da6c28aaSamw 		    ((uint32_t)buf[2] << 8) |
337da6c28aaSamw 		    ((uint32_t)buf[3]);
338da6c28aaSamw 		break;
339da6c28aaSamw 
340a0aa776eSAlan Wright 	case IPPORT_SMB:
341da6c28aaSamw 		ret_hdr->xh_type = buf[0];
342da6c28aaSamw 
343da6c28aaSamw 		if (ret_hdr->xh_type != 0) {
34449b5df1eSGordon Ross 			cmn_err(CE_WARN, "invalid NBT type (%u) from %s",
34549b5df1eSGordon Ross 			    ret_hdr->xh_type, session->ip_addr_str);
346faa1795aSjb150015 			return (EPROTO);
347da6c28aaSamw 		}
348da6c28aaSamw 
349da6c28aaSamw 		ret_hdr->xh_length = ((uint32_t)buf[1] << 16) |
350da6c28aaSamw 		    ((uint32_t)buf[2] << 8) |
351da6c28aaSamw 		    ((uint32_t)buf[3]);
352da6c28aaSamw 		break;
353da6c28aaSamw 
354da6c28aaSamw 	default:
3557f667e74Sjose borrego 		cmn_err(CE_WARN, "invalid port %u", session->s_local_port);
356faa1795aSjb150015 		return (EPROTO);
357da6c28aaSamw 	}
358da6c28aaSamw 
359da6c28aaSamw 	return (0);
360da6c28aaSamw }
361da6c28aaSamw 
362da6c28aaSamw /*
363da6c28aaSamw  * Encode a transport session packet header into a 4-byte buffer.
364da6c28aaSamw  */
365da6c28aaSamw static int
366a90cf9f2SGordon Ross smb_session_xprt_puthdr(smb_session_t *session,
367a90cf9f2SGordon Ross     uint8_t msg_type, uint32_t msg_length,
368da6c28aaSamw     uint8_t *buf, size_t buflen)
369da6c28aaSamw {
370a90cf9f2SGordon Ross 	if (buf == NULL || buflen < NETBIOS_HDR_SZ) {
371da6c28aaSamw 		return (-1);
372da6c28aaSamw 	}
373da6c28aaSamw 
374da6c28aaSamw 	switch (session->s_local_port) {
375a0aa776eSAlan Wright 	case IPPORT_NETBIOS_SSN:
376a90cf9f2SGordon Ross 		/* Per RFC 1001, 1002: msg. len < 128KB */
377a90cf9f2SGordon Ross 		if (msg_length >= (1 << 17))
378a90cf9f2SGordon Ross 			return (-1);
379a90cf9f2SGordon Ross 		buf[0] = msg_type;
380a90cf9f2SGordon Ross 		buf[1] = ((msg_length >> 16) & 1);
381a90cf9f2SGordon Ross 		buf[2] = (msg_length >> 8) & 0xff;
382a90cf9f2SGordon Ross 		buf[3] = msg_length & 0xff;
383da6c28aaSamw 		break;
384da6c28aaSamw 
385a0aa776eSAlan Wright 	case IPPORT_SMB:
386a90cf9f2SGordon Ross 		/*
387a90cf9f2SGordon Ross 		 * SMB over TCP is like NetBIOS but the one byte
388a90cf9f2SGordon Ross 		 * message type is always zero, and the length
389a90cf9f2SGordon Ross 		 * part is three bytes.  It could actually use
390a90cf9f2SGordon Ross 		 * longer messages, but this is conservative.
391a90cf9f2SGordon Ross 		 */
392a90cf9f2SGordon Ross 		if (msg_length >= (1 << 24))
393a90cf9f2SGordon Ross 			return (-1);
394a90cf9f2SGordon Ross 		buf[0] = msg_type;
395a90cf9f2SGordon Ross 		buf[1] = (msg_length >> 16) & 0xff;
396a90cf9f2SGordon Ross 		buf[2] = (msg_length >> 8) & 0xff;
397a90cf9f2SGordon Ross 		buf[3] = msg_length & 0xff;
398da6c28aaSamw 		break;
399da6c28aaSamw 
400da6c28aaSamw 	default:
4017f667e74Sjose borrego 		cmn_err(CE_WARN, "invalid port %u", session->s_local_port);
402da6c28aaSamw 		return (-1);
403da6c28aaSamw 	}
404da6c28aaSamw 
405da6c28aaSamw 	return (0);
406da6c28aaSamw }
407da6c28aaSamw 
408faa1795aSjb150015 static void
409da6c28aaSamw smb_request_init_command_mbuf(smb_request_t *sr)
410da6c28aaSamw {
411da6c28aaSamw 
412da6c28aaSamw 	/*
4137f3ef643SGordon Ross 	 * Setup mbuf using the buffer we allocated.
414da6c28aaSamw 	 */
4157f3ef643SGordon Ross 	MBC_ATTACH_BUF(&sr->command, sr->sr_request_buf, sr->sr_req_length);
416da6c28aaSamw 
417da6c28aaSamw 	sr->command.flags = 0;
4187f3ef643SGordon Ross 	sr->command.shadow_of = NULL;
419da6c28aaSamw }
420da6c28aaSamw 
421da6c28aaSamw /*
422da6c28aaSamw  * smb_request_cancel
423da6c28aaSamw  *
424da6c28aaSamw  * Handle a cancel for a request properly depending on the current request
425da6c28aaSamw  * state.
426da6c28aaSamw  */
427da6c28aaSamw void
428da6c28aaSamw smb_request_cancel(smb_request_t *sr)
429da6c28aaSamw {
4305677e049SGordon Ross 	void (*cancel_method)(smb_request_t *) = NULL;
4315677e049SGordon Ross 
432da6c28aaSamw 	mutex_enter(&sr->sr_mutex);
433da6c28aaSamw 	switch (sr->sr_state) {
434da6c28aaSamw 
435584e0fceSGordon Ross 	case SMB_REQ_STATE_INITIALIZING:
436da6c28aaSamw 	case SMB_REQ_STATE_SUBMITTED:
437da6c28aaSamw 	case SMB_REQ_STATE_ACTIVE:
438da6c28aaSamw 	case SMB_REQ_STATE_CLEANED_UP:
4395677e049SGordon Ross 		sr->sr_state = SMB_REQ_STATE_CANCELLED;
440da6c28aaSamw 		break;
441da6c28aaSamw 
442b210fedeSGordon Ross 	case SMB_REQ_STATE_WAITING_AUTH:
443bfe5e737SGordon Ross 	case SMB_REQ_STATE_WAITING_FCN1:
444bfe5e737SGordon Ross 	case SMB_REQ_STATE_WAITING_LOCK:
445b210fedeSGordon Ross 	case SMB_REQ_STATE_WAITING_PIPE:
446da6c28aaSamw 		/*
4475677e049SGordon Ross 		 * These are states that have a cancel_method.
4485677e049SGordon Ross 		 * Make the state change now, to ensure that
4495677e049SGordon Ross 		 * we call cancel_method exactly once.  Do the
4505677e049SGordon Ross 		 * method call below, after we drop sr_mutex.
4515677e049SGordon Ross 		 * When the cancelled request thread resumes,
4525677e049SGordon Ross 		 * it should re-take sr_mutex and set sr_state
4535677e049SGordon Ross 		 * to CANCELLED, then return STATUS_CANCELLED.
454da6c28aaSamw 		 */
4555677e049SGordon Ross 		sr->sr_state = SMB_REQ_STATE_CANCEL_PENDING;
4565677e049SGordon Ross 		cancel_method = sr->cancel_method;
4575677e049SGordon Ross 		VERIFY(cancel_method != NULL);
458da6c28aaSamw 		break;
459da6c28aaSamw 
460bfe5e737SGordon Ross 	case SMB_REQ_STATE_WAITING_FCN2:
461da6c28aaSamw 	case SMB_REQ_STATE_COMPLETED:
462bfe5e737SGordon Ross 	case SMB_REQ_STATE_CANCEL_PENDING:
4635677e049SGordon Ross 	case SMB_REQ_STATE_CANCELLED:
464da6c28aaSamw 		/*
465da6c28aaSamw 		 * No action required for these states since the request
466da6c28aaSamw 		 * is completing.
467da6c28aaSamw 		 */
468da6c28aaSamw 		break;
469584e0fceSGordon Ross 
470584e0fceSGordon Ross 	case SMB_REQ_STATE_FREE:
471da6c28aaSamw 	default:
4722c2961f8Sjose borrego 		SMB_PANIC();
473da6c28aaSamw 	}
474da6c28aaSamw 	mutex_exit(&sr->sr_mutex);
4755677e049SGordon Ross 
4765677e049SGordon Ross 	if (cancel_method != NULL) {
4775677e049SGordon Ross 		cancel_method(sr);
4785677e049SGordon Ross 	}
479da6c28aaSamw }
480da6c28aaSamw 
481da6c28aaSamw /*
4824163af6aSjose borrego  * smb_session_receiver
483da6c28aaSamw  *
4844163af6aSjose borrego  * Receives request from the network and dispatches them to a worker.
485811599a4SMatt Barden  *
486811599a4SMatt Barden  * When we receive a disconnect here, it _could_ be due to the server
487811599a4SMatt Barden  * having initiated disconnect, in which case the session state will be
488811599a4SMatt Barden  * SMB_SESSION_STATE_TERMINATED and we want to keep that state so later
489811599a4SMatt Barden  * tear-down logic will know which side initiated.
490da6c28aaSamw  */
4914163af6aSjose borrego void
4924163af6aSjose borrego smb_session_receiver(smb_session_t *session)
493da6c28aaSamw {
494b819cea2SGordon Ross 	int	rc = 0;
495*817fa55fSGordon Ross 	timeout_id_t tmo = NULL;
496da6c28aaSamw 
4974163af6aSjose borrego 	SMB_SESSION_VALID(session);
4984163af6aSjose borrego 
4994163af6aSjose borrego 	session->s_thread = curthread;
500da6c28aaSamw 
501a0aa776eSAlan Wright 	if (session->s_local_port == IPPORT_NETBIOS_SSN) {
502a90cf9f2SGordon Ross 		rc = smb_netbios_session_request(session);
5034163af6aSjose borrego 		if (rc != 0) {
504faa1795aSjb150015 			smb_rwx_rwenter(&session->s_lock, RW_WRITER);
505811599a4SMatt Barden 			if (session->s_state != SMB_SESSION_STATE_TERMINATED)
506811599a4SMatt Barden 				session->s_state =
507811599a4SMatt Barden 				    SMB_SESSION_STATE_DISCONNECTED;
508faa1795aSjb150015 			smb_rwx_rwexit(&session->s_lock);
5094163af6aSjose borrego 			return;
510faa1795aSjb150015 		}
511faa1795aSjb150015 	}
512da6c28aaSamw 
513da6c28aaSamw 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
514da6c28aaSamw 	session->s_state = SMB_SESSION_STATE_ESTABLISHED;
515*817fa55fSGordon Ross 	session->s_auth_tmo = timeout((tmo_func_t)smb_session_disconnect,
516*817fa55fSGordon Ross 	    session, SEC_TO_TICK(smb_session_auth_tmo));
517da6c28aaSamw 	smb_rwx_rwexit(&session->s_lock);
518da6c28aaSamw 
519a90cf9f2SGordon Ross 	(void) smb_session_reader(session);
520da6c28aaSamw 
521da6c28aaSamw 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
522811599a4SMatt Barden 	if (session->s_state != SMB_SESSION_STATE_TERMINATED)
523da6c28aaSamw 		session->s_state = SMB_SESSION_STATE_DISCONNECTED;
524*817fa55fSGordon Ross 	tmo = session->s_auth_tmo;
525*817fa55fSGordon Ross 	session->s_auth_tmo = NULL;
526da6c28aaSamw 	smb_rwx_rwexit(&session->s_lock);
527da6c28aaSamw 
528*817fa55fSGordon Ross 	/* Timeout callback takes s_lock. See untimeout(9f) */
529*817fa55fSGordon Ross 	if (tmo != NULL)
530*817fa55fSGordon Ross 		(void) untimeout(tmo);
531*817fa55fSGordon Ross 
532faa1795aSjb150015 	smb_soshutdown(session->sock);
533faa1795aSjb150015 
534da6c28aaSamw 	DTRACE_PROBE2(session__drop, struct session *, session, int, rc);
535da6c28aaSamw 
536da6c28aaSamw 	smb_session_cancel(session);
537da6c28aaSamw 	/*
538da6c28aaSamw 	 * At this point everything related to the session should have been
539da6c28aaSamw 	 * cleaned up and we expect that nothing will attempt to use the
540da6c28aaSamw 	 * socket.
541da6c28aaSamw 	 */
5424163af6aSjose borrego }
543da6c28aaSamw 
5444163af6aSjose borrego /*
5454163af6aSjose borrego  * smb_session_disconnect
5464163af6aSjose borrego  *
547811599a4SMatt Barden  * Server-initiated disconnect (i.e. server shutdown)
5484163af6aSjose borrego  */
5494163af6aSjose borrego void
5504163af6aSjose borrego smb_session_disconnect(smb_session_t *session)
5514163af6aSjose borrego {
5524163af6aSjose borrego 	SMB_SESSION_VALID(session);
5534163af6aSjose borrego 
5544163af6aSjose borrego 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
5554163af6aSjose borrego 	switch (session->s_state) {
5564163af6aSjose borrego 	case SMB_SESSION_STATE_INITIALIZED:
5574163af6aSjose borrego 	case SMB_SESSION_STATE_CONNECTED:
5584163af6aSjose borrego 	case SMB_SESSION_STATE_ESTABLISHED:
5594163af6aSjose borrego 	case SMB_SESSION_STATE_NEGOTIATED:
5604163af6aSjose borrego 		smb_soshutdown(session->sock);
561811599a4SMatt Barden 		session->s_state = SMB_SESSION_STATE_TERMINATED;
562811599a4SMatt Barden 		break;
5634163af6aSjose borrego 	case SMB_SESSION_STATE_DISCONNECTED:
5644163af6aSjose borrego 	case SMB_SESSION_STATE_TERMINATED:
5654163af6aSjose borrego 		break;
5664163af6aSjose borrego 	}
5674163af6aSjose borrego 	smb_rwx_rwexit(&session->s_lock);
568da6c28aaSamw }
569da6c28aaSamw 
570da6c28aaSamw /*
571da6c28aaSamw  * Read and process SMB requests.
572da6c28aaSamw  *
573da6c28aaSamw  * Returns:
574da6c28aaSamw  *	0	Success
575da6c28aaSamw  *	1	Unable to read transport header
576da6c28aaSamw  *	2	Invalid transport header type
577da6c28aaSamw  *	3	Invalid SMB length (too small)
578da6c28aaSamw  *	4	Unable to read SMB header
579da6c28aaSamw  *	5	Invalid SMB header (bad magic number)
580da6c28aaSamw  *	6	Unable to read SMB data
581da6c28aaSamw  */
582da6c28aaSamw static int
583a90cf9f2SGordon Ross smb_session_reader(smb_session_t *session)
584da6c28aaSamw {
585148c5f43SAlan Wright 	smb_server_t	*sv;
586faa1795aSjb150015 	smb_request_t	*sr = NULL;
587da6c28aaSamw 	smb_xprt_t	hdr;
588da6c28aaSamw 	uint8_t		*req_buf;
589da6c28aaSamw 	uint32_t	resid;
590da6c28aaSamw 	int		rc;
591da6c28aaSamw 
592148c5f43SAlan Wright 	sv = session->s_server;
593148c5f43SAlan Wright 
594faa1795aSjb150015 	for (;;) {
595da6c28aaSamw 
596faa1795aSjb150015 		rc = smb_session_xprt_gethdr(session, &hdr);
597faa1795aSjb150015 		if (rc)
598faa1795aSjb150015 			return (rc);
599faa1795aSjb150015 
600faa1795aSjb150015 		DTRACE_PROBE2(session__receive__xprthdr, session_t *, session,
601da6c28aaSamw 		    smb_xprt_t *, &hdr);
602da6c28aaSamw 
603da6c28aaSamw 		if (hdr.xh_type != SESSION_MESSAGE) {
604da6c28aaSamw 			/*
605faa1795aSjb150015 			 * Anything other than SESSION_MESSAGE or
606faa1795aSjb150015 			 * SESSION_KEEP_ALIVE is an error.  A SESSION_REQUEST
607faa1795aSjb150015 			 * may indicate a new session request but we need to
608faa1795aSjb150015 			 * close this session and we can treat it as an error
609faa1795aSjb150015 			 * here.
610da6c28aaSamw 			 */
611da6c28aaSamw 			if (hdr.xh_type == SESSION_KEEP_ALIVE) {
612da6c28aaSamw 				session->keep_alive = smb_keep_alive;
613faa1795aSjb150015 				continue;
614da6c28aaSamw 			}
615faa1795aSjb150015 			return (EPROTO);
616da6c28aaSamw 		}
617da6c28aaSamw 
618a90cf9f2SGordon Ross 		if (hdr.xh_length == 0) {
619a90cf9f2SGordon Ross 			/* zero length is another form of keep alive */
620a90cf9f2SGordon Ross 			session->keep_alive = smb_keep_alive;
621a90cf9f2SGordon Ross 			continue;
622a90cf9f2SGordon Ross 		}
623a90cf9f2SGordon Ross 
624da6c28aaSamw 		if (hdr.xh_length < SMB_HEADER_LEN)
625faa1795aSjb150015 			return (EPROTO);
626a90cf9f2SGordon Ross 		if (hdr.xh_length > session->cmd_max_bytes)
627a90cf9f2SGordon Ross 			return (EPROTO);
628da6c28aaSamw 
629da6c28aaSamw 		session->keep_alive = smb_keep_alive;
630a90cf9f2SGordon Ross 
631da6c28aaSamw 		/*
632a90cf9f2SGordon Ross 		 * Allocate a request context, read the whole message.
633811599a4SMatt Barden 		 * If the request alloc fails, we've disconnected
634811599a4SMatt Barden 		 * and won't be able to send the reply anyway, so bail now.
635da6c28aaSamw 		 */
6369c856e86SMatt Barden 		if ((sr = smb_request_alloc(session, hdr.xh_length)) == NULL)
6379c856e86SMatt Barden 			break;
638da6c28aaSamw 
639da6c28aaSamw 		req_buf = (uint8_t *)sr->sr_request_buf;
640da6c28aaSamw 		resid = hdr.xh_length;
641da6c28aaSamw 
642faa1795aSjb150015 		rc = smb_sorecv(session->sock, req_buf, resid);
643faa1795aSjb150015 		if (rc) {
644da6c28aaSamw 			smb_request_free(sr);
645a90cf9f2SGordon Ross 			break;
646da6c28aaSamw 		}
647a90cf9f2SGordon Ross 
648ac2bf314SMatt Barden 		/* accounting: received bytes */
649148c5f43SAlan Wright 		smb_server_add_rxb(sv,
650148c5f43SAlan Wright 		    (int64_t)(hdr.xh_length + NETBIOS_HDR_SZ));
651a90cf9f2SGordon Ross 
652da6c28aaSamw 		/*
653da6c28aaSamw 		 * Initialize command MBC to represent the received data.
654da6c28aaSamw 		 */
655da6c28aaSamw 		smb_request_init_command_mbuf(sr);
656da6c28aaSamw 
657da6c28aaSamw 		DTRACE_PROBE1(session__receive__smb, smb_request_t *, sr);
658da6c28aaSamw 
659a90cf9f2SGordon Ross 		rc = session->newrq_func(sr);
660a90cf9f2SGordon Ross 		sr = NULL;	/* enqueued or freed */
661a90cf9f2SGordon Ross 		if (rc != 0)
662a90cf9f2SGordon Ross 			break;
6630897f7fbSGordon Ross 
6640897f7fbSGordon Ross 		/* See notes where this is defined (above). */
6650897f7fbSGordon Ross 		if (smb_reader_delay) {
6660897f7fbSGordon Ross 			delay(MSEC_TO_TICK(smb_reader_delay));
6670897f7fbSGordon Ross 		}
668148c5f43SAlan Wright 	}
669a90cf9f2SGordon Ross 	return (rc);
670148c5f43SAlan Wright }
671a90cf9f2SGordon Ross 
672a90cf9f2SGordon Ross /*
673a90cf9f2SGordon Ross  * This is the initial handler for new smb requests, called from
674a90cf9f2SGordon Ross  * from smb_session_reader when we have not yet seen any requests.
675a90cf9f2SGordon Ross  * The first SMB request must be "negotiate", which determines
676a90cf9f2SGordon Ross  * which protocol and dialect we'll be using.  That's the ONLY
677a90cf9f2SGordon Ross  * request type handled here, because with all later requests,
678a90cf9f2SGordon Ross  * we know the protocol and handle those with either the SMB1 or
679a90cf9f2SGordon Ross  * SMB2 handlers:  smb1sr_post() or smb2sr_post().
680a90cf9f2SGordon Ross  * Those do NOT allow SMB negotiate, because that's only allowed
681a90cf9f2SGordon Ross  * as the first request on new session.
682a90cf9f2SGordon Ross  *
683a90cf9f2SGordon Ross  * This and other "post a request" handlers must either enqueue
684a90cf9f2SGordon Ross  * the new request for the session taskq, or smb_request_free it
685a90cf9f2SGordon Ross  * (in case we've decided to drop this connection).  In this
686a90cf9f2SGordon Ross  * (special) new request handler, we always free the request.
68793bc28dbSGordon Ross  *
68893bc28dbSGordon Ross  * Return value is 0 for success, and anything else will
68993bc28dbSGordon Ross  * terminate the reader thread (drop the connection).
690a90cf9f2SGordon Ross  */
691a90cf9f2SGordon Ross static int
692a90cf9f2SGordon Ross smbsr_newrq_initial(smb_request_t *sr)
693a90cf9f2SGordon Ross {
694a90cf9f2SGordon Ross 	uint32_t magic;
695a90cf9f2SGordon Ross 	int rc = EPROTO;
696a90cf9f2SGordon Ross 
697a90cf9f2SGordon Ross 	mutex_enter(&sr->sr_mutex);
698a90cf9f2SGordon Ross 	sr->sr_state = SMB_REQ_STATE_ACTIVE;
699a90cf9f2SGordon Ross 	mutex_exit(&sr->sr_mutex);
700a90cf9f2SGordon Ross 
701a90cf9f2SGordon Ross 	magic = SMB_READ_PROTOCOL(sr->sr_request_buf);
702a90cf9f2SGordon Ross 	if (magic == SMB_PROTOCOL_MAGIC)
703a90cf9f2SGordon Ross 		rc = smb1_newrq_negotiate(sr);
704a90cf9f2SGordon Ross 	if (magic == SMB2_PROTOCOL_MAGIC)
705a90cf9f2SGordon Ross 		rc = smb2_newrq_negotiate(sr);
706a90cf9f2SGordon Ross 
707a90cf9f2SGordon Ross 	mutex_enter(&sr->sr_mutex);
708a90cf9f2SGordon Ross 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
709a90cf9f2SGordon Ross 	mutex_exit(&sr->sr_mutex);
710a90cf9f2SGordon Ross 
711a90cf9f2SGordon Ross 	smb_request_free(sr);
712a90cf9f2SGordon Ross 	return (rc);
713da6c28aaSamw }
714da6c28aaSamw 
715da6c28aaSamw /*
716a0aa776eSAlan Wright  * Port will be IPPORT_NETBIOS_SSN or IPPORT_SMB.
717da6c28aaSamw  */
718da6c28aaSamw smb_session_t *
7197f667e74Sjose borrego smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
7207f667e74Sjose borrego     int family)
721da6c28aaSamw {
722da6c28aaSamw 	struct sockaddr_in	sin;
7230f1702c5SYu Xiangning 	socklen_t		slen;
7247f667e74Sjose borrego 	struct sockaddr_in6	sin6;
725da6c28aaSamw 	smb_session_t		*session;
726d3d50737SRafael Vanoni 	int64_t			now;
727a90cf9f2SGordon Ross 	uint16_t		rport;
728da6c28aaSamw 
7298622ec45SGordon Ross 	session = kmem_cache_alloc(smb_cache_session, KM_SLEEP);
730da6c28aaSamw 	bzero(session, sizeof (smb_session_t));
731da6c28aaSamw 
732da6c28aaSamw 	if (smb_idpool_constructor(&session->s_uid_pool)) {
7338622ec45SGordon Ross 		kmem_cache_free(smb_cache_session, session);
734da6c28aaSamw 		return (NULL);
735da6c28aaSamw 	}
7363b13a1efSThomas Keiser 	if (smb_idpool_constructor(&session->s_tid_pool)) {
7373b13a1efSThomas Keiser 		smb_idpool_destructor(&session->s_uid_pool);
7388622ec45SGordon Ross 		kmem_cache_free(smb_cache_session, session);
7393b13a1efSThomas Keiser 		return (NULL);
7403b13a1efSThomas Keiser 	}
741da6c28aaSamw 
742d3d50737SRafael Vanoni 	now = ddi_get_lbolt64();
743d3d50737SRafael Vanoni 
744811599a4SMatt Barden 	session->s_server = sv;
745da6c28aaSamw 	session->s_kid = SMB_NEW_KID();
746faa1795aSjb150015 	session->s_state = SMB_SESSION_STATE_INITIALIZED;
747da6c28aaSamw 	session->native_os = NATIVE_OS_UNKNOWN;
748d3d50737SRafael Vanoni 	session->opentime = now;
749da6c28aaSamw 	session->keep_alive = smb_keep_alive;
750d3d50737SRafael Vanoni 	session->activity_timestamp = now;
751f9bc6dadSDmitry.Savitsky@nexenta.com 	smb_session_genkey(session);
752f9bc6dadSDmitry.Savitsky@nexenta.com 
753a90cf9f2SGordon Ross 	mutex_init(&session->s_credits_mutex, NULL, MUTEX_DEFAULT, NULL);
754a90cf9f2SGordon Ross 
755da6c28aaSamw 	smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t),
756da6c28aaSamw 	    offsetof(smb_request_t, sr_session_lnd));
757da6c28aaSamw 
758da6c28aaSamw 	smb_llist_constructor(&session->s_user_list, sizeof (smb_user_t),
759da6c28aaSamw 	    offsetof(smb_user_t, u_lnd));
760da6c28aaSamw 
7613b13a1efSThomas Keiser 	smb_llist_constructor(&session->s_tree_list, sizeof (smb_tree_t),
7623b13a1efSThomas Keiser 	    offsetof(smb_tree_t, t_lnd));
7633b13a1efSThomas Keiser 
764da6c28aaSamw 	smb_llist_constructor(&session->s_xa_list, sizeof (smb_xa_t),
765da6c28aaSamw 	    offsetof(smb_xa_t, xa_lnd));
766da6c28aaSamw 
7675cdbe942Sjb150015 	smb_net_txl_constructor(&session->s_txlst);
7685cdbe942Sjb150015 
769da6c28aaSamw 	smb_rwx_init(&session->s_lock);
770da6c28aaSamw 
7718d94f651SGordon Ross 	session->s_srqueue = &sv->sv_srqueue;
7728d94f651SGordon Ross 	smb_server_get_cfg(sv, &session->s_cfg);
7738d94f651SGordon Ross 
7748d94f651SGordon Ross 	if (new_so == NULL) {
7758d94f651SGordon Ross 		/*
7768d94f651SGordon Ross 		 * This call is creating the special "server" session,
7778d94f651SGordon Ross 		 * used for kshare export, oplock breaks, CA import.
7788d94f651SGordon Ross 		 * CA import creates temporary trees on this session
7798d94f651SGordon Ross 		 * and those should never get map/unmap up-calls, so
7808d94f651SGordon Ross 		 * force the map/unmap flags zero on this session.
7818d94f651SGordon Ross 		 * Set a "modern" dialect for CA import too, so
7828d94f651SGordon Ross 		 * pathname parse doesn't do OS/2 stuff, etc.
7838d94f651SGordon Ross 		 */
7848d94f651SGordon Ross 		session->s_cfg.skc_execflags = 0;
7858d94f651SGordon Ross 		session->dialect = session->s_cfg.skc_max_protocol;
7868d94f651SGordon Ross 	} else {
7877f667e74Sjose borrego 		if (family == AF_INET) {
7880f1702c5SYu Xiangning 			slen = sizeof (sin);
7897f667e74Sjose borrego 			(void) ksocket_getsockname(new_so,
7907f667e74Sjose borrego 			    (struct sockaddr *)&sin, &slen, CRED());
791fc724630SAlan Wright 			bcopy(&sin.sin_addr,
792fc724630SAlan Wright 			    &session->local_ipaddr.au_addr.au_ipv4,
793fc724630SAlan Wright 			    sizeof (in_addr_t));
794fc724630SAlan Wright 			slen = sizeof (sin);
7957f667e74Sjose borrego 			(void) ksocket_getpeername(new_so,
7967f667e74Sjose borrego 			    (struct sockaddr *)&sin, &slen, CRED());
797fc724630SAlan Wright 			bcopy(&sin.sin_addr,
798fc724630SAlan Wright 			    &session->ipaddr.au_addr.au_ipv4,
799fc724630SAlan Wright 			    sizeof (in_addr_t));
800a90cf9f2SGordon Ross 			rport = sin.sin_port;
8017f667e74Sjose borrego 		} else {
8027f667e74Sjose borrego 			slen = sizeof (sin6);
8037f667e74Sjose borrego 			(void) ksocket_getsockname(new_so,
8047f667e74Sjose borrego 			    (struct sockaddr *)&sin6, &slen, CRED());
805fc724630SAlan Wright 			bcopy(&sin6.sin6_addr,
806fc724630SAlan Wright 			    &session->local_ipaddr.au_addr.au_ipv6,
807fc724630SAlan Wright 			    sizeof (in6_addr_t));
808fc724630SAlan Wright 			slen = sizeof (sin6);
8097f667e74Sjose borrego 			(void) ksocket_getpeername(new_so,
8107f667e74Sjose borrego 			    (struct sockaddr *)&sin6, &slen, CRED());
811fc724630SAlan Wright 			bcopy(&sin6.sin6_addr,
812fc724630SAlan Wright 			    &session->ipaddr.au_addr.au_ipv6,
813fc724630SAlan Wright 			    sizeof (in6_addr_t));
814a90cf9f2SGordon Ross 			rport = sin6.sin6_port;
8157f667e74Sjose borrego 		}
8167f667e74Sjose borrego 		session->ipaddr.a_family = family;
8177f667e74Sjose borrego 		session->local_ipaddr.a_family = family;
818da6c28aaSamw 		session->s_local_port = port;
819a90cf9f2SGordon Ross 		session->s_remote_port = ntohs(rport);
820da6c28aaSamw 		session->sock = new_so;
82149b5df1eSGordon Ross 		(void) smb_inet_ntop(&session->ipaddr,
82249b5df1eSGordon Ross 		    session->ip_addr_str, INET6_ADDRSTRLEN);
823148c5f43SAlan Wright 		if (port == IPPORT_NETBIOS_SSN)
824148c5f43SAlan Wright 			smb_server_inc_nbt_sess(sv);
825148c5f43SAlan Wright 		else
826148c5f43SAlan Wright 			smb_server_inc_tcp_sess(sv);
827faa1795aSjb150015 	}
828148c5f43SAlan Wright 
829a90cf9f2SGordon Ross 	/*
830a90cf9f2SGordon Ross 	 * The initial new request handler is special,
831a90cf9f2SGordon Ross 	 * and only accepts negotiation requests.
832a90cf9f2SGordon Ross 	 */
833a90cf9f2SGordon Ross 	session->newrq_func = smbsr_newrq_initial;
834a90cf9f2SGordon Ross 
835a90cf9f2SGordon Ross 	/* These may increase in SMB2 negotiate. */
836a90cf9f2SGordon Ross 	session->cmd_max_bytes = SMB_REQ_MAX_SIZE;
837a90cf9f2SGordon Ross 	session->reply_max_bytes = SMB_REQ_MAX_SIZE;
838a90cf9f2SGordon Ross 
839da6c28aaSamw 	session->s_magic = SMB_SESSION_MAGIC;
840da6c28aaSamw 	return (session);
841da6c28aaSamw }
842da6c28aaSamw 
843da6c28aaSamw void
844da6c28aaSamw smb_session_delete(smb_session_t *session)
845da6c28aaSamw {
8462c2961f8Sjose borrego 
847da6c28aaSamw 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
848da6c28aaSamw 
8491160dcf7SMatt Barden 	if (session->enc_mech != NULL)
8501160dcf7SMatt Barden 		smb3_encrypt_fini(session);
8511160dcf7SMatt Barden 
852b819cea2SGordon Ross 	if (session->sign_fini != NULL)
853b819cea2SGordon Ross 		session->sign_fini(session);
854b819cea2SGordon Ross 
85512b65585SGordon Ross 	if (session->signing.mackey != NULL) {
85612b65585SGordon Ross 		kmem_free(session->signing.mackey,
85712b65585SGordon Ross 		    session->signing.mackey_len);
85812b65585SGordon Ross 	}
85912b65585SGordon Ross 
86012b65585SGordon Ross 	session->s_magic = 0;
86112b65585SGordon Ross 
862da6c28aaSamw 	smb_rwx_destroy(&session->s_lock);
8635cdbe942Sjb150015 	smb_net_txl_destructor(&session->s_txlst);
8642c2961f8Sjose borrego 
865a90cf9f2SGordon Ross 	mutex_destroy(&session->s_credits_mutex);
866a90cf9f2SGordon Ross 
867da6c28aaSamw 	smb_slist_destructor(&session->s_req_list);
8683b13a1efSThomas Keiser 	smb_llist_destructor(&session->s_tree_list);
869da6c28aaSamw 	smb_llist_destructor(&session->s_user_list);
870da6c28aaSamw 	smb_llist_destructor(&session->s_xa_list);
871da6c28aaSamw 
872da6c28aaSamw 	ASSERT(session->s_tree_cnt == 0);
873da6c28aaSamw 	ASSERT(session->s_file_cnt == 0);
874da6c28aaSamw 	ASSERT(session->s_dir_cnt == 0);
875da6c28aaSamw 
8763b13a1efSThomas Keiser 	smb_idpool_destructor(&session->s_tid_pool);
877da6c28aaSamw 	smb_idpool_destructor(&session->s_uid_pool);
8784163af6aSjose borrego 	if (session->sock != NULL) {
8794163af6aSjose borrego 		if (session->s_local_port == IPPORT_NETBIOS_SSN)
8804163af6aSjose borrego 			smb_server_dec_nbt_sess(session->s_server);
8814163af6aSjose borrego 		else
8824163af6aSjose borrego 			smb_server_dec_tcp_sess(session->s_server);
8834163af6aSjose borrego 		smb_sodestroy(session->sock);
8844163af6aSjose borrego 	}
8858622ec45SGordon Ross 	kmem_cache_free(smb_cache_session, session);
886da6c28aaSamw }
887da6c28aaSamw 
8882c2961f8Sjose borrego static void
889da6c28aaSamw smb_session_cancel(smb_session_t *session)
890da6c28aaSamw {
891da6c28aaSamw 	smb_xa_t	*xa, *nextxa;
892da6c28aaSamw 
893da6c28aaSamw 	/* All the request currently being treated must be canceled. */
894c8ec8eeaSjose borrego 	smb_session_cancel_requests(session, NULL, NULL);
895da6c28aaSamw 
896da6c28aaSamw 	/*
897da6c28aaSamw 	 * We wait for the completion of all the requests associated with
898da6c28aaSamw 	 * this session.
899da6c28aaSamw 	 */
900da6c28aaSamw 	smb_slist_wait_for_empty(&session->s_req_list);
901da6c28aaSamw 
902da6c28aaSamw 	/*
903*817fa55fSGordon Ross 	 * Cleanup transact state objects
904da6c28aaSamw 	 */
905da6c28aaSamw 	xa = smb_llist_head(&session->s_xa_list);
906da6c28aaSamw 	while (xa) {
907da6c28aaSamw 		nextxa = smb_llist_next(&session->s_xa_list, xa);
908da6c28aaSamw 		smb_xa_close(xa);
909da6c28aaSamw 		xa = nextxa;
910da6c28aaSamw 	}
9119fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
912*817fa55fSGordon Ross 	/*
913*817fa55fSGordon Ross 	 * At this point the reference count of the files and directories
914*817fa55fSGordon Ross 	 * should be zero. It should be possible to destroy them without
915*817fa55fSGordon Ross 	 * any problem, which should trigger the destruction of other objects.
916*817fa55fSGordon Ross 	 */
9179fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	smb_session_logoff(session);
918da6c28aaSamw }
919da6c28aaSamw 
920c8ec8eeaSjose borrego /*
921c8ec8eeaSjose borrego  * Cancel requests.  If a non-null tree is specified, only requests specific
922c8ec8eeaSjose borrego  * to that tree will be cancelled.  If a non-null sr is specified, that sr
923c8ec8eeaSjose borrego  * will be not be cancelled - this would typically be the caller's sr.
924c8ec8eeaSjose borrego  */
925da6c28aaSamw void
926da6c28aaSamw smb_session_cancel_requests(
927c8ec8eeaSjose borrego     smb_session_t	*session,
928c8ec8eeaSjose borrego     smb_tree_t		*tree,
929c8ec8eeaSjose borrego     smb_request_t	*exclude_sr)
930da6c28aaSamw {
931da6c28aaSamw 	smb_request_t	*sr;
932da6c28aaSamw 
933da6c28aaSamw 	smb_slist_enter(&session->s_req_list);
934da6c28aaSamw 	sr = smb_slist_head(&session->s_req_list);
935c8ec8eeaSjose borrego 
936da6c28aaSamw 	while (sr) {
937da6c28aaSamw 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
938c8ec8eeaSjose borrego 		if ((sr != exclude_sr) &&
939c8ec8eeaSjose borrego 		    (tree == NULL || sr->tid_tree == tree))
940da6c28aaSamw 			smb_request_cancel(sr);
941da6c28aaSamw 
942c8ec8eeaSjose borrego 		sr = smb_slist_next(&session->s_req_list, sr);
943da6c28aaSamw 	}
944c8ec8eeaSjose borrego 
945da6c28aaSamw 	smb_slist_exit(&session->s_req_list);
946da6c28aaSamw }
947da6c28aaSamw 
948faa1795aSjb150015 /*
9499fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States  * Find a user on the specified session by SMB UID.
9509fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States  */
9519fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States smb_user_t *
9529fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States smb_session_lookup_uid(smb_session_t *session, uint16_t uid)
9539fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States {
954811599a4SMatt Barden 	return (smb_session_lookup_uid_st(session, 0, uid,
955811599a4SMatt Barden 	    SMB_USER_STATE_LOGGED_ON));
956811599a4SMatt Barden }
957811599a4SMatt Barden 
958811599a4SMatt Barden /*
959811599a4SMatt Barden  * Find a user on the specified session by SMB2 SSNID.
960811599a4SMatt Barden  */
961811599a4SMatt Barden smb_user_t *
962811599a4SMatt Barden smb_session_lookup_ssnid(smb_session_t *session, uint64_t ssnid)
963811599a4SMatt Barden {
964811599a4SMatt Barden 	return (smb_session_lookup_uid_st(session, ssnid, 0,
96512b65585SGordon Ross 	    SMB_USER_STATE_LOGGED_ON));
96612b65585SGordon Ross }
96712b65585SGordon Ross 
96812b65585SGordon Ross smb_user_t *
969811599a4SMatt Barden smb_session_lookup_uid_st(smb_session_t *session, uint64_t ssnid,
970811599a4SMatt Barden     uint16_t uid, smb_user_state_t st)
97112b65585SGordon Ross {
9729fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	smb_user_t	*user;
9739fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	smb_llist_t	*user_list;
9749fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
9759fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	SMB_SESSION_VALID(session);
9769fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
9779fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	user_list = &session->s_user_list;
9789fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	smb_llist_enter(user_list, RW_READER);
9799fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
980811599a4SMatt Barden 	for (user = smb_llist_head(user_list);
981811599a4SMatt Barden 	    user != NULL;
982811599a4SMatt Barden 	    user = smb_llist_next(user_list, user)) {
983811599a4SMatt Barden 
9849fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 		SMB_USER_VALID(user);
9859fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 		ASSERT(user->u_session == session);
9869fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
987811599a4SMatt Barden 		if (user->u_ssnid != ssnid && user->u_uid != uid)
988811599a4SMatt Barden 			continue;
989811599a4SMatt Barden 
990811599a4SMatt Barden 		mutex_enter(&user->u_mutex);
991811599a4SMatt Barden 		if (user->u_state == st) {
992811599a4SMatt Barden 			// smb_user_hold_internal(user);
993811599a4SMatt Barden 			user->u_refcnt++;
994811599a4SMatt Barden 			mutex_exit(&user->u_mutex);
9959fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 			break;
9969fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 		}
997811599a4SMatt Barden 		mutex_exit(&user->u_mutex);
9989fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	}
9999fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
10009fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	smb_llist_exit(user_list);
100112b65585SGordon Ross 	return (user);
10029fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States }
10039fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
10049fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States /*
10053b13a1efSThomas Keiser  * Find a tree by tree-id.
10063b13a1efSThomas Keiser  */
10073b13a1efSThomas Keiser smb_tree_t *
10083b13a1efSThomas Keiser smb_session_lookup_tree(
10093b13a1efSThomas Keiser     smb_session_t	*session,
10103b13a1efSThomas Keiser     uint16_t		tid)
10113b13a1efSThomas Keiser {
10123b13a1efSThomas Keiser 	smb_tree_t	*tree;
10133b13a1efSThomas Keiser 
10143b13a1efSThomas Keiser 	SMB_SESSION_VALID(session);
10153b13a1efSThomas Keiser 
10163b13a1efSThomas Keiser 	smb_llist_enter(&session->s_tree_list, RW_READER);
10173b13a1efSThomas Keiser 	tree = smb_llist_head(&session->s_tree_list);
10183b13a1efSThomas Keiser 
10193b13a1efSThomas Keiser 	while (tree) {
10203b13a1efSThomas Keiser 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
10213b13a1efSThomas Keiser 		ASSERT(tree->t_session == session);
10223b13a1efSThomas Keiser 
10233b13a1efSThomas Keiser 		if (tree->t_tid == tid) {
10243b13a1efSThomas Keiser 			if (smb_tree_hold(tree)) {
10253b13a1efSThomas Keiser 				smb_llist_exit(&session->s_tree_list);
10263b13a1efSThomas Keiser 				return (tree);
10273b13a1efSThomas Keiser 			} else {
10283b13a1efSThomas Keiser 				smb_llist_exit(&session->s_tree_list);
10293b13a1efSThomas Keiser 				return (NULL);
10303b13a1efSThomas Keiser 			}
10313b13a1efSThomas Keiser 		}
10323b13a1efSThomas Keiser 
10333b13a1efSThomas Keiser 		tree = smb_llist_next(&session->s_tree_list, tree);
10343b13a1efSThomas Keiser 	}
10353b13a1efSThomas Keiser 
10363b13a1efSThomas Keiser 	smb_llist_exit(&session->s_tree_list);
10373b13a1efSThomas Keiser 	return (NULL);
10383b13a1efSThomas Keiser }
10393b13a1efSThomas Keiser 
10403b13a1efSThomas Keiser /*
10413b13a1efSThomas Keiser  * Disconnect all trees that match the specified client process-id.
10428d94f651SGordon Ross  * Used by the SMB1 "process exit" request.
10433b13a1efSThomas Keiser  */
10443b13a1efSThomas Keiser void
10453b13a1efSThomas Keiser smb_session_close_pid(
10463b13a1efSThomas Keiser     smb_session_t	*session,
1047a90cf9f2SGordon Ross     uint32_t		pid)
10483b13a1efSThomas Keiser {
10498d94f651SGordon Ross 	smb_llist_t	*tree_list = &session->s_tree_list;
10503b13a1efSThomas Keiser 	smb_tree_t	*tree;
10513b13a1efSThomas Keiser 
10528d94f651SGordon Ross 	smb_llist_enter(tree_list, RW_READER);
10533b13a1efSThomas Keiser 
10548d94f651SGordon Ross 	tree = smb_llist_head(tree_list);
10553b13a1efSThomas Keiser 	while (tree) {
10568d94f651SGordon Ross 		if (smb_tree_hold(tree)) {
10573b13a1efSThomas Keiser 			smb_tree_close_pid(tree, pid);
10583b13a1efSThomas Keiser 			smb_tree_release(tree);
10593b13a1efSThomas Keiser 		}
10608d94f651SGordon Ross 		tree = smb_llist_next(tree_list, tree);
10618d94f651SGordon Ross 	}
10628d94f651SGordon Ross 
10638d94f651SGordon Ross 	smb_llist_exit(tree_list);
10643b13a1efSThomas Keiser }
10653b13a1efSThomas Keiser 
10663b13a1efSThomas Keiser static void
10678d94f651SGordon Ross smb_session_tree_dtor(void *arg)
10683b13a1efSThomas Keiser {
10698d94f651SGordon Ross 	smb_tree_t	*tree = arg;
10703b13a1efSThomas Keiser 
10713b13a1efSThomas Keiser 	smb_tree_disconnect(tree, B_TRUE);
10723b13a1efSThomas Keiser 	/* release the ref acquired during the traversal loop */
10733b13a1efSThomas Keiser 	smb_tree_release(tree);
10743b13a1efSThomas Keiser }
10753b13a1efSThomas Keiser 
10763b13a1efSThomas Keiser 
10773b13a1efSThomas Keiser /*
10783b13a1efSThomas Keiser  * Disconnect all trees that this user has connected.
10793b13a1efSThomas Keiser  */
10803b13a1efSThomas Keiser void
10813b13a1efSThomas Keiser smb_session_disconnect_owned_trees(
10823b13a1efSThomas Keiser     smb_session_t	*session,
10833b13a1efSThomas Keiser     smb_user_t		*owner)
10843b13a1efSThomas Keiser {
10853b13a1efSThomas Keiser 	smb_tree_t	*tree;
10863b13a1efSThomas Keiser 	smb_llist_t	*tree_list = &session->s_tree_list;
10873b13a1efSThomas Keiser 
10883b13a1efSThomas Keiser 	SMB_SESSION_VALID(session);
10893b13a1efSThomas Keiser 	SMB_USER_VALID(owner);
10903b13a1efSThomas Keiser 
10913b13a1efSThomas Keiser 	smb_llist_enter(tree_list, RW_READER);
10923b13a1efSThomas Keiser 
10933b13a1efSThomas Keiser 	tree = smb_llist_head(tree_list);
10943b13a1efSThomas Keiser 	while (tree) {
10953b13a1efSThomas Keiser 		if ((tree->t_owner == owner) &&
10963b13a1efSThomas Keiser 		    smb_tree_hold(tree)) {
10973b13a1efSThomas Keiser 			/*
10983b13a1efSThomas Keiser 			 * smb_tree_hold() succeeded, hence we are in state
10993b13a1efSThomas Keiser 			 * SMB_TREE_STATE_CONNECTED; schedule this tree
1100811599a4SMatt Barden 			 * for disconnect after smb_llist_exit because
1101811599a4SMatt Barden 			 * the "unmap exec" up-call can block, and we'd
1102811599a4SMatt Barden 			 * rather not block with the tree list locked.
11033b13a1efSThomas Keiser 			 */
11043b13a1efSThomas Keiser 			smb_llist_post(tree_list, tree, smb_session_tree_dtor);
11053b13a1efSThomas Keiser 		}
11063b13a1efSThomas Keiser 		tree = smb_llist_next(tree_list, tree);
11073b13a1efSThomas Keiser 	}
11083b13a1efSThomas Keiser 
11093b13a1efSThomas Keiser 	/* drop the lock and flush the dtor queue */
11103b13a1efSThomas Keiser 	smb_llist_exit(tree_list);
11113b13a1efSThomas Keiser }
11123b13a1efSThomas Keiser 
11133b13a1efSThomas Keiser /*
11143b13a1efSThomas Keiser  * Disconnect all trees that this user has connected.
11153b13a1efSThomas Keiser  */
1116811599a4SMatt Barden static void
11173b13a1efSThomas Keiser smb_session_disconnect_trees(
11183b13a1efSThomas Keiser     smb_session_t	*session)
11193b13a1efSThomas Keiser {
11208d94f651SGordon Ross 	smb_llist_t	*tree_list = &session->s_tree_list;
11218d94f651SGordon Ross 	smb_tree_t	*tree;
11223b13a1efSThomas Keiser 
11238d94f651SGordon Ross 	smb_llist_enter(tree_list, RW_READER);
11243b13a1efSThomas Keiser 
11258d94f651SGordon Ross 	tree = smb_llist_head(tree_list);
11263b13a1efSThomas Keiser 	while (tree) {
11278d94f651SGordon Ross 		if (smb_tree_hold(tree)) {
11288d94f651SGordon Ross 			smb_llist_post(tree_list, tree,
11298d94f651SGordon Ross 			    smb_session_tree_dtor);
11303b13a1efSThomas Keiser 		}
11318d94f651SGordon Ross 		tree = smb_llist_next(tree_list, tree);
11328d94f651SGordon Ross 	}
11338d94f651SGordon Ross 
11348d94f651SGordon Ross 	/* drop the lock and flush the dtor queue */
11358d94f651SGordon Ross 	smb_llist_exit(tree_list);
11363b13a1efSThomas Keiser }
11373b13a1efSThomas Keiser 
11383b13a1efSThomas Keiser /*
11398d94f651SGordon Ross  * Variant of smb_session_tree_dtor that also
11408d94f651SGordon Ross  * cancels requests using this tree.
11418d94f651SGordon Ross  */
11428d94f651SGordon Ross static void
11438d94f651SGordon Ross smb_session_tree_kill(void *arg)
11448d94f651SGordon Ross {
11458d94f651SGordon Ross 	smb_tree_t	*tree = arg;
11468d94f651SGordon Ross 
11478d94f651SGordon Ross 	SMB_TREE_VALID(tree);
11488d94f651SGordon Ross 
11498d94f651SGordon Ross 	smb_tree_disconnect(tree, B_TRUE);
11508d94f651SGordon Ross 	smb_session_cancel_requests(tree->t_session, tree, NULL);
11518d94f651SGordon Ross 
11528d94f651SGordon Ross 	/* release the ref acquired during the traversal loop */
11538d94f651SGordon Ross 	smb_tree_release(tree);
11548d94f651SGordon Ross }
11558d94f651SGordon Ross 
11568d94f651SGordon Ross /*
11578d94f651SGordon Ross  * Disconnect all trees that match the specified share name,
11588d94f651SGordon Ross  * and kill requests using those trees.
11593b13a1efSThomas Keiser  */
11603b13a1efSThomas Keiser void
11613b13a1efSThomas Keiser smb_session_disconnect_share(
11623b13a1efSThomas Keiser     smb_session_t	*session,
11633b13a1efSThomas Keiser     const char		*sharename)
11643b13a1efSThomas Keiser {
11658d94f651SGordon Ross 	smb_llist_t	*ll;
11663b13a1efSThomas Keiser 	smb_tree_t	*tree;
11673b13a1efSThomas Keiser 
11683b13a1efSThomas Keiser 	SMB_SESSION_VALID(session);
11693b13a1efSThomas Keiser 
11708d94f651SGordon Ross 	ll = &session->s_tree_list;
11718d94f651SGordon Ross 	smb_llist_enter(ll, RW_READER);
11728d94f651SGordon Ross 
11738d94f651SGordon Ross 	for (tree = smb_llist_head(ll);
11748d94f651SGordon Ross 	    tree != NULL;
11758d94f651SGordon Ross 	    tree = smb_llist_next(ll, tree)) {
11768d94f651SGordon Ross 
11778d94f651SGordon Ross 		SMB_TREE_VALID(tree);
11783b13a1efSThomas Keiser 		ASSERT(tree->t_session == session);
11798d94f651SGordon Ross 
11808d94f651SGordon Ross 		if (smb_strcasecmp(tree->t_sharename, sharename, 0) != 0)
11818d94f651SGordon Ross 			continue;
11828d94f651SGordon Ross 
11838d94f651SGordon Ross 		if (smb_tree_hold(tree)) {
11848d94f651SGordon Ross 			smb_llist_post(ll, tree,
11858d94f651SGordon Ross 			    smb_session_tree_kill);
11863b13a1efSThomas Keiser 		}
11873b13a1efSThomas Keiser 	}
11883b13a1efSThomas Keiser 
11898d94f651SGordon Ross 	smb_llist_exit(ll);
11903b13a1efSThomas Keiser }
11913b13a1efSThomas Keiser 
11923b13a1efSThomas Keiser /*
11939fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States  * Logoff all users associated with the specified session.
1194811599a4SMatt Barden  *
1195811599a4SMatt Barden  * This is called for both server-initiated disconnect
1196811599a4SMatt Barden  * (SMB_SESSION_STATE_TERMINATED) and client-initiated
1197811599a4SMatt Barden  * disconnect (SMB_SESSION_STATE_DISCONNECTED).
1198811599a4SMatt Barden  * If client-initiated, save durable handles.
11999fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States  */
12008d94f651SGordon Ross void
12019fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States smb_session_logoff(smb_session_t *session)
12029fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States {
1203811599a4SMatt Barden 	smb_llist_t	*ulist;
12049fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	smb_user_t	*user;
12059fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
12069fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	SMB_SESSION_VALID(session);
12079fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
1208811599a4SMatt Barden top:
1209811599a4SMatt Barden 	ulist = &session->s_user_list;
1210811599a4SMatt Barden 	smb_llist_enter(ulist, RW_READER);
12113b13a1efSThomas Keiser 
1212811599a4SMatt Barden 	user = smb_llist_head(ulist);
12139fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	while (user) {
12149fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 		SMB_USER_VALID(user);
12159fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 		ASSERT(user->u_session == session);
12169fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
1217811599a4SMatt Barden 		mutex_enter(&user->u_mutex);
121812b65585SGordon Ross 		switch (user->u_state) {
121912b65585SGordon Ross 		case SMB_USER_STATE_LOGGING_ON:
122012b65585SGordon Ross 		case SMB_USER_STATE_LOGGED_ON:
1221811599a4SMatt Barden 			// smb_user_hold_internal(user);
1222811599a4SMatt Barden 			user->u_refcnt++;
1223811599a4SMatt Barden 			mutex_exit(&user->u_mutex);
12249fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 			smb_user_logoff(user);
12259fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 			smb_user_release(user);
122612b65585SGordon Ross 			break;
122712b65585SGordon Ross 
122812b65585SGordon Ross 		case SMB_USER_STATE_LOGGED_OFF:
122912b65585SGordon Ross 		case SMB_USER_STATE_LOGGING_OFF:
1230811599a4SMatt Barden 			mutex_exit(&user->u_mutex);
123112b65585SGordon Ross 			break;
123212b65585SGordon Ross 
123312b65585SGordon Ross 		default:
1234811599a4SMatt Barden 			mutex_exit(&user->u_mutex);
1235*817fa55fSGordon Ross 			ASSERT(0);
123612b65585SGordon Ross 			break;
12379fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 		}
12389fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
1239811599a4SMatt Barden 		user = smb_llist_next(ulist, user);
12409fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 	}
12419fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
1242811599a4SMatt Barden 	/* Needed below (Was the list empty?) */
1243811599a4SMatt Barden 	user = smb_llist_head(ulist);
1244811599a4SMatt Barden 
1245811599a4SMatt Barden 	smb_llist_exit(ulist);
1246811599a4SMatt Barden 
1247811599a4SMatt Barden 	/*
1248811599a4SMatt Barden 	 * It's possible for user objects to remain due to references
1249811599a4SMatt Barden 	 * obtained via smb_server_lookup_ssnid(), when an SMB2
1250811599a4SMatt Barden 	 * session setup is destroying a previous session.
1251811599a4SMatt Barden 	 *
1252811599a4SMatt Barden 	 * Wait for user objects to clear out (last refs. go away,
1253811599a4SMatt Barden 	 * then smb_user_delete takes them out of the list).  When
1254811599a4SMatt Barden 	 * the last user object is removed, the session state is
1255811599a4SMatt Barden 	 * set to SHUTDOWN and s_lock is signaled.
1256811599a4SMatt Barden 	 *
1257811599a4SMatt Barden 	 * Not all places that call smb_user_release necessarily
1258811599a4SMatt Barden 	 * flush the delete queue, so after we wait for the list
1259811599a4SMatt Barden 	 * to empty out, go back to the top and recheck the list
1260811599a4SMatt Barden 	 * delete queue to make sure smb_user_delete happens.
1261811599a4SMatt Barden 	 */
1262811599a4SMatt Barden 	if (user == NULL) {
1263811599a4SMatt Barden 		/* User list is empty. */
1264811599a4SMatt Barden 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
1265811599a4SMatt Barden 		session->s_state = SMB_SESSION_STATE_SHUTDOWN;
1266811599a4SMatt Barden 		smb_rwx_rwexit(&session->s_lock);
1267811599a4SMatt Barden 	} else {
1268811599a4SMatt Barden 		smb_rwx_rwenter(&session->s_lock, RW_READER);
1269811599a4SMatt Barden 		if (session->s_state != SMB_SESSION_STATE_SHUTDOWN) {
1270811599a4SMatt Barden 			(void) smb_rwx_cvwait(&session->s_lock,
1271811599a4SMatt Barden 			    MSEC_TO_TICK(200));
1272811599a4SMatt Barden 			smb_rwx_rwexit(&session->s_lock);
1273811599a4SMatt Barden 			goto top;
1274811599a4SMatt Barden 		}
1275811599a4SMatt Barden 		smb_rwx_rwexit(&session->s_lock);
1276811599a4SMatt Barden 	}
1277811599a4SMatt Barden 	ASSERT(session->s_state == SMB_SESSION_STATE_SHUTDOWN);
1278811599a4SMatt Barden 
1279811599a4SMatt Barden 	/*
1280811599a4SMatt Barden 	 * User list should be empty now.
1281811599a4SMatt Barden 	 */
1282811599a4SMatt Barden #ifdef	DEBUG
1283811599a4SMatt Barden 	if (ulist->ll_count != 0) {
1284811599a4SMatt Barden 		cmn_err(CE_WARN, "user list not empty?");
1285811599a4SMatt Barden 		debug_enter("s_user_list");
1286811599a4SMatt Barden 	}
1287811599a4SMatt Barden #endif
1288811599a4SMatt Barden 
1289811599a4SMatt Barden 	/*
1290811599a4SMatt Barden 	 * User logoff happens first so we'll set preserve_opens
1291811599a4SMatt Barden 	 * for client-initiated disconnect.  When that's done
1292811599a4SMatt Barden 	 * there should be no trees left, but check anyway.
1293811599a4SMatt Barden 	 */
1294811599a4SMatt Barden 	smb_session_disconnect_trees(session);
12959fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States }
12969fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 
12979fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States /*
12981fcced4cSJordan Brown  * Copy the session workstation/client name to buf.  If the workstation
12991fcced4cSJordan Brown  * is an empty string (which it will be on TCP connections), use the
13001fcced4cSJordan Brown  * client IP address.
13011fcced4cSJordan Brown  */
13021fcced4cSJordan Brown void
13031fcced4cSJordan Brown smb_session_getclient(smb_session_t *sn, char *buf, size_t buflen)
13041fcced4cSJordan Brown {
13051fcced4cSJordan Brown 
13061fcced4cSJordan Brown 	*buf = '\0';
13071fcced4cSJordan Brown 
13081fcced4cSJordan Brown 	if (sn->workstation[0] != '\0') {
13091fcced4cSJordan Brown 		(void) strlcpy(buf, sn->workstation, buflen);
13101fcced4cSJordan Brown 		return;
13111fcced4cSJordan Brown 	}
13121fcced4cSJordan Brown 
131349b5df1eSGordon Ross 	(void) strlcpy(buf, sn->ip_addr_str, buflen);
13141fcced4cSJordan Brown }
13151fcced4cSJordan Brown 
13161fcced4cSJordan Brown /*
13171fcced4cSJordan Brown  * Check whether or not the specified client name is the client of this
13181fcced4cSJordan Brown  * session.  The name may be in UNC format (\\CLIENT).
13191fcced4cSJordan Brown  *
13201fcced4cSJordan Brown  * A workstation/client name is setup on NBT connections as part of the
13211fcced4cSJordan Brown  * NetBIOS session request but that isn't available on TCP connections.
13221fcced4cSJordan Brown  * If the session doesn't have a client name we typically return the
13231fcced4cSJordan Brown  * client IP address as the workstation name on MSRPC requests.  So we
13241fcced4cSJordan Brown  * check for the IP address here in addition to the workstation name.
13251fcced4cSJordan Brown  */
13261fcced4cSJordan Brown boolean_t
13271fcced4cSJordan Brown smb_session_isclient(smb_session_t *sn, const char *client)
13281fcced4cSJordan Brown {
13291fcced4cSJordan Brown 
13301fcced4cSJordan Brown 	client += strspn(client, "\\");
13311fcced4cSJordan Brown 
1332bbf6f00cSJordan Brown 	if (smb_strcasecmp(client, sn->workstation, 0) == 0)
13331fcced4cSJordan Brown 		return (B_TRUE);
13341fcced4cSJordan Brown 
133549b5df1eSGordon Ross 	if (smb_strcasecmp(client, sn->ip_addr_str, 0) == 0)
13361fcced4cSJordan Brown 		return (B_TRUE);
13371fcced4cSJordan Brown 
13381fcced4cSJordan Brown 	return (B_FALSE);
13391fcced4cSJordan Brown }
13401fcced4cSJordan Brown 
13411fcced4cSJordan Brown /*
1342faa1795aSjb150015  * smb_request_alloc
1343faa1795aSjb150015  *
1344faa1795aSjb150015  * Allocate an smb_request_t structure from the kmem_cache.  Partially
1345faa1795aSjb150015  * initialize the found/new request.
1346faa1795aSjb150015  *
13479c856e86SMatt Barden  * Returns pointer to a request, or NULL if the session state is
13489c856e86SMatt Barden  * one in which new requests are no longer allowed.
1349faa1795aSjb150015  */
1350faa1795aSjb150015 smb_request_t *
1351faa1795aSjb150015 smb_request_alloc(smb_session_t *session, int req_length)
1352faa1795aSjb150015 {
1353faa1795aSjb150015 	smb_request_t	*sr;
1354faa1795aSjb150015 
1355faa1795aSjb150015 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1356a90cf9f2SGordon Ross 	ASSERT(req_length <= session->cmd_max_bytes);
1357faa1795aSjb150015 
13588622ec45SGordon Ross 	sr = kmem_cache_alloc(smb_cache_request, KM_SLEEP);
1359faa1795aSjb150015 
1360faa1795aSjb150015 	/*
1361faa1795aSjb150015 	 * Future:  Use constructor to pre-initialize some fields.  For now
1362faa1795aSjb150015 	 * there are so many fields that it is easiest just to zero the
1363faa1795aSjb150015 	 * whole thing and start over.
1364faa1795aSjb150015 	 */
1365faa1795aSjb150015 	bzero(sr, sizeof (smb_request_t));
1366faa1795aSjb150015 
1367faa1795aSjb150015 	mutex_init(&sr->sr_mutex, NULL, MUTEX_DEFAULT, NULL);
1368bbf6f00cSJordan Brown 	smb_srm_init(sr);
1369faa1795aSjb150015 	sr->session = session;
1370faa1795aSjb150015 	sr->sr_server = session->s_server;
1371faa1795aSjb150015 	sr->sr_gmtoff = session->s_server->si_gmtoff;
1372faa1795aSjb150015 	sr->sr_cfg = &session->s_cfg;
1373faa1795aSjb150015 	sr->command.max_bytes = req_length;
1374a90cf9f2SGordon Ross 	sr->reply.max_bytes = session->reply_max_bytes;
1375faa1795aSjb150015 	sr->sr_req_length = req_length;
1376faa1795aSjb150015 	if (req_length)
1377faa1795aSjb150015 		sr->sr_request_buf = kmem_alloc(req_length, KM_SLEEP);
1378faa1795aSjb150015 	sr->sr_magic = SMB_REQ_MAGIC;
1379faa1795aSjb150015 	sr->sr_state = SMB_REQ_STATE_INITIALIZING;
13809c856e86SMatt Barden 
13819c856e86SMatt Barden 	/*
13829c856e86SMatt Barden 	 * Only allow new SMB requests in some states.
13839c856e86SMatt Barden 	 */
13849c856e86SMatt Barden 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
13859c856e86SMatt Barden 	switch (session->s_state) {
13869c856e86SMatt Barden 	case SMB_SESSION_STATE_CONNECTED:
13879c856e86SMatt Barden 	case SMB_SESSION_STATE_INITIALIZED:
13889c856e86SMatt Barden 	case SMB_SESSION_STATE_ESTABLISHED:
13899c856e86SMatt Barden 	case SMB_SESSION_STATE_NEGOTIATED:
1390faa1795aSjb150015 		smb_slist_insert_tail(&session->s_req_list, sr);
13919c856e86SMatt Barden 		break;
13929c856e86SMatt Barden 
13939c856e86SMatt Barden 	default:
13949c856e86SMatt Barden 		ASSERT(0);
13959c856e86SMatt Barden 		/* FALLTHROUGH */
13969c856e86SMatt Barden 	case SMB_SESSION_STATE_DISCONNECTED:
1397811599a4SMatt Barden 	case SMB_SESSION_STATE_SHUTDOWN:
13989c856e86SMatt Barden 	case SMB_SESSION_STATE_TERMINATED:
13999c856e86SMatt Barden 		/* Disallow new requests in these states. */
14009c856e86SMatt Barden 		if (sr->sr_request_buf)
14019c856e86SMatt Barden 			kmem_free(sr->sr_request_buf, sr->sr_req_length);
14029c856e86SMatt Barden 		sr->session = NULL;
14039c856e86SMatt Barden 		sr->sr_magic = 0;
14049c856e86SMatt Barden 		mutex_destroy(&sr->sr_mutex);
14059c856e86SMatt Barden 		kmem_cache_free(smb_cache_request, sr);
14069c856e86SMatt Barden 		sr = NULL;
14079c856e86SMatt Barden 		break;
14089c856e86SMatt Barden 	}
14099c856e86SMatt Barden 	smb_rwx_rwexit(&session->s_lock);
14109c856e86SMatt Barden 
1411faa1795aSjb150015 	return (sr);
1412faa1795aSjb150015 }
1413faa1795aSjb150015 
1414faa1795aSjb150015 /*
1415faa1795aSjb150015  * smb_request_free
1416faa1795aSjb150015  *
1417faa1795aSjb150015  * release the memories which have been allocated for a smb request.
1418faa1795aSjb150015  */
1419faa1795aSjb150015 void
1420faa1795aSjb150015 smb_request_free(smb_request_t *sr)
1421faa1795aSjb150015 {
1422faa1795aSjb150015 	ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
1423faa1795aSjb150015 	ASSERT(sr->session);
1424faa1795aSjb150015 	ASSERT(sr->r_xa == NULL);
1425faa1795aSjb150015 
1426cb174861Sjoyce mcintosh 	if (sr->fid_ofile != NULL) {
14272c2961f8Sjose borrego 		smb_ofile_release(sr->fid_ofile);
1428cb174861Sjoyce mcintosh 	}
14292c2961f8Sjose borrego 
14302c2961f8Sjose borrego 	if (sr->tid_tree != NULL)
1431faa1795aSjb150015 		smb_tree_release(sr->tid_tree);
1432faa1795aSjb150015 
14332c2961f8Sjose borrego 	if (sr->uid_user != NULL)
1434faa1795aSjb150015 		smb_user_release(sr->uid_user);
1435faa1795aSjb150015 
14361160dcf7SMatt Barden 	if (sr->tform_ssn != NULL)
14371160dcf7SMatt Barden 		smb_user_release(sr->tform_ssn);
14381160dcf7SMatt Barden 
14398f70e16bSGordon Ross 	/*
14408f70e16bSGordon Ross 	 * The above may have left work on the delete queues
14418f70e16bSGordon Ross 	 */
14428f70e16bSGordon Ross 	smb_llist_flush(&sr->session->s_tree_list);
14438f70e16bSGordon Ross 	smb_llist_flush(&sr->session->s_user_list);
14448f70e16bSGordon Ross 
1445faa1795aSjb150015 	smb_slist_remove(&sr->session->s_req_list, sr);
1446faa1795aSjb150015 
1447faa1795aSjb150015 	sr->session = NULL;
1448faa1795aSjb150015 
1449bbf6f00cSJordan Brown 	smb_srm_fini(sr);
1450faa1795aSjb150015 
1451faa1795aSjb150015 	if (sr->sr_request_buf)
1452faa1795aSjb150015 		kmem_free(sr->sr_request_buf, sr->sr_req_length);
1453faa1795aSjb150015 	if (sr->command.chain)
1454faa1795aSjb150015 		m_freem(sr->command.chain);
1455faa1795aSjb150015 	if (sr->reply.chain)
1456faa1795aSjb150015 		m_freem(sr->reply.chain);
1457faa1795aSjb150015 	if (sr->raw_data.chain)
1458faa1795aSjb150015 		m_freem(sr->raw_data.chain);
1459faa1795aSjb150015 
1460faa1795aSjb150015 	sr->sr_magic = 0;
1461faa1795aSjb150015 	mutex_destroy(&sr->sr_mutex);
14628622ec45SGordon Ross 	kmem_cache_free(smb_cache_request, sr);
1463da6c28aaSamw }
14647f667e74Sjose borrego 
14652c2961f8Sjose borrego boolean_t
14662c2961f8Sjose borrego smb_session_oplocks_enable(smb_session_t *session)
14672c2961f8Sjose borrego {
14682c2961f8Sjose borrego 	SMB_SESSION_VALID(session);
14692c2961f8Sjose borrego 	if (session->s_cfg.skc_oplock_enable == 0)
14702c2961f8Sjose borrego 		return (B_FALSE);
14712c2961f8Sjose borrego 	else
14722c2961f8Sjose borrego 		return (B_TRUE);
14732c2961f8Sjose borrego }
14742c2961f8Sjose borrego 
1475cb174861Sjoyce mcintosh boolean_t
1476cb174861Sjoyce mcintosh smb_session_levelII_oplocks(smb_session_t *session)
1477cb174861Sjoyce mcintosh {
1478cb174861Sjoyce mcintosh 	SMB_SESSION_VALID(session);
1479a90cf9f2SGordon Ross 
1480a90cf9f2SGordon Ross 	/* Older clients only do Level II oplocks if negotiated. */
1481a90cf9f2SGordon Ross 	if ((session->capabilities & CAP_LEVEL_II_OPLOCKS) != 0)
1482a90cf9f2SGordon Ross 		return (B_TRUE);
1483a90cf9f2SGordon Ross 
1484a90cf9f2SGordon Ross 	return (B_FALSE);
1485cb174861Sjoyce mcintosh }
1486cb174861Sjoyce mcintosh 
1487f9bc6dadSDmitry.Savitsky@nexenta.com static void
1488f9bc6dadSDmitry.Savitsky@nexenta.com smb_session_genkey(smb_session_t *session)
1489f9bc6dadSDmitry.Savitsky@nexenta.com {
1490f9bc6dadSDmitry.Savitsky@nexenta.com 	uint8_t		tmp_key[SMB_CHALLENGE_SZ];
1491f9bc6dadSDmitry.Savitsky@nexenta.com 
1492f9bc6dadSDmitry.Savitsky@nexenta.com 	(void) random_get_pseudo_bytes(tmp_key, SMB_CHALLENGE_SZ);
1493f9bc6dadSDmitry.Savitsky@nexenta.com 	bcopy(tmp_key, &session->challenge_key, SMB_CHALLENGE_SZ);
1494f9bc6dadSDmitry.Savitsky@nexenta.com 	session->challenge_len = SMB_CHALLENGE_SZ;
1495f9bc6dadSDmitry.Savitsky@nexenta.com 
1496f9bc6dadSDmitry.Savitsky@nexenta.com 	(void) random_get_pseudo_bytes(tmp_key, 4);
1497f9bc6dadSDmitry.Savitsky@nexenta.com 	session->sesskey = tmp_key[0] | tmp_key[1] << 8 |
1498f9bc6dadSDmitry.Savitsky@nexenta.com 	    tmp_key[2] << 16 | tmp_key[3] << 24;
1499f9bc6dadSDmitry.Savitsky@nexenta.com }
1500