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