xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_user.c (revision bbf6f00c25b6a2bed23c35eac6d62998ecdb338c)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * General Structures Layout
28  * -------------------------
29  *
30  * This is a simplified diagram showing the relationship between most of the
31  * main structures.
32  *
33  * +-------------------+
34  * |     SMB_INFO      |
35  * +-------------------+
36  *          |
37  *          |
38  *          v
39  * +-------------------+       +-------------------+      +-------------------+
40  * |     SESSION       |<----->|     SESSION       |......|      SESSION      |
41  * +-------------------+       +-------------------+      +-------------------+
42  *          |
43  *          |
44  *          v
45  * +-------------------+       +-------------------+      +-------------------+
46  * |       USER        |<----->|       USER        |......|       USER        |
47  * +-------------------+       +-------------------+      +-------------------+
48  *          |
49  *          |
50  *          v
51  * +-------------------+       +-------------------+      +-------------------+
52  * |       TREE        |<----->|       TREE        |......|       TREE        |
53  * +-------------------+       +-------------------+      +-------------------+
54  *      |         |
55  *      |         |
56  *      |         v
57  *      |     +-------+       +-------+      +-------+
58  *      |     | OFILE |<----->| OFILE |......| OFILE |
59  *      |     +-------+       +-------+      +-------+
60  *      |
61  *      |
62  *      v
63  *  +-------+       +------+      +------+
64  *  | ODIR  |<----->| ODIR |......| ODIR |
65  *  +-------+       +------+      +------+
66  *
67  *
68  * User State Machine
69  * ------------------
70  *
71  *    +-----------------------------+	 T0
72  *    |  SMB_USER_STATE_LOGGED_IN   |<----------- Creation/Allocation
73  *    +-----------------------------+
74  *		    |
75  *		    | T1
76  *		    |
77  *		    v
78  *    +-----------------------------+
79  *    |  SMB_USER_STATE_LOGGING_OFF |
80  *    +-----------------------------+
81  *		    |
82  *		    | T2
83  *		    |
84  *		    v
85  *    +-----------------------------+    T3
86  *    |  SMB_USER_STATE_LOGGED_OFF  |----------> Deletion/Free
87  *    +-----------------------------+
88  *
89  * SMB_USER_STATE_LOGGED_IN
90  *
91  *    While in this state:
92  *      - The user is queued in the list of users of his session.
93  *      - References will be given out if the user is looked up.
94  *      - The user can access files and pipes.
95  *
96  * SMB_USER_STATE_LOGGING_OFF
97  *
98  *    While in this state:
99  *      - The user is queued in the list of users of his session.
100  *      - References will not be given out if the user is looked up.
101  *      - The trees the user connected are being disconnected.
102  *      - The resources associated with the user remain.
103  *
104  * SMB_USER_STATE_LOGGING_OFF
105  *
106  *    While in this state:
107  *      - The user is queued in the list of users of his session.
108  *      - References will not be given out if the user is looked up.
109  *      - The user has no more trees connected.
110  *      - The resources associated with the user remain.
111  *
112  * Transition T0
113  *
114  *    This transition occurs in smb_user_login(). A new user is created and
115  *    added to the list of users of a session.
116  *
117  * Transition T1
118  *
119  *    This transition occurs in smb_user_logoff().
120  *
121  * Transition T2
122  *
123  *    This transition occurs in smb_user_release(). The resources associated
124  *    with the user are deleted as well as the user. For the transition to
125  *    occur, the user must be in the SMB_USER_STATE_LOGGED_OFF state and the
126  *    reference count be zero.
127  *
128  * Comments
129  * --------
130  *
131  *    The state machine of the user structures is controlled by 3 elements:
132  *      - The list of users of the session he belongs to.
133  *      - The mutex embedded in the structure itself.
134  *      - The reference count.
135  *
136  *    There's a mutex embedded in the user structure used to protect its fields
137  *    and there's a lock embedded in the list of users of a session. To
138  *    increment or to decrement the reference count the mutex must be entered.
139  *    To insert the user into the list of users of the session and to remove
140  *    the user from it, the lock must be entered in RW_WRITER mode.
141  *
142  *    Rules of access to a user structure:
143  *
144  *    1) In order to avoid deadlocks, when both (mutex and lock of the session
145  *       list) have to be entered, the lock must be entered first.
146  *
147  *    2) All actions applied to a user require a reference count.
148  *
149  *    3) There are 2 ways of getting a reference count. One is when the user
150  *       logs in. The other when the user is looked up. This translates into
151  *       3 functions: smb_user_login(), smb_user_lookup_by_uid() and
152  *       smb_user_lookup_by_credentials.
153  *
154  *    It should be noted that the reference count of a user registers the
155  *    number of references to the user in other structures (such as an smb
156  *    request). The reference count is not incremented in these 2 instances:
157  *
158  *    1) The user is logged in. An user is anchored by his state. If there's
159  *       no activity involving a user currently logged in, the reference
160  *       count of that user is zero.
161  *
162  *    2) The user is queued in the list of users of the session. The fact of
163  *       being queued in that list is NOT registered by incrementing the
164  *       reference count.
165  */
166 #include <smbsrv/smb_kproto.h>
167 #include <smbsrv/smb_door_svc.h>
168 
169 
170 #define	ADMINISTRATORS_SID	"S-1-5-32-544"
171 
172 static smb_sid_t *smb_admins_sid = NULL;
173 
174 static boolean_t smb_user_is_logged_in(smb_user_t *);
175 static int smb_user_enum_private(smb_user_t *, smb_svcenum_t *);
176 static void smb_user_delete(smb_user_t *user);
177 static smb_tree_t *smb_user_get_tree(smb_llist_t *, smb_tree_t *);
178 
179 int
180 smb_user_init(void)
181 {
182 	if (smb_admins_sid != NULL)
183 		return (0);
184 
185 	if ((smb_admins_sid = smb_sid_fromstr(ADMINISTRATORS_SID)) == NULL)
186 		return (-1);
187 
188 	return (0);
189 }
190 
191 void
192 smb_user_fini(void)
193 {
194 	if (smb_admins_sid != NULL) {
195 		smb_sid_free(smb_admins_sid);
196 		smb_admins_sid = NULL;
197 	}
198 }
199 
200 /*
201  * smb_user_login
202  *
203  *
204  */
205 smb_user_t *
206 smb_user_login(
207     smb_session_t	*session,
208     cred_t		*cr,
209     char		*domain_name,
210     char		*account_name,
211     uint32_t		flags,
212     uint32_t		privileges,
213     uint32_t		audit_sid)
214 {
215 	smb_user_t	*user;
216 
217 	ASSERT(session);
218 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
219 	ASSERT(cr);
220 	ASSERT(account_name);
221 	ASSERT(domain_name);
222 
223 	user = kmem_cache_alloc(session->s_server->si_cache_user, KM_SLEEP);
224 	bzero(user, sizeof (smb_user_t));
225 	user->u_refcnt = 1;
226 	user->u_session = session;
227 	user->u_server = session->s_server;
228 	user->u_logon_time = gethrestime_sec();
229 	user->u_flags = flags;
230 	user->u_privileges = privileges;
231 	user->u_name_len = strlen(account_name) + 1;
232 	user->u_domain_len = strlen(domain_name) + 1;
233 	user->u_name = smb_strdup(account_name);
234 	user->u_domain = smb_strdup(domain_name);
235 	user->u_cred = cr;
236 	user->u_privcred = smb_cred_create_privs(cr, privileges);
237 	user->u_audit_sid = audit_sid;
238 
239 	if (!smb_idpool_alloc(&session->s_uid_pool, &user->u_uid)) {
240 		if (!smb_idpool_constructor(&user->u_tid_pool)) {
241 			smb_llist_constructor(&user->u_tree_list,
242 			    sizeof (smb_tree_t), offsetof(smb_tree_t, t_lnd));
243 			mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL);
244 			crhold(user->u_cred);
245 			if (user->u_privcred)
246 				crhold(user->u_privcred);
247 			user->u_state = SMB_USER_STATE_LOGGED_IN;
248 			user->u_magic = SMB_USER_MAGIC;
249 			smb_llist_enter(&session->s_user_list, RW_WRITER);
250 			smb_llist_insert_tail(&session->s_user_list, user);
251 			smb_llist_exit(&session->s_user_list);
252 			atomic_inc_32(&session->s_server->sv_open_users);
253 			return (user);
254 		}
255 		smb_idpool_free(&session->s_uid_pool, user->u_uid);
256 	}
257 	smb_mfree(user->u_name);
258 	smb_mfree(user->u_domain);
259 	kmem_cache_free(session->s_server->si_cache_user, user);
260 	return (NULL);
261 }
262 
263 /*
264  * Create a new user based on an existing user, used to support
265  * additional SessionSetupX requests for a user on a session.
266  *
267  * Assumes the caller has a reference on the original user from
268  * a user_lookup_by_x call.
269  */
270 smb_user_t *
271 smb_user_dup(
272     smb_user_t		*orig_user)
273 {
274 	smb_user_t	*user;
275 
276 	ASSERT(orig_user->u_magic == SMB_USER_MAGIC);
277 	ASSERT(orig_user->u_refcnt);
278 
279 	user = smb_user_login(orig_user->u_session, orig_user->u_cred,
280 	    orig_user->u_domain, orig_user->u_name, orig_user->u_flags,
281 	    orig_user->u_privileges, orig_user->u_audit_sid);
282 
283 	if (user)
284 		smb_user_nonauth_logon(orig_user->u_audit_sid);
285 
286 	return (user);
287 }
288 
289 /*
290  * smb_user_logoff
291  *
292  * Change the user state and disconnect trees.
293  * The user list must not be entered or modified here.
294  */
295 void
296 smb_user_logoff(
297     smb_user_t		*user)
298 {
299 	ASSERT(user->u_magic == SMB_USER_MAGIC);
300 
301 	mutex_enter(&user->u_mutex);
302 	ASSERT(user->u_refcnt);
303 	switch (user->u_state) {
304 	case SMB_USER_STATE_LOGGED_IN: {
305 		/*
306 		 * The user is moved into a state indicating that the log off
307 		 * process has started.
308 		 */
309 		user->u_state = SMB_USER_STATE_LOGGING_OFF;
310 		mutex_exit(&user->u_mutex);
311 		atomic_dec_32(&user->u_server->sv_open_users);
312 		/*
313 		 * All the trees hanging off of this user are disconnected.
314 		 */
315 		smb_user_disconnect_trees(user);
316 		smb_user_auth_logoff(user->u_audit_sid);
317 		mutex_enter(&user->u_mutex);
318 		user->u_state = SMB_USER_STATE_LOGGED_OFF;
319 		break;
320 	}
321 	case SMB_USER_STATE_LOGGED_OFF:
322 	case SMB_USER_STATE_LOGGING_OFF:
323 		break;
324 
325 	default:
326 		ASSERT(0);
327 		break;
328 	}
329 	mutex_exit(&user->u_mutex);
330 }
331 
332 /*
333  * smb_user_logoff_all
334  *
335  *
336  */
337 void
338 smb_user_logoff_all(
339     smb_session_t	*session)
340 {
341 	smb_user_t	*user;
342 
343 	ASSERT(session);
344 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
345 
346 	smb_llist_enter(&session->s_user_list, RW_READER);
347 	user = smb_llist_head(&session->s_user_list);
348 	while (user) {
349 		ASSERT(user->u_magic == SMB_USER_MAGIC);
350 		ASSERT(user->u_session == session);
351 		mutex_enter(&user->u_mutex);
352 		switch (user->u_state) {
353 		case SMB_USER_STATE_LOGGED_IN:
354 			/* The user is still logged in. */
355 			user->u_refcnt++;
356 			mutex_exit(&user->u_mutex);
357 			smb_llist_exit(&session->s_user_list);
358 			smb_user_logoff(user);
359 			smb_user_release(user);
360 			smb_llist_enter(&session->s_user_list, RW_READER);
361 			user = smb_llist_head(&session->s_user_list);
362 			break;
363 		case SMB_USER_STATE_LOGGING_OFF:
364 		case SMB_USER_STATE_LOGGED_OFF:
365 			/*
366 			 * The user is logged off or logging off.
367 			 */
368 			mutex_exit(&user->u_mutex);
369 			user = smb_llist_next(&session->s_user_list, user);
370 			break;
371 		default:
372 			ASSERT(0);
373 			mutex_exit(&user->u_mutex);
374 			user = smb_llist_next(&session->s_user_list, user);
375 			break;
376 		}
377 	}
378 	smb_llist_exit(&session->s_user_list);
379 }
380 
381 /*
382  * Take a reference on a user.
383  */
384 boolean_t
385 smb_user_hold(smb_user_t *user)
386 {
387 	ASSERT(user);
388 	ASSERT(user->u_magic == SMB_USER_MAGIC);
389 
390 	mutex_enter(&user->u_mutex);
391 
392 	if (smb_user_is_logged_in(user)) {
393 		user->u_refcnt++;
394 		mutex_exit(&user->u_mutex);
395 		return (B_TRUE);
396 	}
397 
398 	mutex_exit(&user->u_mutex);
399 	return (B_FALSE);
400 }
401 
402 /*
403  * smb_user_release
404  *
405  *
406  */
407 void
408 smb_user_release(
409     smb_user_t		*user)
410 {
411 	ASSERT(user->u_magic == SMB_USER_MAGIC);
412 
413 	mutex_enter(&user->u_mutex);
414 	ASSERT(user->u_refcnt);
415 	user->u_refcnt--;
416 	switch (user->u_state) {
417 	case SMB_USER_STATE_LOGGED_OFF:
418 		if (user->u_refcnt == 0) {
419 			mutex_exit(&user->u_mutex);
420 			smb_user_delete(user);
421 			return;
422 		}
423 		break;
424 
425 	case SMB_USER_STATE_LOGGED_IN:
426 	case SMB_USER_STATE_LOGGING_OFF:
427 		break;
428 
429 	default:
430 		ASSERT(0);
431 		break;
432 	}
433 	mutex_exit(&user->u_mutex);
434 }
435 
436 /*
437  * smb_user_lookup_by_uid
438  *
439  * Find the appropriate user for this request. The request credentials
440  * set here may be overridden by the tree credentials. In domain mode,
441  * the user and tree credentials should be the same. In share mode, the
442  * tree credentials (defined in the share definition) should override
443  * the user credentials.
444  */
445 smb_user_t *
446 smb_user_lookup_by_uid(
447     smb_session_t	*session,
448     uint16_t		uid)
449 {
450 	smb_user_t	*user;
451 
452 	ASSERT(session);
453 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
454 
455 	smb_llist_enter(&session->s_user_list, RW_READER);
456 	user = smb_llist_head(&session->s_user_list);
457 	while (user) {
458 		ASSERT(user->u_magic == SMB_USER_MAGIC);
459 		ASSERT(user->u_session == session);
460 		if (user->u_uid == uid) {
461 			if (smb_user_hold(user)) {
462 				smb_llist_exit(&session->s_user_list);
463 				return (user);
464 			} else {
465 				smb_llist_exit(&session->s_user_list);
466 				return (NULL);
467 			}
468 		}
469 		user = smb_llist_next(&session->s_user_list, user);
470 	}
471 	smb_llist_exit(&session->s_user_list);
472 	return (NULL);
473 }
474 
475 /*
476  * smb_user_lookup_by_state
477  *
478  * This function returns the first user in the logged in state. If the user
479  * provided is NULL the search starts from the beginning of the list passed
480  * in. It a user is provided the search starts just after that user.
481  */
482 smb_user_t *
483 smb_user_lookup_by_state(
484     smb_session_t	*session,
485     smb_user_t		*user)
486 {
487 	smb_llist_t	*lst;
488 	smb_user_t	*next;
489 
490 	ASSERT(session);
491 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
492 
493 	lst = &session->s_user_list;
494 
495 	smb_llist_enter(lst, RW_READER);
496 	if (user) {
497 		ASSERT(user);
498 		ASSERT(user->u_magic == SMB_USER_MAGIC);
499 		ASSERT(user->u_refcnt);
500 		next = smb_llist_next(lst, user);
501 	} else {
502 		next = smb_llist_head(lst);
503 	}
504 	while (next) {
505 		ASSERT(next->u_magic == SMB_USER_MAGIC);
506 		ASSERT(next->u_session == session);
507 
508 		if (smb_user_hold(next))
509 			break;
510 
511 		next = smb_llist_next(lst, next);
512 	}
513 	smb_llist_exit(lst);
514 
515 	return (next);
516 }
517 
518 /*
519  * Find a tree by tree-id.
520  */
521 smb_tree_t *
522 smb_user_lookup_tree(
523     smb_user_t		*user,
524     uint16_t		tid)
525 
526 {
527 	smb_tree_t	*tree;
528 
529 	ASSERT(user);
530 	ASSERT(user->u_magic == SMB_USER_MAGIC);
531 
532 	smb_llist_enter(&user->u_tree_list, RW_READER);
533 	tree = smb_llist_head(&user->u_tree_list);
534 
535 	while (tree) {
536 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
537 		ASSERT(tree->t_user == user);
538 
539 		if (tree->t_tid == tid) {
540 			if (smb_tree_hold(tree)) {
541 				smb_llist_exit(&user->u_tree_list);
542 				return (tree);
543 			} else {
544 				smb_llist_exit(&user->u_tree_list);
545 				return (NULL);
546 			}
547 		}
548 
549 		tree = smb_llist_next(&user->u_tree_list, tree);
550 	}
551 
552 	smb_llist_exit(&user->u_tree_list);
553 	return (NULL);
554 }
555 
556 /*
557  * Find the first connected tree that matches the specified sharename.
558  * If the specified tree is NULL the search starts from the beginning of
559  * the user's tree list.  If a tree is provided the search starts just
560  * after that tree.
561  */
562 smb_tree_t *
563 smb_user_lookup_share(
564     smb_user_t		*user,
565     const char		*sharename,
566     smb_tree_t		*tree)
567 {
568 	ASSERT(user);
569 	ASSERT(user->u_magic == SMB_USER_MAGIC);
570 	ASSERT(sharename);
571 
572 	smb_llist_enter(&user->u_tree_list, RW_READER);
573 
574 	if (tree) {
575 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
576 		ASSERT(tree->t_user == user);
577 		tree = smb_llist_next(&user->u_tree_list, tree);
578 	} else {
579 		tree = smb_llist_head(&user->u_tree_list);
580 	}
581 
582 	while (tree) {
583 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
584 		ASSERT(tree->t_user == user);
585 		if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) {
586 			if (smb_tree_hold(tree)) {
587 				smb_llist_exit(&user->u_tree_list);
588 				return (tree);
589 			}
590 		}
591 		tree = smb_llist_next(&user->u_tree_list, tree);
592 	}
593 
594 	smb_llist_exit(&user->u_tree_list);
595 	return (NULL);
596 }
597 
598 /*
599  * Find the first connected tree that matches the specified volume name.
600  * If the specified tree is NULL the search starts from the beginning of
601  * the user's tree list.  If a tree is provided the search starts just
602  * after that tree.
603  */
604 smb_tree_t *
605 smb_user_lookup_volume(
606     smb_user_t		*user,
607     const char		*name,
608     smb_tree_t		*tree)
609 {
610 	ASSERT(user);
611 	ASSERT(user->u_magic == SMB_USER_MAGIC);
612 	ASSERT(name);
613 
614 	smb_llist_enter(&user->u_tree_list, RW_READER);
615 
616 	if (tree) {
617 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
618 		ASSERT(tree->t_user == user);
619 		tree = smb_llist_next(&user->u_tree_list, tree);
620 	} else {
621 		tree = smb_llist_head(&user->u_tree_list);
622 	}
623 
624 	while (tree) {
625 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
626 		ASSERT(tree->t_user == user);
627 
628 		if (smb_strcasecmp(tree->t_volume, name, 0) == 0) {
629 			if (smb_tree_hold(tree)) {
630 				smb_llist_exit(&user->u_tree_list);
631 				return (tree);
632 			}
633 		}
634 
635 		tree = smb_llist_next(&user->u_tree_list, tree);
636 	}
637 
638 	smb_llist_exit(&user->u_tree_list);
639 	return (NULL);
640 }
641 
642 /*
643  * Disconnect all trees that match the specified client process-id.
644  */
645 void
646 smb_user_close_pid(
647     smb_user_t		*user,
648     uint16_t		pid)
649 {
650 	smb_tree_t	*tree;
651 
652 	ASSERT(user);
653 	ASSERT(user->u_magic == SMB_USER_MAGIC);
654 
655 	tree = smb_user_get_tree(&user->u_tree_list, NULL);
656 	while (tree) {
657 		smb_tree_t *next;
658 		ASSERT(tree->t_user == user);
659 		smb_tree_close_pid(tree, pid);
660 		next = smb_user_get_tree(&user->u_tree_list, tree);
661 		smb_tree_release(tree);
662 		tree = next;
663 	}
664 }
665 
666 /*
667  * Disconnect all trees that this user has connected.
668  */
669 void
670 smb_user_disconnect_trees(
671     smb_user_t		*user)
672 {
673 	smb_tree_t	*tree;
674 
675 	ASSERT(user);
676 	ASSERT(user->u_magic == SMB_USER_MAGIC);
677 
678 	tree = smb_user_get_tree(&user->u_tree_list, NULL);
679 	while (tree) {
680 		ASSERT(tree->t_user == user);
681 		smb_tree_disconnect(tree, B_TRUE);
682 		smb_tree_release(tree);
683 		tree = smb_user_get_tree(&user->u_tree_list, NULL);
684 	}
685 }
686 
687 /*
688  * Disconnect all trees that match the specified share name.
689  */
690 void
691 smb_user_disconnect_share(
692     smb_user_t		*user,
693     const char		*sharename)
694 {
695 	smb_tree_t	*tree;
696 	smb_tree_t	*next;
697 
698 	ASSERT(user);
699 	ASSERT(user->u_magic == SMB_USER_MAGIC);
700 	ASSERT(user->u_refcnt);
701 
702 	tree = smb_user_lookup_share(user, sharename, NULL);
703 	while (tree) {
704 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
705 		smb_session_cancel_requests(user->u_session, tree, NULL);
706 		smb_tree_disconnect(tree, B_TRUE);
707 		next = smb_user_lookup_share(user, sharename, tree);
708 		smb_tree_release(tree);
709 		tree = next;
710 	}
711 }
712 
713 /*
714  * Close a file by its unique id.
715  */
716 int
717 smb_user_fclose(smb_user_t *user, uint32_t uniqid)
718 {
719 	smb_llist_t	*tree_list;
720 	smb_tree_t	*tree;
721 	int		rc = ENOENT;
722 
723 	ASSERT(user);
724 	ASSERT(user->u_magic == SMB_USER_MAGIC);
725 
726 	tree_list = &user->u_tree_list;
727 	ASSERT(tree_list);
728 
729 	smb_llist_enter(tree_list, RW_READER);
730 	tree = smb_llist_head(tree_list);
731 
732 	while ((tree != NULL) && (rc == ENOENT)) {
733 		ASSERT(tree->t_user == user);
734 
735 		if (smb_tree_hold(tree)) {
736 			rc = smb_tree_fclose(tree, uniqid);
737 			smb_tree_release(tree);
738 		}
739 
740 		tree = smb_llist_next(tree_list, tree);
741 	}
742 
743 	smb_llist_exit(tree_list);
744 	return (rc);
745 }
746 
747 /*
748  * Determine whether or not the user is an administrator.
749  * Members of the administrators group have administrative rights.
750  */
751 boolean_t
752 smb_user_is_admin(
753     smb_user_t		*user)
754 {
755 	cred_t		*u_cred;
756 
757 	ASSERT(user);
758 	u_cred = user->u_cred;
759 	ASSERT(u_cred);
760 
761 	if (smb_admins_sid == NULL)
762 		return (B_FALSE);
763 
764 	if (smb_cred_is_member(u_cred, smb_admins_sid))
765 		return (B_TRUE);
766 
767 	return (B_FALSE);
768 }
769 
770 /*
771  * This function should be called with a hold on the user.
772  */
773 boolean_t
774 smb_user_namecmp(smb_user_t *user, const char *name)
775 {
776 	char		*fq_name;
777 	boolean_t	match;
778 
779 	if (smb_strcasecmp(name, user->u_name, 0) == 0)
780 		return (B_TRUE);
781 
782 	fq_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
783 
784 	(void) snprintf(fq_name, MAXNAMELEN, "%s\\%s",
785 	    user->u_domain, user->u_name);
786 
787 	match = (smb_strcasecmp(name, fq_name, 0) == 0);
788 	if (!match) {
789 		(void) snprintf(fq_name, MAXNAMELEN, "%s@%s",
790 		    user->u_name, user->u_domain);
791 
792 		match = (smb_strcasecmp(name, fq_name, 0) == 0);
793 	}
794 
795 	kmem_free(fq_name, MAXNAMELEN);
796 	return (match);
797 }
798 
799 /*
800  * If the enumeration request is for user data, handle the request
801  * here.  Otherwise, pass it on to the trees.
802  *
803  * This function should be called with a hold on the user.
804  */
805 int
806 smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum)
807 {
808 	smb_tree_t	*tree;
809 	smb_tree_t	*next;
810 	int		rc;
811 
812 	ASSERT(user);
813 	ASSERT(user->u_magic == SMB_USER_MAGIC);
814 
815 	if (svcenum->se_type == SMB_SVCENUM_TYPE_USER)
816 		return (smb_user_enum_private(user, svcenum));
817 
818 	tree = smb_user_get_tree(&user->u_tree_list, NULL);
819 	while (tree) {
820 		ASSERT(tree->t_user == user);
821 
822 		rc = smb_tree_enum(tree, svcenum);
823 		if (rc != 0) {
824 			smb_tree_release(tree);
825 			break;
826 		}
827 
828 		next = smb_user_get_tree(&user->u_tree_list, tree);
829 		smb_tree_release(tree);
830 		tree = next;
831 	}
832 
833 	return (rc);
834 }
835 
836 /* *************************** Static Functions ***************************** */
837 
838 /*
839  * Determine whether or not a user is logged in.
840  * Typically, a reference can only be taken on a logged-in user.
841  *
842  * This is a private function and must be called with the user
843  * mutex held.
844  */
845 static boolean_t
846 smb_user_is_logged_in(smb_user_t *user)
847 {
848 	switch (user->u_state) {
849 	case SMB_USER_STATE_LOGGED_IN:
850 		return (B_TRUE);
851 
852 	case SMB_USER_STATE_LOGGING_OFF:
853 	case SMB_USER_STATE_LOGGED_OFF:
854 		return (B_FALSE);
855 
856 	default:
857 		ASSERT(0);
858 		return (B_FALSE);
859 	}
860 }
861 
862 /*
863  * smb_user_delete
864  */
865 static void
866 smb_user_delete(
867     smb_user_t		*user)
868 {
869 	smb_session_t	*session;
870 
871 	ASSERT(user);
872 	ASSERT(user->u_magic == SMB_USER_MAGIC);
873 	ASSERT(user->u_refcnt == 0);
874 	ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF);
875 
876 	session = user->u_session;
877 	/*
878 	 * Let's remove the user from the list of users of the session. This
879 	 * has to be done before any resources associated with the user are
880 	 * deleted.
881 	 */
882 	smb_llist_enter(&session->s_user_list, RW_WRITER);
883 	smb_llist_remove(&session->s_user_list, user);
884 	smb_llist_exit(&session->s_user_list);
885 
886 	user->u_magic = (uint32_t)~SMB_USER_MAGIC;
887 	mutex_destroy(&user->u_mutex);
888 	smb_llist_destructor(&user->u_tree_list);
889 	smb_idpool_destructor(&user->u_tid_pool);
890 	smb_idpool_free(&session->s_uid_pool, user->u_uid);
891 	crfree(user->u_cred);
892 	if (user->u_privcred)
893 		crfree(user->u_privcred);
894 	smb_mfree(user->u_name);
895 	smb_mfree(user->u_domain);
896 	kmem_cache_free(user->u_server->si_cache_user, user);
897 }
898 
899 /*
900  * Get the next connected tree in the list.  A reference is taken on
901  * the tree, which can be released later with smb_tree_release().
902  *
903  * If the specified tree is NULL the search starts from the beginning of
904  * the tree list.  If a tree is provided the search starts just after
905  * that tree.
906  *
907  * Returns NULL if there are no connected trees in the list.
908  */
909 static smb_tree_t *
910 smb_user_get_tree(
911     smb_llist_t		*tree_list,
912     smb_tree_t		*tree)
913 {
914 	ASSERT(tree_list);
915 
916 	smb_llist_enter(tree_list, RW_READER);
917 
918 	if (tree) {
919 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
920 		tree = smb_llist_next(tree_list, tree);
921 	} else {
922 		tree = smb_llist_head(tree_list);
923 	}
924 
925 	while (tree) {
926 		if (smb_tree_hold(tree))
927 			break;
928 
929 		tree = smb_llist_next(tree_list, tree);
930 	}
931 
932 	smb_llist_exit(tree_list);
933 	return (tree);
934 }
935 
936 cred_t *
937 smb_user_getcred(smb_user_t *user)
938 {
939 	return (user->u_cred);
940 }
941 
942 cred_t *
943 smb_user_getprivcred(smb_user_t *user)
944 {
945 	return ((user->u_privcred)? user->u_privcred : user->u_cred);
946 }
947 
948 /*
949  * Private function to support smb_user_enum.
950  */
951 static int
952 smb_user_enum_private(smb_user_t *user, smb_svcenum_t *svcenum)
953 {
954 	uint8_t *pb;
955 	uint_t nbytes;
956 	int rc;
957 
958 	if (svcenum->se_nskip > 0) {
959 		svcenum->se_nskip--;
960 		return (0);
961 	}
962 
963 	if (svcenum->se_nitems >= svcenum->se_nlimit) {
964 		svcenum->se_nitems = svcenum->se_nlimit;
965 		return (0);
966 	}
967 
968 	pb = &svcenum->se_buf[svcenum->se_bused];
969 	rc = smb_user_netinfo_encode(user, pb, svcenum->se_bavail, &nbytes);
970 	if (rc == 0) {
971 		svcenum->se_bavail -= nbytes;
972 		svcenum->se_bused += nbytes;
973 		svcenum->se_nitems++;
974 	}
975 
976 	return (rc);
977 }
978 
979 /*
980  * Encode the NetInfo for a user into a buffer.  NetInfo contains
981  * information that is often needed in user space to support RPC
982  * requests.
983  */
984 int
985 smb_user_netinfo_encode(smb_user_t *user, uint8_t *buf, size_t buflen,
986     uint32_t *nbytes)
987 {
988 	smb_netuserinfo_t	info;
989 	int			rc;
990 
991 	smb_user_netinfo_init(user, &info);
992 	rc = smb_netuserinfo_encode(&info, buf, buflen, nbytes);
993 	smb_user_netinfo_fini(&info);
994 
995 	return (rc);
996 }
997 
998 void
999 smb_user_netinfo_init(smb_user_t *user, smb_netuserinfo_t *info)
1000 {
1001 	smb_session_t	*session;
1002 	char		*buf;
1003 
1004 	ASSERT(user);
1005 	ASSERT(user->u_domain);
1006 	ASSERT(user->u_name);
1007 
1008 	session = user->u_session;
1009 	ASSERT(session);
1010 	ASSERT(session->workstation);
1011 
1012 	info->ui_session_id = session->s_kid;
1013 	info->ui_native_os = session->native_os;
1014 	info->ui_ipaddr = session->ipaddr;
1015 	info->ui_numopens = session->s_file_cnt;
1016 	info->ui_uid = user->u_uid;
1017 	info->ui_logon_time = user->u_logon_time;
1018 	info->ui_flags = user->u_flags;
1019 
1020 	info->ui_domain_len = user->u_domain_len;
1021 	info->ui_domain = smb_strdup(user->u_domain);
1022 
1023 	info->ui_account_len = user->u_name_len;
1024 	info->ui_account = smb_strdup(user->u_name);
1025 
1026 	buf = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1027 	smb_session_getclient(session, buf, MAXNAMELEN);
1028 	info->ui_workstation_len = strlen(buf) + 1;
1029 	info->ui_workstation = smb_strdup(buf);
1030 	kmem_free(buf, MAXNAMELEN);
1031 }
1032 
1033 void
1034 smb_user_netinfo_fini(smb_netuserinfo_t *info)
1035 {
1036 	if (info == NULL)
1037 		return;
1038 
1039 	if (info->ui_domain)
1040 		smb_mfree(info->ui_domain);
1041 	if (info->ui_account)
1042 		smb_mfree(info->ui_account);
1043 	if (info->ui_workstation)
1044 		smb_mfree(info->ui_workstation);
1045 
1046 	bzero(info, sizeof (smb_netuserinfo_t));
1047 }
1048