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