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/*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * Tree connect and disconnect functions to support SMB shares.
28 * These functions are described in the CIFS draft 1.0 Protocol
29 * Specification (December 19, 1997).
30 */
31
32#include <sys/errno.h>
33#include <string.h>
34#include <strings.h>
35#include <synch.h>
36#include <pthread.h>
37
38#include <smbsrv/libsmbrdr.h>
39#include <smbrdr.h>
40
41
42/*
43 * The table of shares set up with the domain controller.
44 */
45static struct sdb_netuse netuse_table[N_NETUSE_TABLE];
46
47static DWORD smbrdr_tree_connectx(struct sdb_session *session,
48    struct sdb_netuse *netuse, char *path, int path_len);
49
50static struct sdb_netuse *smbrdr_netuse_alloc(struct sdb_session *session,
51    char *sharename);
52
53static void
54smbrdr_netuse_clear(struct sdb_netuse *netuse)
55{
56	bzero(netuse, sizeof (struct sdb_netuse) - sizeof (mutex_t));
57}
58
59static void
60smbrdr_netuse_free(struct sdb_netuse *netuse)
61{
62	smbrdr_netuse_clear(netuse);
63	(void) mutex_unlock(&netuse->mtx);
64}
65
66/*
67 * smbrdr_tree_connect
68 *
69 * Establish a share (tree connect). We need to retrieve the session
70 * for the specified host and allocate a netuse structure. We set up
71 * the path here (UNC encoded) to make handling the malloc/free easier
72 * and pass everything on to smbrdr_tree_connectx where.  If everything
73 * goes well, a valid tid will be stored in the netuse structure.
74 *
75 * On success, a pointer to the netuse is returned. Otherwise the
76 * netuse is cleared and a null pointer is returned.
77 */
78DWORD
79smbrdr_tree_connect(char *hostname, char *domain, char *username,
80    char *sharename, unsigned short *tid)
81{
82	struct sdb_session *session;
83	struct sdb_netuse *netuse;
84	char *path;
85	int path_len;
86	DWORD status;
87
88	*tid = 0;
89
90	if (smbrdr_logon(hostname, domain, username) != 0)
91		return (NT_STATUS_LOGON_FAILURE);
92
93	session = smbrdr_session_lock(hostname, SDB_SLCK_READ);
94	if (session == NULL) {
95		smb_log(smbrdr_log_hdl, LOG_DEBUG,
96		    "smbrdr_tree_connect: no session for %s@%s",
97		    username, hostname);
98		return (NT_STATUS_INTERNAL_ERROR);
99	}
100
101
102	if ((netuse = smbrdr_netuse_alloc(session, sharename)) == 0) {
103		smb_log(smbrdr_log_hdl, LOG_DEBUG,
104		    "smbrdr_tree_connect: init failed");
105		smbrdr_session_unlock(session);
106		return (NT_STATUS_INTERNAL_ERROR);
107	}
108
109	/*
110	 * Add some padding for the back-slash separators
111	 * and the null-terminator.
112	 */
113	path_len = SMB_PI_MAX_HOST + MAX_SHARE_NAME + 5;
114
115	if ((path = (char *)malloc(path_len)) == 0) {
116		smbrdr_netuse_free(netuse);
117		smbrdr_session_unlock(session);
118		smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tree_connect: %s",
119		    strerror(ENOMEM));
120		return (NT_STATUS_NO_MEMORY);
121	}
122
123	bzero(path, path_len);
124	(void) snprintf(path, path_len, "\\\\%s\\%s", hostname, sharename);
125	if (session->remote_caps & CAP_UNICODE)
126		path_len = smb_wcequiv_strlen(path);
127	else
128		path_len = strlen(path);
129
130	if ((status = smbrdr_tree_connectx(session, netuse, path, path_len))
131	    != NT_STATUS_SUCCESS) {
132		smbrdr_netuse_free(netuse);
133		smbrdr_session_unlock(session);
134		smb_log(smbrdr_log_hdl, LOG_DEBUG,
135		    "smbrdr_tree_connect: %s failed", path);
136		free(path);
137		return (status);
138	}
139
140	free(path);
141	*tid = netuse->tid;
142	(void) mutex_unlock(&netuse->mtx);
143	smbrdr_session_unlock(session);
144	return (NT_STATUS_SUCCESS);
145}
146
147
148/*
149 * smbrdr_tree_connectx
150 *
151 * This message requests a share (tree connect) request to the server
152 * associated with the session. The password is not relevant here if
153 * the session was establishment using setup_andx. The outgoing tid
154 * will be ignored - a valid one will be returned by the server.
155 *
156 * Returns 0 on success. Otherwise returns a -ve error code.
157 */
158static DWORD
159smbrdr_tree_connectx(struct sdb_session *session, struct sdb_netuse *netuse,
160    char *path, int path_len)
161{
162	smb_hdr_t smb_hdr;
163	smbrdr_handle_t srh;
164	smb_msgbuf_t *mb;
165	unsigned short flags;
166	char *password;
167	unsigned short password_len;
168	char *service;
169	unsigned service_len;
170	unsigned short data_bytes;
171	DWORD status;
172	int rc;
173
174	status = smbrdr_request_init(&srh, SMB_COM_TREE_CONNECT_ANDX,
175	    session, &session->logon, 0);
176
177	if (status != NT_STATUS_SUCCESS) {
178		smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tree_connectx: %s",
179		    xlate_nt_status(status));
180		return (status);
181	}
182
183	mb = &srh.srh_mbuf;
184
185	flags = 0;			/* no flags */
186	password = "";
187	password_len = 1;		/* including nul */
188	service = "?????";		/* does this work? */
189	service_len = strlen(service);
190
191	/*
192	 * Calculate the BCC. The path is in UNICODE
193	 * but the service is in ASCII.
194	 */
195	data_bytes  = password_len;
196	data_bytes += path_len + 1;
197	data_bytes += service_len + 1;
198
199	rc = smb_msgbuf_encode(mb, "bb1.wwww#cus",
200	    4,				/* smb_wct */
201	    0xff,			/* AndXCommand (none) */
202	    0xffff,			/* AndXOffset */
203	    flags,			/* Flags */
204	    password_len,		/* PasswordLength */
205	    data_bytes+1,		/* smb_bcc */
206	    password_len, password,	/* Password */
207	    path,			/* Path */
208	    service);			/* Service */
209
210	if (rc <= 0) {
211		smb_log(smbrdr_log_hdl, LOG_DEBUG,
212		    "smbrdr_tree_connectx: encode failed");
213		smbrdr_handle_free(&srh);
214		return (NT_STATUS_INTERNAL_ERROR);
215	}
216
217	status = smbrdr_exchange(&srh, &smb_hdr, 0);
218	if (status != NT_STATUS_SUCCESS) {
219		smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tree_connectx: %s",
220		    xlate_nt_status(status));
221		smbrdr_handle_free(&srh);
222		return (status);
223	}
224
225	netuse->tid = smb_hdr.tid;
226	netuse->state = SDB_NSTATE_CONNECTED;
227	smbrdr_handle_free(&srh);
228	return (NT_STATUS_SUCCESS);
229}
230
231/*
232 * smbrdr_netuse_logoff
233 *
234 * This function can be used when closing a session to ensure that all
235 * shares associated with the specified session are disconnected and
236 * the resources released. We also notify the pipe interface to ensure
237 * that any pipes associated with this share are also closed. This
238 * function silently ignores errors because we have no idea what state
239 * the session is in. We are more interested in releasing resources.
240 */
241void
242smbrdr_netuse_logoff(unsigned short uid)
243{
244	struct sdb_netuse *netuse;
245	int i;
246
247	for (i = 0; i < N_NETUSE_TABLE; ++i) {
248		netuse = &netuse_table[i];
249		(void) mutex_lock(&netuse->mtx);
250		if (netuse->uid == uid)
251			(void) smbrdr_tdcon(netuse);
252		(void) mutex_unlock(&netuse->mtx);
253	}
254}
255
256int
257smbrdr_tree_disconnect(unsigned short tid)
258{
259	struct sdb_netuse *netuse;
260	int rc = -1;
261
262	netuse = smbrdr_netuse_get(tid);
263	if (netuse) {
264		(void) smbrdr_tdcon(netuse);
265		smbrdr_netuse_put(netuse);
266		rc = 0;
267	}
268
269	return (rc);
270}
271
272/*
273 * smbrdr_tdcon
274 *
275 * Disconnect a share. This message informs the server that we no longer
276 * wish to access the resource specified by tid, obtained via a prior
277 * smbrdr_tree_connect. The tid is passed in the SMB header so the setup
278 * for this call is very straightforward.
279 *
280 * Returns 0 on success. Otherwise returns a -ve error code.
281 */
282int
283smbrdr_tdcon(struct sdb_netuse *netuse)
284{
285	struct sdb_session *session;
286	smbrdr_handle_t srh;
287	smb_hdr_t smb_hdr;
288	DWORD status;
289	int rc;
290
291	netuse->state = SDB_NSTATE_DISCONNECTING;
292	smbrdr_ofile_end_of_share(netuse->tid);
293
294	if ((session = netuse->session) == NULL) {
295		smbrdr_netuse_clear(netuse);
296		return (0);
297	}
298
299	if ((session->state != SDB_SSTATE_NEGOTIATED) &&
300	    (session->state != SDB_SSTATE_DISCONNECTING)) {
301		smbrdr_netuse_clear(netuse);
302		return (0);
303	}
304
305	status = smbrdr_request_init(&srh, SMB_COM_TREE_DISCONNECT,
306	    session, &session->logon, netuse);
307
308	if (status != NT_STATUS_SUCCESS) {
309		smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tdcon: %s",
310		    xlate_nt_status(status));
311		/* should we clear here? */
312		smbrdr_netuse_clear(netuse);
313		return (-1);
314	}
315
316	rc = smb_msgbuf_encode(&srh.srh_mbuf, "bw.", 0, 0);
317	if (rc < 0) {
318		smb_log(smbrdr_log_hdl, LOG_DEBUG,
319		    "smbrdr_tdcon: encode failed");
320		smbrdr_handle_free(&srh);
321		/* should we clear here? */
322		smbrdr_netuse_clear(netuse);
323		return (rc);
324	}
325
326	status = smbrdr_exchange(&srh, &smb_hdr, 0);
327	if (status != NT_STATUS_SUCCESS) {
328		smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tdcon: %s",
329		    xlate_nt_status(status));
330		rc = -1;
331	} else {
332		rc = 0;
333	}
334
335	smbrdr_handle_free(&srh);
336	smbrdr_netuse_clear(netuse);
337	return (rc);
338}
339
340
341/*
342 * smbrdr_netuse_alloc
343 *
344 * Find a slot in the table for a share. Each share is associated with
345 * a session and assigned a local drive letter name and a sharename.
346 * If a slot is already allocated to the specified share, a pointer to
347 * it is returned. Otherwise we allocate and initialize a new slot in
348 * the table. If the table is full, a null pointer will be returned.
349 *
350 * IMPORTANT! the returned netuse will be locked caller has to unlock
351 *            it after it's done with the pointer.
352 */
353static struct sdb_netuse *
354smbrdr_netuse_alloc(struct sdb_session *session, char *sharename)
355{
356	struct sdb_netuse *netuse;
357	int i;
358
359	if (session == NULL || sharename == NULL)
360		return (NULL);
361
362	for (i = 0; i < N_NETUSE_TABLE; ++i) {
363		netuse = &netuse_table[i];
364
365		(void) mutex_lock(&netuse->mtx);
366		if (netuse->state == SDB_NSTATE_START) {
367			netuse->session = session;
368			netuse->letter = i + '0';
369			netuse->sid = session->sid;
370			netuse->uid = session->logon.uid;
371			netuse->tid = 0;
372			(void) strcpy(netuse->share, sharename);
373			netuse->state = SDB_NSTATE_INIT;
374			return (netuse);
375		}
376		(void) mutex_unlock(&netuse->mtx);
377	}
378
379	smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_netuse_alloc: table full");
380	return (0);
381}
382
383/*
384 * smbrdr_netuse_put
385 *
386 * Unlock given netuse structure.
387 */
388void
389smbrdr_netuse_put(struct sdb_netuse *netuse)
390{
391	(void) mutex_unlock(&netuse->mtx);
392}
393
394/*
395 * smbrdr_netuse_get
396 *
397 * Find the netuse structure associated with the specified tid and
398 * return a pointer to it. A null pointer is returned if no match
399 * can be found.
400 *
401 * IMPORTANT! the returned netuse will be locked caller has to unlock
402 *            it after it's done with the pointer.
403 */
404struct sdb_netuse *
405smbrdr_netuse_get(int tid)
406{
407	struct sdb_session *session;
408	struct sdb_netuse *netuse;
409	int i;
410
411	for (i = 0; i < N_NETUSE_TABLE; ++i) {
412		netuse = &netuse_table[i];
413
414		(void) mutex_lock(&netuse->mtx);
415
416		if (netuse->tid == tid) {
417			session = netuse->session;
418
419			/*
420			 * status check:
421			 * make sure all the structures are in the right state
422			 */
423			if (session &&
424			    (netuse->state == SDB_NSTATE_CONNECTED) &&
425			    (session->logon.state == SDB_LSTATE_SETUP) &&
426			    (session->state == SDB_SSTATE_NEGOTIATED)) {
427				/* sanity check */
428				if ((netuse->sid == session->sid) &&
429				    (netuse->uid == session->logon.uid))
430					return (netuse);
431				else
432					/* invalid structure */
433					smbrdr_netuse_clear(netuse);
434			}
435
436		}
437
438		(void) mutex_unlock(&netuse->mtx);
439	}
440
441	smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_netuse_get: %d: no such TID",
442	    tid);
443	return (0);
444}
445
446/*
447 * smbrdr_dump_netuse
448 */
449void
450smbrdr_dump_netuse()
451{
452	struct sdb_netuse *netuse;
453	int i;
454
455	for (i = 0; i < N_NETUSE_TABLE; ++i) {
456		netuse = &netuse_table[i];
457		(void) mutex_lock(&netuse->mtx);
458		if (netuse->session) {
459			smb_log(smbrdr_log_hdl, LOG_DEBUG,
460			    "smbrdr_dump_netuse: tree[%d]: %s (tid=%d)", i,
461			    netuse->share, netuse->tid);
462			smb_log(smbrdr_log_hdl, LOG_DEBUG,
463			    "smbrdr_dump_netuse: tree[%d]: session(%d), "
464			    "user(%d)", i, netuse->session->sock, netuse->uid);
465		}
466		(void) mutex_unlock(&netuse->mtx);
467	}
468}
469