smb_session.c revision b819cea2f73f98c5662230cc9affc8cc84f77fcf
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
38static 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 */
44uint32_t smb_keep_alive = SMB_PI_KEEP_ALIVE_MIN / 60;
45
46static void smb_session_cancel(smb_session_t *);
47static int smb_session_message(smb_session_t *);
48static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *,
49    uint8_t *, size_t);
50static smb_user_t *smb_session_lookup_user(smb_session_t *, char *, char *);
51static smb_tree_t *smb_session_get_tree(smb_session_t *, smb_tree_t *);
52static void smb_session_logoff(smb_session_t *);
53static void smb_request_init_command_mbuf(smb_request_t *sr);
54void dump_smb_inaddr(smb_inaddr_t *ipaddr);
55static void smb_session_genkey(smb_session_t *);
56
57void
58smb_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
78void
79smb_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 */
121int
122smb_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 */
179static int
180smb_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 */
272int
273smb_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 */
316static int
317smb_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
349static void
350smb_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 */
368void
369smb_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 */
426void
427smb_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 */
472void
473smb_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 */
506static int
507smb_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 */
609smb_session_t *
610smb_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
704void
705smb_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
739static void
740smb_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 */
773void
774smb_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
796void
797smb_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 */
834static smb_user_t *
835smb_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 */
867smb_user_t *
868smb_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 */
887smb_user_t *
888smb_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
918void
919smb_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 */
932smb_tree_t *
933smb_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 */
972smb_tree_t *
973smb_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 */
1013smb_tree_t *
1014smb_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 */
1053void
1054smb_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
1074static void
1075smb_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 */
1088void
1089smb_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 */
1123void
1124smb_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 */
1144void
1145smb_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
1166void
1167smb_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 */
1188static smb_tree_t *
1189smb_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 */
1221static void
1222smb_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 */
1253void
1254smb_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 */
1285boolean_t
1286smb_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 */
1314smb_request_t *
1315smb_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 */
1353void
1354smb_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
1393void
1394dump_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
1404boolean_t
1405smb_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
1414boolean_t
1415smb_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 */
1427void
1428smb_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
1465static void
1466smb_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