1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2017-2022 Tintri by DDN, Inc. All rights reserved. 14 * Copyright 2021-2023 RackTop Systems, Inc. 15 */ 16 17 /* 18 * SMB2 Durable Handle support 19 */ 20 21 #include <sys/types.h> 22 #include <sys/cmn_err.h> 23 #include <sys/fcntl.h> 24 #include <sys/nbmlock.h> 25 #include <sys/sid.h> 26 #include <smbsrv/string.h> 27 #include <smbsrv/smb_kproto.h> 28 #include <smbsrv/smb_fsops.h> 29 #include <smbsrv/smbinfo.h> 30 #include <smbsrv/smb2_kproto.h> 31 32 /* Windows default values from [MS-SMB2] */ 33 /* 34 * (times in seconds) 35 * resilient: 36 * MaxTimeout = 300 (win7+) 37 * if timeout > MaxTimeout, ERROR 38 * if timeout != 0, timeout = req.timeout 39 * if timeout == 0, timeout = (infinity) (Win7/w2k8r2) 40 * if timeout == 0, timeout = 120 (Win8+) 41 * v2: 42 * if timeout != 0, timeout = MIN(timeout, 300) (spec) 43 * if timeout != 0, timeout = timeout (win8/2k12) 44 * if timeout == 0, timeout = Share.CATimeout. \ 45 * if Share.CATimeout == 0, timeout = 60 (win8/w2k12) 46 * if timeout == 0, timeout = 180 (win8.1/w2k12r2) 47 * open.timeout = 60 (win8/w2k12r2) (i.e. we ignore the request) 48 * v1: 49 * open.timeout = 16 minutes 50 */ 51 52 uint32_t smb2_dh_def_timeout = 60 * MILLISEC; /* mSec. */ 53 uint32_t smb2_dh_max_timeout = 300 * MILLISEC; /* mSec. */ 54 55 uint32_t smb2_res_def_timeout = 120 * MILLISEC; /* mSec. */ 56 uint32_t smb2_res_max_timeout = 300 * MILLISEC; /* mSec. */ 57 58 uint32_t smb2_persist_timeout = 300 * MILLISEC; /* mSec. */ 59 60 /* 61 * Max. size of the file used to store a CA handle. 62 * Don't adjust this while the server is running. 63 */ 64 static uint32_t smb2_dh_max_cah_size = 64 * 1024; 65 static uint32_t smb2_ca_info_version = 1; 66 67 /* 68 * Want this to have invariant layout on disk, where the 69 * last two uint32_t values are stored as a uint64_t 70 */ 71 struct nvlk { 72 uint64_t lk_start; 73 uint64_t lk_len; 74 /* (lk_pid << 32) | lk_type */ 75 #ifdef _BIG_ENDIAN 76 uint32_t lk_pid, lk_type; 77 #else 78 uint32_t lk_type, lk_pid; 79 #endif 80 }; 81 82 static void smb2_dh_import_share(void *); 83 static smb_ofile_t *smb2_dh_import_handle(smb_request_t *, smb_node_t *, 84 char *, uint64_t); 85 static int smb2_dh_read_nvlist(smb_request_t *, smb_node_t *, 86 char *, struct nvlist **); 87 static int smb2_dh_import_cred(smb_ofile_t *, char *); 88 89 #define DH_SN_SIZE 24 /* size of DH stream name buffers */ 90 /* 91 * Build the stream name used to store a CA handle. 92 * i.e. ":0123456789abcdef:$CA" 93 * Note: smb_fsop_create adds the SUNWsmb prefix, 94 * so we compose the name without the prefix. 95 */ 96 static inline void 97 smb2_dh_make_stream_name(char *buf, size_t buflen, uint64_t id) 98 { 99 ASSERT(buflen >= DH_SN_SIZE); 100 (void) snprintf(buf, buflen, 101 ":%016" PRIx64 ":$CA", id); 102 } 103 104 /* 105 * smb_dh_should_save 106 * 107 * During session tear-down, decide whether to keep a durable handle. 108 * 109 * There are two cases where we save durable handles: 110 * 1. An SMB2 LOGOFF request was received 111 * 2. An unexpected disconnect from the client 112 * Note: Specifying a PrevSessionID in session setup 113 * is considered a disconnect (we just haven't learned about it yet) 114 * In every other case, we close durable handles. 115 * 116 * [MS-SMB2] 3.3.5.6 SMB2_LOGOFF 117 * [MS-SMB2] 3.3.7.1 Handling Loss of a Connection 118 * 119 * If any of the following are true, preserve for reconnect: 120 * 121 * - Open.IsResilient is TRUE. 122 * 123 * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_BATCH and 124 * Open.OplockState == Held, and Open.IsDurable is TRUE. 125 * 126 * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_LEASE, 127 * Lease.LeaseState SMB2_LEASE_HANDLE_CACHING, 128 * Open.OplockState == Held, and Open.IsDurable is TRUE. 129 * 130 * - Open.IsPersistent is TRUE. 131 * 132 * We also deal with some special cases for shutdown of the 133 * server, session, user, tree (in that order). Other than 134 * the cases above, shutdown (or forced termination) should 135 * destroy durable handles. 136 */ 137 boolean_t 138 smb_dh_should_save(smb_ofile_t *of) 139 { 140 ASSERT(MUTEX_HELD(&of->f_mutex)); 141 ASSERT(of->dh_vers != SMB2_NOT_DURABLE); 142 143 /* SMB service shutting down, destroy DH */ 144 if (of->f_server->sv_state == SMB_SERVER_STATE_STOPPING) 145 return (B_FALSE); 146 147 /* 148 * SMB Session (connection) going away (server up). 149 * If server initiated disconnect, destroy DH 150 * If client initiated disconnect, save all DH. 151 */ 152 if (of->f_session->s_state == SMB_SESSION_STATE_TERMINATED) 153 return (B_FALSE); 154 if (of->f_session->s_state == SMB_SESSION_STATE_DISCONNECTED) 155 return (B_TRUE); 156 157 /* 158 * SMB User logoff, session still "up". 159 * Action depends on why/how this logoff happened, 160 * determined based on user->preserve_opens 161 */ 162 if (of->f_user->u_state == SMB_USER_STATE_LOGGING_OFF) { 163 switch (of->f_user->preserve_opens) { 164 case SMB2_DH_PRESERVE_NONE: 165 /* Server-initiated */ 166 return (B_FALSE); 167 case SMB2_DH_PRESERVE_SOME: 168 /* Previous session logoff. */ 169 goto preserve_some; 170 case SMB2_DH_PRESERVE_ALL: 171 /* Protocol logoff request */ 172 return (B_TRUE); 173 } 174 } 175 176 /* 177 * SMB tree disconnecting (user still logged on) 178 * i.e. when kshare export forces disconnection. 179 */ 180 if (of->f_tree->t_state == SMB_TREE_STATE_DISCONNECTING) 181 return (B_FALSE); 182 183 preserve_some: 184 /* preserve_opens == SMB2_DH_PRESERVE_SOME */ 185 186 switch (of->dh_vers) { 187 uint32_t ol_state; 188 189 case SMB2_RESILIENT: 190 return (B_TRUE); 191 192 case SMB2_DURABLE_V2: 193 if (of->dh_persist) 194 return (B_TRUE); 195 /* FALLTHROUGH */ 196 case SMB2_DURABLE_V1: 197 /* IS durable (v1 or v2) */ 198 if (of->f_lease != NULL) 199 ol_state = of->f_lease->ls_state; 200 else 201 ol_state = of->f_oplock.og_state; 202 if ((ol_state & (OPLOCK_LEVEL_BATCH | 203 OPLOCK_LEVEL_CACHE_HANDLE)) != 0) 204 return (B_TRUE); 205 /* FALLTHROUGH */ 206 case SMB2_NOT_DURABLE: 207 default: 208 break; 209 } 210 211 return (B_FALSE); 212 } 213 214 /* 215 * Is this stream name a CA handle? i.e. 216 * ":0123456789abcdef:$CA" 217 */ 218 static boolean_t 219 smb2_dh_match_ca_name(const char *name, uint64_t *idp) 220 { 221 static const char suffix[] = ":$CA"; 222 u_longlong_t ull; 223 const char *p = name; 224 char *p2 = NULL; 225 int len, rc; 226 227 if (*p++ != ':') 228 return (B_FALSE); 229 230 rc = ddi_strtoull(p, &p2, 16, &ull); 231 if (rc != 0 || p2 != (p + 16)) 232 return (B_FALSE); 233 p += 16; 234 235 len = sizeof (suffix) - 1; 236 if (strncmp(p, suffix, len) != 0) 237 return (B_FALSE); 238 p += len; 239 240 if (*p != '\0') 241 return (B_FALSE); 242 243 *idp = (uint64_t)ull; 244 return (B_TRUE); 245 } 246 247 /* 248 * smb2_dh_new_ca_share 249 * 250 * Called when a new share has ca=true. Find or create the CA dir, 251 * and start a thread to import persistent handles. 252 */ 253 int 254 smb2_dh_new_ca_share(smb_server_t *sv, smb_kshare_t *shr) 255 { 256 smb_kshare_t *shr2; 257 smb_request_t *sr; 258 taskqid_t tqid; 259 260 ASSERT(STYPE_ISDSK(shr->shr_type)); 261 262 /* 263 * Need to lookup the kshare again, to get a hold. 264 * Add a function to just get the hold? 265 */ 266 shr2 = smb_kshare_lookup(sv, shr->shr_name); 267 if (shr2 != shr) 268 return (EINVAL); 269 270 sr = smb_request_alloc(sv->sv_session, 0); 271 if (sr == NULL) { 272 /* shutting down? */ 273 smb_kshare_release(sv, shr); 274 return (EINTR); 275 } 276 sr->sr_state = SMB_REQ_STATE_SUBMITTED; 277 278 /* 279 * Mark this share as "busy importing persistent handles" 280 * so we can hold off tree connect until that's done. 281 * Will clear and wakeup below. 282 */ 283 mutex_enter(&shr->shr_mutex); 284 shr->shr_import_busy = sr; 285 mutex_exit(&shr->shr_mutex); 286 287 /* 288 * Start a taskq job to import any CA handles. 289 * The hold on the kshare is given to this job, 290 * which releases it when it's done. 291 */ 292 sr->arg.tcon.si = shr; /* hold from above */ 293 tqid = taskq_dispatch(sv->sv_worker_pool, 294 smb2_dh_import_share, sr, TQ_SLEEP); 295 VERIFY(tqid != TASKQID_INVALID); 296 297 return (0); 298 } 299 300 int smb2_dh_import_delay = 0; 301 302 static void 303 smb2_dh_import_share(void *arg) 304 { 305 smb_request_t *sr = arg; 306 smb_kshare_t *shr = sr->arg.tcon.si; 307 smb_node_t *snode; 308 cred_t *kcr = zone_kcred(); 309 smb_streaminfo_t *str_info = NULL; 310 char *nvl_buf = NULL; 311 uint64_t id; 312 smb_node_t *str_node; 313 smb_odir_t *od = NULL; 314 smb_ofile_t *of; 315 int rc; 316 boolean_t eof; 317 318 sr->sr_state = SMB_REQ_STATE_ACTIVE; 319 320 if (smb2_dh_import_delay > 0) 321 delay(SEC_TO_TICK(smb2_dh_import_delay)); 322 323 /* 324 * Borrow the server's "root" user. 325 * 326 * This takes the place of smb_session_lookup_ssnid() 327 * that would happen in smb2_dispatch for a normal SR. 328 * As usual, this hold is released in smb_request_free. 329 */ 330 sr->uid_user = sr->sr_server->sv_rootuser; 331 smb_user_hold_internal(sr->uid_user); 332 sr->user_cr = sr->uid_user->u_cred; 333 334 /* 335 * Create a temporary tree connect 336 */ 337 sr->tid_tree = smb_tree_alloc(sr, shr, shr->shr_root_node, 338 ACE_ALL_PERMS, 0); 339 if (sr->tid_tree == NULL) { 340 cmn_err(CE_NOTE, "smb2_dh_import_share: " 341 "failed connect share <%s>", shr->shr_name); 342 goto out; 343 } 344 snode = sr->tid_tree->t_snode; 345 346 /* 347 * Get the buffers we'll use to read CA handle data. 348 * Also get a buffer for the stream name info. 349 */ 350 nvl_buf = kmem_alloc(smb2_dh_max_cah_size, KM_SLEEP); 351 str_info = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP); 352 353 /* 354 * Open the ext. attr dir under the share root and 355 * import CA handles for this share. 356 */ 357 if (smb_odir_openat(sr, snode, &od, B_FALSE) != 0) { 358 cmn_err(CE_NOTE, "!Share [%s] CA import, no xattr dir?", 359 shr->shr_name); 360 goto out; 361 } 362 363 eof = B_FALSE; 364 do { 365 /* 366 * If the kshare gets unshared before we finish, 367 * bail out so we don't hold things up. 368 */ 369 if (shr->shr_flags & SMB_SHRF_REMOVED) 370 break; 371 372 /* 373 * If the server's stopping, no point importing. 374 */ 375 if (smb_server_is_stopping(sr->sr_server)) 376 break; 377 378 /* 379 * Read a stream name and info 380 */ 381 rc = smb_odir_read_streaminfo(sr, od, str_info, &eof); 382 if ((rc != 0) || (eof)) 383 break; 384 385 /* 386 * Skip anything not a CA handle. 387 */ 388 if (!smb2_dh_match_ca_name(str_info->si_name, &id)) { 389 continue; 390 } 391 392 /* 393 * Lookup stream node and import 394 */ 395 str_node = NULL; 396 rc = smb_fsop_lookup_name(sr, kcr, SMB_CASE_SENSITIVE, 397 snode, snode, str_info->si_name, &str_node); 398 if (rc != 0) { 399 cmn_err(CE_NOTE, "Share [%s] CA import, " 400 "lookup <%s> failed rc=%d", 401 shr->shr_name, str_info->si_name, rc); 402 continue; 403 } 404 of = smb2_dh_import_handle(sr, str_node, nvl_buf, id); 405 smb_node_release(str_node); 406 if (of != NULL) { 407 smb_ofile_release(of); 408 of = NULL; 409 } 410 sr->fid_ofile = NULL; 411 smb_lavl_flush(&sr->tid_tree->t_ofile_list); 412 413 } while (!eof); 414 415 out: 416 if (od != NULL) { 417 smb_odir_close(od); 418 smb_odir_release(od); 419 } 420 421 if (str_info != NULL) 422 kmem_free(str_info, sizeof (smb_streaminfo_t)); 423 if (nvl_buf != NULL) 424 kmem_free(nvl_buf, smb2_dh_max_cah_size); 425 426 /* 427 * We did a (temporary, internal) tree connect above, 428 * which we need to undo before we return. Note that 429 * smb_request_free will do the final release of 430 * sr->tid_tree, sr->uid_user 431 */ 432 if (sr->tid_tree != NULL) 433 smb_tree_disconnect(sr->tid_tree, B_FALSE); 434 435 /* 436 * Wake up any waiting tree connect(s). 437 * See smb_tree_connect_disk(). 438 */ 439 mutex_enter(&shr->shr_mutex); 440 shr->shr_import_busy = NULL; 441 cv_broadcast(&shr->shr_cv); 442 mutex_exit(&shr->shr_mutex); 443 444 smb_kshare_release(sr->sr_server, shr); 445 smb_request_free(sr); 446 } 447 448 /* 449 * This returns the new ofile mostly for dtrace. 450 */ 451 static smb_ofile_t * 452 smb2_dh_import_handle(smb_request_t *sr, smb_node_t *str_node, 453 char *nvl_buf, uint64_t persist_id) 454 { 455 uint8_t client_uuid[UUID_LEN]; 456 smb_tree_t *tree = sr->tid_tree; 457 smb_arg_open_t *op = &sr->arg.open; 458 smb_pathname_t *pn = &op->fqi.fq_path; 459 cred_t *kcr = zone_kcred(); 460 struct nvlist *nvl = NULL; 461 char *sidstr = NULL; 462 smb_ofile_t *of = NULL; 463 smb_attr_t *pa; 464 boolean_t did_open = B_FALSE; 465 boolean_t have_lease = B_FALSE; 466 hrtime_t hrt; 467 uint64_t *u64p; 468 uint64_t u64; 469 uint32_t u32; 470 uint32_t status; 471 char *s; 472 uint8_t *u8p; 473 uint_t alen; 474 int rc; 475 476 /* 477 * While we're called with arg.tcon, we now want to use 478 * smb_arg_open for the rest of import, so clear it. 479 */ 480 bzero(op, sizeof (*op)); 481 op->create_disposition = FILE_OPEN; 482 483 /* 484 * Read and unpack the NVL 485 */ 486 rc = smb2_dh_read_nvlist(sr, str_node, nvl_buf, &nvl); 487 if (rc != 0) 488 return (NULL); 489 490 /* 491 * Known CA info version? 492 */ 493 u32 = 0; 494 rc = nvlist_lookup_uint32(nvl, "info_version", &u32); 495 if (rc != 0 || u32 != smb2_ca_info_version) { 496 cmn_err(CE_NOTE, "CA import (%s/%s) bad vers=%d", 497 tree->t_resource, str_node->od_name, u32); 498 goto errout; 499 } 500 501 /* 502 * The persist ID in the nvlist should match the one 503 * encoded in the file name. (not enforced) 504 */ 505 u64 = 0; 506 rc = nvlist_lookup_uint64(nvl, "file_persistid", &u64); 507 if (rc != 0 || u64 != persist_id) { 508 cmn_err(CE_WARN, "CA import (%s/%s) bad id=%016" PRIx64, 509 tree->t_resource, str_node->od_name, u64); 510 /* goto errout? (allow) */ 511 } 512 513 /* 514 * Does it belong in the share being imported? 515 */ 516 s = NULL; 517 rc = nvlist_lookup_string(nvl, "share_name", &s); 518 if (rc != 0) { 519 cmn_err(CE_NOTE, "CA import (%s/%s) no share_name", 520 tree->t_resource, str_node->od_name); 521 goto errout; 522 } 523 if (smb_strcasecmp(s, tree->t_sharename, 0) != 0) { 524 /* Normal (not an error) */ 525 #ifdef DEBUG 526 cmn_err(CE_NOTE, "CA import (%s/%s) other share", 527 tree->t_resource, str_node->od_name); 528 #endif 529 goto errout; 530 } 531 532 /* 533 * Get the path name (for lookup) 534 */ 535 rc = nvlist_lookup_string(nvl, "path_name", &pn->pn_path); 536 if (rc != 0) { 537 cmn_err(CE_NOTE, "CA import (%s/%s) no path_name", 538 tree->t_resource, str_node->od_name); 539 goto errout; 540 } 541 542 /* 543 * owner sid 544 */ 545 rc = nvlist_lookup_string(nvl, "owner_sid", &sidstr); 546 if (rc != 0) { 547 cmn_err(CE_NOTE, "CA import (%s/%s) no owner_sid", 548 tree->t_resource, str_node->od_name); 549 goto errout; 550 } 551 552 /* 553 * granted access 554 */ 555 rc = nvlist_lookup_uint32(nvl, 556 "granted_access", &op->desired_access); 557 if (rc != 0) { 558 cmn_err(CE_NOTE, "CA import (%s/%s) no granted_access", 559 tree->t_resource, str_node->od_name); 560 goto errout; 561 } 562 563 /* 564 * share access 565 */ 566 rc = nvlist_lookup_uint32(nvl, 567 "share_access", &op->share_access); 568 if (rc != 0) { 569 cmn_err(CE_NOTE, "CA import (%s/%s) no share_access", 570 tree->t_resource, str_node->od_name); 571 goto errout; 572 } 573 574 /* 575 * create options 576 */ 577 rc = nvlist_lookup_uint32(nvl, 578 "create_options", &op->create_options); 579 if (rc != 0) { 580 cmn_err(CE_NOTE, "CA import (%s/%s) no create_options", 581 tree->t_resource, str_node->od_name); 582 goto errout; 583 } 584 585 /* 586 * create guid (client-assigned) 587 */ 588 alen = UUID_LEN; 589 u8p = NULL; 590 rc = nvlist_lookup_uint8_array(nvl, "file_guid", &u8p, &alen); 591 if (rc != 0 || alen != UUID_LEN) { 592 cmn_err(CE_NOTE, "CA import (%s/%s) bad file_guid", 593 tree->t_resource, str_node->od_name); 594 goto errout; 595 } 596 bcopy(u8p, op->create_guid, UUID_LEN); 597 598 /* 599 * client uuid (identifies the client) 600 */ 601 alen = UUID_LEN; 602 u8p = NULL; 603 rc = nvlist_lookup_uint8_array(nvl, "client_uuid", &u8p, &alen); 604 if (rc != 0 || alen != UUID_LEN) { 605 cmn_err(CE_NOTE, "CA import (%s/%s) no client_uuid", 606 tree->t_resource, str_node->od_name); 607 goto errout; 608 } 609 bcopy(u8p, client_uuid, UUID_LEN); 610 611 /* 612 * Lease key (optional) 613 */ 614 alen = SMB_LEASE_KEY_SZ; 615 u8p = NULL; 616 rc = nvlist_lookup_uint8_array(nvl, "lease_uuid", &u8p, &alen); 617 if (rc == 0) { 618 bcopy(u8p, op->lease_key, UUID_LEN); 619 (void) nvlist_lookup_uint32(nvl, 620 "lease_state", &op->lease_state); 621 (void) nvlist_lookup_uint16(nvl, 622 "lease_epoch", &op->lease_epoch); 623 (void) nvlist_lookup_uint16(nvl, 624 "lease_version", &op->lease_version); 625 have_lease = B_TRUE; 626 } else { 627 (void) nvlist_lookup_uint32(nvl, 628 "oplock_state", &op->op_oplock_state); 629 } 630 631 /* 632 * Done getting what we need from the NV list. 633 * (re)open the file 634 */ 635 status = smb_common_open(sr); 636 if (status != 0) { 637 cmn_err(CE_NOTE, "CA import (%s/%s) open failed 0x%x", 638 tree->t_resource, str_node->od_name, status); 639 (void) smb_node_set_delete_on_close(str_node, kcr, 0); 640 goto errout; 641 } 642 of = sr->fid_ofile; 643 did_open = B_TRUE; 644 645 /* 646 * Now restore the rest of the SMB2 level state. 647 * See smb2_create after smb_common_open 648 */ 649 650 /* 651 * Setup of->f_cr with owner SID 652 */ 653 rc = smb2_dh_import_cred(of, sidstr); 654 if (rc != 0) { 655 cmn_err(CE_NOTE, "CA import (%s/%s) import cred failed", 656 tree->t_resource, str_node->od_name); 657 goto errout; 658 } 659 660 /* 661 * Use the persist ID we previously assigned. 662 * Like smb_ofile_set_persistid_ph() 663 */ 664 rc = smb_ofile_insert_persistid(of, persist_id); 665 if (rc != 0) { 666 cmn_err(CE_NOTE, "CA import (%s/%s) " 667 "insert_persistid rc=%d", 668 tree->t_resource, str_node->od_name, rc); 669 goto errout; 670 } 671 672 /* 673 * Like smb2_lease_create() 674 * 675 * Lease state is stored in each persistent handle, but 676 * only one handle has the state we want. As we import 677 * each handle, "upgrade" the lease if the handle we're 678 * importing has a "better" lease state (higher epoch or 679 * more cache rights). After all handles are imported, 680 * that will get the lease to the right state. 681 */ 682 if (have_lease) { 683 smb_lease_t *ls; 684 status = smb2_lease_create(sr, client_uuid); 685 if (status != 0) { 686 cmn_err(CE_NOTE, "CA import (%s/%s) get lease 0x%x", 687 tree->t_resource, str_node->od_name, status); 688 goto errout; 689 } 690 ls = of->f_lease; 691 692 /* Use most current "epoch". */ 693 mutex_enter(&ls->ls_mutex); 694 if (ls->ls_epoch < op->lease_epoch) 695 ls->ls_epoch = op->lease_epoch; 696 mutex_exit(&ls->ls_mutex); 697 698 /* 699 * Get the lease (and oplock) 700 * uses op->lease_state 701 */ 702 op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE; 703 smb2_lease_acquire(sr); 704 705 } else { 706 /* 707 * No lease; maybe get an oplock 708 * uses: op->op_oplock_level 709 */ 710 if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) { 711 op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH; 712 } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) { 713 op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; 714 } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) { 715 op->op_oplock_level = SMB2_OPLOCK_LEVEL_II; 716 } else { 717 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 718 } 719 smb2_oplock_acquire(sr); 720 } 721 722 /* 723 * Byte range locks 724 */ 725 alen = 0; 726 u64p = NULL; 727 if (nvlist_lookup_uint64_array(nvl, "locks", &u64p, &alen) == 0) { 728 uint_t i; 729 uint_t nlocks = alen / 3; 730 struct nvlk *nlp; 731 732 nlp = (struct nvlk *)u64p; 733 for (i = 0; i < nlocks; i++) { 734 status = smb_lock_range( 735 sr, 736 nlp->lk_start, 737 nlp->lk_len, 738 nlp->lk_pid, 739 nlp->lk_type, 740 0); 741 if (status != 0) { 742 cmn_err(CE_NOTE, "CA import (%s/%s) " 743 "get lock %d failed 0x%x", 744 tree->t_resource, 745 str_node->od_name, 746 i, status); 747 } 748 nlp++; 749 } 750 } 751 alen = SMB_OFILE_LSEQ_MAX; 752 u8p = NULL; 753 if (nvlist_lookup_uint8_array(nvl, "lockseq", &u8p, &alen) == 0) { 754 if (alen != SMB_OFILE_LSEQ_MAX) { 755 cmn_err(CE_NOTE, "CA import (%s/%s) " 756 "get lockseq bad len=%d", 757 tree->t_resource, 758 str_node->od_name, 759 alen); 760 } else { 761 mutex_enter(&of->f_mutex); 762 bcopy(u8p, of->f_lock_seq, alen); 763 mutex_exit(&of->f_mutex); 764 } 765 } 766 767 /* 768 * Optional "sticky" times (set pending attributes) 769 */ 770 mutex_enter(&of->f_mutex); 771 pa = &of->f_pending_attr; 772 if (nvlist_lookup_hrtime(nvl, "atime", &hrt) == 0) { 773 hrt2ts(hrt, &pa->sa_vattr.va_atime); 774 pa->sa_mask |= SMB_AT_ATIME; 775 } 776 if (nvlist_lookup_hrtime(nvl, "mtime", &hrt) == 0) { 777 hrt2ts(hrt, &pa->sa_vattr.va_mtime); 778 pa->sa_mask |= SMB_AT_MTIME; 779 } 780 if (nvlist_lookup_hrtime(nvl, "ctime", &hrt) == 0) { 781 hrt2ts(hrt, &pa->sa_vattr.va_ctime); 782 pa->sa_mask |= SMB_AT_CTIME; 783 } 784 mutex_exit(&of->f_mutex); 785 786 /* 787 * Make durable and persistent. 788 * See smb2_dh_make_persistent() 789 */ 790 of->dh_vers = SMB2_DURABLE_V2; 791 bcopy(op->create_guid, of->dh_create_guid, UUID_LEN); 792 of->dh_persist = B_TRUE; 793 of->dh_nvfile = str_node; 794 smb_node_ref(str_node); 795 of->dh_nvlist = nvl; 796 nvl = NULL; 797 798 /* 799 * Now make it state orphaned... 800 * See smb_ofile_drop(), then 801 * smb_ofile_save_dh() 802 */ 803 mutex_enter(&of->f_mutex); 804 of->f_state = SMB_OFILE_STATE_SAVE_DH; 805 of->dh_timeout_offset = MSEC2NSEC(smb2_persist_timeout); 806 mutex_exit(&of->f_mutex); 807 808 /* 809 * Finished! 810 */ 811 return (of); 812 813 errout: 814 if (did_open) { 815 smb_ofile_close(of, 0); 816 smb_ofile_release(of); 817 } else { 818 ASSERT(of == NULL); 819 } 820 821 if (nvl != NULL) 822 nvlist_free(nvl); 823 824 return (NULL); 825 } 826 827 static int 828 smb2_dh_read_nvlist(smb_request_t *sr, smb_node_t *node, 829 char *fbuf, struct nvlist **nvlpp) 830 { 831 smb_attr_t attr; 832 iovec_t iov; 833 uio_t uio; 834 smb_tree_t *tree = sr->tid_tree; 835 cred_t *kcr = zone_kcred(); 836 size_t flen; 837 int rc; 838 839 bzero(&attr, sizeof (attr)); 840 attr.sa_mask = SMB_AT_SIZE; 841 rc = smb_node_getattr(NULL, node, kcr, NULL, &attr); 842 if (rc != 0) { 843 cmn_err(CE_NOTE, "CA import (%s/%s) getattr rc=%d", 844 tree->t_resource, node->od_name, rc); 845 return (rc); 846 } 847 848 if (attr.sa_vattr.va_size < 4 || 849 attr.sa_vattr.va_size > smb2_dh_max_cah_size) { 850 cmn_err(CE_NOTE, "CA import (%s/%s) bad size=%" PRIu64, 851 tree->t_resource, node->od_name, 852 (uint64_t)attr.sa_vattr.va_size); 853 return (EINVAL); 854 } 855 flen = (size_t)attr.sa_vattr.va_size; 856 857 bzero(&uio, sizeof (uio)); 858 iov.iov_base = fbuf; 859 iov.iov_len = flen; 860 uio.uio_iov = &iov; 861 uio.uio_iovcnt = 1; 862 uio.uio_resid = flen; 863 uio.uio_segflg = UIO_SYSSPACE; 864 uio.uio_extflg = UIO_COPY_DEFAULT; 865 rc = smb_fsop_read(sr, kcr, node, NULL, &uio, 0); 866 if (rc != 0) { 867 cmn_err(CE_NOTE, "CA import (%s/%s) read, rc=%d", 868 tree->t_resource, node->od_name, rc); 869 return (rc); 870 } 871 if (uio.uio_resid != 0) { 872 cmn_err(CE_NOTE, "CA import (%s/%s) short read", 873 tree->t_resource, node->od_name); 874 return (EIO); 875 } 876 877 rc = nvlist_unpack(fbuf, flen, nvlpp, KM_SLEEP); 878 if (rc != 0) { 879 cmn_err(CE_NOTE, "CA import (%s/%s) unpack, rc=%d", 880 tree->t_resource, node->od_name, rc); 881 return (rc); 882 } 883 884 return (0); 885 } 886 887 /* 888 * Setup a vestigial credential in of->f_cr just good enough for 889 * smb_is_same_user to determine if the caller owned this ofile. 890 * At reconnect, of->f_cr will be replaced with the caller's. 891 */ 892 static int 893 smb2_dh_import_cred(smb_ofile_t *of, char *sidstr) 894 { 895 #ifdef _FAKE_KERNEL 896 _NOTE(ARGUNUSED(sidstr)) 897 /* fksmbd doesn't have real credentials. */ 898 of->f_cr = CRED(); 899 crhold(of->f_cr); 900 #else 901 char tmpstr[SMB_SID_STRSZ]; 902 ksid_t ksid; 903 cred_t *cr, *oldcr; 904 int rc; 905 906 (void) strlcpy(tmpstr, sidstr, sizeof (tmpstr)); 907 bzero(&ksid, sizeof (ksid)); 908 909 rc = smb_sid_splitstr(tmpstr, &ksid.ks_rid); 910 if (rc != 0) 911 return (rc); 912 cr = crget(); 913 914 ksid.ks_domain = ksid_lookupdomain(tmpstr); 915 crsetsid(cr, &ksid, KSID_USER); 916 ksiddomain_hold(ksid.ks_domain); 917 crsetsid(cr, &ksid, KSID_OWNER); 918 919 /* 920 * Just to avoid leaving the KSID_GROUP slot NULL, 921 * put the "everyone" SID there (S-1-1-0). 922 */ 923 ksid.ks_domain = ksid_lookupdomain("S-1-1"); 924 ksid.ks_rid = 0; 925 crsetsid(cr, &ksid, KSID_GROUP); 926 927 oldcr = of->f_cr; 928 of->f_cr = cr; 929 if (oldcr != NULL) 930 crfree(oldcr); 931 #endif 932 933 return (0); 934 } 935 936 /* 937 * Set Delete-on-Close (DoC) on the persistent state file so it will be 938 * removed when the last ref. goes away (in smb2_dh_close_persistent). 939 * 940 * This is called in just two places: 941 * (1) SMB2_close request -- client tells us to destroy the handle. 942 * (2) smb2_dh_expire -- client has forgotten about this handle. 943 * All other (server-initiated) close calls should leave these 944 * persistent state files in the file system. 945 */ 946 void 947 smb2_dh_setdoc_persistent(smb_ofile_t *of) 948 { 949 smb_node_t *strnode; 950 uint32_t status; 951 952 mutex_enter(&of->dh_nvlock); 953 if ((strnode = of->dh_nvfile) != NULL) 954 smb_node_ref(strnode); 955 mutex_exit(&of->dh_nvlock); 956 957 if (strnode != NULL) { 958 status = smb_node_set_delete_on_close(strnode, 959 zone_kcred(), SMB_CASE_SENSITIVE); 960 if (status != 0) { 961 cmn_err(CE_WARN, "Can't set DoC on CA file: %s", 962 strnode->od_name); 963 DTRACE_PROBE1(rm__ca__err, smb_ofile_t *, of); 964 } 965 smb_node_release(strnode); 966 } 967 } 968 969 /* 970 * During ofile close, free the persistent handle state nvlist and 971 * drop our reference to the state file node (which may unlink it 972 * if smb2_dh_setdoc_persistent was called). 973 */ 974 void 975 smb2_dh_close_persistent(smb_ofile_t *of) 976 { 977 smb_node_t *strnode; 978 struct nvlist *nvl; 979 980 /* 981 * Clear out nvlist and stream linkage 982 */ 983 mutex_enter(&of->dh_nvlock); 984 strnode = of->dh_nvfile; 985 of->dh_nvfile = NULL; 986 nvl = of->dh_nvlist; 987 of->dh_nvlist = NULL; 988 mutex_exit(&of->dh_nvlock); 989 990 if (nvl != NULL) 991 nvlist_free(nvl); 992 993 if (strnode != NULL) 994 smb_node_release(strnode); 995 } 996 997 /* 998 * Make this durable handle persistent. 999 * If we succeed, set of->dh_persist = TRUE. 1000 */ 1001 int 1002 smb2_dh_make_persistent(smb_request_t *sr, smb_ofile_t *of) 1003 { 1004 char fname[DH_SN_SIZE]; 1005 char sidstr[SMB_SID_STRSZ]; 1006 smb_attr_t attr; 1007 smb_arg_open_t *op = &sr->arg.open; 1008 cred_t *kcr = zone_kcred(); 1009 smb_node_t *dnode = of->f_tree->t_snode; 1010 smb_node_t *fnode = NULL; 1011 ksid_t *ksid; 1012 int rc; 1013 1014 ASSERT(of->dh_nvfile == NULL); 1015 1016 /* 1017 * Create the persistent handle nvlist file. 1018 * It's a named stream in the share root. 1019 */ 1020 smb2_dh_make_stream_name(fname, sizeof (fname), of->f_persistid); 1021 1022 bzero(&attr, sizeof (attr)); 1023 attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_SIZE; 1024 attr.sa_vattr.va_type = VREG; 1025 attr.sa_vattr.va_mode = 0640; 1026 attr.sa_vattr.va_size = 4; 1027 rc = smb_fsop_create(sr, kcr, dnode, fname, &attr, &fnode); 1028 if (rc != 0) 1029 return (rc); 1030 1031 mutex_enter(&of->dh_nvlock); 1032 1033 /* fnode is held. rele in smb2_dh_close_persistent */ 1034 of->dh_nvfile = fnode; 1035 (void) nvlist_alloc(&of->dh_nvlist, NV_UNIQUE_NAME, KM_SLEEP); 1036 1037 /* 1038 * Want the ksid as a string 1039 */ 1040 ksid = crgetsid(of->f_user->u_cred, KSID_USER); 1041 (void) snprintf(sidstr, sizeof (sidstr), "%s-%u", 1042 ksid->ks_domain->kd_name, ksid->ks_rid); 1043 1044 /* 1045 * Fill in the fixed parts of the nvlist 1046 */ 1047 (void) nvlist_add_uint32(of->dh_nvlist, 1048 "info_version", smb2_ca_info_version); 1049 (void) nvlist_add_string(of->dh_nvlist, 1050 "owner_sid", sidstr); 1051 (void) nvlist_add_string(of->dh_nvlist, 1052 "share_name", of->f_tree->t_sharename); 1053 (void) nvlist_add_uint64(of->dh_nvlist, 1054 "file_persistid", of->f_persistid); 1055 (void) nvlist_add_uint8_array(of->dh_nvlist, 1056 "file_guid", of->dh_create_guid, UUID_LEN); 1057 (void) nvlist_add_string(of->dh_nvlist, 1058 "client_ipaddr", sr->session->ip_addr_str); 1059 (void) nvlist_add_uint8_array(of->dh_nvlist, 1060 "client_uuid", sr->session->clnt_uuid, UUID_LEN); 1061 (void) nvlist_add_string(of->dh_nvlist, 1062 "path_name", op->fqi.fq_path.pn_path); 1063 (void) nvlist_add_uint32(of->dh_nvlist, 1064 "granted_access", of->f_granted_access); 1065 (void) nvlist_add_uint32(of->dh_nvlist, 1066 "share_access", of->f_share_access); 1067 (void) nvlist_add_uint32(of->dh_nvlist, 1068 "create_options", of->f_create_options); 1069 if (of->f_lease != NULL) { 1070 smb_lease_t *ls = of->f_lease; 1071 (void) nvlist_add_uint8_array(of->dh_nvlist, 1072 "lease_uuid", ls->ls_key, 16); 1073 (void) nvlist_add_uint32(of->dh_nvlist, 1074 "lease_state", ls->ls_state); 1075 (void) nvlist_add_uint16(of->dh_nvlist, 1076 "lease_epoch", ls->ls_epoch); 1077 (void) nvlist_add_uint16(of->dh_nvlist, 1078 "lease_version", ls->ls_version); 1079 } else { 1080 (void) nvlist_add_uint32(of->dh_nvlist, 1081 "oplock_state", of->f_oplock.og_state); 1082 } 1083 mutex_exit(&of->dh_nvlock); 1084 1085 smb2_dh_update_locks(sr, of); 1086 1087 /* Tell sr update nvlist file */ 1088 sr->dh_nvl_dirty = B_TRUE; 1089 1090 return (0); 1091 } 1092 1093 void 1094 smb2_dh_update_nvfile(smb_request_t *sr) 1095 { 1096 smb_attr_t attr; 1097 iovec_t iov; 1098 uio_t uio; 1099 smb_ofile_t *of = sr->fid_ofile; 1100 cred_t *kcr = zone_kcred(); 1101 char *buf = NULL; 1102 size_t buflen = 0; 1103 uint32_t wcnt; 1104 int rc; 1105 1106 if (of == NULL || of->dh_persist == B_FALSE) 1107 return; 1108 1109 mutex_enter(&of->dh_nvlock); 1110 if (of->dh_nvlist == NULL || of->dh_nvfile == NULL) { 1111 mutex_exit(&of->dh_nvlock); 1112 return; 1113 } 1114 1115 rc = nvlist_size(of->dh_nvlist, &buflen, NV_ENCODE_XDR); 1116 if (rc != 0) 1117 goto out; 1118 buf = kmem_zalloc(buflen, KM_SLEEP); 1119 1120 rc = nvlist_pack(of->dh_nvlist, &buf, &buflen, 1121 NV_ENCODE_XDR, KM_SLEEP); 1122 if (rc != 0) 1123 goto out; 1124 1125 bzero(&attr, sizeof (attr)); 1126 attr.sa_mask = SMB_AT_SIZE; 1127 attr.sa_vattr.va_size = buflen; 1128 rc = smb_node_setattr(sr, of->dh_nvfile, kcr, NULL, &attr); 1129 if (rc != 0) 1130 goto out; 1131 1132 bzero(&uio, sizeof (uio)); 1133 iov.iov_base = (void *) buf; 1134 iov.iov_len = buflen; 1135 uio.uio_iov = &iov; 1136 uio.uio_iovcnt = 1; 1137 uio.uio_resid = buflen; 1138 uio.uio_segflg = UIO_SYSSPACE; 1139 uio.uio_extflg = UIO_COPY_DEFAULT; 1140 rc = smb_fsop_write(sr, kcr, of->dh_nvfile, 1141 NULL, &uio, &wcnt, 0); 1142 if (rc == 0 && wcnt != buflen) 1143 rc = EIO; 1144 1145 out: 1146 mutex_exit(&of->dh_nvlock); 1147 1148 if (rc != 0) { 1149 cmn_err(CE_WARN, 1150 "clnt(%s) failed to update persistent handle, rc=%d", 1151 sr->session->ip_addr_str, rc); 1152 } 1153 1154 if (buf != NULL) { 1155 kmem_free(buf, buflen); 1156 } 1157 } 1158 1159 /* 1160 * Called after f_oplock (and lease) changes 1161 * If lease, update: lease_state, lease_epoch 1162 * else (oplock) update: oplock_state 1163 */ 1164 void 1165 smb2_dh_update_oplock(smb_request_t *sr, smb_ofile_t *of) 1166 { 1167 smb_lease_t *ls; 1168 1169 mutex_enter(&of->dh_nvlock); 1170 if (of->dh_nvlist == NULL) { 1171 mutex_exit(&of->dh_nvlock); 1172 return; 1173 } 1174 1175 if (of->f_lease != NULL) { 1176 ls = of->f_lease; 1177 (void) nvlist_add_uint32(of->dh_nvlist, 1178 "lease_state", ls->ls_state); 1179 (void) nvlist_add_uint16(of->dh_nvlist, 1180 "lease_epoch", ls->ls_epoch); 1181 } else { 1182 (void) nvlist_add_uint32(of->dh_nvlist, 1183 "oplock_state", of->f_oplock.og_state); 1184 } 1185 mutex_exit(&of->dh_nvlock); 1186 1187 sr->dh_nvl_dirty = B_TRUE; 1188 } 1189 1190 /* 1191 * Save locks from this ofile as an array of uint64_t, where the 1192 * elements are triplets: (start, length, (pid << 32) | type) 1193 * Note pid should always be zero for SMB2, so we could use 1194 * that 32-bit spot for something else if needed. 1195 */ 1196 void 1197 smb2_dh_update_locks(smb_request_t *sr, smb_ofile_t *of) 1198 { 1199 uint8_t lseq[SMB_OFILE_LSEQ_MAX]; 1200 smb_node_t *node = of->f_node; 1201 smb_llist_t *llist = &node->n_lock_list; 1202 size_t vec_sz; // storage size 1203 uint_t my_cnt = 0; 1204 uint64_t *vec = NULL; 1205 struct nvlk *nlp; 1206 smb_lock_t *lock; 1207 1208 smb_llist_enter(llist, RW_READER); 1209 vec_sz = (llist->ll_count + 1) * sizeof (struct nvlk); 1210 vec = kmem_alloc(vec_sz, KM_SLEEP); 1211 nlp = (struct nvlk *)vec; 1212 for (lock = smb_llist_head(llist); 1213 lock != NULL; 1214 lock = smb_llist_next(llist, lock)) { 1215 if (lock->l_file != of) 1216 continue; 1217 nlp->lk_start = lock->l_start; 1218 nlp->lk_len = lock->l_length; 1219 nlp->lk_pid = lock->l_pid; 1220 nlp->lk_type = lock->l_type; 1221 nlp++; 1222 my_cnt++; 1223 } 1224 smb_llist_exit(llist); 1225 1226 mutex_enter(&of->f_mutex); 1227 bcopy(of->f_lock_seq, lseq, sizeof (lseq)); 1228 mutex_exit(&of->f_mutex); 1229 1230 mutex_enter(&of->dh_nvlock); 1231 if (of->dh_nvlist != NULL) { 1232 1233 (void) nvlist_add_uint64_array(of->dh_nvlist, 1234 "locks", vec, my_cnt * 3); 1235 1236 (void) nvlist_add_uint8_array(of->dh_nvlist, 1237 "lockseq", lseq, sizeof (lseq)); 1238 } 1239 mutex_exit(&of->dh_nvlock); 1240 1241 kmem_free(vec, vec_sz); 1242 1243 sr->dh_nvl_dirty = B_TRUE; 1244 } 1245 1246 /* 1247 * Save "sticky" times 1248 */ 1249 void 1250 smb2_dh_update_times(smb_request_t *sr, smb_ofile_t *of, smb_attr_t *attr) 1251 { 1252 hrtime_t t; 1253 1254 mutex_enter(&of->dh_nvlock); 1255 if (of->dh_nvlist == NULL) { 1256 mutex_exit(&of->dh_nvlock); 1257 return; 1258 } 1259 1260 if (attr->sa_mask & SMB_AT_ATIME) { 1261 t = ts2hrt(&attr->sa_vattr.va_atime); 1262 (void) nvlist_add_hrtime(of->dh_nvlist, "atime", t); 1263 } 1264 if (attr->sa_mask & SMB_AT_MTIME) { 1265 t = ts2hrt(&attr->sa_vattr.va_mtime); 1266 (void) nvlist_add_hrtime(of->dh_nvlist, "mtime", t); 1267 } 1268 if (attr->sa_mask & SMB_AT_CTIME) { 1269 t = ts2hrt(&attr->sa_vattr.va_ctime); 1270 (void) nvlist_add_hrtime(of->dh_nvlist, "ctime", t); 1271 } 1272 mutex_exit(&of->dh_nvlock); 1273 1274 sr->dh_nvl_dirty = B_TRUE; 1275 } 1276 1277 1278 /* 1279 * Requirements for ofile found during reconnect (MS-SMB2 3.3.5.9.7): 1280 * - security descriptor must match provided descriptor 1281 * 1282 * If file is leased: 1283 * - lease must be requested 1284 * - client guid must match session guid 1285 * - file name must match given name 1286 * - lease key must match provided lease key 1287 * If file is not leased: 1288 * - Lease must not be requested 1289 * 1290 * dh_v2 only: 1291 * - SMB2_DHANDLE_FLAG_PERSISTENT must be set if dh_persist is true 1292 * - SMB2_DHANDLE_FLAG_PERSISTENT must not be set if dh_persist is false 1293 * - desired access, share access, and create_options must be ignored 1294 * - createguid must match 1295 */ 1296 static uint32_t 1297 smb2_dh_reconnect_checks(smb_request_t *sr, smb_ofile_t *of) 1298 { 1299 smb_arg_open_t *op = &sr->sr_open; 1300 char *fname; 1301 1302 if (of->f_lease != NULL) { 1303 if (bcmp(sr->session->clnt_uuid, 1304 of->f_lease->ls_clnt, 16) != 0) 1305 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1306 1307 if (op->op_oplock_level != SMB2_OPLOCK_LEVEL_LEASE) 1308 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1309 if (bcmp(op->lease_key, of->f_lease->ls_key, 1310 SMB_LEASE_KEY_SZ) != 0) 1311 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1312 1313 /* 1314 * We're supposed to check the name is the same. 1315 * Not really necessary to do this, so just do 1316 * minimal effort (check last component) 1317 */ 1318 fname = strrchr(op->fqi.fq_path.pn_path, '\\'); 1319 if (fname != NULL) 1320 fname++; 1321 else 1322 fname = op->fqi.fq_path.pn_path; 1323 if (smb_strcasecmp(fname, of->f_node->od_name, 0) != 0) { 1324 #ifdef DEBUG 1325 cmn_err(CE_NOTE, "reconnect name <%s> of name <%s>", 1326 fname, of->f_node->od_name); 1327 #endif 1328 return (NT_STATUS_INVALID_PARAMETER); 1329 } 1330 } else { 1331 if (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) 1332 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1333 } 1334 1335 if (op->dh_vers == SMB2_DURABLE_V2) { 1336 boolean_t op_persist = 1337 ((op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0); 1338 if (of->dh_persist != op_persist) 1339 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1340 if (memcmp(op->create_guid, of->dh_create_guid, UUID_LEN)) 1341 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1342 } 1343 1344 if (!smb_is_same_user(sr->user_cr, of->f_cr)) 1345 return (NT_STATUS_ACCESS_DENIED); 1346 1347 return (NT_STATUS_SUCCESS); 1348 } 1349 1350 /* 1351 * [MS-SMB2] 3.3.5.9.7 and 3.3.5.9.12 (durable reconnect v1/v2) 1352 * 1353 * Looks up an ofile on the server's sv_dh_list by the persistid. 1354 * If found, it validates the request. 1355 * (see smb2_dh_reconnect_checks() for details) 1356 * If the checks are passed, add it onto the new tree's list. 1357 * 1358 * Note that the oplock break code path can get to an ofile via the node 1359 * ofile list. It starts with a ref taken in smb_ofile_hold_olbrk, which 1360 * waits if the ofile is found in state RECONNECT. That wait happens with 1361 * the node ofile list lock held as reader, and the oplock mutex held. 1362 * Implications of that are: While we're in state RECONNECT, we shoud NOT 1363 * block (at least, not for long) and must not try to enter any of the 1364 * node ofile list lock or oplock mutex. Thankfully, we don't need to 1365 * enter those while reclaiming an orphaned ofile. 1366 */ 1367 uint32_t 1368 smb2_dh_reconnect(smb_request_t *sr) 1369 { 1370 smb_arg_open_t *op = &sr->sr_open; 1371 smb_tree_t *tree = sr->tid_tree; 1372 smb_ofile_t *of; 1373 cred_t *old_cr; 1374 uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND; 1375 uint16_t fid = 0; 1376 1377 if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) 1378 return (NT_STATUS_TOO_MANY_OPENED_FILES); 1379 1380 /* Find orphaned handle. */ 1381 of = smb_ofile_lookup_by_persistid(sr, op->dh_fileid.persistent); 1382 if (of == NULL) 1383 goto errout; 1384 1385 mutex_enter(&of->f_mutex); 1386 if (of->f_state != SMB_OFILE_STATE_ORPHANED) { 1387 mutex_exit(&of->f_mutex); 1388 goto errout; 1389 } 1390 1391 status = smb2_dh_reconnect_checks(sr, of); 1392 if (status != NT_STATUS_SUCCESS) { 1393 mutex_exit(&of->f_mutex); 1394 goto errout; 1395 } 1396 1397 /* 1398 * Note: cv_broadcast(&of->f_cv) when we're 1399 * done messing around in this state. 1400 * See: smb_ofile_hold_olbrk() 1401 */ 1402 of->f_state = SMB_OFILE_STATE_RECONNECT; 1403 mutex_exit(&of->f_mutex); 1404 1405 /* 1406 * At this point, we should be the only thread with a ref on the 1407 * ofile, and the RECONNECT state should prevent new refs from 1408 * being granted, or other durable threads from observing or 1409 * reclaiming it. Put this ofile in the new tree, similar to 1410 * the last part of smb_ofile_open. 1411 */ 1412 1413 old_cr = of->f_cr; 1414 of->f_cr = sr->user_cr; 1415 crhold(of->f_cr); 1416 crfree(old_cr); 1417 1418 of->f_session = sr->session; /* hold is via user and tree */ 1419 smb_user_hold_internal(sr->uid_user); 1420 of->f_user = sr->uid_user; 1421 smb_tree_hold_internal(tree); 1422 of->f_tree = tree; 1423 of->f_fid = fid; 1424 1425 smb_lavl_enter(&tree->t_ofile_list, RW_WRITER); 1426 smb_lavl_insert(&tree->t_ofile_list, of); 1427 smb_lavl_exit(&tree->t_ofile_list); 1428 atomic_inc_32(&tree->t_open_files); 1429 atomic_inc_32(&sr->session->s_file_cnt); 1430 1431 /* 1432 * The ofile is now in the caller's session & tree. 1433 * 1434 * In case smb_ofile_hold or smb_oplock_send_break() are 1435 * waiting for state RECONNECT to complete, wakeup. 1436 */ 1437 mutex_enter(&of->f_mutex); 1438 of->dh_expire_time = 0; 1439 of->f_state = SMB_OFILE_STATE_OPEN; 1440 cv_broadcast(&of->f_cv); 1441 mutex_exit(&of->f_mutex); 1442 1443 /* 1444 * The ofile is now visible in the new session. 1445 * From here, this is similar to the last part of 1446 * smb_common_open(). 1447 */ 1448 op->fqi.fq_fattr.sa_mask = SMB_AT_ALL; 1449 (void) smb_node_getattr(sr, of->f_node, zone_kcred(), of, 1450 &op->fqi.fq_fattr); 1451 1452 /* 1453 * Set up the fileid and dosattr in open_param for response 1454 */ 1455 op->fileid = op->fqi.fq_fattr.sa_vattr.va_nodeid; 1456 op->dattr = op->fqi.fq_fattr.sa_dosattr; 1457 1458 /* 1459 * Set up the file type in open_param for the response 1460 * The ref. from ofile lookup is "given" to fid_ofile. 1461 */ 1462 op->ftype = SMB_FTYPE_DISK; 1463 sr->smb_fid = of->f_fid; 1464 sr->fid_ofile = of; 1465 1466 if (smb_node_is_file(of->f_node)) { 1467 op->dsize = op->fqi.fq_fattr.sa_vattr.va_size; 1468 } else { 1469 /* directory or symlink */ 1470 op->dsize = 0; 1471 } 1472 1473 op->create_options = 0; /* no more modifications wanted */ 1474 op->action_taken = SMB_OACT_OPENED; 1475 return (NT_STATUS_SUCCESS); 1476 1477 errout: 1478 if (of != NULL) 1479 smb_ofile_release(of); 1480 if (fid != 0) 1481 smb_idpool_free(&tree->t_fid_pool, fid); 1482 1483 return (status); 1484 } 1485 1486 /* 1487 * Durable handle expiration 1488 * ofile state is _EXPIRED 1489 */ 1490 static void 1491 smb2_dh_expire(void *arg) 1492 { 1493 smb_ofile_t *of = (smb_ofile_t *)arg; 1494 1495 if (of->dh_persist) 1496 smb2_dh_setdoc_persistent(of); 1497 smb_ofile_close(of, 0); 1498 smb_ofile_release(of); 1499 } 1500 1501 /* 1502 * Called once a minute to do expiration of durable handles. 1503 * 1504 * Normally expired durable handles should be in state "orphaned", 1505 * having transitioned from state SAVE_DH through SAVING to state 1506 * ORPHANED after all ofile references go away. If an ofile has 1507 * leaked references and the client disconnects, it will be found 1508 * here still in state SAVE_DH and past it's expiration time. 1509 * Call smb2_dh_expire for these as well, which will move them 1510 * from state SAVE_DH to state CLOSING, so they can no longer 1511 * cause sharing violations for new opens. 1512 */ 1513 void 1514 smb2_durable_timers(smb_server_t *sv) 1515 { 1516 smb_hash_t *hash; 1517 smb_llist_t *bucket; 1518 smb_ofile_t *of; 1519 hrtime_t now; 1520 int i; 1521 1522 hash = sv->sv_persistid_ht; 1523 now = gethrtime(); 1524 1525 for (i = 0; i < hash->num_buckets; i++) { 1526 bucket = &hash->buckets[i].b_list; 1527 smb_llist_enter(bucket, RW_READER); 1528 for (of = smb_llist_head(bucket); 1529 of != NULL; 1530 of = smb_llist_next(bucket, of)) { 1531 SMB_OFILE_VALID(of); 1532 1533 /* 1534 * Check outside the mutex first to avoid some 1535 * mutex_enter work in this loop. If the state 1536 * changes under foot, the worst that happens 1537 * is we either enter the mutex when we might 1538 * not have needed to, or we miss some DH in 1539 * this pass and get it on the next. 1540 */ 1541 if (of->f_state != SMB_OFILE_STATE_ORPHANED && 1542 of->f_state != SMB_OFILE_STATE_SAVE_DH) 1543 continue; 1544 1545 mutex_enter(&of->f_mutex); 1546 if ((of->f_state == SMB_OFILE_STATE_ORPHANED || 1547 of->f_state == SMB_OFILE_STATE_SAVE_DH) && 1548 of->dh_expire_time != 0 && 1549 of->dh_expire_time <= now) { 1550 1551 of->f_state = SMB_OFILE_STATE_EXPIRED; 1552 /* inline smb_ofile_hold_internal() */ 1553 of->f_refcnt++; 1554 smb_llist_post(bucket, of, smb2_dh_expire); 1555 } 1556 mutex_exit(&of->f_mutex); 1557 } 1558 smb_llist_exit(bucket); 1559 } 1560 } 1561 1562 /* 1563 * This is called when we're about to add a new open to some node. 1564 * If we still have orphaned durable handles on this node, let's 1565 * assume the client has lost interest in those and close them, 1566 * otherwise we might conflict with our own orphaned handles. 1567 * 1568 * We need this because we import persistent handles "speculatively" 1569 * during share import (before the client ever asks for reconnect). 1570 * That allows us to avoid any need for a "create blackout" (or 1571 * "grace period") because the imported handles prevent unwanted 1572 * conflicting opens from other clients. However, if some client 1573 * "forgets" about a persistent handle (*cough* Hyper-V) and tries 1574 * a new (conflicting) open instead of a reconnect, that might 1575 * fail unless we expire our orphaned durables handle first. 1576 * 1577 * Logic similar to smb_node_open_check() 1578 */ 1579 void 1580 smb2_dh_close_my_orphans(smb_request_t *sr, smb_ofile_t *new_of) 1581 { 1582 smb_node_t *node = new_of->f_node; 1583 smb_ofile_t *of; 1584 1585 SMB_NODE_VALID(node); 1586 1587 smb_llist_enter(&node->n_ofile_list, RW_READER); 1588 for (of = smb_llist_head(&node->n_ofile_list); 1589 of != NULL; 1590 of = smb_llist_next(&node->n_ofile_list, of)) { 1591 1592 /* Same client? */ 1593 if (of->f_lease != NULL && 1594 bcmp(sr->session->clnt_uuid, 1595 of->f_lease->ls_clnt, 16) != 0) 1596 continue; 1597 1598 if (!smb_is_same_user(sr->user_cr, of->f_cr)) 1599 continue; 1600 1601 mutex_enter(&of->f_mutex); 1602 if (of->f_state == SMB_OFILE_STATE_ORPHANED || 1603 of->f_state == SMB_OFILE_STATE_SAVE_DH) { 1604 of->f_state = SMB_OFILE_STATE_EXPIRED; 1605 /* inline smb_ofile_hold_internal() */ 1606 of->f_refcnt++; 1607 smb_llist_post(&node->n_ofile_list, 1608 of, smb2_dh_expire); 1609 } 1610 mutex_exit(&of->f_mutex); 1611 } 1612 1613 smb_llist_exit(&node->n_ofile_list); 1614 } 1615 1616 /* 1617 * Called for each orphaned DH during shutdown. 1618 * Clean out any in-memory state, but leave any 1619 * on-disk persistent handle state in place. 1620 */ 1621 static void 1622 smb2_dh_cleanup(void *arg) 1623 { 1624 smb_ofile_t *of = (smb_ofile_t *)arg; 1625 smb_node_t *strnode; 1626 struct nvlist *nvl; 1627 1628 /* 1629 * Intentionally skip smb2_dh_close_persistent by 1630 * clearing dh_nvfile before smb_ofile_close(). 1631 */ 1632 mutex_enter(&of->dh_nvlock); 1633 strnode = of->dh_nvfile; 1634 of->dh_nvfile = NULL; 1635 nvl = of->dh_nvlist; 1636 of->dh_nvlist = NULL; 1637 mutex_exit(&of->dh_nvlock); 1638 1639 if (nvl != NULL) 1640 nvlist_free(nvl); 1641 1642 if (strnode != NULL) 1643 smb_node_release(strnode); 1644 1645 smb_ofile_close(of, 0); 1646 smb_ofile_release(of); 1647 } 1648 1649 /* 1650 * Clean out durable handles during shutdown. 1651 * 1652 * Like, smb2_durable_timers but cleanup only in-memory state, 1653 * and leave any persistent state there for later reconnect. 1654 */ 1655 void 1656 smb2_dh_shutdown(smb_server_t *sv) 1657 { 1658 static const smb_oplock_grant_t og0 = { 0 }; 1659 smb_hash_t *hash; 1660 smb_llist_t *bucket; 1661 smb_ofile_t *of; 1662 int i; 1663 1664 hash = sv->sv_persistid_ht; 1665 1666 for (i = 0; i < hash->num_buckets; i++) { 1667 bucket = &hash->buckets[i].b_list; 1668 smb_llist_enter(bucket, RW_READER); 1669 of = smb_llist_head(bucket); 1670 while (of != NULL) { 1671 SMB_OFILE_VALID(of); 1672 mutex_enter(&of->f_mutex); 1673 1674 switch (of->f_state) { 1675 case SMB_OFILE_STATE_ORPHANED: 1676 case SMB_OFILE_STATE_SAVE_DH: 1677 of->f_state = SMB_OFILE_STATE_EXPIRED; 1678 /* inline smb_ofile_hold_internal() */ 1679 of->f_refcnt++; 1680 smb_llist_post(bucket, of, smb2_dh_cleanup); 1681 break; 1682 1683 default: 1684 /* 1685 * Should not be possible, but try to 1686 * make this zombie ofile harmless. 1687 */ 1688 cmn_err(CE_NOTE, "!dh_shutdown found " 1689 "of = %p with invalid state = %d", 1690 (void *)of, of->f_state); 1691 DTRACE_PROBE1(bad_ofile, smb_ofile_t *, of); 1692 ASSERT(0); 1693 of->f_oplock = og0; 1694 break; 1695 } 1696 mutex_exit(&of->f_mutex); 1697 of = smb_llist_next(bucket, of); 1698 } 1699 smb_llist_exit(bucket); 1700 } 1701 1702 #ifdef DEBUG 1703 for (i = 0; i < hash->num_buckets; i++) { 1704 bucket = &hash->buckets[i].b_list; 1705 smb_llist_enter(bucket, RW_READER); 1706 of = smb_llist_head(bucket); 1707 while (of != NULL) { 1708 SMB_OFILE_VALID(of); 1709 cmn_err(CE_NOTE, "dh_shutdown leaked of=%p", 1710 (void *)of); 1711 of = smb_llist_next(bucket, of); 1712 } 1713 smb_llist_exit(bucket); 1714 } 1715 #endif // DEBUG 1716 } 1717 1718 uint32_t 1719 smb2_fsctl_set_resilient(smb_request_t *sr, smb_fsctl_t *fsctl) 1720 { 1721 uint32_t timeout; 1722 smb_ofile_t *of = sr->fid_ofile; 1723 1724 /* 1725 * Note: The spec does not explicitly prohibit resilient directories 1726 * the same way it prohibits durable directories. We prohibit them 1727 * anyway as a simplifying assumption, as there doesn't seem to be 1728 * much use for it. (HYPER-V only seems to use it on files anyway) 1729 */ 1730 if (fsctl->InputCount < 8 || !smb_node_is_file(of->f_node)) 1731 return (NT_STATUS_INVALID_PARAMETER); 1732 1733 (void) smb_mbc_decodef(fsctl->in_mbc, "l4.", 1734 &timeout); /* milliseconds */ 1735 1736 if (smb2_enable_dh == 0) 1737 return (NT_STATUS_NOT_SUPPORTED); 1738 1739 /* 1740 * The spec wants us to return INVALID_PARAMETER if the timeout 1741 * is too large, but we have no way of informing the client 1742 * what an appropriate timeout is, so just set the timeout to 1743 * our max and return SUCCESS. 1744 */ 1745 if (timeout == 0) 1746 timeout = smb2_res_def_timeout; 1747 if (timeout > smb2_res_max_timeout) 1748 timeout = smb2_res_max_timeout; 1749 1750 mutex_enter(&of->f_mutex); 1751 of->dh_vers = SMB2_RESILIENT; 1752 of->dh_timeout_offset = MSEC2NSEC(timeout); 1753 mutex_exit(&of->f_mutex); 1754 1755 return (NT_STATUS_SUCCESS); 1756 } 1757