xref: /titanic_44/usr/src/uts/common/fs/smbsrv/smb_session.c (revision 1d9cde1dcd9c3d71413dae0f9e9b3845a667cd9c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 #include <sys/atomic.h>
27 #include <sys/synch.h>
28 #include <sys/types.h>
29 #include <sys/sdt.h>
30 #include <sys/random.h>
31 #include <smbsrv/netbios.h>
32 #include <smbsrv/smb_kproto.h>
33 #include <smbsrv/string.h>
34 #include <netinet/tcp.h>
35 
36 #define	SMB_NEW_KID()	atomic_inc_64_nv(&smb_kids)
37 
38 static volatile uint64_t smb_kids;
39 
40 /*
41  * We track the keepalive in minutes, but this constant
42  * specifies it in seconds, so convert to minutes.
43  */
44 uint32_t smb_keep_alive = SMB_PI_KEEP_ALIVE_MIN / 60;
45 
46 static void smb_session_cancel(smb_session_t *);
47 static int smb_session_message(smb_session_t *);
48 static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *,
49     uint8_t *, size_t);
50 static smb_user_t *smb_session_lookup_user(smb_session_t *, char *, char *);
51 static smb_tree_t *smb_session_get_tree(smb_session_t *, smb_tree_t *);
52 static void smb_session_logoff(smb_session_t *);
53 static void smb_request_init_command_mbuf(smb_request_t *sr);
54 void dump_smb_inaddr(smb_inaddr_t *ipaddr);
55 static void smb_session_genkey(smb_session_t *);
56 
57 void
58 smb_session_timers(smb_llist_t *ll)
59 {
60 	smb_session_t	*session;
61 
62 	smb_llist_enter(ll, RW_READER);
63 	session = smb_llist_head(ll);
64 	while (session != NULL) {
65 		/*
66 		 * Walk through the table and decrement each keep_alive
67 		 * timer that has not timed out yet. (keepalive > 0)
68 		 */
69 		SMB_SESSION_VALID(session);
70 		if (session->keep_alive &&
71 		    (session->keep_alive != (uint32_t)-1))
72 			session->keep_alive--;
73 		session = smb_llist_next(ll, session);
74 	}
75 	smb_llist_exit(ll);
76 }
77 
78 void
79 smb_session_correct_keep_alive_values(smb_llist_t *ll, uint32_t new_keep_alive)
80 {
81 	smb_session_t		*sn;
82 
83 	/*
84 	 * Caller specifies seconds, but we track in minutes, so
85 	 * convert to minutes (rounded up).
86 	 */
87 	new_keep_alive = (new_keep_alive + 59) / 60;
88 
89 	if (new_keep_alive == smb_keep_alive)
90 		return;
91 	/*
92 	 * keep alive == 0 means do not drop connection if it's idle
93 	 */
94 	smb_keep_alive = (new_keep_alive) ? new_keep_alive : -1;
95 
96 	/*
97 	 * Walk through the table and set each session to the new keep_alive
98 	 * value if they have not already timed out.  Block clock interrupts.
99 	 */
100 	smb_llist_enter(ll, RW_READER);
101 	sn = smb_llist_head(ll);
102 	while (sn != NULL) {
103 		SMB_SESSION_VALID(sn);
104 		if (sn->keep_alive != 0)
105 			sn->keep_alive = new_keep_alive;
106 		sn = smb_llist_next(ll, sn);
107 	}
108 	smb_llist_exit(ll);
109 }
110 
111 /*
112  * Send a session message - supports SMB-over-NBT and SMB-over-TCP.
113  *
114  * The mbuf chain is copied into a contiguous buffer so that the whole
115  * message is submitted to smb_sosend as a single request.  This should
116  * help Ethereal/Wireshark delineate the packets correctly even though
117  * TCP_NODELAY has been set on the socket.
118  *
119  * If an mbuf chain is provided, it will be freed and set to NULL here.
120  */
121 int
122 smb_session_send(smb_session_t *session, uint8_t type, mbuf_chain_t *mbc)
123 {
124 	smb_txreq_t	*txr;
125 	smb_xprt_t	hdr;
126 	int		rc;
127 
128 	switch (session->s_state) {
129 	case SMB_SESSION_STATE_DISCONNECTED:
130 	case SMB_SESSION_STATE_TERMINATED:
131 		if ((mbc != NULL) && (mbc->chain != NULL)) {
132 			m_freem(mbc->chain);
133 			mbc->chain = NULL;
134 			mbc->flags = 0;
135 		}
136 		return (ENOTCONN);
137 	default:
138 		break;
139 	}
140 
141 	txr = smb_net_txr_alloc();
142 
143 	if ((mbc != NULL) && (mbc->chain != NULL)) {
144 		rc = mbc_moveout(mbc, (caddr_t)&txr->tr_buf[NETBIOS_HDR_SZ],
145 		    sizeof (txr->tr_buf) - NETBIOS_HDR_SZ, &txr->tr_len);
146 		if (rc != 0) {
147 			smb_net_txr_free(txr);
148 			return (rc);
149 		}
150 	}
151 
152 	hdr.xh_type = type;
153 	hdr.xh_length = (uint32_t)txr->tr_len;
154 
155 	rc = smb_session_xprt_puthdr(session, &hdr, txr->tr_buf,
156 	    NETBIOS_HDR_SZ);
157 
158 	if (rc != 0) {
159 		smb_net_txr_free(txr);
160 		return (rc);
161 	}
162 	txr->tr_len += NETBIOS_HDR_SZ;
163 	smb_server_add_txb(session->s_server, (int64_t)txr->tr_len);
164 	return (smb_net_txr_send(session->sock, &session->s_txlst, txr));
165 }
166 
167 /*
168  * Read, process and respond to a NetBIOS session request.
169  *
170  * A NetBIOS session must be established for SMB-over-NetBIOS.  Validate
171  * the calling and called name format and save the client NetBIOS name,
172  * which is used when a NetBIOS session is established to check for and
173  * cleanup leftover state from a previous session.
174  *
175  * Session requests are not valid for SMB-over-TCP, which is unfortunate
176  * because without the client name leftover state cannot be cleaned up
177  * if the client is behind a NAT server.
178  */
179 static int
180 smb_session_request(struct smb_session *session)
181 {
182 	int			rc;
183 	char			*calling_name;
184 	char			*called_name;
185 	char 			client_name[NETBIOS_NAME_SZ];
186 	struct mbuf_chain 	mbc;
187 	char 			*names = NULL;
188 	smb_wchar_t		*wbuf = NULL;
189 	smb_xprt_t		hdr;
190 	char *p;
191 	int rc1, rc2;
192 
193 	session->keep_alive = smb_keep_alive;
194 
195 	if ((rc = smb_session_xprt_gethdr(session, &hdr)) != 0)
196 		return (rc);
197 
198 	DTRACE_PROBE2(receive__session__req__xprthdr, struct session *, session,
199 	    smb_xprt_t *, &hdr);
200 
201 	if ((hdr.xh_type != SESSION_REQUEST) ||
202 	    (hdr.xh_length != NETBIOS_SESSION_REQUEST_DATA_LENGTH)) {
203 		DTRACE_PROBE1(receive__session__req__failed,
204 		    struct session *, session);
205 		return (EINVAL);
206 	}
207 
208 	names = kmem_alloc(hdr.xh_length, KM_SLEEP);
209 
210 	if ((rc = smb_sorecv(session->sock, names, hdr.xh_length)) != 0) {
211 		kmem_free(names, hdr.xh_length);
212 		DTRACE_PROBE1(receive__session__req__failed,
213 		    struct session *, session);
214 		return (rc);
215 	}
216 
217 	DTRACE_PROBE3(receive__session__req__data, struct session *, session,
218 	    char *, names, uint32_t, hdr.xh_length);
219 
220 	called_name = &names[0];
221 	calling_name = &names[NETBIOS_ENCODED_NAME_SZ + 2];
222 
223 	rc1 = netbios_name_isvalid(called_name, 0);
224 	rc2 = netbios_name_isvalid(calling_name, client_name);
225 
226 	if (rc1 == 0 || rc2 == 0) {
227 
228 		DTRACE_PROBE3(receive__invalid__session__req,
229 		    struct session *, session, char *, names,
230 		    uint32_t, hdr.xh_length);
231 
232 		kmem_free(names, hdr.xh_length);
233 		MBC_INIT(&mbc, MAX_DATAGRAM_LENGTH);
234 		(void) smb_mbc_encodef(&mbc, "b",
235 		    DATAGRAM_INVALID_SOURCE_NAME_FORMAT);
236 		(void) smb_session_send(session, NEGATIVE_SESSION_RESPONSE,
237 		    &mbc);
238 		return (EINVAL);
239 	}
240 
241 	DTRACE_PROBE3(receive__session__req__calling__decoded,
242 	    struct session *, session,
243 	    char *, calling_name, char *, client_name);
244 
245 	/*
246 	 * The client NetBIOS name is in oem codepage format.
247 	 * We need to convert it to unicode and store it in
248 	 * multi-byte format.  We also need to strip off any
249 	 * spaces added as part of the NetBIOS name encoding.
250 	 */
251 	wbuf = kmem_alloc((SMB_PI_MAX_HOST * sizeof (smb_wchar_t)), KM_SLEEP);
252 	(void) oemtoucs(wbuf, client_name, SMB_PI_MAX_HOST, OEM_CPG_850);
253 	(void) smb_wcstombs(session->workstation, wbuf, SMB_PI_MAX_HOST);
254 	kmem_free(wbuf, (SMB_PI_MAX_HOST * sizeof (smb_wchar_t)));
255 
256 	if ((p = strchr(session->workstation, ' ')) != 0)
257 		*p = '\0';
258 
259 	kmem_free(names, hdr.xh_length);
260 	return (smb_session_send(session, POSITIVE_SESSION_RESPONSE, NULL));
261 }
262 
263 /*
264  * Read 4-byte header from the session socket and build an in-memory
265  * session transport header.  See smb_xprt_t definition for header
266  * format information.
267  *
268  * Direct hosted NetBIOS-less SMB (SMB-over-TCP) uses port 445.  The
269  * first byte of the four-byte header must be 0 and the next three
270  * bytes contain the length of the remaining data.
271  */
272 int
273 smb_session_xprt_gethdr(smb_session_t *session, smb_xprt_t *ret_hdr)
274 {
275 	int		rc;
276 	unsigned char	buf[NETBIOS_HDR_SZ];
277 
278 	if ((rc = smb_sorecv(session->sock, buf, NETBIOS_HDR_SZ)) != 0)
279 		return (rc);
280 
281 	switch (session->s_local_port) {
282 	case IPPORT_NETBIOS_SSN:
283 		ret_hdr->xh_type = buf[0];
284 		ret_hdr->xh_length = (((uint32_t)buf[1] & 1) << 16) |
285 		    ((uint32_t)buf[2] << 8) |
286 		    ((uint32_t)buf[3]);
287 		break;
288 
289 	case IPPORT_SMB:
290 		ret_hdr->xh_type = buf[0];
291 
292 		if (ret_hdr->xh_type != 0) {
293 			cmn_err(CE_WARN, "invalid type (%u)", ret_hdr->xh_type);
294 			dump_smb_inaddr(&session->ipaddr);
295 			return (EPROTO);
296 		}
297 
298 		ret_hdr->xh_length = ((uint32_t)buf[1] << 16) |
299 		    ((uint32_t)buf[2] << 8) |
300 		    ((uint32_t)buf[3]);
301 		break;
302 
303 	default:
304 		cmn_err(CE_WARN, "invalid port %u", session->s_local_port);
305 		dump_smb_inaddr(&session->ipaddr);
306 		return (EPROTO);
307 	}
308 
309 	return (0);
310 }
311 
312 /*
313  * Encode a transport session packet header into a 4-byte buffer.
314  * See smb_xprt_t definition for header format information.
315  */
316 static int
317 smb_session_xprt_puthdr(smb_session_t *session, smb_xprt_t *hdr,
318     uint8_t *buf, size_t buflen)
319 {
320 	if (session == NULL || hdr == NULL ||
321 	    buf == NULL || buflen < NETBIOS_HDR_SZ) {
322 		return (-1);
323 	}
324 
325 	switch (session->s_local_port) {
326 	case IPPORT_NETBIOS_SSN:
327 		buf[0] = hdr->xh_type;
328 		buf[1] = ((hdr->xh_length >> 16) & 1);
329 		buf[2] = (hdr->xh_length >> 8) & 0xff;
330 		buf[3] = hdr->xh_length & 0xff;
331 		break;
332 
333 	case IPPORT_SMB:
334 		buf[0] = hdr->xh_type;
335 		buf[1] = (hdr->xh_length >> 16) & 0xff;
336 		buf[2] = (hdr->xh_length >> 8) & 0xff;
337 		buf[3] = hdr->xh_length & 0xff;
338 		break;
339 
340 	default:
341 		cmn_err(CE_WARN, "invalid port %u", session->s_local_port);
342 		dump_smb_inaddr(&session->ipaddr);
343 		return (-1);
344 	}
345 
346 	return (0);
347 }
348 
349 static void
350 smb_request_init_command_mbuf(smb_request_t *sr)
351 {
352 
353 	/*
354 	 * Setup mbuf using the buffer we allocated.
355 	 */
356 	MBC_ATTACH_BUF(&sr->command, sr->sr_request_buf, sr->sr_req_length);
357 
358 	sr->command.flags = 0;
359 	sr->command.shadow_of = NULL;
360 }
361 
362 /*
363  * smb_request_cancel
364  *
365  * Handle a cancel for a request properly depending on the current request
366  * state.
367  */
368 void
369 smb_request_cancel(smb_request_t *sr)
370 {
371 	mutex_enter(&sr->sr_mutex);
372 	switch (sr->sr_state) {
373 
374 	case SMB_REQ_STATE_INITIALIZING:
375 	case SMB_REQ_STATE_SUBMITTED:
376 	case SMB_REQ_STATE_ACTIVE:
377 	case SMB_REQ_STATE_CLEANED_UP:
378 		sr->sr_state = SMB_REQ_STATE_CANCELED;
379 		break;
380 
381 	case SMB_REQ_STATE_WAITING_LOCK:
382 		/*
383 		 * This request is waiting on a lock.  Wakeup everything
384 		 * waiting on the lock so that the relevant thread regains
385 		 * control and notices that is has been canceled.  The
386 		 * other lock request threads waiting on this lock will go
387 		 * back to sleep when they discover they are still blocked.
388 		 */
389 		sr->sr_state = SMB_REQ_STATE_CANCELED;
390 
391 		ASSERT(sr->sr_awaiting != NULL);
392 		mutex_enter(&sr->sr_awaiting->l_mutex);
393 		cv_broadcast(&sr->sr_awaiting->l_cv);
394 		mutex_exit(&sr->sr_awaiting->l_mutex);
395 		break;
396 
397 	case SMB_REQ_STATE_WAITING_EVENT:
398 		/*
399 		 * This request is waiting in change notify.
400 		 */
401 		sr->sr_state = SMB_REQ_STATE_CANCELED;
402 		cv_signal(&sr->sr_ncr.nc_cv);
403 		break;
404 
405 	case SMB_REQ_STATE_EVENT_OCCURRED:
406 	case SMB_REQ_STATE_COMPLETED:
407 	case SMB_REQ_STATE_CANCELED:
408 		/*
409 		 * No action required for these states since the request
410 		 * is completing.
411 		 */
412 		break;
413 
414 	case SMB_REQ_STATE_FREE:
415 	default:
416 		SMB_PANIC();
417 	}
418 	mutex_exit(&sr->sr_mutex);
419 }
420 
421 /*
422  * smb_session_receiver
423  *
424  * Receives request from the network and dispatches them to a worker.
425  */
426 void
427 smb_session_receiver(smb_session_t *session)
428 {
429 	int	rc = 0;
430 
431 	SMB_SESSION_VALID(session);
432 
433 	session->s_thread = curthread;
434 
435 	if (session->s_local_port == IPPORT_NETBIOS_SSN) {
436 		rc = smb_session_request(session);
437 		if (rc != 0) {
438 			smb_rwx_rwenter(&session->s_lock, RW_WRITER);
439 			session->s_state = SMB_SESSION_STATE_DISCONNECTED;
440 			smb_rwx_rwexit(&session->s_lock);
441 			return;
442 		}
443 	}
444 
445 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
446 	session->s_state = SMB_SESSION_STATE_ESTABLISHED;
447 	smb_rwx_rwexit(&session->s_lock);
448 
449 	(void) smb_session_message(session);
450 
451 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
452 	session->s_state = SMB_SESSION_STATE_DISCONNECTED;
453 	smb_rwx_rwexit(&session->s_lock);
454 
455 	smb_soshutdown(session->sock);
456 
457 	DTRACE_PROBE2(session__drop, struct session *, session, int, rc);
458 
459 	smb_session_cancel(session);
460 	/*
461 	 * At this point everything related to the session should have been
462 	 * cleaned up and we expect that nothing will attempt to use the
463 	 * socket.
464 	 */
465 }
466 
467 /*
468  * smb_session_disconnect
469  *
470  * Disconnects the session passed in.
471  */
472 void
473 smb_session_disconnect(smb_session_t *session)
474 {
475 	SMB_SESSION_VALID(session);
476 
477 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
478 	switch (session->s_state) {
479 	case SMB_SESSION_STATE_INITIALIZED:
480 	case SMB_SESSION_STATE_CONNECTED:
481 	case SMB_SESSION_STATE_ESTABLISHED:
482 	case SMB_SESSION_STATE_NEGOTIATED:
483 	case SMB_SESSION_STATE_OPLOCK_BREAKING:
484 		smb_soshutdown(session->sock);
485 		session->s_state = SMB_SESSION_STATE_DISCONNECTED;
486 		_NOTE(FALLTHRU)
487 	case SMB_SESSION_STATE_DISCONNECTED:
488 	case SMB_SESSION_STATE_TERMINATED:
489 		break;
490 	}
491 	smb_rwx_rwexit(&session->s_lock);
492 }
493 
494 /*
495  * Read and process SMB requests.
496  *
497  * Returns:
498  *	0	Success
499  *	1	Unable to read transport header
500  *	2	Invalid transport header type
501  *	3	Invalid SMB length (too small)
502  *	4	Unable to read SMB header
503  *	5	Invalid SMB header (bad magic number)
504  *	6	Unable to read SMB data
505  */
506 static int
507 smb_session_message(smb_session_t *session)
508 {
509 	smb_server_t	*sv;
510 	smb_request_t	*sr = NULL;
511 	smb_xprt_t	hdr;
512 	uint8_t		*req_buf;
513 	uint32_t	resid;
514 	int		rc;
515 
516 	sv = session->s_server;
517 
518 	for (;;) {
519 
520 		rc = smb_session_xprt_gethdr(session, &hdr);
521 		if (rc)
522 			return (rc);
523 
524 		DTRACE_PROBE2(session__receive__xprthdr, session_t *, session,
525 		    smb_xprt_t *, &hdr);
526 
527 		if (hdr.xh_type != SESSION_MESSAGE) {
528 			/*
529 			 * Anything other than SESSION_MESSAGE or
530 			 * SESSION_KEEP_ALIVE is an error.  A SESSION_REQUEST
531 			 * may indicate a new session request but we need to
532 			 * close this session and we can treat it as an error
533 			 * here.
534 			 */
535 			if (hdr.xh_type == SESSION_KEEP_ALIVE) {
536 				session->keep_alive = smb_keep_alive;
537 				continue;
538 			}
539 			return (EPROTO);
540 		}
541 
542 		if (hdr.xh_length < SMB_HEADER_LEN)
543 			return (EPROTO);
544 
545 		session->keep_alive = smb_keep_alive;
546 		/*
547 		 * Allocate a request context, read the SMB header and validate
548 		 * it. The sr includes a buffer large enough to hold the SMB
549 		 * request payload.  If the header looks valid, read any
550 		 * remaining data.
551 		 */
552 		sr = smb_request_alloc(session, hdr.xh_length);
553 
554 		req_buf = (uint8_t *)sr->sr_request_buf;
555 		resid = hdr.xh_length;
556 
557 		rc = smb_sorecv(session->sock, req_buf, SMB_HEADER_LEN);
558 		if (rc) {
559 			smb_request_free(sr);
560 			return (rc);
561 		}
562 
563 		if (SMB_PROTOCOL_MAGIC_INVALID(sr)) {
564 			smb_request_free(sr);
565 			return (EPROTO);
566 		}
567 
568 		if (resid > SMB_HEADER_LEN) {
569 			req_buf += SMB_HEADER_LEN;
570 			resid -= SMB_HEADER_LEN;
571 
572 			rc = smb_sorecv(session->sock, req_buf, resid);
573 			if (rc) {
574 				smb_request_free(sr);
575 				return (rc);
576 			}
577 		}
578 		smb_server_add_rxb(sv,
579 		    (int64_t)(hdr.xh_length + NETBIOS_HDR_SZ));
580 		/*
581 		 * Initialize command MBC to represent the received data.
582 		 */
583 		smb_request_init_command_mbuf(sr);
584 
585 		DTRACE_PROBE1(session__receive__smb, smb_request_t *, sr);
586 
587 		if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
588 			if (SMB_IS_NT_CANCEL(sr)) {
589 				sr->session->signing.seqnum++;
590 				sr->sr_seqnum = sr->session->signing.seqnum + 1;
591 				sr->reply_seqnum = 0;
592 			} else {
593 				sr->session->signing.seqnum += 2;
594 				sr->sr_seqnum = sr->session->signing.seqnum;
595 				sr->reply_seqnum = sr->sr_seqnum + 1;
596 			}
597 		}
598 		sr->sr_time_submitted = gethrtime();
599 		sr->sr_state = SMB_REQ_STATE_SUBMITTED;
600 		smb_srqueue_waitq_enter(session->s_srqueue);
601 		(void) taskq_dispatch(session->s_server->sv_worker_pool,
602 		    smb_session_worker, sr, TQ_SLEEP);
603 	}
604 }
605 
606 /*
607  * Port will be IPPORT_NETBIOS_SSN or IPPORT_SMB.
608  */
609 smb_session_t *
610 smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
611     int family)
612 {
613 	struct sockaddr_in	sin;
614 	socklen_t		slen;
615 	struct sockaddr_in6	sin6;
616 	smb_session_t		*session;
617 	int64_t			now;
618 
619 	session = kmem_cache_alloc(smb_cache_session, KM_SLEEP);
620 	bzero(session, sizeof (smb_session_t));
621 
622 	if (smb_idpool_constructor(&session->s_uid_pool)) {
623 		kmem_cache_free(smb_cache_session, session);
624 		return (NULL);
625 	}
626 	if (smb_idpool_constructor(&session->s_tid_pool)) {
627 		smb_idpool_destructor(&session->s_uid_pool);
628 		kmem_cache_free(smb_cache_session, session);
629 		return (NULL);
630 	}
631 
632 	now = ddi_get_lbolt64();
633 
634 	session->s_kid = SMB_NEW_KID();
635 	session->s_state = SMB_SESSION_STATE_INITIALIZED;
636 	session->native_os = NATIVE_OS_UNKNOWN;
637 	session->opentime = now;
638 	session->keep_alive = smb_keep_alive;
639 	session->activity_timestamp = now;
640 
641 	smb_session_genkey(session);
642 
643 	smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t),
644 	    offsetof(smb_request_t, sr_session_lnd));
645 
646 	smb_llist_constructor(&session->s_user_list, sizeof (smb_user_t),
647 	    offsetof(smb_user_t, u_lnd));
648 
649 	smb_llist_constructor(&session->s_tree_list, sizeof (smb_tree_t),
650 	    offsetof(smb_tree_t, t_lnd));
651 
652 	smb_llist_constructor(&session->s_xa_list, sizeof (smb_xa_t),
653 	    offsetof(smb_xa_t, xa_lnd));
654 
655 	smb_net_txl_constructor(&session->s_txlst);
656 
657 	smb_rwx_init(&session->s_lock);
658 
659 	if (new_so != NULL) {
660 		if (family == AF_INET) {
661 			slen = sizeof (sin);
662 			(void) ksocket_getsockname(new_so,
663 			    (struct sockaddr *)&sin, &slen, CRED());
664 			bcopy(&sin.sin_addr,
665 			    &session->local_ipaddr.au_addr.au_ipv4,
666 			    sizeof (in_addr_t));
667 			slen = sizeof (sin);
668 			(void) ksocket_getpeername(new_so,
669 			    (struct sockaddr *)&sin, &slen, CRED());
670 			bcopy(&sin.sin_addr,
671 			    &session->ipaddr.au_addr.au_ipv4,
672 			    sizeof (in_addr_t));
673 		} else {
674 			slen = sizeof (sin6);
675 			(void) ksocket_getsockname(new_so,
676 			    (struct sockaddr *)&sin6, &slen, CRED());
677 			bcopy(&sin6.sin6_addr,
678 			    &session->local_ipaddr.au_addr.au_ipv6,
679 			    sizeof (in6_addr_t));
680 			slen = sizeof (sin6);
681 			(void) ksocket_getpeername(new_so,
682 			    (struct sockaddr *)&sin6, &slen, CRED());
683 			bcopy(&sin6.sin6_addr,
684 			    &session->ipaddr.au_addr.au_ipv6,
685 			    sizeof (in6_addr_t));
686 		}
687 		session->ipaddr.a_family = family;
688 		session->local_ipaddr.a_family = family;
689 		session->s_local_port = port;
690 		session->sock = new_so;
691 		if (port == IPPORT_NETBIOS_SSN)
692 			smb_server_inc_nbt_sess(sv);
693 		else
694 			smb_server_inc_tcp_sess(sv);
695 	}
696 	session->s_server = sv;
697 	smb_server_get_cfg(sv, &session->s_cfg);
698 	session->s_srqueue = &sv->sv_srqueue;
699 
700 	session->s_magic = SMB_SESSION_MAGIC;
701 	return (session);
702 }
703 
704 void
705 smb_session_delete(smb_session_t *session)
706 {
707 
708 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
709 
710 	session->s_magic = 0;
711 
712 	if (session->sign_fini != NULL)
713 		session->sign_fini(session);
714 
715 	smb_rwx_destroy(&session->s_lock);
716 	smb_net_txl_destructor(&session->s_txlst);
717 
718 	smb_slist_destructor(&session->s_req_list);
719 	smb_llist_destructor(&session->s_tree_list);
720 	smb_llist_destructor(&session->s_user_list);
721 	smb_llist_destructor(&session->s_xa_list);
722 
723 	ASSERT(session->s_tree_cnt == 0);
724 	ASSERT(session->s_file_cnt == 0);
725 	ASSERT(session->s_dir_cnt == 0);
726 
727 	smb_idpool_destructor(&session->s_tid_pool);
728 	smb_idpool_destructor(&session->s_uid_pool);
729 	if (session->sock != NULL) {
730 		if (session->s_local_port == IPPORT_NETBIOS_SSN)
731 			smb_server_dec_nbt_sess(session->s_server);
732 		else
733 			smb_server_dec_tcp_sess(session->s_server);
734 		smb_sodestroy(session->sock);
735 	}
736 	kmem_cache_free(smb_cache_session, session);
737 }
738 
739 static void
740 smb_session_cancel(smb_session_t *session)
741 {
742 	smb_xa_t	*xa, *nextxa;
743 
744 	/* All the request currently being treated must be canceled. */
745 	smb_session_cancel_requests(session, NULL, NULL);
746 
747 	/*
748 	 * We wait for the completion of all the requests associated with
749 	 * this session.
750 	 */
751 	smb_slist_wait_for_empty(&session->s_req_list);
752 
753 	/*
754 	 * At this point the reference count of the users, trees, files,
755 	 * directories should be zero. It should be possible to destroy them
756 	 * without any problem.
757 	 */
758 	xa = smb_llist_head(&session->s_xa_list);
759 	while (xa) {
760 		nextxa = smb_llist_next(&session->s_xa_list, xa);
761 		smb_xa_close(xa);
762 		xa = nextxa;
763 	}
764 
765 	smb_session_logoff(session);
766 }
767 
768 /*
769  * Cancel requests.  If a non-null tree is specified, only requests specific
770  * to that tree will be cancelled.  If a non-null sr is specified, that sr
771  * will be not be cancelled - this would typically be the caller's sr.
772  */
773 void
774 smb_session_cancel_requests(
775     smb_session_t	*session,
776     smb_tree_t		*tree,
777     smb_request_t	*exclude_sr)
778 {
779 	smb_request_t	*sr;
780 
781 	smb_slist_enter(&session->s_req_list);
782 	sr = smb_slist_head(&session->s_req_list);
783 
784 	while (sr) {
785 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
786 		if ((sr != exclude_sr) &&
787 		    (tree == NULL || sr->tid_tree == tree))
788 			smb_request_cancel(sr);
789 
790 		sr = smb_slist_next(&session->s_req_list, sr);
791 	}
792 
793 	smb_slist_exit(&session->s_req_list);
794 }
795 
796 void
797 smb_session_worker(void	*arg)
798 {
799 	smb_request_t	*sr;
800 	smb_srqueue_t	*srq;
801 
802 	sr = (smb_request_t *)arg;
803 	SMB_REQ_VALID(sr);
804 
805 	srq = sr->session->s_srqueue;
806 	smb_srqueue_waitq_to_runq(srq);
807 	sr->sr_worker = curthread;
808 	mutex_enter(&sr->sr_mutex);
809 	sr->sr_time_active = gethrtime();
810 	switch (sr->sr_state) {
811 	case SMB_REQ_STATE_SUBMITTED:
812 		mutex_exit(&sr->sr_mutex);
813 		if (smb_dispatch_request(sr)) {
814 			mutex_enter(&sr->sr_mutex);
815 			sr->sr_state = SMB_REQ_STATE_COMPLETED;
816 			mutex_exit(&sr->sr_mutex);
817 			smb_request_free(sr);
818 		}
819 		break;
820 
821 	default:
822 		ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED);
823 		sr->sr_state = SMB_REQ_STATE_COMPLETED;
824 		mutex_exit(&sr->sr_mutex);
825 		smb_request_free(sr);
826 		break;
827 	}
828 	smb_srqueue_runq_exit(srq);
829 }
830 
831 /*
832  * smb_session_lookup_user
833  */
834 static smb_user_t *
835 smb_session_lookup_user(smb_session_t *session, char *domain, char *name)
836 {
837 	smb_user_t	*user;
838 	smb_llist_t	*ulist;
839 
840 	ulist = &session->s_user_list;
841 	smb_llist_enter(ulist, RW_READER);
842 	user = smb_llist_head(ulist);
843 	while (user) {
844 		ASSERT(user->u_magic == SMB_USER_MAGIC);
845 		if (!smb_strcasecmp(user->u_name, name, 0) &&
846 		    !smb_strcasecmp(user->u_domain, domain, 0)) {
847 			if (smb_user_hold(user))
848 				break;
849 		}
850 		user = smb_llist_next(ulist, user);
851 	}
852 	smb_llist_exit(ulist);
853 
854 	return (user);
855 }
856 
857 /*
858  * If a user attempts to log in subsequently from the specified session,
859  * duplicates the existing SMB user instance such that all SMB user
860  * instances that corresponds to the same user on the given session
861  * reference the same user's cred.
862  *
863  * Returns NULL if the given user hasn't yet logged in from this
864  * specified session.  Otherwise, returns a user instance that corresponds
865  * to this subsequent login.
866  */
867 smb_user_t *
868 smb_session_dup_user(smb_session_t *session, char *domain, char *account_name)
869 {
870 	smb_user_t *orig_user = NULL;
871 	smb_user_t *user = NULL;
872 
873 	orig_user = smb_session_lookup_user(session, domain,
874 	    account_name);
875 
876 	if (orig_user) {
877 		user = smb_user_dup(orig_user);
878 		smb_user_release(orig_user);
879 	}
880 
881 	return (user);
882 }
883 
884 /*
885  * Find a user on the specified session by SMB UID.
886  */
887 smb_user_t *
888 smb_session_lookup_uid(smb_session_t *session, uint16_t uid)
889 {
890 	smb_user_t	*user;
891 	smb_llist_t	*user_list;
892 
893 	SMB_SESSION_VALID(session);
894 
895 	user_list = &session->s_user_list;
896 	smb_llist_enter(user_list, RW_READER);
897 
898 	user = smb_llist_head(user_list);
899 	while (user) {
900 		SMB_USER_VALID(user);
901 		ASSERT(user->u_session == session);
902 
903 		if (user->u_uid == uid) {
904 			if (!smb_user_hold(user))
905 				break;
906 
907 			smb_llist_exit(user_list);
908 			return (user);
909 		}
910 
911 		user = smb_llist_next(user_list, user);
912 	}
913 
914 	smb_llist_exit(user_list);
915 	return (NULL);
916 }
917 
918 void
919 smb_session_post_user(smb_session_t *session, smb_user_t *user)
920 {
921 	SMB_USER_VALID(user);
922 	ASSERT(user->u_refcnt == 0);
923 	ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF);
924 	ASSERT(user->u_session == session);
925 
926 	smb_llist_post(&session->s_user_list, user, smb_user_delete);
927 }
928 
929 /*
930  * Find a tree by tree-id.
931  */
932 smb_tree_t *
933 smb_session_lookup_tree(
934     smb_session_t	*session,
935     uint16_t		tid)
936 
937 {
938 	smb_tree_t	*tree;
939 
940 	SMB_SESSION_VALID(session);
941 
942 	smb_llist_enter(&session->s_tree_list, RW_READER);
943 	tree = smb_llist_head(&session->s_tree_list);
944 
945 	while (tree) {
946 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
947 		ASSERT(tree->t_session == session);
948 
949 		if (tree->t_tid == tid) {
950 			if (smb_tree_hold(tree)) {
951 				smb_llist_exit(&session->s_tree_list);
952 				return (tree);
953 			} else {
954 				smb_llist_exit(&session->s_tree_list);
955 				return (NULL);
956 			}
957 		}
958 
959 		tree = smb_llist_next(&session->s_tree_list, tree);
960 	}
961 
962 	smb_llist_exit(&session->s_tree_list);
963 	return (NULL);
964 }
965 
966 /*
967  * Find the first connected tree that matches the specified sharename.
968  * If the specified tree is NULL the search starts from the beginning of
969  * the user's tree list.  If a tree is provided the search starts just
970  * after that tree.
971  */
972 smb_tree_t *
973 smb_session_lookup_share(
974     smb_session_t	*session,
975     const char		*sharename,
976     smb_tree_t		*tree)
977 {
978 	SMB_SESSION_VALID(session);
979 	ASSERT(sharename);
980 
981 	smb_llist_enter(&session->s_tree_list, RW_READER);
982 
983 	if (tree) {
984 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
985 		ASSERT(tree->t_session == session);
986 		tree = smb_llist_next(&session->s_tree_list, tree);
987 	} else {
988 		tree = smb_llist_head(&session->s_tree_list);
989 	}
990 
991 	while (tree) {
992 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
993 		ASSERT(tree->t_session == session);
994 		if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) {
995 			if (smb_tree_hold(tree)) {
996 				smb_llist_exit(&session->s_tree_list);
997 				return (tree);
998 			}
999 		}
1000 		tree = smb_llist_next(&session->s_tree_list, tree);
1001 	}
1002 
1003 	smb_llist_exit(&session->s_tree_list);
1004 	return (NULL);
1005 }
1006 
1007 /*
1008  * Find the first connected tree that matches the specified volume name.
1009  * If the specified tree is NULL the search starts from the beginning of
1010  * the user's tree list.  If a tree is provided the search starts just
1011  * after that tree.
1012  */
1013 smb_tree_t *
1014 smb_session_lookup_volume(
1015     smb_session_t	*session,
1016     const char		*name,
1017     smb_tree_t		*tree)
1018 {
1019 	SMB_SESSION_VALID(session);
1020 	ASSERT(name);
1021 
1022 	smb_llist_enter(&session->s_tree_list, RW_READER);
1023 
1024 	if (tree) {
1025 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
1026 		ASSERT(tree->t_session == session);
1027 		tree = smb_llist_next(&session->s_tree_list, tree);
1028 	} else {
1029 		tree = smb_llist_head(&session->s_tree_list);
1030 	}
1031 
1032 	while (tree) {
1033 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
1034 		ASSERT(tree->t_session == session);
1035 
1036 		if (smb_strcasecmp(tree->t_volume, name, 0) == 0) {
1037 			if (smb_tree_hold(tree)) {
1038 				smb_llist_exit(&session->s_tree_list);
1039 				return (tree);
1040 			}
1041 		}
1042 
1043 		tree = smb_llist_next(&session->s_tree_list, tree);
1044 	}
1045 
1046 	smb_llist_exit(&session->s_tree_list);
1047 	return (NULL);
1048 }
1049 
1050 /*
1051  * Disconnect all trees that match the specified client process-id.
1052  */
1053 void
1054 smb_session_close_pid(
1055     smb_session_t	*session,
1056     uint16_t		pid)
1057 {
1058 	smb_tree_t	*tree;
1059 
1060 	SMB_SESSION_VALID(session);
1061 
1062 	tree = smb_session_get_tree(session, NULL);
1063 	while (tree) {
1064 		smb_tree_t *next;
1065 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
1066 		ASSERT(tree->t_session == session);
1067 		smb_tree_close_pid(tree, pid);
1068 		next = smb_session_get_tree(session, tree);
1069 		smb_tree_release(tree);
1070 		tree = next;
1071 	}
1072 }
1073 
1074 static void
1075 smb_session_tree_dtor(void *t)
1076 {
1077 	smb_tree_t	*tree = (smb_tree_t *)t;
1078 
1079 	smb_tree_disconnect(tree, B_TRUE);
1080 	/* release the ref acquired during the traversal loop */
1081 	smb_tree_release(tree);
1082 }
1083 
1084 
1085 /*
1086  * Disconnect all trees that this user has connected.
1087  */
1088 void
1089 smb_session_disconnect_owned_trees(
1090     smb_session_t	*session,
1091     smb_user_t		*owner)
1092 {
1093 	smb_tree_t	*tree;
1094 	smb_llist_t	*tree_list = &session->s_tree_list;
1095 
1096 	SMB_SESSION_VALID(session);
1097 	SMB_USER_VALID(owner);
1098 
1099 	smb_llist_enter(tree_list, RW_READER);
1100 
1101 	tree = smb_llist_head(tree_list);
1102 	while (tree) {
1103 		if ((tree->t_owner == owner) &&
1104 		    smb_tree_hold(tree)) {
1105 			/*
1106 			 * smb_tree_hold() succeeded, hence we are in state
1107 			 * SMB_TREE_STATE_CONNECTED; schedule this tree
1108 			 * for asynchronous disconnect, which will fire
1109 			 * after we drop the llist traversal lock.
1110 			 */
1111 			smb_llist_post(tree_list, tree, smb_session_tree_dtor);
1112 		}
1113 		tree = smb_llist_next(tree_list, tree);
1114 	}
1115 
1116 	/* drop the lock and flush the dtor queue */
1117 	smb_llist_exit(tree_list);
1118 }
1119 
1120 /*
1121  * Disconnect all trees that this user has connected.
1122  */
1123 void
1124 smb_session_disconnect_trees(
1125     smb_session_t	*session)
1126 {
1127 	smb_tree_t	*tree;
1128 
1129 	SMB_SESSION_VALID(session);
1130 
1131 	tree = smb_session_get_tree(session, NULL);
1132 	while (tree) {
1133 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
1134 		ASSERT(tree->t_session == session);
1135 		smb_tree_disconnect(tree, B_TRUE);
1136 		smb_tree_release(tree);
1137 		tree = smb_session_get_tree(session, NULL);
1138 	}
1139 }
1140 
1141 /*
1142  * Disconnect all trees that match the specified share name.
1143  */
1144 void
1145 smb_session_disconnect_share(
1146     smb_session_t	*session,
1147     const char		*sharename)
1148 {
1149 	smb_tree_t	*tree;
1150 	smb_tree_t	*next;
1151 
1152 	SMB_SESSION_VALID(session);
1153 
1154 	tree = smb_session_lookup_share(session, sharename, NULL);
1155 	while (tree) {
1156 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
1157 		ASSERT(tree->t_session == session);
1158 		smb_session_cancel_requests(session, tree, NULL);
1159 		smb_tree_disconnect(tree, B_TRUE);
1160 		next = smb_session_lookup_share(session, sharename, tree);
1161 		smb_tree_release(tree);
1162 		tree = next;
1163 	}
1164 }
1165 
1166 void
1167 smb_session_post_tree(smb_session_t *session, smb_tree_t *tree)
1168 {
1169 	SMB_SESSION_VALID(session);
1170 	SMB_TREE_VALID(tree);
1171 	ASSERT0(tree->t_refcnt);
1172 	ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
1173 	ASSERT(tree->t_session == session);
1174 
1175 	smb_llist_post(&session->s_tree_list, tree, smb_tree_dealloc);
1176 }
1177 
1178 /*
1179  * Get the next connected tree in the list.  A reference is taken on
1180  * the tree, which can be released later with smb_tree_release().
1181  *
1182  * If the specified tree is NULL the search starts from the beginning of
1183  * the tree list.  If a tree is provided the search starts just after
1184  * that tree.
1185  *
1186  * Returns NULL if there are no connected trees in the list.
1187  */
1188 static smb_tree_t *
1189 smb_session_get_tree(
1190     smb_session_t	*session,
1191     smb_tree_t		*tree)
1192 {
1193 	smb_llist_t	*tree_list;
1194 
1195 	SMB_SESSION_VALID(session);
1196 	tree_list = &session->s_tree_list;
1197 
1198 	smb_llist_enter(tree_list, RW_READER);
1199 
1200 	if (tree) {
1201 		ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
1202 		tree = smb_llist_next(tree_list, tree);
1203 	} else {
1204 		tree = smb_llist_head(tree_list);
1205 	}
1206 
1207 	while (tree) {
1208 		if (smb_tree_hold(tree))
1209 			break;
1210 
1211 		tree = smb_llist_next(tree_list, tree);
1212 	}
1213 
1214 	smb_llist_exit(tree_list);
1215 	return (tree);
1216 }
1217 
1218 /*
1219  * Logoff all users associated with the specified session.
1220  */
1221 static void
1222 smb_session_logoff(smb_session_t *session)
1223 {
1224 	smb_user_t	*user;
1225 
1226 	SMB_SESSION_VALID(session);
1227 
1228 	smb_session_disconnect_trees(session);
1229 
1230 	smb_llist_enter(&session->s_user_list, RW_READER);
1231 
1232 	user = smb_llist_head(&session->s_user_list);
1233 	while (user) {
1234 		SMB_USER_VALID(user);
1235 		ASSERT(user->u_session == session);
1236 
1237 		if (smb_user_hold(user)) {
1238 			smb_user_logoff(user);
1239 			smb_user_release(user);
1240 		}
1241 
1242 		user = smb_llist_next(&session->s_user_list, user);
1243 	}
1244 
1245 	smb_llist_exit(&session->s_user_list);
1246 }
1247 
1248 /*
1249  * Copy the session workstation/client name to buf.  If the workstation
1250  * is an empty string (which it will be on TCP connections), use the
1251  * client IP address.
1252  */
1253 void
1254 smb_session_getclient(smb_session_t *sn, char *buf, size_t buflen)
1255 {
1256 	char		ipbuf[INET6_ADDRSTRLEN];
1257 	smb_inaddr_t	*ipaddr;
1258 
1259 	ASSERT(sn);
1260 	ASSERT(buf);
1261 	ASSERT(buflen);
1262 
1263 	*buf = '\0';
1264 
1265 	if (sn->workstation[0] != '\0') {
1266 		(void) strlcpy(buf, sn->workstation, buflen);
1267 		return;
1268 	}
1269 
1270 	ipaddr = &sn->ipaddr;
1271 	if (smb_inet_ntop(ipaddr, ipbuf, SMB_IPSTRLEN(ipaddr->a_family)))
1272 		(void) strlcpy(buf, ipbuf, buflen);
1273 }
1274 
1275 /*
1276  * Check whether or not the specified client name is the client of this
1277  * session.  The name may be in UNC format (\\CLIENT).
1278  *
1279  * A workstation/client name is setup on NBT connections as part of the
1280  * NetBIOS session request but that isn't available on TCP connections.
1281  * If the session doesn't have a client name we typically return the
1282  * client IP address as the workstation name on MSRPC requests.  So we
1283  * check for the IP address here in addition to the workstation name.
1284  */
1285 boolean_t
1286 smb_session_isclient(smb_session_t *sn, const char *client)
1287 {
1288 	char		buf[INET6_ADDRSTRLEN];
1289 	smb_inaddr_t	*ipaddr;
1290 
1291 	client += strspn(client, "\\");
1292 
1293 	if (smb_strcasecmp(client, sn->workstation, 0) == 0)
1294 		return (B_TRUE);
1295 
1296 	ipaddr = &sn->ipaddr;
1297 	if (smb_inet_ntop(ipaddr, buf, SMB_IPSTRLEN(ipaddr->a_family)) == NULL)
1298 		return (B_FALSE);
1299 
1300 	if (smb_strcasecmp(client, buf, 0) == 0)
1301 		return (B_TRUE);
1302 
1303 	return (B_FALSE);
1304 }
1305 
1306 /*
1307  * smb_request_alloc
1308  *
1309  * Allocate an smb_request_t structure from the kmem_cache.  Partially
1310  * initialize the found/new request.
1311  *
1312  * Returns pointer to a request
1313  */
1314 smb_request_t *
1315 smb_request_alloc(smb_session_t *session, int req_length)
1316 {
1317 	smb_request_t	*sr;
1318 
1319 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1320 
1321 	sr = kmem_cache_alloc(smb_cache_request, KM_SLEEP);
1322 
1323 	/*
1324 	 * Future:  Use constructor to pre-initialize some fields.  For now
1325 	 * there are so many fields that it is easiest just to zero the
1326 	 * whole thing and start over.
1327 	 */
1328 	bzero(sr, sizeof (smb_request_t));
1329 
1330 	mutex_init(&sr->sr_mutex, NULL, MUTEX_DEFAULT, NULL);
1331 	cv_init(&sr->sr_ncr.nc_cv, NULL, CV_DEFAULT, NULL);
1332 	smb_srm_init(sr);
1333 	sr->session = session;
1334 	sr->sr_server = session->s_server;
1335 	sr->sr_gmtoff = session->s_server->si_gmtoff;
1336 	sr->sr_cfg = &session->s_cfg;
1337 	sr->command.max_bytes = req_length;
1338 	sr->reply.max_bytes = smb_maxbufsize;
1339 	sr->sr_req_length = req_length;
1340 	if (req_length)
1341 		sr->sr_request_buf = kmem_alloc(req_length, KM_SLEEP);
1342 	sr->sr_magic = SMB_REQ_MAGIC;
1343 	sr->sr_state = SMB_REQ_STATE_INITIALIZING;
1344 	smb_slist_insert_tail(&session->s_req_list, sr);
1345 	return (sr);
1346 }
1347 
1348 /*
1349  * smb_request_free
1350  *
1351  * release the memories which have been allocated for a smb request.
1352  */
1353 void
1354 smb_request_free(smb_request_t *sr)
1355 {
1356 	ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
1357 	ASSERT(sr->session);
1358 	ASSERT(sr->r_xa == NULL);
1359 	ASSERT(sr->sr_ncr.nc_fname == NULL);
1360 
1361 	if (sr->fid_ofile != NULL) {
1362 		smb_ofile_request_complete(sr->fid_ofile);
1363 		smb_ofile_release(sr->fid_ofile);
1364 	}
1365 
1366 	if (sr->tid_tree != NULL)
1367 		smb_tree_release(sr->tid_tree);
1368 
1369 	if (sr->uid_user != NULL)
1370 		smb_user_release(sr->uid_user);
1371 
1372 	smb_slist_remove(&sr->session->s_req_list, sr);
1373 
1374 	sr->session = NULL;
1375 
1376 	smb_srm_fini(sr);
1377 
1378 	if (sr->sr_request_buf)
1379 		kmem_free(sr->sr_request_buf, sr->sr_req_length);
1380 	if (sr->command.chain)
1381 		m_freem(sr->command.chain);
1382 	if (sr->reply.chain)
1383 		m_freem(sr->reply.chain);
1384 	if (sr->raw_data.chain)
1385 		m_freem(sr->raw_data.chain);
1386 
1387 	sr->sr_magic = 0;
1388 	cv_destroy(&sr->sr_ncr.nc_cv);
1389 	mutex_destroy(&sr->sr_mutex);
1390 	kmem_cache_free(smb_cache_request, sr);
1391 }
1392 
1393 void
1394 dump_smb_inaddr(smb_inaddr_t *ipaddr)
1395 {
1396 	char ipstr[INET6_ADDRSTRLEN];
1397 
1398 	if (smb_inet_ntop(ipaddr, ipstr, SMB_IPSTRLEN(ipaddr->a_family)))
1399 		cmn_err(CE_WARN, "error ipstr=%s", ipstr);
1400 	else
1401 		cmn_err(CE_WARN, "error converting ip address");
1402 }
1403 
1404 boolean_t
1405 smb_session_oplocks_enable(smb_session_t *session)
1406 {
1407 	SMB_SESSION_VALID(session);
1408 	if (session->s_cfg.skc_oplock_enable == 0)
1409 		return (B_FALSE);
1410 	else
1411 		return (B_TRUE);
1412 }
1413 
1414 boolean_t
1415 smb_session_levelII_oplocks(smb_session_t *session)
1416 {
1417 	SMB_SESSION_VALID(session);
1418 	return (session->capabilities & CAP_LEVEL_II_OPLOCKS);
1419 }
1420 
1421 /*
1422  * smb_session_oplock_break
1423  *
1424  * The session lock must NOT be held by the caller of this thread;
1425  * as this would cause a deadlock.
1426  */
1427 void
1428 smb_session_oplock_break(smb_session_t *session,
1429     uint16_t tid, uint16_t fid, uint8_t brk)
1430 {
1431 	mbuf_chain_t	*mbc;
1432 
1433 	SMB_SESSION_VALID(session);
1434 
1435 	mbc = smb_mbc_alloc(MLEN);
1436 
1437 	(void) smb_mbc_encodef(mbc, "Mb19.wwwwbb3.wbb10.",
1438 	    SMB_COM_LOCKING_ANDX,
1439 	    tid,
1440 	    0xFFFF, 0, 0xFFFF, 8, 0xFF,
1441 	    fid,
1442 	    LOCKING_ANDX_OPLOCK_RELEASE,
1443 	    (brk == SMB_OPLOCK_BREAK_TO_LEVEL_II) ? 1 : 0);
1444 
1445 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
1446 	switch (session->s_state) {
1447 	case SMB_SESSION_STATE_NEGOTIATED:
1448 	case SMB_SESSION_STATE_OPLOCK_BREAKING:
1449 		session->s_state = SMB_SESSION_STATE_OPLOCK_BREAKING;
1450 		(void) smb_session_send(session, 0, mbc);
1451 		smb_mbc_free(mbc);
1452 		break;
1453 
1454 	case SMB_SESSION_STATE_DISCONNECTED:
1455 	case SMB_SESSION_STATE_TERMINATED:
1456 		smb_mbc_free(mbc);
1457 		break;
1458 
1459 	default:
1460 		SMB_PANIC();
1461 	}
1462 	smb_rwx_rwexit(&session->s_lock);
1463 }
1464 
1465 static void
1466 smb_session_genkey(smb_session_t *session)
1467 {
1468 	uint8_t		tmp_key[SMB_CHALLENGE_SZ];
1469 
1470 	(void) random_get_pseudo_bytes(tmp_key, SMB_CHALLENGE_SZ);
1471 	bcopy(tmp_key, &session->challenge_key, SMB_CHALLENGE_SZ);
1472 	session->challenge_len = SMB_CHALLENGE_SZ;
1473 
1474 	(void) random_get_pseudo_bytes(tmp_key, 4);
1475 	session->sesskey = tmp_key[0] | tmp_key[1] << 8 |
1476 	    tmp_key[2] << 16 | tmp_key[3] << 24;
1477 }
1478