xref: /titanic_50/usr/src/uts/common/fs/smbsrv/smb_user.c (revision a23420cf95f05ac67f2c299116a3225581e519d1)
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 2010 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.
151  *
152  *    It should be noted that the reference count of a user registers the
153  *    number of references to the user in other structures (such as an smb
154  *    request). The reference count is not incremented in these 2 instances:
155  *
156  *    1) The user is logged in. An user is anchored by his state. If there's
157  *       no activity involving a user currently logged in, the reference
158  *       count of that user is zero.
159  *
160  *    2) The user is queued in the list of users of the session. The fact of
161  *       being queued in that list is NOT registered by incrementing the
162  *       reference count.
163  */
164 #include <smbsrv/smb_kproto.h>
165 #include <smbsrv/smb_door.h>
166 
167 
168 #define	ADMINISTRATORS_SID	"S-1-5-32-544"
169 
170 static smb_sid_t *smb_admins_sid = NULL;
171 
172 static boolean_t smb_user_is_logged_in(smb_user_t *);
173 static int smb_user_enum_private(smb_user_t *, smb_svcenum_t *);
174 static smb_tree_t *smb_user_get_tree(smb_llist_t *, smb_tree_t *);
175 static void smb_user_nonauth_logon(uint32_t);
176 static void smb_user_auth_logoff(uint32_t);
177 
178 int
179 smb_user_init(void)
180 {
181 	if (smb_admins_sid != NULL)
182 		return (0);
183 
184 	if ((smb_admins_sid = smb_sid_fromstr(ADMINISTRATORS_SID)) == NULL)
185 		return (-1);
186 
187 	return (0);
188 }
189 
190 void
191 smb_user_fini(void)
192 {
193 	if (smb_admins_sid != NULL) {
194 		smb_sid_free(smb_admins_sid);
195 		smb_admins_sid = NULL;
196 	}
197 }
198 
199 /*
200  * smb_user_login
201  *
202  *
203  */
204 smb_user_t *
205 smb_user_login(
206     smb_session_t	*session,
207     cred_t		*cr,
208     char		*domain_name,
209     char		*account_name,
210     uint32_t		flags,
211     uint32_t		privileges,
212     uint32_t		audit_sid)
213 {
214 	smb_user_t	*user;
215 
216 	ASSERT(session);
217 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
218 	ASSERT(cr);
219 	ASSERT(account_name);
220 	ASSERT(domain_name);
221 
222 	user = kmem_cache_alloc(session->s_server->si_cache_user, KM_SLEEP);
223 	bzero(user, sizeof (smb_user_t));
224 	user->u_refcnt = 1;
225 	user->u_session = session;
226 	user->u_server = session->s_server;
227 	user->u_logon_time = gethrestime_sec();
228 	user->u_flags = flags;
229 	user->u_privileges = privileges;
230 	user->u_name_len = strlen(account_name) + 1;
231 	user->u_domain_len = strlen(domain_name) + 1;
232 	user->u_name = smb_mem_strdup(account_name);
233 	user->u_domain = smb_mem_strdup(domain_name);
234 	user->u_cred = cr;
235 	user->u_privcred = smb_cred_create_privs(cr, privileges);
236 	user->u_audit_sid = audit_sid;
237 
238 	if (!smb_idpool_alloc(&session->s_uid_pool, &user->u_uid)) {
239 		if (!smb_idpool_constructor(&user->u_tid_pool)) {
240 			smb_llist_constructor(&user->u_tree_list,
241 			    sizeof (smb_tree_t), offsetof(smb_tree_t, t_lnd));
242 			mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL);
243 			crhold(user->u_cred);
244 			if (user->u_privcred)
245 				crhold(user->u_privcred);
246 			user->u_state = SMB_USER_STATE_LOGGED_IN;
247 			user->u_magic = SMB_USER_MAGIC;
248 			smb_llist_enter(&session->s_user_list, RW_WRITER);
249 			smb_llist_insert_tail(&session->s_user_list, user);
250 			smb_llist_exit(&session->s_user_list);
251 			atomic_inc_32(&session->s_server->sv_open_users);
252 			return (user);
253 		}
254 		smb_idpool_free(&session->s_uid_pool, user->u_uid);
255 	}
256 	smb_mem_free(user->u_name);
257 	smb_mem_free(user->u_domain);
258 	kmem_cache_free(session->s_server->si_cache_user, user);
259 	return (NULL);
260 }
261 
262 /*
263  * Create a new user based on an existing user, used to support
264  * additional SessionSetupX requests for a user on a session.
265  *
266  * Assumes the caller has a reference on the original user from
267  * a user_lookup_by_x call.
268  */
269 smb_user_t *
270 smb_user_dup(
271     smb_user_t		*orig_user)
272 {
273 	smb_user_t	*user;
274 
275 	ASSERT(orig_user->u_magic == SMB_USER_MAGIC);
276 	ASSERT(orig_user->u_refcnt);
277 
278 	user = smb_user_login(orig_user->u_session, orig_user->u_cred,
279 	    orig_user->u_domain, orig_user->u_name, orig_user->u_flags,
280 	    orig_user->u_privileges, orig_user->u_audit_sid);
281 
282 	if (user)
283 		smb_user_nonauth_logon(orig_user->u_audit_sid);
284 
285 	return (user);
286 }
287 
288 /*
289  * smb_user_logoff
290  *
291  * Change the user state and disconnect trees.
292  * The user list must not be entered or modified here.
293  */
294 void
295 smb_user_logoff(
296     smb_user_t		*user)
297 {
298 	ASSERT(user->u_magic == SMB_USER_MAGIC);
299 
300 	mutex_enter(&user->u_mutex);
301 	ASSERT(user->u_refcnt);
302 	switch (user->u_state) {
303 	case SMB_USER_STATE_LOGGED_IN: {
304 		/*
305 		 * The user is moved into a state indicating that the log off
306 		 * process has started.
307 		 */
308 		user->u_state = SMB_USER_STATE_LOGGING_OFF;
309 		mutex_exit(&user->u_mutex);
310 		atomic_dec_32(&user->u_server->sv_open_users);
311 		/*
312 		 * All the trees hanging off of this user are disconnected.
313 		 */
314 		smb_user_disconnect_trees(user);
315 		smb_user_auth_logoff(user->u_audit_sid);
316 		mutex_enter(&user->u_mutex);
317 		user->u_state = SMB_USER_STATE_LOGGED_OFF;
318 		break;
319 	}
320 	case SMB_USER_STATE_LOGGED_OFF:
321 	case SMB_USER_STATE_LOGGING_OFF:
322 		break;
323 
324 	default:
325 		ASSERT(0);
326 		break;
327 	}
328 	mutex_exit(&user->u_mutex);
329 }
330 
331 /*
332  * Take a reference on a user.
333  */
334 boolean_t
335 smb_user_hold(smb_user_t *user)
336 {
337 	ASSERT(user);
338 	ASSERT(user->u_magic == SMB_USER_MAGIC);
339 
340 	mutex_enter(&user->u_mutex);
341 
342 	if (smb_user_is_logged_in(user)) {
343 		user->u_refcnt++;
344 		mutex_exit(&user->u_mutex);
345 		return (B_TRUE);
346 	}
347 
348 	mutex_exit(&user->u_mutex);
349 	return (B_FALSE);
350 }
351 
352 /*
353  * Release a reference on a user.  If the reference count falls to
354  * zero and the user has logged off, post the object for deletion.
355  * Object deletion is deferred to avoid modifying a list while an
356  * iteration may be in progress.
357  */
358 void
359 smb_user_release(
360     smb_user_t		*user)
361 {
362 	ASSERT(user->u_magic == SMB_USER_MAGIC);
363 
364 	mutex_enter(&user->u_mutex);
365 	ASSERT(user->u_refcnt);
366 	user->u_refcnt--;
367 	switch (user->u_state) {
368 	case SMB_USER_STATE_LOGGED_OFF:
369 		if (user->u_refcnt == 0)
370 			smb_session_post_user(user->u_session, user);
371 		break;
372 
373 	case SMB_USER_STATE_LOGGED_IN:
374 	case SMB_USER_STATE_LOGGING_OFF:
375 		break;
376 
377 	default:
378 		ASSERT(0);
379 		break;
380 	}
381 	mutex_exit(&user->u_mutex);
382 }
383 
384 void
385 smb_user_post_tree(smb_user_t *user, smb_tree_t *tree)
386 {
387 	SMB_USER_VALID(user);
388 	SMB_TREE_VALID(tree);
389 	ASSERT(tree->t_refcnt == 0);
390 	ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
391 	ASSERT(tree->t_user == user);
392 
393 	smb_llist_post(&user->u_tree_list, tree, smb_tree_dealloc);
394 }
395 
396 
397 /*
398  * Find a tree by tree-id.
399  */
400 smb_tree_t *
401 smb_user_lookup_tree(
402     smb_user_t		*user,
403     uint16_t		tid)
404 
405 {
406 	smb_tree_t	*tree;
407 
408 	ASSERT(user);
409 	ASSERT(user->u_magic == SMB_USER_MAGIC);
410 
411 	smb_llist_enter(&user->u_tree_list, RW_READER);
412 	tree = smb_llist_head(&user->u_tree_list);
413 
414 	while (tree) {
415 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
416 		ASSERT(tree->t_user == user);
417 
418 		if (tree->t_tid == tid) {
419 			if (smb_tree_hold(tree)) {
420 				smb_llist_exit(&user->u_tree_list);
421 				return (tree);
422 			} else {
423 				smb_llist_exit(&user->u_tree_list);
424 				return (NULL);
425 			}
426 		}
427 
428 		tree = smb_llist_next(&user->u_tree_list, tree);
429 	}
430 
431 	smb_llist_exit(&user->u_tree_list);
432 	return (NULL);
433 }
434 
435 /*
436  * Find the first connected tree that matches the specified sharename.
437  * If the specified tree is NULL the search starts from the beginning of
438  * the user's tree list.  If a tree is provided the search starts just
439  * after that tree.
440  */
441 smb_tree_t *
442 smb_user_lookup_share(
443     smb_user_t		*user,
444     const char		*sharename,
445     smb_tree_t		*tree)
446 {
447 	ASSERT(user);
448 	ASSERT(user->u_magic == SMB_USER_MAGIC);
449 	ASSERT(sharename);
450 
451 	smb_llist_enter(&user->u_tree_list, RW_READER);
452 
453 	if (tree) {
454 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
455 		ASSERT(tree->t_user == user);
456 		tree = smb_llist_next(&user->u_tree_list, tree);
457 	} else {
458 		tree = smb_llist_head(&user->u_tree_list);
459 	}
460 
461 	while (tree) {
462 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
463 		ASSERT(tree->t_user == user);
464 		if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) {
465 			if (smb_tree_hold(tree)) {
466 				smb_llist_exit(&user->u_tree_list);
467 				return (tree);
468 			}
469 		}
470 		tree = smb_llist_next(&user->u_tree_list, tree);
471 	}
472 
473 	smb_llist_exit(&user->u_tree_list);
474 	return (NULL);
475 }
476 
477 /*
478  * Find the first connected tree that matches the specified volume name.
479  * If the specified tree is NULL the search starts from the beginning of
480  * the user's tree list.  If a tree is provided the search starts just
481  * after that tree.
482  */
483 smb_tree_t *
484 smb_user_lookup_volume(
485     smb_user_t		*user,
486     const char		*name,
487     smb_tree_t		*tree)
488 {
489 	ASSERT(user);
490 	ASSERT(user->u_magic == SMB_USER_MAGIC);
491 	ASSERT(name);
492 
493 	smb_llist_enter(&user->u_tree_list, RW_READER);
494 
495 	if (tree) {
496 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
497 		ASSERT(tree->t_user == user);
498 		tree = smb_llist_next(&user->u_tree_list, tree);
499 	} else {
500 		tree = smb_llist_head(&user->u_tree_list);
501 	}
502 
503 	while (tree) {
504 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
505 		ASSERT(tree->t_user == user);
506 
507 		if (smb_strcasecmp(tree->t_volume, name, 0) == 0) {
508 			if (smb_tree_hold(tree)) {
509 				smb_llist_exit(&user->u_tree_list);
510 				return (tree);
511 			}
512 		}
513 
514 		tree = smb_llist_next(&user->u_tree_list, tree);
515 	}
516 
517 	smb_llist_exit(&user->u_tree_list);
518 	return (NULL);
519 }
520 
521 /*
522  * Disconnect all trees that match the specified client process-id.
523  */
524 void
525 smb_user_close_pid(
526     smb_user_t		*user,
527     uint16_t		pid)
528 {
529 	smb_tree_t	*tree;
530 
531 	ASSERT(user);
532 	ASSERT(user->u_magic == SMB_USER_MAGIC);
533 
534 	tree = smb_user_get_tree(&user->u_tree_list, NULL);
535 	while (tree) {
536 		smb_tree_t *next;
537 		ASSERT(tree->t_user == user);
538 		smb_tree_close_pid(tree, pid);
539 		next = smb_user_get_tree(&user->u_tree_list, tree);
540 		smb_tree_release(tree);
541 		tree = next;
542 	}
543 }
544 
545 /*
546  * Disconnect all trees that this user has connected.
547  */
548 void
549 smb_user_disconnect_trees(
550     smb_user_t		*user)
551 {
552 	smb_tree_t	*tree;
553 
554 	ASSERT(user);
555 	ASSERT(user->u_magic == SMB_USER_MAGIC);
556 
557 	tree = smb_user_get_tree(&user->u_tree_list, NULL);
558 	while (tree) {
559 		ASSERT(tree->t_user == user);
560 		smb_tree_disconnect(tree, B_TRUE);
561 		smb_tree_release(tree);
562 		tree = smb_user_get_tree(&user->u_tree_list, NULL);
563 	}
564 }
565 
566 /*
567  * Disconnect all trees that match the specified share name.
568  */
569 void
570 smb_user_disconnect_share(
571     smb_user_t		*user,
572     const char		*sharename)
573 {
574 	smb_tree_t	*tree;
575 	smb_tree_t	*next;
576 
577 	ASSERT(user);
578 	ASSERT(user->u_magic == SMB_USER_MAGIC);
579 	ASSERT(user->u_refcnt);
580 
581 	tree = smb_user_lookup_share(user, sharename, NULL);
582 	while (tree) {
583 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
584 		smb_session_cancel_requests(user->u_session, tree, NULL);
585 		smb_tree_disconnect(tree, B_TRUE);
586 		next = smb_user_lookup_share(user, sharename, tree);
587 		smb_tree_release(tree);
588 		tree = next;
589 	}
590 }
591 
592 /*
593  * Close a file by its unique id.
594  */
595 int
596 smb_user_fclose(smb_user_t *user, uint32_t uniqid)
597 {
598 	smb_llist_t	*tree_list;
599 	smb_tree_t	*tree;
600 	int		rc = ENOENT;
601 
602 	ASSERT(user);
603 	ASSERT(user->u_magic == SMB_USER_MAGIC);
604 
605 	tree_list = &user->u_tree_list;
606 	ASSERT(tree_list);
607 
608 	smb_llist_enter(tree_list, RW_READER);
609 	tree = smb_llist_head(tree_list);
610 
611 	while ((tree != NULL) && (rc == ENOENT)) {
612 		ASSERT(tree->t_user == user);
613 
614 		if (smb_tree_hold(tree)) {
615 			rc = smb_tree_fclose(tree, uniqid);
616 			smb_tree_release(tree);
617 		}
618 
619 		tree = smb_llist_next(tree_list, tree);
620 	}
621 
622 	smb_llist_exit(tree_list);
623 	return (rc);
624 }
625 
626 /*
627  * Determine whether or not the user is an administrator.
628  * Members of the administrators group have administrative rights.
629  */
630 boolean_t
631 smb_user_is_admin(
632     smb_user_t		*user)
633 {
634 	cred_t		*u_cred;
635 
636 	ASSERT(user);
637 	u_cred = user->u_cred;
638 	ASSERT(u_cred);
639 
640 	if (smb_admins_sid == NULL)
641 		return (B_FALSE);
642 
643 	if (smb_cred_is_member(u_cred, smb_admins_sid))
644 		return (B_TRUE);
645 
646 	return (B_FALSE);
647 }
648 
649 /*
650  * This function should be called with a hold on the user.
651  */
652 boolean_t
653 smb_user_namecmp(smb_user_t *user, const char *name)
654 {
655 	char		*fq_name;
656 	boolean_t	match;
657 
658 	if (smb_strcasecmp(name, user->u_name, 0) == 0)
659 		return (B_TRUE);
660 
661 	fq_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
662 
663 	(void) snprintf(fq_name, MAXNAMELEN, "%s\\%s",
664 	    user->u_domain, user->u_name);
665 
666 	match = (smb_strcasecmp(name, fq_name, 0) == 0);
667 	if (!match) {
668 		(void) snprintf(fq_name, MAXNAMELEN, "%s@%s",
669 		    user->u_name, user->u_domain);
670 
671 		match = (smb_strcasecmp(name, fq_name, 0) == 0);
672 	}
673 
674 	kmem_free(fq_name, MAXNAMELEN);
675 	return (match);
676 }
677 
678 /*
679  * If the enumeration request is for user data, handle the request
680  * here.  Otherwise, pass it on to the trees.
681  *
682  * This function should be called with a hold on the user.
683  */
684 int
685 smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum)
686 {
687 	smb_tree_t	*tree;
688 	smb_tree_t	*next;
689 	int		rc;
690 
691 	ASSERT(user);
692 	ASSERT(user->u_magic == SMB_USER_MAGIC);
693 
694 	if (svcenum->se_type == SMB_SVCENUM_TYPE_USER)
695 		return (smb_user_enum_private(user, svcenum));
696 
697 	tree = smb_user_get_tree(&user->u_tree_list, NULL);
698 	while (tree) {
699 		ASSERT(tree->t_user == user);
700 
701 		rc = smb_tree_enum(tree, svcenum);
702 		if (rc != 0) {
703 			smb_tree_release(tree);
704 			break;
705 		}
706 
707 		next = smb_user_get_tree(&user->u_tree_list, tree);
708 		smb_tree_release(tree);
709 		tree = next;
710 	}
711 
712 	return (rc);
713 }
714 
715 /* *************************** Static Functions ***************************** */
716 
717 /*
718  * Determine whether or not a user is logged in.
719  * Typically, a reference can only be taken on a logged-in user.
720  *
721  * This is a private function and must be called with the user
722  * mutex held.
723  */
724 static boolean_t
725 smb_user_is_logged_in(smb_user_t *user)
726 {
727 	switch (user->u_state) {
728 	case SMB_USER_STATE_LOGGED_IN:
729 		return (B_TRUE);
730 
731 	case SMB_USER_STATE_LOGGING_OFF:
732 	case SMB_USER_STATE_LOGGED_OFF:
733 		return (B_FALSE);
734 
735 	default:
736 		ASSERT(0);
737 		return (B_FALSE);
738 	}
739 }
740 
741 /*
742  * Delete a user.  The tree list should be empty.
743  *
744  * Remove the user from the session's user list before freeing resources
745  * associated with the user.
746  */
747 void
748 smb_user_delete(void *arg)
749 {
750 	smb_session_t	*session;
751 	smb_user_t	*user = (smb_user_t *)arg;
752 
753 	SMB_USER_VALID(user);
754 	ASSERT(user->u_refcnt == 0);
755 	ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF);
756 
757 	session = user->u_session;
758 	smb_llist_enter(&session->s_user_list, RW_WRITER);
759 	smb_llist_remove(&session->s_user_list, user);
760 	smb_idpool_free(&session->s_uid_pool, user->u_uid);
761 	smb_llist_exit(&session->s_user_list);
762 
763 	mutex_enter(&user->u_mutex);
764 	mutex_exit(&user->u_mutex);
765 
766 	user->u_magic = (uint32_t)~SMB_USER_MAGIC;
767 	mutex_destroy(&user->u_mutex);
768 	smb_llist_destructor(&user->u_tree_list);
769 	smb_idpool_destructor(&user->u_tid_pool);
770 	crfree(user->u_cred);
771 	if (user->u_privcred)
772 		crfree(user->u_privcred);
773 	smb_mem_free(user->u_name);
774 	smb_mem_free(user->u_domain);
775 	kmem_cache_free(user->u_server->si_cache_user, user);
776 }
777 
778 /*
779  * Get the next connected tree in the list.  A reference is taken on
780  * the tree, which can be released later with smb_tree_release().
781  *
782  * If the specified tree is NULL the search starts from the beginning of
783  * the tree list.  If a tree is provided the search starts just after
784  * that tree.
785  *
786  * Returns NULL if there are no connected trees in the list.
787  */
788 static smb_tree_t *
789 smb_user_get_tree(
790     smb_llist_t		*tree_list,
791     smb_tree_t		*tree)
792 {
793 	ASSERT(tree_list);
794 
795 	smb_llist_enter(tree_list, RW_READER);
796 
797 	if (tree) {
798 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
799 		tree = smb_llist_next(tree_list, tree);
800 	} else {
801 		tree = smb_llist_head(tree_list);
802 	}
803 
804 	while (tree) {
805 		if (smb_tree_hold(tree))
806 			break;
807 
808 		tree = smb_llist_next(tree_list, tree);
809 	}
810 
811 	smb_llist_exit(tree_list);
812 	return (tree);
813 }
814 
815 cred_t *
816 smb_user_getcred(smb_user_t *user)
817 {
818 	return (user->u_cred);
819 }
820 
821 cred_t *
822 smb_user_getprivcred(smb_user_t *user)
823 {
824 	return ((user->u_privcred)? user->u_privcred : user->u_cred);
825 }
826 
827 /*
828  * Private function to support smb_user_enum.
829  */
830 static int
831 smb_user_enum_private(smb_user_t *user, smb_svcenum_t *svcenum)
832 {
833 	uint8_t *pb;
834 	uint_t nbytes;
835 	int rc;
836 
837 	if (svcenum->se_nskip > 0) {
838 		svcenum->se_nskip--;
839 		return (0);
840 	}
841 
842 	if (svcenum->se_nitems >= svcenum->se_nlimit) {
843 		svcenum->se_nitems = svcenum->se_nlimit;
844 		return (0);
845 	}
846 
847 	pb = &svcenum->se_buf[svcenum->se_bused];
848 	rc = smb_user_netinfo_encode(user, pb, svcenum->se_bavail, &nbytes);
849 	if (rc == 0) {
850 		svcenum->se_bavail -= nbytes;
851 		svcenum->se_bused += nbytes;
852 		svcenum->se_nitems++;
853 	}
854 
855 	return (rc);
856 }
857 
858 /*
859  * Encode the NetInfo for a user into a buffer.  NetInfo contains
860  * information that is often needed in user space to support RPC
861  * requests.
862  */
863 int
864 smb_user_netinfo_encode(smb_user_t *user, uint8_t *buf, size_t buflen,
865     uint32_t *nbytes)
866 {
867 	smb_netuserinfo_t	info;
868 	int			rc;
869 
870 	smb_user_netinfo_init(user, &info);
871 	rc = smb_netuserinfo_encode(&info, buf, buflen, nbytes);
872 	smb_user_netinfo_fini(&info);
873 
874 	return (rc);
875 }
876 
877 void
878 smb_user_netinfo_init(smb_user_t *user, smb_netuserinfo_t *info)
879 {
880 	smb_session_t	*session;
881 	char		*buf;
882 
883 	ASSERT(user);
884 	ASSERT(user->u_domain);
885 	ASSERT(user->u_name);
886 
887 	session = user->u_session;
888 	ASSERT(session);
889 	ASSERT(session->workstation);
890 
891 	info->ui_session_id = session->s_kid;
892 	info->ui_native_os = session->native_os;
893 	info->ui_ipaddr = session->ipaddr;
894 	info->ui_numopens = session->s_file_cnt;
895 	info->ui_uid = user->u_uid;
896 	info->ui_logon_time = user->u_logon_time;
897 	info->ui_flags = user->u_flags;
898 
899 	info->ui_domain_len = user->u_domain_len;
900 	info->ui_domain = smb_mem_strdup(user->u_domain);
901 
902 	info->ui_account_len = user->u_name_len;
903 	info->ui_account = smb_mem_strdup(user->u_name);
904 
905 	buf = kmem_alloc(MAXNAMELEN, KM_SLEEP);
906 	smb_session_getclient(session, buf, MAXNAMELEN);
907 	info->ui_workstation_len = strlen(buf) + 1;
908 	info->ui_workstation = smb_mem_strdup(buf);
909 	kmem_free(buf, MAXNAMELEN);
910 }
911 
912 void
913 smb_user_netinfo_fini(smb_netuserinfo_t *info)
914 {
915 	if (info == NULL)
916 		return;
917 
918 	if (info->ui_domain)
919 		smb_mem_free(info->ui_domain);
920 	if (info->ui_account)
921 		smb_mem_free(info->ui_account);
922 	if (info->ui_workstation)
923 		smb_mem_free(info->ui_workstation);
924 
925 	bzero(info, sizeof (smb_netuserinfo_t));
926 }
927 
928 static void
929 smb_user_nonauth_logon(uint32_t audit_sid)
930 {
931 	(void) smb_kdoor_upcall(SMB_DR_USER_NONAUTH_LOGON,
932 	    &audit_sid, xdr_uint32_t, NULL, NULL);
933 }
934 
935 static void
936 smb_user_auth_logoff(uint32_t audit_sid)
937 {
938 	(void) smb_kdoor_upcall(SMB_DR_USER_AUTH_LOGOFF,
939 	    &audit_sid, xdr_uint32_t, NULL, NULL);
940 }
941 
942 smb_token_t *
943 smb_get_token(smb_logon_t *user_info)
944 {
945 	smb_token_t	*token;
946 	int		rc;
947 
948 	token = kmem_zalloc(sizeof (smb_token_t), KM_SLEEP);
949 
950 	rc = smb_kdoor_upcall(SMB_DR_USER_AUTH_LOGON,
951 	    user_info, smb_logon_xdr, token, smb_token_xdr);
952 
953 	if (rc != 0) {
954 		kmem_free(token, sizeof (smb_token_t));
955 		return (NULL);
956 	}
957 
958 	if (!smb_token_valid(token)) {
959 		smb_token_free(token);
960 		return (NULL);
961 	}
962 
963 	return (token);
964 }
965