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