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 2007 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(smb_info.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_logon_time = gethrestime_sec(); 202 user->u_flags = flags; 203 user->u_privileges = privileges; 204 user->u_name_len = strlen(account_name) + 1; 205 user->u_domain_len = strlen(domain_name) + 1; 206 user->u_name = smb_kstrdup(account_name, user->u_name_len); 207 user->u_domain = smb_kstrdup(domain_name, user->u_domain_len); 208 user->u_cred = cr; 209 user->u_audit_sid = audit_sid; 210 211 if (!smb_idpool_alloc(&session->s_uid_pool, &user->u_uid)) { 212 if (!smb_idpool_constructor(&user->u_tid_pool)) { 213 smb_llist_constructor(&user->u_tree_list, 214 sizeof (smb_tree_t), offsetof(smb_tree_t, t_lnd)); 215 mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL); 216 crhold(cr); 217 user->u_state = SMB_USER_STATE_LOGGED_IN; 218 user->u_magic = SMB_USER_MAGIC; 219 smb_llist_enter(&session->s_user_list, RW_WRITER); 220 smb_llist_insert_tail(&session->s_user_list, user); 221 smb_llist_exit(&session->s_user_list); 222 atomic_inc_32(&smb_info.open_users); 223 return (user); 224 } 225 smb_idpool_free(&session->s_uid_pool, user->u_uid); 226 } 227 kmem_free(user->u_name, (size_t)user->u_name_len); 228 kmem_free(user->u_domain, (size_t)user->u_domain_len); 229 kmem_cache_free(smb_info.si_cache_user, user); 230 return (NULL); 231 } 232 233 /* 234 * Create a new user based on an existing user, used to support 235 * additional SessionSetupX requests for a user on a session. 236 * 237 * Assumes the caller has a reference on the original user from 238 * a user_lookup_by_x call. 239 */ 240 smb_user_t * 241 smb_user_dup( 242 smb_user_t *orig_user) 243 { 244 smb_user_t *user; 245 246 ASSERT(orig_user->u_magic == SMB_USER_MAGIC); 247 ASSERT(orig_user->u_refcnt); 248 249 user = smb_user_login(orig_user->u_session, orig_user->u_cred, 250 orig_user->u_domain, orig_user->u_name, orig_user->u_flags, 251 orig_user->u_privileges, orig_user->u_audit_sid); 252 253 if (user) 254 smb_user_nonauth_logon(orig_user->u_audit_sid); 255 256 return (user); 257 } 258 259 /* 260 * smb_user_logoff 261 * 262 * 263 */ 264 void 265 smb_user_logoff( 266 smb_user_t *user) 267 { 268 ASSERT(user->u_magic == SMB_USER_MAGIC); 269 270 mutex_enter(&user->u_mutex); 271 ASSERT(user->u_refcnt); 272 switch (user->u_state) { 273 case SMB_USER_STATE_LOGGED_IN: { 274 /* 275 * The user is moved into a state indicating that the log off 276 * process has started. 277 */ 278 user->u_state = SMB_USER_STATE_LOGGING_OFF; 279 mutex_exit(&user->u_mutex); 280 atomic_dec_32(&smb_info.open_users); 281 /* 282 * All the trees hanging off of this user are disconnected. 283 */ 284 smb_tree_disconnect_all(user); 285 smb_user_auth_logoff(user->u_audit_sid); 286 mutex_enter(&user->u_mutex); 287 user->u_state = SMB_USER_STATE_LOGGED_OFF; 288 break; 289 } 290 case SMB_USER_STATE_LOGGED_OFF: 291 case SMB_USER_STATE_LOGGING_OFF: 292 break; 293 294 default: 295 ASSERT(0); 296 break; 297 } 298 mutex_exit(&user->u_mutex); 299 } 300 301 /* 302 * smb_user_logoff_all 303 * 304 * 305 */ 306 void 307 smb_user_logoff_all( 308 smb_session_t *session) 309 { 310 smb_user_t *user; 311 312 ASSERT(session); 313 ASSERT(session->s_magic == SMB_SESSION_MAGIC); 314 315 smb_llist_enter(&session->s_user_list, RW_READER); 316 user = smb_llist_head(&session->s_user_list); 317 while (user) { 318 ASSERT(user->u_magic == SMB_USER_MAGIC); 319 ASSERT(user->u_session == session); 320 mutex_enter(&user->u_mutex); 321 switch (user->u_state) { 322 case SMB_USER_STATE_LOGGED_IN: 323 /* The user is still logged in. */ 324 user->u_refcnt++; 325 mutex_exit(&user->u_mutex); 326 smb_llist_exit(&session->s_user_list); 327 smb_user_logoff(user); 328 smb_user_release(user); 329 smb_llist_enter(&session->s_user_list, RW_READER); 330 user = smb_llist_head(&session->s_user_list); 331 break; 332 case SMB_USER_STATE_LOGGING_OFF: 333 case SMB_USER_STATE_LOGGED_OFF: 334 /* 335 * The user is logged off or logging off. 336 */ 337 mutex_exit(&user->u_mutex); 338 user = smb_llist_next(&session->s_user_list, user); 339 break; 340 default: 341 ASSERT(0); 342 mutex_exit(&user->u_mutex); 343 user = smb_llist_next(&session->s_user_list, user); 344 break; 345 } 346 } 347 smb_llist_exit(&session->s_user_list); 348 } 349 350 /* 351 * smb_user_release 352 * 353 * 354 */ 355 void 356 smb_user_release( 357 smb_user_t *user) 358 { 359 ASSERT(user->u_magic == SMB_USER_MAGIC); 360 361 mutex_enter(&user->u_mutex); 362 ASSERT(user->u_refcnt); 363 user->u_refcnt--; 364 switch (user->u_state) { 365 case SMB_USER_STATE_LOGGED_OFF: 366 if (user->u_refcnt == 0) { 367 mutex_exit(&user->u_mutex); 368 smb_user_delete(user); 369 return; 370 } 371 break; 372 373 case SMB_USER_STATE_LOGGED_IN: 374 case SMB_USER_STATE_LOGGING_OFF: 375 break; 376 377 default: 378 ASSERT(0); 379 break; 380 } 381 mutex_exit(&user->u_mutex); 382 } 383 384 /* 385 * smb_user_lookup_by_uid 386 * 387 * Find the appropriate user for this request. The request credentials 388 * set here may be overridden by the tree credentials. In domain mode, 389 * the user and tree credentials should be the same. In share mode, the 390 * tree credentials (defined in the share definition) should override 391 * the user credentials. 392 */ 393 smb_user_t * 394 smb_user_lookup_by_uid( 395 smb_session_t *session, 396 cred_t **cr, 397 uint16_t uid) 398 { 399 smb_user_t *user; 400 401 ASSERT(session); 402 ASSERT(session->s_magic == SMB_SESSION_MAGIC); 403 ASSERT(cr); 404 405 smb_llist_enter(&session->s_user_list, RW_READER); 406 user = smb_llist_head(&session->s_user_list); 407 while (user) { 408 ASSERT(user->u_magic == SMB_USER_MAGIC); 409 ASSERT(user->u_session == session); 410 if (user->u_uid == uid) { 411 mutex_enter(&user->u_mutex); 412 switch (user->u_state) { 413 414 case SMB_USER_STATE_LOGGED_IN: 415 /* The user exists and is still logged in. */ 416 *cr = user->u_cred; 417 user->u_refcnt++; 418 mutex_exit(&user->u_mutex); 419 smb_llist_exit(&session->s_user_list); 420 return (user); 421 422 case SMB_USER_STATE_LOGGING_OFF: 423 case SMB_USER_STATE_LOGGED_OFF: 424 /* 425 * The user exists but has logged off or is in 426 * the process of logging off. 427 */ 428 mutex_exit(&user->u_mutex); 429 smb_llist_exit(&session->s_user_list); 430 return (NULL); 431 432 default: 433 ASSERT(0); 434 mutex_exit(&user->u_mutex); 435 smb_llist_exit(&session->s_user_list); 436 return (NULL); 437 } 438 } 439 user = smb_llist_next(&session->s_user_list, user); 440 } 441 smb_llist_exit(&session->s_user_list); 442 return (NULL); 443 } 444 445 /* 446 * smb_user_lookup_by_name 447 */ 448 smb_user_t * 449 smb_user_lookup_by_name(smb_session_t *session, char *domain, char *name) 450 { 451 smb_user_t *user; 452 smb_llist_t *ulist; 453 454 ulist = &session->s_user_list; 455 smb_llist_enter(ulist, RW_READER); 456 user = smb_llist_head(ulist); 457 while (user) { 458 ASSERT(user->u_magic == SMB_USER_MAGIC); 459 if (!utf8_strcasecmp(user->u_name, name) && 460 !utf8_strcasecmp(user->u_domain, domain)) { 461 mutex_enter(&user->u_mutex); 462 if (user->u_state == SMB_USER_STATE_LOGGED_IN) { 463 user->u_refcnt++; 464 mutex_exit(&user->u_mutex); 465 break; 466 } 467 mutex_exit(&user->u_mutex); 468 } 469 user = smb_llist_next(ulist, user); 470 } 471 smb_llist_exit(ulist); 472 473 return (user); 474 } 475 476 /* 477 * smb_user_lookup_by_state 478 * 479 * This function returns the first user in the logged in state. If the user 480 * provided is NULL the search starts from the beginning of the list passed 481 * in. It a user is provided the search starts just after that user. 482 */ 483 smb_user_t * 484 smb_user_lookup_by_state( 485 smb_session_t *session, 486 smb_user_t *user) 487 { 488 smb_llist_t *lst; 489 smb_user_t *next; 490 491 ASSERT(session); 492 ASSERT(session->s_magic == SMB_SESSION_MAGIC); 493 494 lst = &session->s_user_list; 495 496 smb_llist_enter(lst, RW_READER); 497 if (user) { 498 ASSERT(user); 499 ASSERT(user->u_magic == SMB_USER_MAGIC); 500 ASSERT(user->u_refcnt); 501 next = smb_llist_next(lst, user); 502 } else { 503 next = smb_llist_head(lst); 504 } 505 while (next) { 506 ASSERT(next->u_magic == SMB_USER_MAGIC); 507 ASSERT(next->u_session == session); 508 mutex_enter(&next->u_mutex); 509 if (next->u_state == SMB_USER_STATE_LOGGED_IN) { 510 next->u_refcnt++; 511 mutex_exit(&next->u_mutex); 512 break; 513 } else { 514 ASSERT((next->u_state == SMB_USER_STATE_LOGGING_OFF) || 515 (next->u_state == SMB_USER_STATE_LOGGED_OFF)); 516 mutex_exit(&next->u_mutex); 517 next = smb_llist_next(lst, next); 518 } 519 } 520 smb_llist_exit(lst); 521 522 return (next); 523 } 524 525 /* 526 * smb_user_disconnect_share 527 * 528 * This function disconnects all the trees that have the sharename passed in. 529 */ 530 void 531 smb_user_disconnect_share( 532 smb_user_t *user, 533 char *sharename) 534 { 535 smb_tree_t *tree; 536 smb_tree_t *next; 537 538 ASSERT(user); 539 ASSERT(user->u_magic == SMB_USER_MAGIC); 540 ASSERT(user->u_refcnt); 541 542 tree = smb_tree_lookup_by_name(user, sharename, NULL); 543 while (tree) { 544 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 545 smb_tree_disconnect(tree); 546 smbsr_rq_notify(NULL, user->u_session, tree); 547 next = smb_tree_lookup_by_name(user, sharename, 548 tree); 549 smb_tree_release(tree); 550 tree = next; 551 } 552 } 553 554 /* 555 * smb_user_disconnect_share 556 * 557 * This function disconnects all the trees that match fsd passed in. 558 */ 559 void 560 smb_user_disconnect_volume( 561 smb_user_t *user, 562 fs_desc_t *fsd) 563 { 564 smb_tree_t *tree; 565 smb_tree_t *next; 566 567 ASSERT(user); 568 ASSERT(user->u_magic == SMB_USER_MAGIC); 569 ASSERT(user->u_refcnt); 570 571 tree = smb_tree_lookup_by_fsd(user, fsd, NULL); 572 while (tree) { 573 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 574 smb_tree_disconnect(tree); 575 smbsr_rq_notify(NULL, user->u_session, tree); 576 next = smb_tree_lookup_by_fsd(user, fsd, tree); 577 smb_tree_release(tree); 578 tree = next; 579 } 580 } 581 582 /* *************************** Static Functions ***************************** */ 583 584 /* 585 * smb_user_delete 586 * 587 * 588 */ 589 static void 590 smb_user_delete( 591 smb_user_t *user) 592 { 593 smb_session_t *session; 594 595 ASSERT(user); 596 ASSERT(user->u_magic == SMB_USER_MAGIC); 597 ASSERT(user->u_refcnt == 0); 598 ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF); 599 600 session = user->u_session; 601 /* 602 * Let's remove the user from the list of users of the session. This 603 * has to be done before any resources associated with the user are 604 * deleted. 605 */ 606 smb_llist_enter(&session->s_user_list, RW_WRITER); 607 smb_llist_remove(&session->s_user_list, user); 608 smb_llist_exit(&session->s_user_list); 609 610 user->u_magic = (uint32_t)~SMB_USER_MAGIC; 611 mutex_destroy(&user->u_mutex); 612 smb_llist_destructor(&user->u_tree_list); 613 smb_idpool_destructor(&user->u_tid_pool); 614 smb_idpool_free(&session->s_uid_pool, user->u_uid); 615 crfree(user->u_cred); 616 kmem_free(user->u_name, (size_t)user->u_name_len); 617 kmem_free(user->u_domain, (size_t)user->u_domain_len); 618 kmem_cache_free(smb_info.si_cache_user, user); 619 } 620