2da6c28aamw * CDDL HEADER START
3da6c28aamw *
4da6c28aamw * The contents of this file are subject to the terms of the
5da6c28aamw * Common Development and Distribution License (the "License").
6da6c28aamw * You may not use this file except in compliance with the License.
7da6c28aamw *
8da6c28aamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aamw * or http://www.opensolaris.org/os/licensing.
10da6c28aamw * See the License for the specific language governing permissions
11da6c28aamw * and limitations under the License.
12da6c28aamw *
13da6c28aamw * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aamw * If applicable, add the following below this CDDL HEADER, with the
16da6c28aamw * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aamw * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aamw *
19da6c28aamw * CDDL HEADER END
20da6c28aamw */
22148c5f4Alan Wright * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
238d94f65Gordon Ross * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
24da6c28aamw */
25b819ceaGordon Ross
26da6c28aamw#include <sys/atomic.h>
27da6c28aamw#include <sys/synch.h>
28da6c28aamw#include <sys/types.h>
29da6c28aamw#include <sys/sdt.h>
30f9bc6daDmitry.Savitsky@nexenta.com#include <sys/random.h>
31da6c28aamw#include <smbsrv/netbios.h>
32a90cf9fGordon Ross#include <smbsrv/smb2_kproto.h>
33bbf6f00Jordan Brown#include <smbsrv/string.h>
34b819ceaGordon Ross#include <netinet/tcp.h>
35b819ceaGordon Ross
36a90cf9fGordon Ross/* How many iovec we'll handle as a local array (no allocation) */
37a90cf9fGordon Ross#define	SMB_LOCAL_IOV_MAX	16
38a90cf9fGordon Ross
39b819ceaGordon Ross#define	SMB_NEW_KID()	atomic_inc_64_nv(&smb_kids)
41faa1795jbstatic volatile uint64_t smb_kids;
43b819ceaGordon Ross/*
44b819ceaGordon Ross * We track the keepalive in minutes, but this constant
45b819ceaGordon Ross * specifies it in seconds, so convert to minutes.
46b819ceaGordon Ross */
47b819ceaGordon Rossuint32_t smb_keep_alive = SMB_PI_KEEP_ALIVE_MIN / 60;
490897f7fGordon Ross/*
50817fa55Gordon Ross * This is the maximum time we'll allow a "session" to exist with no
51817fa55Gordon Ross * authenticated smb_user_t objects on it.  This allows a client to
52817fa55Gordon Ross * logoff their "one and only" user session and then logon as some
53817fa55Gordon Ross * different user.  (There are some tests that do that.)  The same
54817fa55Gordon Ross * timeout mechanism also reduces the impact of clients that might
55817fa55Gordon Ross * open TCP connections but never authenticate.
56817fa55Gordon Ross */
57817fa55Gordon Rossint smb_session_auth_tmo = 30; /* sec. */
58817fa55Gordon Ross
59817fa55Gordon Ross/*
600897f7fGordon Ross * There are many smbtorture test cases that send
610897f7fGordon Ross * racing requests, and where the tests fail if we
620897f7fGordon Ross * don't execute them in exactly the order sent.
630897f7fGordon Ross * These are test bugs.  The protocol makes no
640897f7fGordon Ross * guarantees about execution order of requests
650897f7fGordon Ross * that are concurrently active.
660897f7fGordon Ross *
670897f7fGordon Ross * Nonetheless, smbtorture has many useful tests,
680897f7fGordon Ross * so we have this work-around we can enable to
690897f7fGordon Ross * basically force sequential execution.  When
700897f7fGordon Ross * enabled, insert a delay after each request is
710897f7fGordon Ross * issued a taskq job.  Enable this with mdb by
720897f7fGordon Ross * setting smb_reader_delay to 10.  Don't make it
730897f7fGordon Ross * more than 500 or so or the server will appear
740897f7fGordon Ross * to be so slow that tests may time out.
750897f7fGordon Ross */
760897f7fGordon Rossint smb_reader_delay = 0;  /* mSec. */
770897f7fGordon Ross
78a90cf9fGordon Rossstatic int  smbsr_newrq_initial(smb_request_t *);
79a90cf9fGordon Ross
802c2961fjose borregostatic void smb_session_cancel(smb_session_t *);
81a90cf9fGordon Rossstatic int smb_session_reader(smb_session_t *);
82a90cf9fGordon Rossstatic int smb_session_xprt_puthdr(smb_session_t *,
83a90cf9fGordon Ross    uint8_t msg_type, uint32_t msg_len,
84a90cf9fGordon Ross    uint8_t *dst, size_t dstlen);
85811599aMatt Bardenstatic void smb_session_disconnect_trees(smb_session_t	*);
86faa1795jbstatic void smb_request_init_command_mbuf(smb_request_t *sr);
87f9bc6daDmitry.Savitsky@nexenta.comstatic void smb_session_genkey(smb_session_t *);
89811599aMatt Barden/*
90811599aMatt Barden * This (legacy) code is in support of an "idle timeout" feature,
91811599aMatt Barden * which is apparently incomplete.  To complete it, we should:
92811599aMatt Barden * when the keep_alive timer expires, check whether the client
93811599aMatt Barden * has any open files, and if not then kill their session.
94811599aMatt Barden * Right now the timers are there, but nothing happens when
95811599aMatt Barden * a timer expires.
96811599aMatt Barden *
97811599aMatt Barden * Todo: complete logic to kill idle sessions.
98811599aMatt Barden *
99811599aMatt Barden * Only called when sv_cfg.skc_keepalive != 0
100811599aMatt Barden */
102811599aMatt Bardensmb_session_timers(smb_server_t *sv)
104faa1795jb	smb_session_t	*session;
105811599aMatt Barden	smb_llist_t	*ll;
107811599aMatt Barden	ll = &sv->sv_session_list;
1084163af6jose borrego	smb_llist_enter(ll, RW_READER);
1094163af6jose borrego	session = smb_llist_head(ll);
1104163af6jose borrego	while (session != NULL) {
111da6c28aamw		/*
112da6c28aamw		 * Walk through the table and decrement each keep_alive
113da6c28aamw		 * timer that has not timed out yet. (keepalive > 0)
114da6c28aamw		 */
1154163af6jose borrego		SMB_SESSION_VALID(session);
116faa1795jb		if (session->keep_alive &&
117faa1795jb		    (session->keep_alive != (uint32_t)-1))
118faa1795jb			session->keep_alive--;
120811599aMatt Barden		session = smb_llist_next(ll, session);
121faa1795jb	}
1224163af6jose borrego	smb_llist_exit(ll);
126da6c28aamw * Send a session message - supports SMB-over-NBT and SMB-over-TCP.
127a90cf9fGordon Ross * If an mbuf chain is provided (optional), it will be freed and
128a90cf9fGordon Ross * set to NULL -- unconditionally!  (error or not)
129da6c28aamw *
130a90cf9fGordon Ross * Builds a I/O vector (uio/iov) to do the send from mbufs, plus one
131a90cf9fGordon Ross * segment for the 4-byte NBT header.
132da6c28aamw */
134a90cf9fGordon Rosssmb_session_send(smb_session_t *session, uint8_t nbt_type, mbuf_chain_t *mbc)
136a90cf9fGordon Ross	uio_t		uio;
137a90cf9fGordon Ross	iovec_t		local_iov[SMB_LOCAL_IOV_MAX];
138a90cf9fGordon Ross	iovec_t		*alloc_iov = NULL;
139a90cf9fGordon Ross	int		alloc_sz = 0;
140a90cf9fGordon Ross	mbuf_t		*m;
141a90cf9fGordon Ross	uint8_t		nbt_hdr[NETBIOS_HDR_SZ];
142a90cf9fGordon Ross	uint32_t	nbt_len;
143a90cf9fGordon Ross	int		i, nseg;
1445cdbe94jb	int		rc;
146da6c28aamw	switch (session->s_state) {
149a90cf9fGordon Ross		rc = ENOTCONN;
150a90cf9fGordon Ross		goto out;
151da6c28aamw	default:
152da6c28aamw		break;
153da6c28aamw	}
155a90cf9fGordon Ross	/*
156a90cf9fGordon Ross	 * Setup the IOV.  First, count the number of IOV segments
157a90cf9fGordon Ross	 * (plus one for the NBT header) and decide whether we
158a90cf9fGordon Ross	 * need to allocate an iovec or can use local_iov;
159a90cf9fGordon Ross	 */
160a90cf9fGordon Ross	bzero(&uio, sizeof (uio));
161a90cf9fGordon Ross	nseg = 1;
162a90cf9fGordon Ross	m = (mbc != NULL) ? mbc->chain : NULL;
163a90cf9fGordon Ross	while (m != NULL) {
164a90cf9fGordon Ross		nseg++;
165a90cf9fGordon Ross		m = m->m_next;
166da6c28aamw	}
167a90cf9fGordon Ross	if (nseg <= SMB_LOCAL_IOV_MAX) {
168a90cf9fGordon Ross		uio.uio_iov = local_iov;
169a90cf9fGordon Ross	} else {
170a90cf9fGordon Ross		alloc_sz = nseg * sizeof (iovec_t);
171a90cf9fGordon Ross		alloc_iov = kmem_alloc(alloc_sz, KM_SLEEP);
172a90cf9fGordon Ross		uio.uio_iov = alloc_iov;
173a90cf9fGordon Ross	}
174a90cf9fGordon Ross	uio.uio_iovcnt = nseg;
175a90cf9fGordon Ross	uio.uio_segflg = UIO_SYSSPACE;
176a90cf9fGordon Ross	uio.uio_extflg = UIO_COPY_DEFAULT;
178a90cf9fGordon Ross	/*
179a90cf9fGordon Ross	 * Build the iov list, meanwhile computing the length of
180a90cf9fGordon Ross	 * the SMB payload (to put in the NBT header).
181a90cf9fGordon Ross	 */
182a90cf9fGordon Ross	uio.uio_iov[0].iov_base = (void *)nbt_hdr;
183a90cf9fGordon Ross	uio.uio_iov[0].iov_len = sizeof (nbt_hdr);
184a90cf9fGordon Ross	i = 1;
185a90cf9fGordon Ross	nbt_len = 0;
186a90cf9fGordon Ross	m = (mbc != NULL) ? mbc->chain : NULL;
187a90cf9fGordon Ross	while (m != NULL) {
188a90cf9fGordon Ross		uio.uio_iov[i].iov_base = m->m_data;
189a90cf9fGordon Ross		uio.uio_iov[i++].iov_len = m->m_len;
190a90cf9fGordon Ross		nbt_len += m->m_len;
191a90cf9fGordon Ross		m = m->m_next;
192a90cf9fGordon Ross	}
193a90cf9fGordon Ross	ASSERT3S(i, ==, nseg);
195a90cf9fGordon Ross	/*
196a90cf9fGordon Ross	 * Set the NBT header, set uio_resid
197a90cf9fGordon Ross	 */
198a90cf9fGordon Ross	uio.uio_resid = nbt_len + NETBIOS_HDR_SZ;
199a90cf9fGordon Ross	rc = smb_session_xprt_puthdr(session, nbt_type, nbt_len,
200a90cf9fGordon Ross	    nbt_hdr, NETBIOS_HDR_SZ);
201a90cf9fGordon Ross	if (rc != 0)
202a90cf9fGordon Ross		goto out;
203a90cf9fGordon Ross
204a90cf9fGordon Ross	smb_server_add_txb(session->s_server, (int64_t)uio.uio_resid);
205a90cf9fGordon Ross	rc = smb_net_send_uio(session, &uio);
206a90cf9fGordon Ross
207a90cf9fGordon Rossout:
208a90cf9fGordon Ross	if (alloc_iov != NULL)
209a90cf9fGordon Ross		kmem_free(alloc_iov, alloc_sz);
210a90cf9fGordon Ross	if ((mbc != NULL) && (mbc->chain != NULL)) {
211a90cf9fGordon Ross		m_freem(mbc->chain);
212a90cf9fGordon Ross		mbc->chain = NULL;
213a90cf9fGordon Ross		mbc->flags = 0;
214da6c28aamw	}
215a90cf9fGordon Ross	return (rc);
219da6c28aamw * Read, process and respond to a NetBIOS session request.
220da6c28aamw *
221da6c28aamw * A NetBIOS session must be established for SMB-over-NetBIOS.  Validate
222da6c28aamw * the calling and called name format and save the client NetBIOS name,
223da6c28aamw * which is used when a NetBIOS session is established to check for and
224da6c28aamw * cleanup leftover state from a previous session.
225da6c28aamw *
226da6c28aamw * Session requests are not valid for SMB-over-TCP, which is unfortunate
227da6c28aamw * because without the client name leftover state cannot be cleaned up
228da6c28aamw * if the client is behind a NAT server.
229da6c28aamw */
230da6c28aamwstatic int
231a90cf9fGordon Rosssmb_netbios_session_request(struct smb_session *session)
233da6c28aamw	int			rc;
234da6c28aamw	char			*calling_name;
235da6c28aamw	char			*called_name;
236811599aMatt Barden	char			client_name[NETBIOS_NAME_SZ];
237811599aMatt Barden	struct mbuf_chain	mbc;
238811599aMatt Barden	char			*names = NULL;
239bbf6f00Jordan Brown	smb_wchar_t		*wbuf = NULL;
240da6c28aamw	smb_xprt_t		hdr;
241da6c28aamw	char *p;
242da6c28aamw	int rc1, rc2;
244da6c28aamw	session->keep_alive = smb_keep_alive;
246faa1795jb	if ((rc = smb_session_xprt_gethdr(session, &hdr)) != 0)
247faa1795jb		return (rc);
249da6c28aamw	DTRACE_PROBE2(receive__session__req__xprthdr, struct session *, session,
250da6c28aamw	    smb_xprt_t *, &hdr);
252da6c28aamw	if ((hdr.xh_type != SESSION_REQUEST) ||
253da6c28aamw	    (hdr.xh_length != NETBIOS_SESSION_REQUEST_DATA_LENGTH)) {
254da6c28aamw		DTRACE_PROBE1(receive__session__req__failed,
255da6c28aamw		    struct session *, session);
256da6c28aamw		return (EINVAL);
257da6c28aamw	}
259da6c28aamw	names = kmem_alloc(hdr.xh_length, KM_SLEEP);
261da6c28aamw	if ((rc = smb_sorecv(session->sock, names, hdr.xh_length)) != 0) {
262da6c28aamw		kmem_free(names, hdr.xh_length);
263da6c28aamw		DTRACE_PROBE1(receive__session__req__failed,
264da6c28aamw		    struct session *, session);
265da6c28aamw		return (rc);
266da6c28aamw	}
268da6c28aamw	DTRACE_PROBE3(receive__session__req__data, struct session *, session,
269da6c28aamw	    char *, names, uint32_t, hdr.xh_length);
271da6c28aamw	called_name = &names[0];
272da6c28aamw	calling_name = &names[NETBIOS_ENCODED_NAME_SZ + 2];
274da6c28aamw	rc1 = netbios_name_isvalid(called_name, 0);
275da6c28aamw	rc2 = netbios_name_isvalid(calling_name, client_name);
277da6c28aamw	if (rc1 == 0 || rc2 == 0) {
279da6c28aamw		DTRACE_PROBE3(receive__invalid__session__req,