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