Home | History | Annotate | Download | only in smbsrv
      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