xref: /titanic_50/usr/src/uts/common/fs/smbsrv/smb_tree.c (revision a90cf9f29973990687fa61de9f1f6ea22e924e40)
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  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 /*
28  * General Structures Layout
29  * -------------------------
30  *
31  * This is a simplified diagram showing the relationship between most of the
32  * main structures.
33  *
34  * +-------------------+
35  * |     SMB_INFO      |
36  * +-------------------+
37  *          |
38  *          |
39  *          v
40  * +-------------------+       +-------------------+      +-------------------+
41  * |     SESSION       |<----->|     SESSION       |......|      SESSION      |
42  * +-------------------+       +-------------------+      +-------------------+
43  *   |          |
44  *   |          |
45  *   |          v
46  *   |  +-------------------+     +-------------------+   +-------------------+
47  *   |  |       USER        |<--->|       USER        |...|       USER        |
48  *   |  +-------------------+     +-------------------+   +-------------------+
49  *   |
50  *   |
51  *   v
52  * +-------------------+       +-------------------+      +-------------------+
53  * |       TREE        |<----->|       TREE        |......|       TREE        |
54  * +-------------------+       +-------------------+      +-------------------+
55  *      |         |
56  *      |         |
57  *      |         v
58  *      |     +-------+       +-------+      +-------+
59  *      |     | OFILE |<----->| OFILE |......| OFILE |
60  *      |     +-------+       +-------+      +-------+
61  *      |
62  *      |
63  *      v
64  *  +-------+       +------+      +------+
65  *  | ODIR  |<----->| ODIR |......| ODIR |
66  *  +-------+       +------+      +------+
67  *
68  *
69  * Tree State Machine
70  * ------------------
71  *
72  *    +-----------------------------+	 T0
73  *    |  SMB_TREE_STATE_CONNECTED   |<----------- Creation/Allocation
74  *    +-----------------------------+
75  *		    |
76  *		    | T1
77  *		    |
78  *		    v
79  *    +------------------------------+
80  *    | SMB_TREE_STATE_DISCONNECTING |
81  *    +------------------------------+
82  *		    |
83  *		    | T2
84  *		    |
85  *		    v
86  *    +-----------------------------+    T3
87  *    | SMB_TREE_STATE_DISCONNECTED |----------> Deletion/Free
88  *    +-----------------------------+
89  *
90  * SMB_TREE_STATE_CONNECTED
91  *
92  *    While in this state:
93  *      - The tree is queued in the list of trees of its user.
94  *      - References will be given out if the tree is looked up.
95  *      - Files under that tree can be accessed.
96  *
97  * SMB_TREE_STATE_DISCONNECTING
98  *
99  *    While in this state:
100  *      - The tree is queued in the list of trees of its user.
101  *      - References will not be given out if the tree is looked up.
102  *      - The files and directories open under the tree are being closed.
103  *      - The resources associated with the tree remain.
104  *
105  * SMB_TREE_STATE_DISCONNECTED
106  *
107  *    While in this state:
108  *      - The tree is queued in the list of trees of its user.
109  *      - References will not be given out if the tree is looked up.
110  *      - The tree has no more files and directories opened.
111  *      - The resources associated with the tree remain.
112  *
113  * Transition T0
114  *
115  *    This transition occurs in smb_tree_connect(). A new tree is created and
116  *    added to the list of trees of a user.
117  *
118  * Transition T1
119  *
120  *    This transition occurs in smb_tree_disconnect().
121  *
122  * Transition T2
123  *
124  *    This transition occurs in smb_tree_release(). The resources associated
125  *    with the tree are freed as well as the tree structure. For the transition
126  *    to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and
127  *    the reference count be zero.
128  *
129  * Comments
130  * --------
131  *
132  *    The state machine of the tree structures is controlled by 3 elements:
133  *      - The list of trees of the user it belongs to.
134  *      - The mutex embedded in the structure itself.
135  *      - The reference count.
136  *
137  *    There's a mutex embedded in the tree structure used to protect its fields
138  *    and there's a lock embedded in the list of trees of a user. To
139  *    increment or to decrement the reference count the mutex must be entered.
140  *    To insert the tree into the list of trees of the user and to remove
141  *    the tree from it, the lock must be entered in RW_WRITER mode.
142  *
143  *    Rules of access to a tree structure:
144  *
145  *    1) In order to avoid deadlocks, when both (mutex and lock of the user
146  *       list) have to be entered, the lock must be entered first.
147  *
148  *    2) All actions applied to a tree require a reference count.
149  *
150  *    3) There are 2 ways of getting a reference count: when a tree is
151  *       connected and when a tree is looked up.
152  *
153  *    It should be noted that the reference count of a tree registers the
154  *    number of references to the tree in other structures (such as an smb
155  *    request). The reference count is not incremented in these 2 instances:
156  *
157  *    1) The tree is connected. An tree is anchored by his state. If there's
158  *       no activity involving a tree currently connected, the reference
159  *       count of that tree is zero.
160  *
161  *    2) The tree is queued in the list of trees of the user. The fact of
162  *       being queued in that list is NOT registered by incrementing the
163  *       reference count.
164  */
165 
166 #include <sys/refstr_impl.h>
167 #include <smbsrv/smb_kproto.h>
168 #include <smbsrv/smb_ktypes.h>
169 #include <smbsrv/smb_fsops.h>
170 #include <smbsrv/smb_share.h>
171 
172 int smb_tcon_mute = 0;
173 
174 uint32_t	smb_tree_connect_core(smb_request_t *);
175 uint32_t	smb_tree_connect_disk(smb_request_t *, smb_arg_tcon_t *);
176 uint32_t	smb_tree_connect_printq(smb_request_t *, smb_arg_tcon_t *);
177 uint32_t	smb_tree_connect_ipc(smb_request_t *, smb_arg_tcon_t *);
178 static smb_tree_t *smb_tree_alloc(smb_request_t *, const smb_kshare_t *,
179     smb_node_t *, uint32_t, uint32_t);
180 static boolean_t smb_tree_is_connected_locked(smb_tree_t *);
181 static boolean_t smb_tree_is_disconnected(smb_tree_t *);
182 static char *smb_tree_get_sharename(char *);
183 static int smb_tree_getattr(const smb_kshare_t *, smb_node_t *, smb_tree_t *);
184 static void smb_tree_get_volname(vfs_t *, smb_tree_t *);
185 static void smb_tree_get_flags(const smb_kshare_t *, vfs_t *, smb_tree_t *);
186 static void smb_tree_log(smb_request_t *, const char *, const char *, ...);
187 static void smb_tree_close_odirs(smb_tree_t *, uint16_t);
188 static smb_ofile_t *smb_tree_get_ofile(smb_tree_t *, smb_ofile_t *);
189 static smb_odir_t *smb_tree_get_odir(smb_tree_t *, smb_odir_t *);
190 static void smb_tree_set_execinfo(smb_tree_t *, smb_shr_execinfo_t *, int);
191 static int smb_tree_enum_private(smb_tree_t *, smb_svcenum_t *);
192 static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *);
193 static void smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *);
194 static void smb_tree_netinfo_fini(smb_netconnectinfo_t *);
195 
196 uint32_t
smb_tree_connect(smb_request_t * sr)197 smb_tree_connect(smb_request_t *sr)
198 {
199 	smb_server_t	*sv = sr->sr_server;
200 	uint32_t status;
201 
202 	if (smb_threshold_enter(&sv->sv_tcon_ct) != 0) {
203 		return (NT_STATUS_INSUFF_SERVER_RESOURCES);
204 	}
205 
206 	status = smb_tree_connect_core(sr);
207 	smb_threshold_exit(&sv->sv_tcon_ct);
208 	return (status);
209 }
210 
211 /*
212  * Lookup the share name dispatch the appropriate stype handler.
213  * Share names are case insensitive so we map the share name to
214  * lower-case as a convenience for internal processing.
215  *
216  * Valid service values are:
217  *	A:      Disk share
218  *	LPT1:   Printer
219  *	IPC     Named pipe (IPC$ is reserved as the named pipe share).
220  *	COMM    Communications device
221  *	?????   Any type of device (wildcard)
222  */
223 uint32_t
smb_tree_connect_core(smb_request_t * sr)224 smb_tree_connect_core(smb_request_t *sr)
225 {
226 	smb_arg_tcon_t	*tcon = &sr->sr_tcon;
227 	smb_kshare_t	*si;
228 	char		*name;
229 	uint32_t	status;
230 
231 	(void) smb_strlwr(tcon->path);
232 
233 	if ((name = smb_tree_get_sharename(tcon->path)) == NULL) {
234 		smb_tree_log(sr, tcon->path, "invalid UNC path");
235 		return (NT_STATUS_BAD_NETWORK_NAME);
236 	}
237 
238 	si = smb_kshare_lookup(sr->sr_server, name);
239 	if (si == NULL) {
240 		smb_tree_log(sr, name, "share not found");
241 		return (NT_STATUS_BAD_NETWORK_NAME);
242 	}
243 
244 	if (!strcasecmp(SMB_SHARE_PRINT, name)) {
245 		smb_kshare_release(sr->sr_server, si);
246 		smb_tree_log(sr, name, "access not permitted");
247 		return (NT_STATUS_ACCESS_DENIED);
248 	}
249 
250 	/* NB: name points into tcon->path - don't free it. */
251 	tcon->name = name;
252 	sr->sr_tcon.si = si;
253 
254 	switch (si->shr_type & STYPE_MASK) {
255 	case STYPE_DISKTREE:
256 		status = smb_tree_connect_disk(sr, &sr->sr_tcon);
257 		break;
258 	case STYPE_IPC:
259 		status = smb_tree_connect_ipc(sr, &sr->sr_tcon);
260 		break;
261 	case STYPE_PRINTQ:
262 		status = smb_tree_connect_printq(sr, &sr->sr_tcon);
263 		break;
264 	default:
265 		status = NT_STATUS_BAD_DEVICE_TYPE;
266 		break;
267 	}
268 
269 	smb_kshare_release(sr->sr_server, si);
270 	sr->sr_tcon.si = NULL;
271 
272 	return (status);
273 }
274 
275 /*
276  * Disconnect a tree.
277  */
278 void
smb_tree_disconnect(smb_tree_t * tree,boolean_t do_exec)279 smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec)
280 {
281 	smb_shr_execinfo_t execinfo;
282 
283 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
284 
285 	mutex_enter(&tree->t_mutex);
286 	ASSERT(tree->t_refcnt);
287 
288 	if (smb_tree_is_connected_locked(tree)) {
289 		/*
290 		 * Indicate that the disconnect process has started.
291 		 */
292 		tree->t_state = SMB_TREE_STATE_DISCONNECTING;
293 		mutex_exit(&tree->t_mutex);
294 
295 		if (do_exec) {
296 			/*
297 			 * The files opened under this tree are closed.
298 			 */
299 			smb_ofile_close_all(tree);
300 			/*
301 			 * The directories opened under this tree are closed.
302 			 */
303 			smb_tree_close_odirs(tree, 0);
304 		}
305 
306 		mutex_enter(&tree->t_mutex);
307 		tree->t_state = SMB_TREE_STATE_DISCONNECTED;
308 		smb_server_dec_trees(tree->t_server);
309 	}
310 
311 	mutex_exit(&tree->t_mutex);
312 
313 	if (do_exec && (tree->t_state == SMB_TREE_STATE_DISCONNECTED) &&
314 	    (tree->t_execflags & SMB_EXEC_UNMAP)) {
315 
316 		smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_UNMAP);
317 		(void) smb_kshare_exec(tree->t_server, &execinfo);
318 	}
319 }
320 
321 /*
322  * Take a reference on a tree.
323  */
324 boolean_t
smb_tree_hold(smb_tree_t * tree)325 smb_tree_hold(
326     smb_tree_t		*tree)
327 {
328 	SMB_TREE_VALID(tree);
329 
330 	mutex_enter(&tree->t_mutex);
331 
332 	if (smb_tree_is_connected_locked(tree)) {
333 		tree->t_refcnt++;
334 		mutex_exit(&tree->t_mutex);
335 		return (B_TRUE);
336 	}
337 
338 	mutex_exit(&tree->t_mutex);
339 	return (B_FALSE);
340 }
341 
342 /*
343  * Bump the hold count regardless of the tree state.  This is used in
344  * some internal code paths where we've already checked that we had a
345  * valid tree connection, and don't want to deal with the possiblity
346  * that the tree state might have changed to disconnecting after our
347  * original hold was taken.  It's correct to continue processing a
348  * request even when new requests cannot lookup that tree anymore.
349  */
350 void
smb_tree_hold_internal(smb_tree_t * tree)351 smb_tree_hold_internal(
352     smb_tree_t		*tree)
353 {
354 	SMB_TREE_VALID(tree);
355 
356 	mutex_enter(&tree->t_mutex);
357 	tree->t_refcnt++;
358 	mutex_exit(&tree->t_mutex);
359 }
360 
361 /*
362  * Release a reference on a tree.  If the tree is disconnected and the
363  * reference count falls to zero, post the object for deletion.
364  * Object deletion is deferred to avoid modifying a list while an
365  * iteration may be in progress.
366  */
367 void
smb_tree_release(smb_tree_t * tree)368 smb_tree_release(
369     smb_tree_t		*tree)
370 {
371 	SMB_TREE_VALID(tree);
372 
373 	mutex_enter(&tree->t_mutex);
374 	ASSERT(tree->t_refcnt);
375 	tree->t_refcnt--;
376 
377 	/* flush the ofile and odir lists' delete queues */
378 	smb_llist_flush(&tree->t_ofile_list);
379 	smb_llist_flush(&tree->t_odir_list);
380 
381 	if (smb_tree_is_disconnected(tree) && (tree->t_refcnt == 0))
382 		smb_session_post_tree(tree->t_session, tree);
383 
384 	mutex_exit(&tree->t_mutex);
385 }
386 
387 void
smb_tree_post_ofile(smb_tree_t * tree,smb_ofile_t * of)388 smb_tree_post_ofile(smb_tree_t *tree, smb_ofile_t *of)
389 {
390 	SMB_TREE_VALID(tree);
391 	SMB_OFILE_VALID(of);
392 	ASSERT(of->f_refcnt == 0);
393 	ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED);
394 	ASSERT(of->f_tree == tree);
395 
396 	smb_llist_post(&tree->t_ofile_list, of, smb_ofile_delete);
397 }
398 
399 void
smb_tree_post_odir(smb_tree_t * tree,smb_odir_t * od)400 smb_tree_post_odir(smb_tree_t *tree, smb_odir_t *od)
401 {
402 	SMB_TREE_VALID(tree);
403 	SMB_ODIR_VALID(od);
404 	ASSERT(od->d_refcnt == 0);
405 	ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED);
406 	ASSERT(od->d_tree == tree);
407 
408 	smb_llist_post(&tree->t_odir_list, od, smb_odir_delete);
409 }
410 
411 /*
412  * Close ofiles and odirs that match pid.
413  */
414 void
smb_tree_close_pid(smb_tree_t * tree,uint32_t pid)415 smb_tree_close_pid(
416     smb_tree_t		*tree,
417     uint32_t		pid)
418 {
419 	ASSERT(tree);
420 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
421 
422 	smb_ofile_close_all_by_pid(tree, pid);
423 	smb_tree_close_odirs(tree, pid);
424 }
425 
426 /*
427  * Check whether or not a tree supports the features identified by flags.
428  */
429 boolean_t
smb_tree_has_feature(smb_tree_t * tree,uint32_t flags)430 smb_tree_has_feature(smb_tree_t *tree, uint32_t flags)
431 {
432 	ASSERT(tree);
433 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
434 
435 	return ((tree->t_flags & flags) == flags);
436 }
437 
438 /*
439  * If the enumeration request is for tree data, handle the request
440  * here.  Otherwise, pass it on to the ofiles.
441  *
442  * This function should be called with a hold on the tree.
443  */
444 int
smb_tree_enum(smb_tree_t * tree,smb_svcenum_t * svcenum)445 smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum)
446 {
447 	smb_ofile_t	*of;
448 	smb_ofile_t	*next;
449 	int		rc = 0;
450 
451 	ASSERT(tree);
452 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
453 
454 	if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE)
455 		return (smb_tree_enum_private(tree, svcenum));
456 
457 	of = smb_tree_get_ofile(tree, NULL);
458 	while (of) {
459 		ASSERT(of->f_tree == tree);
460 
461 		rc = smb_ofile_enum(of, svcenum);
462 		if (rc != 0) {
463 			smb_ofile_release(of);
464 			break;
465 		}
466 
467 		next = smb_tree_get_ofile(tree, of);
468 		smb_ofile_release(of);
469 		of = next;
470 	}
471 
472 	return (rc);
473 }
474 
475 /*
476  * Close a file by its unique id.
477  */
478 int
smb_tree_fclose(smb_tree_t * tree,uint32_t uniqid)479 smb_tree_fclose(smb_tree_t *tree, uint32_t uniqid)
480 {
481 	smb_ofile_t	*of;
482 
483 	ASSERT(tree);
484 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
485 
486 	if ((of = smb_ofile_lookup_by_uniqid(tree, uniqid)) == NULL)
487 		return (ENOENT);
488 
489 	if (smb_ofile_disallow_fclose(of)) {
490 		smb_ofile_release(of);
491 		return (EACCES);
492 	}
493 
494 	smb_ofile_close(of, 0);
495 	smb_ofile_release(of);
496 	return (0);
497 }
498 
499 /* *************************** Static Functions ***************************** */
500 
501 #define	SHARES_DIR	".zfs/shares/"
502 
503 /*
504  * Calculates permissions given by the share's ACL to the
505  * user in the passed request.  The default is full access.
506  * If any error occurs, full access is granted.
507  *
508  * Using the vnode of the share path find the root directory
509  * of the mounted file system. Then look to see if there is a
510  * .zfs/shares directory and if there is, lookup the file with
511  * the same name as the share name in it. The ACL set for this
512  * file is the share's ACL which is used for access check here.
513  */
514 static uint32_t
smb_tree_acl_access(smb_request_t * sr,const smb_kshare_t * si,vnode_t * pathvp)515 smb_tree_acl_access(smb_request_t *sr, const smb_kshare_t *si, vnode_t *pathvp)
516 {
517 	smb_user_t	*user;
518 	cred_t		*cred;
519 	int		rc;
520 	vfs_t		*vfsp;
521 	vnode_t		*root = NULL;
522 	vnode_t		*sharevp = NULL;
523 	char		*sharepath;
524 	struct pathname	pnp;
525 	size_t		size;
526 	uint32_t	access;
527 
528 	user = sr->uid_user;
529 	cred = user->u_cred;
530 	access = ACE_ALL_PERMS;
531 
532 	if (si->shr_flags & SMB_SHRF_AUTOHOME) {
533 		/*
534 		 * An autohome share owner gets full access to the share.
535 		 * Everyone else is denied access.
536 		 */
537 		if (si->shr_uid != crgetuid(cred))
538 			access = 0;
539 
540 		return (access);
541 	}
542 
543 	/*
544 	 * The hold on 'root' is released by the lookuppnvp() that follows
545 	 */
546 	vfsp = pathvp->v_vfsp;
547 	if (vfsp != NULL)
548 		rc = VFS_ROOT(vfsp, &root);
549 	else
550 		rc = ENOENT;
551 
552 	if (rc != 0)
553 		return (access);
554 
555 
556 	size = sizeof (SHARES_DIR) + strlen(si->shr_name) + 1;
557 	sharepath = smb_srm_alloc(sr, size);
558 	(void) snprintf(sharepath, size, "%s%s", SHARES_DIR, si->shr_name);
559 
560 	pn_alloc(&pnp);
561 	(void) pn_set(&pnp, sharepath);
562 	rc = lookuppnvp(&pnp, NULL, NO_FOLLOW, NULL, &sharevp, rootdir, root,
563 	    zone_kcred());
564 	pn_free(&pnp);
565 
566 	/*
567 	 * Now get the effective access value based on cred and ACL values.
568 	 */
569 	if (rc == 0) {
570 		smb_vop_eaccess(sharevp, (int *)&access, V_ACE_MASK, NULL,
571 		    cred);
572 		VN_RELE(sharevp);
573 	}
574 
575 	return (access);
576 }
577 
578 /*
579  * Performs the following access checks for a disk share:
580  *
581  *  - No IPC/anonymous user is allowed
582  *
583  *  - If user is Guest, guestok property of the share should be
584  *    enabled
585  *
586  *  - If this is an Admin share, the user should have administrative
587  *    privileges
588  *
589  *  - Host based access control lists
590  *
591  *  - Share ACL
592  *
593  *  Returns the access allowed or 0 if access is denied.
594  */
595 static uint32_t
smb_tree_chkaccess(smb_request_t * sr,smb_kshare_t * shr,vnode_t * vp)596 smb_tree_chkaccess(smb_request_t *sr, smb_kshare_t *shr, vnode_t *vp)
597 {
598 	smb_user_t *user = sr->uid_user;
599 	char *sharename = shr->shr_name;
600 	uint32_t host_access;
601 	uint32_t acl_access;
602 	uint32_t access;
603 
604 	if (user->u_flags & SMB_USER_FLAG_ANON) {
605 		smb_tree_log(sr, sharename, "access denied: IPC only");
606 		return (0);
607 	}
608 
609 	if ((user->u_flags & SMB_USER_FLAG_GUEST) &&
610 	    ((shr->shr_flags & SMB_SHRF_GUEST_OK) == 0)) {
611 		smb_tree_log(sr, sharename, "access denied: guest disabled");
612 		return (0);
613 	}
614 
615 	if ((shr->shr_flags & SMB_SHRF_ADMIN) && !smb_user_is_admin(user)) {
616 		smb_tree_log(sr, sharename, "access denied: not admin");
617 		return (0);
618 	}
619 
620 	host_access = smb_kshare_hostaccess(shr, sr->session);
621 	if ((host_access & ACE_ALL_PERMS) == 0) {
622 		smb_tree_log(sr, sharename, "access denied: host access");
623 		return (0);
624 	}
625 
626 	acl_access = smb_tree_acl_access(sr, shr, vp);
627 	if ((acl_access & ACE_ALL_PERMS) == 0) {
628 		smb_tree_log(sr, sharename, "access denied: share ACL");
629 		return (0);
630 	}
631 
632 	access = host_access & acl_access;
633 	if ((access & ACE_ALL_PERMS) == 0) {
634 		smb_tree_log(sr, sharename, "access denied");
635 		return (0);
636 	}
637 
638 	return (access);
639 }
640 
641 /*
642  * Connect a share for use with files and directories.
643  */
644 uint32_t
smb_tree_connect_disk(smb_request_t * sr,smb_arg_tcon_t * tcon)645 smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
646 {
647 	char			*sharename = tcon->path;
648 	const char		*any = "?????";
649 	smb_user_t		*user = sr->uid_user;
650 	smb_node_t		*dnode = NULL;
651 	smb_node_t		*snode = NULL;
652 	smb_kshare_t 		*si = tcon->si;
653 	char			*service = tcon->service;
654 	char			last_component[MAXNAMELEN];
655 	smb_tree_t		*tree;
656 	int			rc;
657 	uint32_t		access;
658 	smb_shr_execinfo_t	execinfo;
659 
660 	ASSERT(user);
661 	ASSERT(user->u_cred);
662 
663 	if (service != NULL &&
664 	    strcmp(service, any) != 0 &&
665 	    strcasecmp(service, "A:") != 0) {
666 		smb_tree_log(sr, sharename, "invalid service (%s)", service);
667 		return (NT_STATUS_BAD_DEVICE_TYPE);
668 	}
669 
670 	/*
671 	 * Check that the shared directory exists.
672 	 */
673 	rc = smb_pathname_reduce(sr, user->u_cred, si->shr_path, 0, 0, &dnode,
674 	    last_component);
675 	if (rc == 0) {
676 		rc = smb_fsop_lookup(sr, user->u_cred, SMB_FOLLOW_LINKS,
677 		    sr->sr_server->si_root_smb_node, dnode, last_component,
678 		    &snode);
679 
680 		smb_node_release(dnode);
681 	}
682 
683 	if (rc) {
684 		if (snode)
685 			smb_node_release(snode);
686 
687 		smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
688 		return (NT_STATUS_BAD_NETWORK_NAME);
689 	}
690 
691 	if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
692 		smb_node_release(snode);
693 		return (NT_STATUS_ACCESS_DENIED);
694 	}
695 
696 	/*
697 	 * Set up the OptionalSupport for this share.
698 	 */
699 	tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
700 
701 	switch (si->shr_flags & SMB_SHRF_CSC_MASK) {
702 	case SMB_SHRF_CSC_DISABLED:
703 		tcon->optional_support |= SMB_CSC_CACHE_NONE;
704 		break;
705 	case SMB_SHRF_CSC_AUTO:
706 		tcon->optional_support |= SMB_CSC_CACHE_AUTO_REINT;
707 		break;
708 	case SMB_SHRF_CSC_VDO:
709 		tcon->optional_support |= SMB_CSC_CACHE_VDO;
710 		break;
711 	case SMB_SHRF_CSC_MANUAL:
712 	default:
713 		/*
714 		 * Default to SMB_CSC_CACHE_MANUAL_REINT.
715 		 */
716 		break;
717 	}
718 
719 	/* ABE support */
720 	if (si->shr_flags & SMB_SHRF_ABE)
721 		tcon->optional_support |=
722 		    SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM;
723 
724 	if (si->shr_flags & SMB_SHRF_DFSROOT)
725 		tcon->optional_support |= SMB_SHARE_IS_IN_DFS;
726 
727 	/* if 'smb' zfs property: shortnames=disabled */
728 	if (!smb_shortnames)
729 		sr->arg.tcon.optional_support |= SMB_UNIQUE_FILE_NAME;
730 
731 	tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
732 
733 	smb_node_release(snode);
734 
735 	if (tree == NULL)
736 		return (NT_STATUS_INSUFF_SERVER_RESOURCES);
737 
738 	if (tree->t_execflags & SMB_EXEC_MAP) {
739 		smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_MAP);
740 
741 		rc = smb_kshare_exec(tree->t_server, &execinfo);
742 
743 		if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) {
744 			smb_tree_disconnect(tree, B_FALSE);
745 			smb_tree_release(tree);
746 			return (NT_STATUS_ACCESS_DENIED);
747 		}
748 	}
749 
750 	sr->tid_tree = tree;
751 	sr->smb_tid  = tree->t_tid;
752 
753 	return (0);
754 }
755 
756 /*
757  * Shares have both a share and host based access control.  The access
758  * granted will be minimum permissions based on both hostaccess
759  * (permissions allowed by host based access) and aclaccess (from the
760  * share ACL).
761  */
762 uint32_t
smb_tree_connect_printq(smb_request_t * sr,smb_arg_tcon_t * tcon)763 smb_tree_connect_printq(smb_request_t *sr, smb_arg_tcon_t *tcon)
764 {
765 	char			*sharename = tcon->path;
766 	const char		*any = "?????";
767 	smb_user_t		*user = sr->uid_user;
768 	smb_node_t		*dnode = NULL;
769 	smb_node_t		*snode = NULL;
770 	smb_kshare_t 		*si = tcon->si;
771 	char			*service = tcon->service;
772 	char			last_component[MAXNAMELEN];
773 	smb_tree_t		*tree;
774 	int			rc;
775 	uint32_t		access;
776 
777 	ASSERT(user);
778 	ASSERT(user->u_cred);
779 
780 	if (sr->sr_server->sv_cfg.skc_print_enable == 0) {
781 		smb_tree_log(sr, sharename, "printing disabled");
782 		return (NT_STATUS_BAD_NETWORK_NAME);
783 	}
784 
785 	if (service != NULL &&
786 	    strcmp(service, any) != 0 &&
787 	    strcasecmp(service, "LPT1:") != 0) {
788 		smb_tree_log(sr, sharename, "invalid service (%s)", service);
789 		return (NT_STATUS_BAD_DEVICE_TYPE);
790 	}
791 
792 	/*
793 	 * Check that the shared directory exists.
794 	 */
795 	rc = smb_pathname_reduce(sr, user->u_cred, si->shr_path, 0, 0, &dnode,
796 	    last_component);
797 	if (rc == 0) {
798 		rc = smb_fsop_lookup(sr, user->u_cred, SMB_FOLLOW_LINKS,
799 		    sr->sr_server->si_root_smb_node, dnode, last_component,
800 		    &snode);
801 
802 		smb_node_release(dnode);
803 	}
804 
805 	if (rc) {
806 		if (snode)
807 			smb_node_release(snode);
808 
809 		smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
810 		return (NT_STATUS_BAD_NETWORK_NAME);
811 	}
812 
813 	if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
814 		smb_node_release(snode);
815 		return (NT_STATUS_ACCESS_DENIED);
816 	}
817 
818 	tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
819 
820 	tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
821 
822 	smb_node_release(snode);
823 
824 	if (tree == NULL)
825 		return (NT_STATUS_INSUFF_SERVER_RESOURCES);
826 
827 	sr->tid_tree = tree;
828 	sr->smb_tid  = tree->t_tid;
829 
830 	return (0);
831 }
832 
833 /*
834  * Connect an IPC share for use with named pipes.
835  */
836 uint32_t
smb_tree_connect_ipc(smb_request_t * sr,smb_arg_tcon_t * tcon)837 smb_tree_connect_ipc(smb_request_t *sr, smb_arg_tcon_t *tcon)
838 {
839 	char		*name = tcon->path;
840 	const char	*any = "?????";
841 	smb_user_t	*user = sr->uid_user;
842 	smb_tree_t	*tree;
843 	smb_kshare_t	*si = tcon->si;
844 	char		*service = tcon->service;
845 
846 	ASSERT(user);
847 
848 	if (service != NULL &&
849 	    strcmp(service, any) != 0 &&
850 	    strcasecmp(service, "IPC") != 0) {
851 		smb_tree_log(sr, name, "invalid service (%s)", service);
852 		return (NT_STATUS_BAD_DEVICE_TYPE);
853 	}
854 
855 	if ((user->u_flags & SMB_USER_FLAG_ANON) &&
856 	    sr->sr_cfg->skc_restrict_anon) {
857 		smb_tree_log(sr, name, "access denied: restrict anonymous");
858 		return (NT_STATUS_ACCESS_DENIED);
859 	}
860 
861 	tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
862 
863 	tree = smb_tree_alloc(sr, si, NULL, ACE_ALL_PERMS, 0);
864 	if (tree == NULL)
865 		return (NT_STATUS_INSUFF_SERVER_RESOURCES);
866 
867 	sr->tid_tree = tree;
868 	sr->smb_tid  = tree->t_tid;
869 
870 	return (0);
871 }
872 
873 /*
874  * Allocate a tree.
875  */
876 static smb_tree_t *
smb_tree_alloc(smb_request_t * sr,const smb_kshare_t * si,smb_node_t * snode,uint32_t access,uint32_t execflags)877 smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si,
878     smb_node_t *snode, uint32_t access, uint32_t execflags)
879 {
880 	smb_session_t	*session = sr->session;
881 	smb_tree_t	*tree;
882 	uint32_t	stype = si->shr_type;
883 	uint16_t	tid;
884 
885 	if (smb_idpool_alloc(&session->s_tid_pool, &tid))
886 		return (NULL);
887 
888 	tree = kmem_cache_alloc(smb_cache_tree, KM_SLEEP);
889 	bzero(tree, sizeof (smb_tree_t));
890 
891 	tree->t_session = session;
892 	tree->t_server = session->s_server;
893 
894 	/* grab a ref for tree->t_owner */
895 	smb_user_hold_internal(sr->uid_user);
896 	tree->t_owner = sr->uid_user;
897 
898 	if (STYPE_ISDSK(stype) || STYPE_ISPRN(stype)) {
899 		if (smb_tree_getattr(si, snode, tree) != 0) {
900 			smb_idpool_free(&session->s_tid_pool, tid);
901 			kmem_cache_free(smb_cache_tree, tree);
902 			return (NULL);
903 		}
904 	}
905 
906 	if (smb_idpool_constructor(&tree->t_fid_pool)) {
907 		smb_idpool_free(&session->s_tid_pool, tid);
908 		kmem_cache_free(smb_cache_tree, tree);
909 		return (NULL);
910 	}
911 
912 	if (smb_idpool_constructor(&tree->t_odid_pool)) {
913 		smb_idpool_destructor(&tree->t_fid_pool);
914 		smb_idpool_free(&session->s_tid_pool, tid);
915 		kmem_cache_free(smb_cache_tree, tree);
916 		return (NULL);
917 	}
918 
919 	smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t),
920 	    offsetof(smb_ofile_t, f_lnd));
921 
922 	smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t),
923 	    offsetof(smb_odir_t, d_lnd));
924 
925 	(void) strlcpy(tree->t_sharename, si->shr_name,
926 	    sizeof (tree->t_sharename));
927 	(void) strlcpy(tree->t_resource, si->shr_path,
928 	    sizeof (tree->t_resource));
929 
930 	mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL);
931 
932 	tree->t_refcnt = 1;
933 	tree->t_tid = tid;
934 	tree->t_res_type = stype;
935 	tree->t_state = SMB_TREE_STATE_CONNECTED;
936 	tree->t_magic = SMB_TREE_MAGIC;
937 	tree->t_access = access;
938 	tree->t_connect_time = gethrestime_sec();
939 	tree->t_execflags = execflags;
940 
941 	/* if FS is readonly, enforce that here */
942 	if (tree->t_flags & SMB_TREE_READONLY)
943 		tree->t_access &= ~ACE_ALL_WRITE_PERMS;
944 
945 	if (STYPE_ISDSK(stype) || STYPE_ISPRN(stype)) {
946 		smb_node_ref(snode);
947 		tree->t_snode = snode;
948 		tree->t_acltype = smb_fsop_acltype(snode);
949 	}
950 
951 	smb_llist_enter(&session->s_tree_list, RW_WRITER);
952 	smb_llist_insert_head(&session->s_tree_list, tree);
953 	smb_llist_exit(&session->s_tree_list);
954 	atomic_inc_32(&session->s_tree_cnt);
955 	smb_server_inc_trees(session->s_server);
956 	return (tree);
957 }
958 
959 /*
960  * Deallocate a tree.  The open file and open directory lists should be
961  * empty.
962  *
963  * Remove the tree from the user's tree list before freeing resources
964  * associated with the tree.
965  */
966 void
smb_tree_dealloc(void * arg)967 smb_tree_dealloc(void *arg)
968 {
969 	smb_session_t	*session;
970 	smb_tree_t	*tree = (smb_tree_t *)arg;
971 
972 	SMB_TREE_VALID(tree);
973 	ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
974 	ASSERT(tree->t_refcnt == 0);
975 
976 	session = tree->t_session;
977 	smb_llist_enter(&session->s_tree_list, RW_WRITER);
978 	smb_llist_remove(&session->s_tree_list, tree);
979 	smb_idpool_free(&session->s_tid_pool, tree->t_tid);
980 	atomic_dec_32(&session->s_tree_cnt);
981 	smb_llist_exit(&session->s_tree_list);
982 
983 	mutex_enter(&tree->t_mutex);
984 	mutex_exit(&tree->t_mutex);
985 
986 	tree->t_magic = (uint32_t)~SMB_TREE_MAGIC;
987 
988 	if (tree->t_snode)
989 		smb_node_release(tree->t_snode);
990 
991 	mutex_destroy(&tree->t_mutex);
992 	smb_llist_destructor(&tree->t_ofile_list);
993 	smb_llist_destructor(&tree->t_odir_list);
994 	smb_idpool_destructor(&tree->t_fid_pool);
995 	smb_idpool_destructor(&tree->t_odid_pool);
996 
997 	SMB_USER_VALID(tree->t_owner);
998 	smb_user_release(tree->t_owner);
999 
1000 	kmem_cache_free(smb_cache_tree, tree);
1001 }
1002 
1003 /*
1004  * Determine whether or not a tree is connected.
1005  * This function must be called with the tree mutex held.
1006  */
1007 static boolean_t
smb_tree_is_connected_locked(smb_tree_t * tree)1008 smb_tree_is_connected_locked(smb_tree_t *tree)
1009 {
1010 	switch (tree->t_state) {
1011 	case SMB_TREE_STATE_CONNECTED:
1012 		return (B_TRUE);
1013 
1014 	case SMB_TREE_STATE_DISCONNECTING:
1015 	case SMB_TREE_STATE_DISCONNECTED:
1016 		/*
1017 		 * The tree exists but being diconnected or destroyed.
1018 		 */
1019 		return (B_FALSE);
1020 
1021 	default:
1022 		ASSERT(0);
1023 		return (B_FALSE);
1024 	}
1025 }
1026 
1027 /*
1028  * Determine whether or not a tree is disconnected.
1029  * This function must be called with the tree mutex held.
1030  */
1031 static boolean_t
smb_tree_is_disconnected(smb_tree_t * tree)1032 smb_tree_is_disconnected(smb_tree_t *tree)
1033 {
1034 	switch (tree->t_state) {
1035 	case SMB_TREE_STATE_DISCONNECTED:
1036 		return (B_TRUE);
1037 
1038 	case SMB_TREE_STATE_CONNECTED:
1039 	case SMB_TREE_STATE_DISCONNECTING:
1040 		return (B_FALSE);
1041 
1042 	default:
1043 		ASSERT(0);
1044 		return (B_FALSE);
1045 	}
1046 }
1047 
1048 /*
1049  * Return a pointer to the share name within a share resource path.
1050  *
1051  * The share path may be a Uniform Naming Convention (UNC) string
1052  * (\\server\share) or simply the share name.  We validate the UNC
1053  * format but we don't look at the server name.
1054  */
1055 static char *
smb_tree_get_sharename(char * unc_path)1056 smb_tree_get_sharename(char *unc_path)
1057 {
1058 	char *sharename = unc_path;
1059 
1060 	if (sharename[0] == '\\') {
1061 		/*
1062 		 * Looks like a UNC path, validate the format.
1063 		 */
1064 		if (sharename[1] != '\\')
1065 			return (NULL);
1066 
1067 		if ((sharename = strchr(sharename+2, '\\')) == NULL)
1068 			return (NULL);
1069 
1070 		++sharename;
1071 	} else if (strchr(sharename, '\\') != NULL) {
1072 		/*
1073 		 * This should be a share name (no embedded \'s).
1074 		 */
1075 		return (NULL);
1076 	}
1077 
1078 	return (sharename);
1079 }
1080 
1081 /*
1082  * Obtain the tree attributes: volume name, typename and flags.
1083  */
1084 static int
smb_tree_getattr(const smb_kshare_t * si,smb_node_t * node,smb_tree_t * tree)1085 smb_tree_getattr(const smb_kshare_t *si, smb_node_t *node, smb_tree_t *tree)
1086 {
1087 	vfs_t *vfsp = SMB_NODE_VFS(node);
1088 
1089 	ASSERT(vfsp);
1090 
1091 	if (getvfs(&vfsp->vfs_fsid) != vfsp)
1092 		return (ESTALE);
1093 
1094 	smb_tree_get_volname(vfsp, tree);
1095 	smb_tree_get_flags(si, vfsp, tree);
1096 
1097 	VFS_RELE(vfsp);
1098 	return (0);
1099 }
1100 
1101 /*
1102  * Extract the volume name.
1103  */
1104 static void
smb_tree_get_volname(vfs_t * vfsp,smb_tree_t * tree)1105 smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree)
1106 {
1107 #ifdef	_FAKE_KERNEL
1108 	_NOTE(ARGUNUSED(vfsp))
1109 	(void) strlcpy(tree->t_volume, "fake", SMB_VOLNAMELEN);
1110 #else	/* _FAKE_KERNEL */
1111 	refstr_t *vfs_mntpoint;
1112 	const char *s;
1113 	char *name;
1114 
1115 	vfs_mntpoint = vfs_getmntpoint(vfsp);
1116 
1117 	s = refstr_value(vfs_mntpoint);
1118 	s += strspn(s, "/");
1119 	(void) strlcpy(tree->t_volume, s, SMB_VOLNAMELEN);
1120 
1121 	refstr_rele(vfs_mntpoint);
1122 
1123 	name = tree->t_volume;
1124 	(void) strsep((char **)&name, "/");
1125 #endif	/* _FAKE_KERNEL */
1126 }
1127 
1128 /*
1129  * Always set "unicode on disk" because we always use utf8 names locally.
1130  * Always set ACL support because the VFS will fake ACLs for file systems
1131  * that don't support them.
1132  *
1133  * Some flags are dependent on the typename, which is also set up here.
1134  * File system types are hardcoded in uts/common/os/vfs_conf.c.
1135  */
1136 static void
smb_tree_get_flags(const smb_kshare_t * si,vfs_t * vfsp,smb_tree_t * tree)1137 smb_tree_get_flags(const smb_kshare_t *si, vfs_t *vfsp, smb_tree_t *tree)
1138 {
1139 	smb_session_t *ssn = tree->t_session;
1140 	struct vfssw	*vswp;
1141 
1142 	typedef struct smb_mtype {
1143 		char		*mt_name;
1144 		size_t		mt_namelen;
1145 		uint32_t	mt_flags;
1146 	} smb_mtype_t;
1147 
1148 	static smb_mtype_t smb_mtype[] = {
1149 		{ "zfs",    3,	SMB_TREE_QUOTA | SMB_TREE_SPARSE},
1150 		{ "ufs",    3,	0 },
1151 		{ "nfs",    3,	SMB_TREE_NFS_MOUNTED },
1152 		{ "tmpfs",  5,	SMB_TREE_NO_EXPORT }
1153 	};
1154 	smb_mtype_t	*mtype;
1155 	char		*name;
1156 	uint32_t	flags =
1157 	    SMB_TREE_SUPPORTS_ACLS |
1158 	    SMB_TREE_UNICODE_ON_DISK;
1159 	int		i;
1160 
1161 	if (si->shr_flags & SMB_SHRF_DFSROOT)
1162 		flags |= SMB_TREE_DFSROOT;
1163 
1164 	if (si->shr_flags & SMB_SHRF_CATIA)
1165 		flags |= SMB_TREE_CATIA;
1166 
1167 	if (si->shr_flags & SMB_SHRF_ABE)
1168 		flags |= SMB_TREE_ABE;
1169 
1170 	if (ssn->s_cfg.skc_oplock_enable) {
1171 		/* if 'smb' zfs property: oplocks=enabled */
1172 		flags |= SMB_TREE_OPLOCKS;
1173 	}
1174 
1175 	/* Global config option for now.  Later make per-share. */
1176 	if (ssn->s_cfg.skc_traverse_mounts)
1177 		flags |= SMB_TREE_TRAVERSE_MOUNTS;
1178 
1179 	/* if 'smb' zfs property: shortnames=enabled */
1180 	if (smb_shortnames)
1181 		flags |= SMB_TREE_SHORTNAMES;
1182 
1183 	if (vfsp->vfs_flag & VFS_RDONLY)
1184 		flags |= SMB_TREE_READONLY;
1185 
1186 	if (vfsp->vfs_flag & VFS_XATTR)
1187 		flags |= SMB_TREE_STREAMS;
1188 
1189 	vswp = vfs_getvfsswbyvfsops(vfs_getops(vfsp));
1190 	if (vswp != NULL) {
1191 		name = vswp->vsw_name;
1192 		vfs_unrefvfssw(vswp);
1193 	} else {
1194 		name = "?";
1195 	}
1196 
1197 	for (i = 0; i < sizeof (smb_mtype) / sizeof (smb_mtype[0]); ++i) {
1198 		mtype = &smb_mtype[i];
1199 		if (strncasecmp(name, mtype->mt_name, mtype->mt_namelen) == 0)
1200 			flags |= mtype->mt_flags;
1201 	}
1202 
1203 	(void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN);
1204 	(void) smb_strupr((char *)tree->t_typename);
1205 
1206 	if (vfs_has_feature(vfsp, VFSFT_XVATTR))
1207 		flags |= SMB_TREE_XVATTR;
1208 
1209 	if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE))
1210 		flags |= SMB_TREE_CASEINSENSITIVE;
1211 
1212 	if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE))
1213 		flags |= SMB_TREE_NO_CASESENSITIVE;
1214 
1215 	if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS))
1216 		flags |= SMB_TREE_DIRENTFLAGS;
1217 
1218 	if (vfs_has_feature(vfsp, VFSFT_ACLONCREATE))
1219 		flags |= SMB_TREE_ACLONCREATE;
1220 
1221 	if (vfs_has_feature(vfsp, VFSFT_ACEMASKONACCESS))
1222 		flags |= SMB_TREE_ACEMASKONACCESS;
1223 
1224 	DTRACE_PROBE2(smb__tree__flags, uint32_t, flags, char *, name);
1225 
1226 
1227 	tree->t_flags = flags;
1228 }
1229 
1230 /*
1231  * Report share access result to syslog.
1232  */
1233 static void
smb_tree_log(smb_request_t * sr,const char * sharename,const char * fmt,...)1234 smb_tree_log(smb_request_t *sr, const char *sharename, const char *fmt, ...)
1235 {
1236 	va_list ap;
1237 	char buf[128];
1238 	smb_user_t *user = sr->uid_user;
1239 
1240 	ASSERT(user);
1241 
1242 	if (smb_tcon_mute)
1243 		return;
1244 
1245 	if ((user->u_name) && (strcasecmp(sharename, "IPC$") == 0)) {
1246 		/*
1247 		 * Only report normal users, i.e. ignore W2K misuse
1248 		 * of the IPC connection by filtering out internal
1249 		 * names such as nobody and root.
1250 		 */
1251 		if ((strcmp(user->u_name, "root") == 0) ||
1252 		    (strcmp(user->u_name, "nobody") == 0)) {
1253 			return;
1254 		}
1255 	}
1256 
1257 	va_start(ap, fmt);
1258 	(void) vsnprintf(buf, 128, fmt, ap);
1259 	va_end(ap);
1260 
1261 	cmn_err(CE_NOTE, "smbd[%s\\%s]: %s %s",
1262 	    user->u_domain, user->u_name, sharename, buf);
1263 }
1264 
1265 /*
1266  * smb_tree_lookup_odir
1267  *
1268  * Find the specified odir in the tree's list of odirs, and
1269  * attempt to obtain a hold on the odir.
1270  *
1271  * Returns NULL if odir not found or a hold cannot be obtained.
1272  */
1273 smb_odir_t *
smb_tree_lookup_odir(smb_request_t * sr,uint16_t odid)1274 smb_tree_lookup_odir(smb_request_t *sr, uint16_t odid)
1275 {
1276 	smb_odir_t	*od;
1277 	smb_llist_t	*od_list;
1278 	smb_tree_t	*tree = sr->tid_tree;
1279 
1280 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1281 
1282 	od_list = &tree->t_odir_list;
1283 
1284 	smb_llist_enter(od_list, RW_READER);
1285 	od = smb_llist_head(od_list);
1286 	while (od) {
1287 		if (od->d_odid == odid)
1288 			break;
1289 		od = smb_llist_next(od_list, od);
1290 	}
1291 	if (od == NULL)
1292 		goto out;
1293 
1294 	/*
1295 	 * Only allow use of a given Search ID with the same UID that
1296 	 * was used to create it.  MS-CIFS 3.3.5.14
1297 	 */
1298 	if (od->d_user != sr->uid_user) {
1299 		od = NULL;
1300 		goto out;
1301 	}
1302 	if (!smb_odir_hold(od))
1303 		od = NULL;
1304 
1305 out:
1306 	smb_llist_exit(od_list);
1307 	return (od);
1308 }
1309 
1310 boolean_t
smb_tree_is_connected(smb_tree_t * tree)1311 smb_tree_is_connected(smb_tree_t *tree)
1312 {
1313 	boolean_t	rb;
1314 
1315 	mutex_enter(&tree->t_mutex);
1316 	rb = smb_tree_is_connected_locked(tree);
1317 	mutex_exit(&tree->t_mutex);
1318 	return (rb);
1319 }
1320 
1321 /*
1322  * Get the next open ofile in the list.  A reference is taken on
1323  * the ofile, which can be released later with smb_ofile_release().
1324  *
1325  * If the specified ofile is NULL, search from the beginning of the
1326  * list.  Otherwise, the search starts just after that ofile.
1327  *
1328  * Returns NULL if there are no open files in the list.
1329  */
1330 static smb_ofile_t *
smb_tree_get_ofile(smb_tree_t * tree,smb_ofile_t * of)1331 smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of)
1332 {
1333 	smb_llist_t *ofile_list;
1334 
1335 	ASSERT(tree);
1336 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1337 
1338 	ofile_list = &tree->t_ofile_list;
1339 	smb_llist_enter(ofile_list, RW_READER);
1340 
1341 	if (of) {
1342 		ASSERT(of->f_magic == SMB_OFILE_MAGIC);
1343 		of = smb_llist_next(ofile_list, of);
1344 	} else {
1345 		of = smb_llist_head(ofile_list);
1346 	}
1347 
1348 	while (of) {
1349 		if (smb_ofile_hold(of))
1350 			break;
1351 
1352 		of = smb_llist_next(ofile_list, of);
1353 	}
1354 
1355 	smb_llist_exit(ofile_list);
1356 	return (of);
1357 }
1358 
1359 /*
1360  * smb_tree_get_odir
1361  *
1362  * Find the next odir in the tree's list of odirs, and obtain a
1363  * hold on it.
1364  * If the specified odir is NULL the search starts at the beginning
1365  * of the tree's odir list, otherwise the search starts after the
1366  * specified odir.
1367  */
1368 static smb_odir_t *
smb_tree_get_odir(smb_tree_t * tree,smb_odir_t * od)1369 smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od)
1370 {
1371 	smb_llist_t *od_list;
1372 
1373 	ASSERT(tree);
1374 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1375 
1376 	od_list = &tree->t_odir_list;
1377 	smb_llist_enter(od_list, RW_READER);
1378 
1379 	if (od) {
1380 		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
1381 		od = smb_llist_next(od_list, od);
1382 	} else {
1383 		od = smb_llist_head(od_list);
1384 	}
1385 
1386 	while (od) {
1387 		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
1388 
1389 		if (smb_odir_hold(od))
1390 			break;
1391 		od = smb_llist_next(od_list, od);
1392 	}
1393 
1394 	smb_llist_exit(od_list);
1395 	return (od);
1396 }
1397 
1398 /*
1399  * smb_tree_close_odirs
1400  *
1401  * Close all open odirs in the tree's list which were opened by
1402  * the process identified by pid.
1403  * If pid is zero, close all open odirs in the tree's list.
1404  */
1405 static void
smb_tree_close_odirs(smb_tree_t * tree,uint16_t pid)1406 smb_tree_close_odirs(smb_tree_t *tree, uint16_t pid)
1407 {
1408 	smb_odir_t *od, *next_od;
1409 
1410 	ASSERT(tree);
1411 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1412 
1413 	od = smb_tree_get_odir(tree, NULL);
1414 	while (od) {
1415 		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
1416 		ASSERT(od->d_tree == tree);
1417 
1418 		next_od = smb_tree_get_odir(tree, od);
1419 		if ((pid == 0) || (od->d_opened_by_pid == pid))
1420 			smb_odir_close(od);
1421 		smb_odir_release(od);
1422 
1423 		od = next_od;
1424 	}
1425 }
1426 
1427 static void
smb_tree_set_execinfo(smb_tree_t * tree,smb_shr_execinfo_t * exec,int exec_type)1428 smb_tree_set_execinfo(smb_tree_t *tree, smb_shr_execinfo_t *exec,
1429     int exec_type)
1430 {
1431 	exec->e_sharename = tree->t_sharename;
1432 	exec->e_winname = tree->t_owner->u_name;
1433 	exec->e_userdom = tree->t_owner->u_domain;
1434 	exec->e_srv_ipaddr = tree->t_session->local_ipaddr;
1435 	exec->e_cli_ipaddr = tree->t_session->ipaddr;
1436 	exec->e_cli_netbiosname = tree->t_session->workstation;
1437 	exec->e_uid = crgetuid(tree->t_owner->u_cred);
1438 	exec->e_type = exec_type;
1439 }
1440 
1441 /*
1442  * Private function to support smb_tree_enum.
1443  */
1444 static int
smb_tree_enum_private(smb_tree_t * tree,smb_svcenum_t * svcenum)1445 smb_tree_enum_private(smb_tree_t *tree, smb_svcenum_t *svcenum)
1446 {
1447 	uint8_t *pb;
1448 	uint_t nbytes;
1449 	int rc;
1450 
1451 	if (svcenum->se_nskip > 0) {
1452 		svcenum->se_nskip--;
1453 		return (0);
1454 	}
1455 
1456 	if (svcenum->se_nitems >= svcenum->se_nlimit) {
1457 		svcenum->se_nitems = svcenum->se_nlimit;
1458 		return (0);
1459 	}
1460 
1461 	pb = &svcenum->se_buf[svcenum->se_bused];
1462 	rc = smb_tree_netinfo_encode(tree, pb, svcenum->se_bavail, &nbytes);
1463 	if (rc == 0) {
1464 		svcenum->se_bavail -= nbytes;
1465 		svcenum->se_bused += nbytes;
1466 		svcenum->se_nitems++;
1467 	}
1468 
1469 	return (rc);
1470 }
1471 
1472 /*
1473  * Encode connection information into a buffer: connection information
1474  * needed in user space to support RPC requests.
1475  */
1476 static int
smb_tree_netinfo_encode(smb_tree_t * tree,uint8_t * buf,size_t buflen,uint32_t * nbytes)1477 smb_tree_netinfo_encode(smb_tree_t *tree, uint8_t *buf, size_t buflen,
1478     uint32_t *nbytes)
1479 {
1480 	smb_netconnectinfo_t	info;
1481 	int			rc;
1482 
1483 	smb_tree_netinfo_init(tree, &info);
1484 	rc = smb_netconnectinfo_encode(&info, buf, buflen, nbytes);
1485 	smb_tree_netinfo_fini(&info);
1486 
1487 	return (rc);
1488 }
1489 
1490 static void
smb_tree_netinfo_username(smb_tree_t * tree,char ** namestr,uint32_t * namelen)1491 smb_tree_netinfo_username(smb_tree_t *tree, char **namestr, uint32_t *namelen)
1492 {
1493 	smb_user_t		*user = tree->t_owner;
1494 
1495 	/*
1496 	 * u_domain_len and u_name_len include the '\0' in their
1497 	 * lengths, hence the sum of the two lengths gives us room
1498 	 * for both the '\\' and '\0' chars.
1499 	 */
1500 	ASSERT(namestr);
1501 	ASSERT(namelen);
1502 	ASSERT(user->u_domain_len > 0);
1503 	ASSERT(user->u_name_len > 0);
1504 	*namelen = user->u_domain_len + user->u_name_len;
1505 	*namestr = kmem_alloc(*namelen, KM_SLEEP);
1506 	(void) snprintf(*namestr, *namelen, "%s\\%s", user->u_domain,
1507 	    user->u_name);
1508 }
1509 
1510 /*
1511  * Note: ci_numusers should be the number of users connected to
1512  * the share rather than the number of references on the tree but
1513  * we don't have a mechanism to track users/share in smbsrv yet.
1514  */
1515 static void
smb_tree_netinfo_init(smb_tree_t * tree,smb_netconnectinfo_t * info)1516 smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *info)
1517 {
1518 	ASSERT(tree);
1519 
1520 	info->ci_id = tree->t_tid;
1521 	info->ci_type = tree->t_res_type;
1522 	info->ci_numopens = tree->t_open_files;
1523 	info->ci_numusers = tree->t_refcnt;
1524 	info->ci_time = gethrestime_sec() - tree->t_connect_time;
1525 
1526 	info->ci_sharelen = strlen(tree->t_sharename) + 1;
1527 	info->ci_share = smb_mem_strdup(tree->t_sharename);
1528 
1529 	smb_tree_netinfo_username(tree, &info->ci_username, &info->ci_namelen);
1530 }
1531 
1532 static void
smb_tree_netinfo_fini(smb_netconnectinfo_t * info)1533 smb_tree_netinfo_fini(smb_netconnectinfo_t *info)
1534 {
1535 	if (info == NULL)
1536 		return;
1537 
1538 	if (info->ci_username)
1539 		kmem_free(info->ci_username, info->ci_namelen);
1540 	if (info->ci_share)
1541 		smb_mem_free(info->ci_share);
1542 
1543 	bzero(info, sizeof (smb_netconnectinfo_t));
1544 }
1545