xref: /onnv-gate/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c (revision 12508:edb7861a1533)
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  */
45 static struct sdb_netuse netuse_table[N_NETUSE_TABLE];
46 
47 static DWORD smbrdr_tree_connectx(struct sdb_session *session,
48     struct sdb_netuse *netuse, char *path, int path_len);
49 
50 static struct sdb_netuse *smbrdr_netuse_alloc(struct sdb_session *session,
51     char *sharename);
52 
53 static void
smbrdr_netuse_clear(struct sdb_netuse * netuse)54 smbrdr_netuse_clear(struct sdb_netuse *netuse)
55 {
56 	bzero(netuse, sizeof (struct sdb_netuse) - sizeof (mutex_t));
57 }
58 
59 static void
smbrdr_netuse_free(struct sdb_netuse * netuse)60 smbrdr_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  */
78 DWORD
smbrdr_tree_connect(char * hostname,char * domain,char * username,char * sharename,unsigned short * tid)79 smbrdr_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  */
158 static DWORD
smbrdr_tree_connectx(struct sdb_session * session,struct sdb_netuse * netuse,char * path,int path_len)159 smbrdr_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  */
241 void
smbrdr_netuse_logoff(unsigned short uid)242 smbrdr_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 
256 int
smbrdr_tree_disconnect(unsigned short tid)257 smbrdr_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  */
282 int
smbrdr_tdcon(struct sdb_netuse * netuse)283 smbrdr_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  */
353 static struct sdb_netuse *
smbrdr_netuse_alloc(struct sdb_session * session,char * sharename)354 smbrdr_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  */
388 void
smbrdr_netuse_put(struct sdb_netuse * netuse)389 smbrdr_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  */
404 struct sdb_netuse *
smbrdr_netuse_get(int tid)405 smbrdr_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  */
449 void
smbrdr_dump_netuse()450 smbrdr_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