xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_user.c (revision f998c95e3b7029fe5f7542e115f7474ddb8024d7)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * General Structures Layout
30  * -------------------------
31  *
32  * This is a simplified diagram showing the relationship between most of the
33  * main structures.
34  *
35  * +-------------------+
36  * |     SMB_INFO      |
37  * +-------------------+
38  *          |
39  *          |
40  *          v
41  * +-------------------+       +-------------------+      +-------------------+
42  * |     SESSION       |<----->|     SESSION       |......|      SESSION      |
43  * +-------------------+       +-------------------+      +-------------------+
44  *          |
45  *          |
46  *          v
47  * +-------------------+       +-------------------+      +-------------------+
48  * |       USER        |<----->|       USER        |......|       USER        |
49  * +-------------------+       +-------------------+      +-------------------+
50  *          |
51  *          |
52  *          v
53  * +-------------------+       +-------------------+      +-------------------+
54  * |       TREE        |<----->|       TREE        |......|       TREE        |
55  * +-------------------+       +-------------------+      +-------------------+
56  *      |         |
57  *      |         |
58  *      |         v
59  *      |     +-------+       +-------+      +-------+
60  *      |     | OFILE |<----->| OFILE |......| OFILE |
61  *      |     +-------+       +-------+      +-------+
62  *      |
63  *      |
64  *      v
65  *  +-------+       +------+      +------+
66  *  | ODIR  |<----->| ODIR |......| ODIR |
67  *  +-------+       +------+      +------+
68  *
69  *
70  * User State Machine
71  * ------------------
72  *
73  *    +-----------------------------+	 T0
74  *    |  SMB_USER_STATE_LOGGED_IN   |<----------- Creation/Allocation
75  *    +-----------------------------+
76  *		    |
77  *		    | T1
78  *		    |
79  *		    v
80  *    +-----------------------------+
81  *    |  SMB_USER_STATE_LOGGING_OFF |
82  *    +-----------------------------+
83  *		    |
84  *		    | T2
85  *		    |
86  *		    v
87  *    +-----------------------------+    T3
88  *    |  SMB_USER_STATE_LOGGED_OFF  |----------> Deletion/Free
89  *    +-----------------------------+
90  *
91  * SMB_USER_STATE_LOGGED_IN
92  *
93  *    While in this state:
94  *      - The user is queued in the list of users of his session.
95  *      - References will be given out if the user is looked up.
96  *      - The user can access files and pipes.
97  *
98  * SMB_USER_STATE_LOGGING_OFF
99  *
100  *    While in this state:
101  *      - The user is queued in the list of users of his session.
102  *      - References will not be given out if the user is looked up.
103  *      - The trees the user connected are being disconnected.
104  *      - The resources associated with the user remain.
105  *
106  * SMB_USER_STATE_LOGGING_OFF
107  *
108  *    While in this state:
109  *      - The user is queued in the list of users of his session.
110  *      - References will not be given out if the user is looked up.
111  *      - The user has no more trees connected.
112  *      - The resources associated with the user remain.
113  *
114  * Transition T0
115  *
116  *    This transition occurs in smb_user_login(). A new user is created and
117  *    added to the list of users of a session.
118  *
119  * Transition T1
120  *
121  *    This transition occurs in smb_user_logoff().
122  *
123  * Transition T2
124  *
125  *    This transition occurs in smb_user_release(). The resources associated
126  *    with the user are deleted as well as the user. For the transition to
127  *    occur, the user must be in the SMB_USER_STATE_LOGGED_OFF state and the
128  *    reference count be zero.
129  *
130  * Comments
131  * --------
132  *
133  *    The state machine of the user structures is controlled by 3 elements:
134  *      - The list of users of the session he belongs to.
135  *      - The mutex embedded in the structure itself.
136  *      - The reference count.
137  *
138  *    There's a mutex embedded in the user structure used to protect its fields
139  *    and there's a lock embedded in the list of users of a session. To
140  *    increment or to decrement the reference count the mutex must be entered.
141  *    To insert the user into the list of users of the session and to remove
142  *    the user from it, the lock must be entered in RW_WRITER mode.
143  *
144  *    Rules of access to a user structure:
145  *
146  *    1) In order to avoid deadlocks, when both (mutex and lock of the session
147  *       list) have to be entered, the lock must be entered first.
148  *
149  *    2) All actions applied to a user require a reference count.
150  *
151  *    3) There are 2 ways of getting a reference count. One is when the user
152  *       logs in. The other when the user is looked up. This translates into
153  *       3 functions: smb_user_login(), smb_user_lookup_by_uid() and
154  *       smb_user_lookup_by_credentials.
155  *
156  *    It should be noted that the reference count of a user registers the
157  *    number of references to the user in other structures (such as an smb
158  *    request). The reference count is not incremented in these 2 instances:
159  *
160  *    1) The user is logged in. An user is anchored by his state. If there's
161  *       no activity involving a user currently logged in, the reference
162  *       count of that user is zero.
163  *
164  *    2) The user is queued in the list of users of the session. The fact of
165  *       being queued in that list is NOT registered by incrementing the
166  *       reference count.
167  */
168 #include <smbsrv/smb_incl.h>
169 #include <smbsrv/smb_door_svc.h>
170 
171 /* Static functions defined further down this file. */
172 static void	smb_user_delete(smb_user_t *user);
173 
174 /*
175  * smb_user_login
176  *
177  *
178  */
179 smb_user_t *
180 smb_user_login(
181     smb_session_t	*session,
182     cred_t		*cr,
183     char		*domain_name,
184     char		*account_name,
185     uint32_t		flags,
186     uint32_t		privileges,
187     uint32_t		audit_sid)
188 {
189 	smb_user_t	*user;
190 
191 	ASSERT(session);
192 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
193 	ASSERT(cr);
194 	ASSERT(account_name);
195 	ASSERT(domain_name);
196 
197 	user = kmem_cache_alloc(session->s_server->si_cache_user, KM_SLEEP);
198 	bzero(user, sizeof (smb_user_t));
199 	user->u_refcnt = 1;
200 	user->u_session = session;
201 	user->u_server = session->s_server;
202 	user->u_logon_time = gethrestime_sec();
203 	user->u_flags = flags;
204 	user->u_privileges = privileges;
205 	user->u_name_len = strlen(account_name) + 1;
206 	user->u_domain_len = strlen(domain_name) + 1;
207 	user->u_name = smb_kstrdup(account_name, user->u_name_len);
208 	user->u_domain = smb_kstrdup(domain_name, user->u_domain_len);
209 	user->u_cred = cr;
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 			crhold(cr);
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 			atomic_inc_32(&session->s_server->sv_open_users);
224 			return (user);
225 		}
226 		smb_idpool_free(&session->s_uid_pool, user->u_uid);
227 	}
228 	kmem_free(user->u_name, (size_t)user->u_name_len);
229 	kmem_free(user->u_domain, (size_t)user->u_domain_len);
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  *
264  */
265 void
266 smb_user_logoff(
267     smb_user_t		*user)
268 {
269 	ASSERT(user->u_magic == SMB_USER_MAGIC);
270 
271 	mutex_enter(&user->u_mutex);
272 	ASSERT(user->u_refcnt);
273 	switch (user->u_state) {
274 	case SMB_USER_STATE_LOGGED_IN: {
275 		/*
276 		 * The user is moved into a state indicating that the log off
277 		 * process has started.
278 		 */
279 		user->u_state = SMB_USER_STATE_LOGGING_OFF;
280 		mutex_exit(&user->u_mutex);
281 		atomic_dec_32(&user->u_server->sv_open_users);
282 		/*
283 		 * All the trees hanging off of this user are disconnected.
284 		 */
285 		smb_tree_disconnect_all(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 		break;
290 	}
291 	case SMB_USER_STATE_LOGGED_OFF:
292 	case SMB_USER_STATE_LOGGING_OFF:
293 		break;
294 
295 	default:
296 		ASSERT(0);
297 		break;
298 	}
299 	mutex_exit(&user->u_mutex);
300 }
301 
302 /*
303  * smb_user_logoff_all
304  *
305  *
306  */
307 void
308 smb_user_logoff_all(
309     smb_session_t	*session)
310 {
311 	smb_user_t	*user;
312 
313 	ASSERT(session);
314 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
315 
316 	smb_llist_enter(&session->s_user_list, RW_READER);
317 	user = smb_llist_head(&session->s_user_list);
318 	while (user) {
319 		ASSERT(user->u_magic == SMB_USER_MAGIC);
320 		ASSERT(user->u_session == session);
321 		mutex_enter(&user->u_mutex);
322 		switch (user->u_state) {
323 		case SMB_USER_STATE_LOGGED_IN:
324 			/* The user is still logged in. */
325 			user->u_refcnt++;
326 			mutex_exit(&user->u_mutex);
327 			smb_llist_exit(&session->s_user_list);
328 			smb_user_logoff(user);
329 			smb_user_release(user);
330 			smb_llist_enter(&session->s_user_list, RW_READER);
331 			user = smb_llist_head(&session->s_user_list);
332 			break;
333 		case SMB_USER_STATE_LOGGING_OFF:
334 		case SMB_USER_STATE_LOGGED_OFF:
335 			/*
336 			 * The user is logged off or logging off.
337 			 */
338 			mutex_exit(&user->u_mutex);
339 			user = smb_llist_next(&session->s_user_list, user);
340 			break;
341 		default:
342 			ASSERT(0);
343 			mutex_exit(&user->u_mutex);
344 			user = smb_llist_next(&session->s_user_list, user);
345 			break;
346 		}
347 	}
348 	smb_llist_exit(&session->s_user_list);
349 }
350 
351 /*
352  * smb_user_release
353  *
354  *
355  */
356 void
357 smb_user_release(
358     smb_user_t		*user)
359 {
360 	ASSERT(user->u_magic == SMB_USER_MAGIC);
361 
362 	mutex_enter(&user->u_mutex);
363 	ASSERT(user->u_refcnt);
364 	user->u_refcnt--;
365 	switch (user->u_state) {
366 	case SMB_USER_STATE_LOGGED_OFF:
367 		if (user->u_refcnt == 0) {
368 			mutex_exit(&user->u_mutex);
369 			smb_user_delete(user);
370 			return;
371 		}
372 		break;
373 
374 	case SMB_USER_STATE_LOGGED_IN:
375 	case SMB_USER_STATE_LOGGING_OFF:
376 		break;
377 
378 	default:
379 		ASSERT(0);
380 		break;
381 	}
382 	mutex_exit(&user->u_mutex);
383 }
384 
385 /*
386  * smb_user_lookup_by_uid
387  *
388  * Find the appropriate user for this request. The request credentials
389  * set here may be overridden by the tree credentials. In domain mode,
390  * the user and tree credentials should be the same. In share mode, the
391  * tree credentials (defined in the share definition) should override
392  * the user credentials.
393  */
394 smb_user_t *
395 smb_user_lookup_by_uid(
396     smb_session_t	*session,
397     cred_t		**cr,
398     uint16_t		uid)
399 {
400 	smb_user_t	*user;
401 
402 	ASSERT(session);
403 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
404 	ASSERT(cr);
405 
406 	smb_llist_enter(&session->s_user_list, RW_READER);
407 	user = smb_llist_head(&session->s_user_list);
408 	while (user) {
409 		ASSERT(user->u_magic == SMB_USER_MAGIC);
410 		ASSERT(user->u_session == session);
411 		if (user->u_uid == uid) {
412 			mutex_enter(&user->u_mutex);
413 			switch (user->u_state) {
414 
415 			case SMB_USER_STATE_LOGGED_IN:
416 				/* The user exists and is still logged in. */
417 				*cr = user->u_cred;
418 				user->u_refcnt++;
419 				mutex_exit(&user->u_mutex);
420 				smb_llist_exit(&session->s_user_list);
421 				return (user);
422 
423 			case SMB_USER_STATE_LOGGING_OFF:
424 			case SMB_USER_STATE_LOGGED_OFF:
425 				/*
426 				 * The user exists but has logged off or is in
427 				 * the process of logging off.
428 				 */
429 				mutex_exit(&user->u_mutex);
430 				smb_llist_exit(&session->s_user_list);
431 				return (NULL);
432 
433 			default:
434 				ASSERT(0);
435 				mutex_exit(&user->u_mutex);
436 				smb_llist_exit(&session->s_user_list);
437 				return (NULL);
438 			}
439 		}
440 		user = smb_llist_next(&session->s_user_list, user);
441 	}
442 	smb_llist_exit(&session->s_user_list);
443 	return (NULL);
444 }
445 
446 /*
447  * smb_user_lookup_by_name
448  */
449 smb_user_t *
450 smb_user_lookup_by_name(smb_session_t *session, char *domain, char *name)
451 {
452 	smb_user_t	*user;
453 	smb_llist_t	*ulist;
454 
455 	ulist = &session->s_user_list;
456 	smb_llist_enter(ulist, RW_READER);
457 	user = smb_llist_head(ulist);
458 	while (user) {
459 		ASSERT(user->u_magic == SMB_USER_MAGIC);
460 		if (!utf8_strcasecmp(user->u_name, name) &&
461 		    !utf8_strcasecmp(user->u_domain, domain)) {
462 			mutex_enter(&user->u_mutex);
463 			if (user->u_state == SMB_USER_STATE_LOGGED_IN) {
464 				user->u_refcnt++;
465 				mutex_exit(&user->u_mutex);
466 				break;
467 			}
468 			mutex_exit(&user->u_mutex);
469 		}
470 		user = smb_llist_next(ulist, user);
471 	}
472 	smb_llist_exit(ulist);
473 
474 	return (user);
475 }
476 
477 /*
478  * smb_user_lookup_by_state
479  *
480  * This function returns the first user in the logged in state. If the user
481  * provided is NULL the search starts from the beginning of the list passed
482  * in. It a user is provided the search starts just after that user.
483  */
484 smb_user_t *
485 smb_user_lookup_by_state(
486     smb_session_t	*session,
487     smb_user_t		*user)
488 {
489 	smb_llist_t	*lst;
490 	smb_user_t	*next;
491 
492 	ASSERT(session);
493 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
494 
495 	lst = &session->s_user_list;
496 
497 	smb_llist_enter(lst, RW_READER);
498 	if (user) {
499 		ASSERT(user);
500 		ASSERT(user->u_magic == SMB_USER_MAGIC);
501 		ASSERT(user->u_refcnt);
502 		next = smb_llist_next(lst, user);
503 	} else {
504 		next = smb_llist_head(lst);
505 	}
506 	while (next) {
507 		ASSERT(next->u_magic == SMB_USER_MAGIC);
508 		ASSERT(next->u_session == session);
509 		mutex_enter(&next->u_mutex);
510 		if (next->u_state == SMB_USER_STATE_LOGGED_IN) {
511 			next->u_refcnt++;
512 			mutex_exit(&next->u_mutex);
513 			break;
514 		} else {
515 			ASSERT((next->u_state == SMB_USER_STATE_LOGGING_OFF) ||
516 			    (next->u_state == SMB_USER_STATE_LOGGED_OFF));
517 			mutex_exit(&next->u_mutex);
518 			next = smb_llist_next(lst, next);
519 		}
520 	}
521 	smb_llist_exit(lst);
522 
523 	return (next);
524 }
525 
526 /*
527  * smb_user_disconnect_share
528  *
529  * This function disconnects all the trees that have the sharename passed in.
530  */
531 void
532 smb_user_disconnect_share(
533     smb_user_t		*user,
534     char		*sharename)
535 {
536 	smb_tree_t	*tree;
537 	smb_tree_t	*next;
538 
539 	ASSERT(user);
540 	ASSERT(user->u_magic == SMB_USER_MAGIC);
541 	ASSERT(user->u_refcnt);
542 
543 	tree = smb_tree_lookup_by_name(user, sharename, NULL);
544 	while (tree) {
545 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
546 		smb_tree_disconnect(tree);
547 		smbsr_rq_notify(NULL, user->u_session, tree);
548 		next = smb_tree_lookup_by_name(user, sharename,
549 		    tree);
550 		smb_tree_release(tree);
551 		tree = next;
552 	}
553 }
554 
555 /*
556  * smb_user_disconnect_share
557  *
558  * This function disconnects all the trees that match fsd passed in.
559  */
560 void
561 smb_user_disconnect_volume(
562     smb_user_t	*user,
563     fs_desc_t	*fsd)
564 {
565 	smb_tree_t	*tree;
566 	smb_tree_t	*next;
567 
568 	ASSERT(user);
569 	ASSERT(user->u_magic == SMB_USER_MAGIC);
570 	ASSERT(user->u_refcnt);
571 
572 	tree = smb_tree_lookup_by_fsd(user, fsd, NULL);
573 	while (tree) {
574 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
575 		smb_tree_disconnect(tree);
576 		smbsr_rq_notify(NULL, user->u_session, tree);
577 		next = smb_tree_lookup_by_fsd(user, fsd, tree);
578 		smb_tree_release(tree);
579 		tree = next;
580 	}
581 }
582 
583 /* *************************** Static Functions ***************************** */
584 
585 /*
586  * smb_user_delete
587  *
588  *
589  */
590 static void
591 smb_user_delete(
592     smb_user_t		*user)
593 {
594 	smb_session_t	*session;
595 
596 	ASSERT(user);
597 	ASSERT(user->u_magic == SMB_USER_MAGIC);
598 	ASSERT(user->u_refcnt == 0);
599 	ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF);
600 
601 	session = user->u_session;
602 	/*
603 	 * Let's remove the user from the list of users of the session. This
604 	 * has to be done before any resources associated with the user are
605 	 * deleted.
606 	 */
607 	smb_llist_enter(&session->s_user_list, RW_WRITER);
608 	smb_llist_remove(&session->s_user_list, user);
609 	smb_llist_exit(&session->s_user_list);
610 
611 	user->u_magic = (uint32_t)~SMB_USER_MAGIC;
612 	mutex_destroy(&user->u_mutex);
613 	smb_llist_destructor(&user->u_tree_list);
614 	smb_idpool_destructor(&user->u_tid_pool);
615 	smb_idpool_free(&session->s_uid_pool, user->u_uid);
616 	crfree(user->u_cred);
617 	kmem_free(user->u_name, (size_t)user->u_name_len);
618 	kmem_free(user->u_domain, (size_t)user->u_domain_len);
619 	kmem_cache_free(user->u_server->si_cache_user, user);
620 }
621