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