1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. 24 * Copyright 2022 RackTop Systems, Inc. 25 */ 26 27 #include <smbsrv/smb_kproto.h> 28 #include <smbsrv/smb_fsops.h> 29 #include <sys/pathname.h> 30 #include <sys/sdt.h> 31 32 static char *smb_pathname_catia_v5tov4(smb_request_t *, char *, char *, int); 33 static char *smb_pathname_catia_v4tov5(smb_request_t *, char *, char *, int); 34 static int smb_pathname_lookup(pathname_t *, pathname_t *, int, 35 vnode_t **, vnode_t *, vnode_t *, smb_attr_t *attr, cred_t *); 36 static char *smb_pathname_strdup(smb_request_t *, const char *); 37 static char *smb_pathname_strcat(smb_request_t *, char *, const char *); 38 static void smb_pathname_preprocess(smb_request_t *, smb_pathname_t *); 39 static void smb_pathname_preprocess_quota(smb_request_t *, smb_pathname_t *); 40 static int smb_pathname_dfs_preprocess(smb_request_t *, char *, size_t); 41 static void smb_pathname_preprocess_adminshare(smb_request_t *, 42 smb_pathname_t *); 43 44 45 uint32_t 46 smb_is_executable(char *path) 47 { 48 char extension[5]; 49 int len = strlen(path); 50 51 if ((len >= 4) && (path[len - 4] == '.')) { 52 (void) strcpy(extension, &path[len - 3]); 53 (void) smb_strupr(extension); 54 55 if (strcmp(extension, "EXE") == 0) 56 return (NODE_FLAGS_EXECUTABLE); 57 58 if (strcmp(extension, "COM") == 0) 59 return (NODE_FLAGS_EXECUTABLE); 60 61 if (strcmp(extension, "DLL") == 0) 62 return (NODE_FLAGS_EXECUTABLE); 63 64 if (strcmp(extension, "SYM") == 0) 65 return (NODE_FLAGS_EXECUTABLE); 66 } 67 68 return (0); 69 } 70 71 /* 72 * smb_pathname_reduce 73 * 74 * smb_pathname_reduce() takes a path and returns the smb_node for the 75 * second-to-last component of the path. It also returns the name of the last 76 * component. Pointers for both of these fields must be supplied by the caller. 77 * 78 * Upon success, 0 is returned. 79 * 80 * Upon error, *dir_node will be set to 0. 81 * 82 * *sr (in) 83 * --- 84 * smb_request structure pointer 85 * 86 * *cred (in) 87 * ----- 88 * credential 89 * 90 * *path (in) 91 * ----- 92 * pathname to be looked up 93 * 94 * *share_root_node (in) 95 * ---------------- 96 * File operations which are share-relative should pass sr->tid_tree->t_snode. 97 * If the call is not for a share-relative operation, this parameter must be 0 98 * (e.g. the call from smbsr_setup_share()). (Such callers will have path 99 * operations done using root_smb_node.) This parameter is used to determine 100 * whether mount points can be crossed. 101 * 102 * share_root_node should have at least one reference on it. This reference 103 * will stay intact throughout this routine. 104 * 105 * *cur_node (in) 106 * --------- 107 * The smb_node for the current directory (for relative paths). 108 * cur_node should have at least one reference on it. 109 * This reference will stay intact throughout this routine. 110 * 111 * **dir_node (out) 112 * ---------- 113 * Directory for the penultimate component of the original path. 114 * (Note that this is not the same as the parent directory of the ultimate 115 * target in the case of a link.) 116 * 117 * The directory smb_node is returned held. The caller will need to release 118 * the hold or otherwise make sure it will get released (e.g. in a destroy 119 * routine if made part of a global structure). 120 * 121 * last_component (out) 122 * -------------- 123 * The last component of the path. (This may be different from the name of any 124 * link target to which the last component may resolve.) 125 * 126 * 127 * ____________________________ 128 * 129 * The CIFS server lookup path needs to have logic equivalent to that of 130 * smb_fsop_lookup(), smb_vop_lookup() and other smb_vop_*() routines in the 131 * following areas: 132 * 133 * - traversal of child mounts (handled by smb_pathname_reduce) 134 * - unmangling (handled in smb_pathname) 135 * - "chroot" behavior of share root (handled by lookuppnvp) 136 * 137 * In addition, it needs to replace backslashes with forward slashes. It also 138 * ensures that link processing is done correctly, and that directory 139 * information requested by the caller is correctly returned (i.e. for paths 140 * with a link in the last component, the directory information of the 141 * link and not the target needs to be returned). 142 */ 143 144 int 145 smb_pathname_reduce( 146 smb_request_t *sr, 147 cred_t *cred, 148 const char *path, 149 smb_node_t *share_root_node, 150 smb_node_t *cur_node, 151 smb_node_t **dir_node, 152 char *last_component) 153 { 154 smb_node_t *root_node; 155 pathname_t ppn = {0}; 156 pathname_t mnt_pn = {0}; 157 char *usepath; 158 int lookup_flags = FOLLOW; 159 int trailing_slash = 0; 160 int err = 0; 161 int len; 162 smb_node_t *vss_node; 163 smb_node_t *local_cur_node; 164 smb_node_t *local_root_node; 165 boolean_t chk_vss; 166 char *gmttoken; 167 168 ASSERT(dir_node); 169 ASSERT(last_component); 170 171 *dir_node = NULL; 172 *last_component = '\0'; 173 vss_node = NULL; 174 gmttoken = NULL; 175 chk_vss = B_FALSE; 176 177 if (sr && sr->tid_tree) { 178 if (STYPE_ISIPC(sr->tid_tree->t_res_type)) 179 return (EACCES); 180 } 181 182 if (SMB_TREE_IS_CASEINSENSITIVE(sr)) 183 lookup_flags |= FIGNORECASE; 184 185 if (path == NULL) 186 return (EINVAL); 187 188 if (*path == '\0') 189 return (ENOENT); 190 191 usepath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP); 192 193 len = strlcpy(usepath, path, SMB_MAXPATHLEN); 194 if (len >= SMB_MAXPATHLEN) { 195 kmem_free(usepath, SMB_MAXPATHLEN); 196 return (ENAMETOOLONG); 197 } 198 199 (void) strsubst(usepath, '\\', '/'); 200 201 if (share_root_node) 202 root_node = share_root_node; 203 else 204 root_node = sr->sr_server->si_root_smb_node; 205 206 if (cur_node == NULL) 207 cur_node = root_node; 208 209 local_cur_node = cur_node; 210 local_root_node = root_node; 211 212 if (SMB_TREE_IS_DFSROOT(sr)) { 213 int is_dfs; 214 if (sr->session->dialect >= SMB_VERS_2_BASE) 215 is_dfs = sr->smb2_hdr_flags & 216 SMB2_FLAGS_DFS_OPERATIONS; 217 else 218 is_dfs = sr->smb_flg2 & SMB_FLAGS2_DFS; 219 if (is_dfs != 0) { 220 err = smb_pathname_dfs_preprocess(sr, usepath, 221 SMB_MAXPATHLEN); 222 if (err != 0) { 223 kmem_free(usepath, SMB_MAXPATHLEN); 224 return (err); 225 } 226 len = strlen(usepath); 227 } 228 } 229 230 if (sr != NULL) { 231 if (sr->session->dialect >= SMB_VERS_2_BASE) { 232 chk_vss = sr->arg.open.create_timewarp; 233 } else { 234 chk_vss = (sr->smb_flg2 & 235 SMB_FLAGS2_REPARSE_PATH) != 0; 236 237 if (chk_vss) { 238 gmttoken = kmem_alloc(SMB_VSS_GMT_SIZE, 239 KM_SLEEP); 240 err = smb_vss_extract_gmttoken(usepath, 241 gmttoken); 242 if (err != 0) { 243 kmem_free(usepath, SMB_MAXPATHLEN); 244 kmem_free(gmttoken, SMB_VSS_GMT_SIZE); 245 return (err); 246 } 247 len = strlen(usepath); 248 } 249 } 250 if (chk_vss) 251 (void) pn_alloc(&mnt_pn); 252 } 253 254 if (usepath[len - 1] == '/') 255 trailing_slash = 1; 256 257 (void) strcanon(usepath, "/"); 258 259 (void) pn_alloc_sz(&ppn, SMB_MAXPATHLEN); 260 261 if ((err = pn_set(&ppn, usepath)) != 0) { 262 (void) pn_free(&ppn); 263 kmem_free(usepath, SMB_MAXPATHLEN); 264 if (chk_vss) 265 (void) pn_free(&mnt_pn); 266 if (gmttoken != NULL) 267 kmem_free(gmttoken, SMB_VSS_GMT_SIZE); 268 return (err); 269 } 270 271 /* 272 * If a path does not have a trailing slash, strip off the 273 * last component. (We only need to return an smb_node for 274 * the second to last component; a name is returned for the 275 * last component.) 276 * 277 * For VSS requests, the last component might be a filesystem of its 278 * own, and we need to discover that before exiting this function, 279 * so allow the lookup to happen on the last component. 280 * We'll correct this later when we convert to the snapshot. 281 */ 282 283 if (!chk_vss) { 284 if (trailing_slash) { 285 (void) strlcpy(last_component, ".", MAXNAMELEN); 286 } else { 287 (void) pn_setlast(&ppn); 288 if (ppn.pn_pathlen >= MAXNAMELEN) { 289 err = ENAMETOOLONG; 290 goto end_not_vss; 291 } 292 (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN); 293 ppn.pn_path[0] = '\0'; 294 } 295 } 296 297 if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) { 298 smb_node_ref(local_cur_node); 299 *dir_node = local_cur_node; 300 } else { 301 err = smb_pathname(sr, ppn.pn_buf, lookup_flags, 302 local_root_node, local_cur_node, NULL, dir_node, cred, 303 chk_vss ? &mnt_pn : NULL); 304 } 305 306 end_not_vss: 307 (void) pn_free(&ppn); 308 kmem_free(usepath, SMB_MAXPATHLEN); 309 310 /* 311 * We need to try and convert to snapshots, even on error. 312 * This is to handle the following cases: 313 * - We're on the lowest level filesystem, but a directory got renamed 314 * on the live version. We'll get ENOENT, but can still find it in 315 * the snapshot. 316 * - The last component was actually a file. We need to leave the last 317 * component in in case it is, itself, a mountpoint, but that means 318 * we might get ENOTDIR if it's not actually a directory. 319 * 320 * Note that if you change the share-relative name of a mountpoint, 321 * you won't be able to access previous versions of files under it. 322 */ 323 if (chk_vss && *dir_node != NULL) { 324 if ((err = smb_vss_lookup_nodes(sr, *dir_node, &vss_node, 325 gmttoken)) == 0) { 326 char *p = mnt_pn.pn_path; 327 size_t pathleft; 328 329 smb_node_release(*dir_node); 330 *dir_node = NULL; 331 pathleft = pn_pathleft(&mnt_pn); 332 333 if (pathleft == 0 || trailing_slash) { 334 (void) strlcpy(last_component, ".", MAXNAMELEN); 335 } else { 336 (void) pn_setlast(&mnt_pn); 337 if (ppn.pn_pathlen >= MAXNAMELEN) { 338 err = ENAMETOOLONG; 339 goto end_chk_vss; 340 } 341 (void) strlcpy(last_component, mnt_pn.pn_path, 342 MAXNAMELEN); 343 mnt_pn.pn_path[0] = '\0'; 344 pathleft -= strlen(last_component); 345 } 346 347 if (pathleft != 0) { 348 err = smb_pathname(sr, p, lookup_flags, 349 vss_node, vss_node, NULL, dir_node, cred, 350 NULL); 351 } else { 352 *dir_node = vss_node; 353 vss_node = NULL; 354 } 355 } 356 } 357 358 end_chk_vss: 359 if (chk_vss) 360 (void) pn_free(&mnt_pn); 361 if (gmttoken != NULL) 362 kmem_free(gmttoken, SMB_VSS_GMT_SIZE); 363 364 /* 365 * Prevent traversal to another file system if mount point 366 * traversal is disabled. 367 * 368 * Note that we disregard whether the traversal of the path went 369 * outside of the file system and then came back (say via a link). 370 * This means that only symlinks that are expressed relatively to 371 * the share root work. 372 * 373 * share_root_node is NULL when mapping a share, so we disregard 374 * that case. 375 */ 376 377 if ((err == 0) && share_root_node) { 378 if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) { 379 err = EACCES; 380 if ((sr) && (sr)->tid_tree && 381 smb_tree_has_feature((sr)->tid_tree, 382 SMB_TREE_TRAVERSE_MOUNTS)) 383 err = 0; 384 } 385 } 386 387 if (err) { 388 if (*dir_node) { 389 (void) smb_node_release(*dir_node); 390 *dir_node = NULL; 391 } 392 *last_component = 0; 393 } 394 395 if (vss_node != NULL) 396 (void) smb_node_release(vss_node); 397 return (err); 398 } 399 400 /* 401 * smb_pathname() 402 * wrapper to lookuppnvp(). Handles name unmangling. 403 * 404 * *dir_node is the true directory of the target *node. 405 * 406 * If any component but the last in the path is not found, ENOTDIR instead of 407 * ENOENT will be returned. 408 * 409 * Path components are processed one at a time so that smb_nodes can be 410 * created for each component. This allows the n_dnode field in the 411 * smb_node to be properly populated. 412 * 413 * Because of the above, links are also processed in this routine 414 * (i.e., we do not pass the FOLLOW flag to lookuppnvp()). This 415 * will allow smb_nodes to be created for each component of a link. 416 * 417 * Mangle checking is per component. If a name is mangled, when the 418 * unmangled name is passed to smb_pathname_lookup() do not pass 419 * FIGNORECASE, since the unmangled name is the real on-disk name. 420 * Otherwise pass FIGNORECASE if it's set in flags. This will cause the 421 * file system to return "first match" in the event of a case collision. 422 * 423 * If CATIA character translation is enabled it is applied to each 424 * component before passing the component to smb_pathname_lookup(). 425 * After smb_pathname_lookup() the reverse translation is applied. 426 */ 427 428 int 429 smb_pathname(smb_request_t *sr, char *path, int flags, 430 smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node, 431 smb_node_t **ret_node, cred_t *cred, pathname_t *mnt_pn) 432 { 433 char *component, *real_name, *namep; 434 pathname_t pn, rpn, upn, link_pn; 435 smb_node_t *dnode, *fnode, *mnt_node; 436 smb_attr_t attr; 437 vnode_t *rootvp, *vp; 438 size_t pathleft; 439 int err = 0; 440 int nlink = 0; 441 int local_flags; 442 uint32_t abe_flag = 0; 443 char namebuf[MAXNAMELEN]; 444 vnode_t *fsrootvp = NULL; 445 446 if (path == NULL) 447 return (EINVAL); 448 449 ASSERT(root_node); 450 ASSERT(cur_node); 451 ASSERT(ret_node); 452 453 *ret_node = NULL; 454 455 if (dir_node) 456 *dir_node = NULL; 457 458 (void) pn_alloc_sz(&upn, SMB_MAXPATHLEN); 459 460 if ((err = pn_set(&upn, path)) != 0) { 461 (void) pn_free(&upn); 462 return (err); 463 } 464 465 if (mnt_pn != NULL && (err = pn_set(mnt_pn, path) != 0)) { 466 (void) pn_free(&upn); 467 return (err); 468 } 469 470 if (SMB_TREE_SUPPORTS_ABE(sr)) 471 abe_flag = SMB_ABE; 472 473 (void) pn_alloc(&pn); 474 (void) pn_alloc(&rpn); 475 476 component = kmem_alloc(MAXNAMELEN, KM_SLEEP); 477 real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 478 479 if (mnt_pn != NULL) { 480 mnt_node = cur_node; 481 smb_node_ref(cur_node); 482 } else 483 mnt_node = NULL; 484 fnode = NULL; 485 dnode = cur_node; 486 smb_node_ref(dnode); 487 rootvp = root_node->vp; 488 489 while ((pathleft = pn_pathleft(&upn)) != 0) { 490 if (fnode) { 491 smb_node_release(dnode); 492 dnode = fnode; 493 fnode = NULL; 494 } 495 496 if ((err = pn_getcomponent(&upn, component)) != 0) 497 break; 498 499 if ((namep = smb_pathname_catia_v5tov4(sr, component, 500 namebuf, sizeof (namebuf))) == NULL) { 501 err = EILSEQ; 502 break; 503 } 504 505 if ((err = pn_set(&pn, namep)) != 0) 506 break; 507 508 /* We want the DOS attributes. */ 509 bzero(&attr, sizeof (attr)); 510 attr.sa_mask = SMB_AT_DOSATTR; 511 512 local_flags = flags & FIGNORECASE; 513 err = smb_pathname_lookup(&pn, &rpn, local_flags, 514 &vp, rootvp, dnode->vp, &attr, cred); 515 516 if (err) { 517 if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) || 518 !smb_maybe_mangled(component)) 519 break; 520 521 if ((err = smb_unmangle(dnode, component, 522 real_name, MAXNAMELEN, abe_flag)) != 0) 523 break; 524 525 if ((namep = smb_pathname_catia_v5tov4(sr, real_name, 526 namebuf, sizeof (namebuf))) == NULL) { 527 err = EILSEQ; 528 break; 529 } 530 531 if ((err = pn_set(&pn, namep)) != 0) 532 break; 533 534 local_flags = 0; 535 err = smb_pathname_lookup(&pn, &rpn, local_flags, 536 &vp, rootvp, dnode->vp, &attr, cred); 537 if (err) 538 break; 539 } 540 541 /* 542 * This check MUST be done before symlink check 543 * since a reparse point is of type VLNK but should 544 * not be handled like a regular symlink. 545 */ 546 if (attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) { 547 err = EREMOTE; 548 VN_RELE(vp); 549 break; 550 } 551 552 if ((vp->v_type == VLNK) && 553 ((flags & FOLLOW) || pn_pathleft(&upn))) { 554 555 if (++nlink > MAXSYMLINKS) { 556 err = ELOOP; 557 VN_RELE(vp); 558 break; 559 } 560 561 (void) pn_alloc(&link_pn); 562 err = pn_getsymlink(vp, &link_pn, cred); 563 VN_RELE(vp); 564 565 if (err == 0) { 566 if (pn_pathleft(&link_pn) == 0) 567 (void) pn_set(&link_pn, "."); 568 err = pn_insert(&upn, &link_pn, 569 strlen(component)); 570 } 571 pn_free(&link_pn); 572 573 if (err) 574 break; 575 576 if (upn.pn_pathlen == 0) { 577 err = ENOENT; 578 break; 579 } 580 581 if (upn.pn_path[0] == '/') { 582 fnode = root_node; 583 smb_node_ref(fnode); 584 } 585 586 if (pn_fixslash(&upn)) 587 flags |= FOLLOW; 588 589 } else { 590 if (flags & FIGNORECASE) { 591 if (strcmp(rpn.pn_path, "/") != 0) 592 pn_setlast(&rpn); 593 namep = rpn.pn_path; 594 } else { 595 namep = pn.pn_path; 596 } 597 598 namep = smb_pathname_catia_v4tov5(sr, namep, 599 namebuf, sizeof (namebuf)); 600 601 fnode = smb_node_lookup(sr, NULL, cred, vp, namep, 602 dnode, NULL); 603 VN_RELE(vp); 604 605 if (fnode == NULL) { 606 err = ENOMEM; 607 break; 608 } 609 } 610 611 while (upn.pn_path[0] == '/') { 612 upn.pn_path++; 613 upn.pn_pathlen--; 614 } 615 616 /* 617 * If the node we looked up is the root of a filesystem, 618 * snapshot the lookup so we can replay this after discovering 619 * the lowest mounted filesystem. 620 */ 621 if (mnt_pn != NULL && 622 fnode != NULL && 623 (err = VFS_ROOT(fnode->vp->v_vfsp, &fsrootvp)) == 0) { 624 if (fsrootvp == fnode->vp) { 625 mnt_pn->pn_pathlen = pn_pathleft(&upn); 626 mnt_pn->pn_path = mnt_pn->pn_buf + 627 ((ptrdiff_t)upn.pn_path - 628 (ptrdiff_t)upn.pn_buf); 629 630 smb_node_ref(fnode); 631 if (mnt_node != NULL) 632 smb_node_release(mnt_node); 633 mnt_node = fnode; 634 635 } 636 VN_RELE(fsrootvp); 637 } 638 } 639 640 if ((pathleft) && (err == ENOENT)) 641 err = ENOTDIR; 642 643 if (mnt_node == NULL) 644 mnt_pn = NULL; 645 646 /* 647 * We always want to return a node when we're doing VSS 648 * (mnt_pn != NULL) 649 */ 650 if (mnt_pn == NULL && err != 0) { 651 if (fnode) 652 smb_node_release(fnode); 653 if (dnode) 654 smb_node_release(dnode); 655 } else { 656 if (mnt_pn != NULL) { 657 *ret_node = mnt_node; 658 if (fnode != NULL) 659 smb_node_release(fnode); 660 } else { 661 *ret_node = fnode; 662 } 663 664 if (dir_node) 665 *dir_node = dnode; 666 else 667 smb_node_release(dnode); 668 } 669 670 kmem_free(component, MAXNAMELEN); 671 kmem_free(real_name, MAXNAMELEN); 672 (void) pn_free(&pn); 673 (void) pn_free(&rpn); 674 (void) pn_free(&upn); 675 676 return (err); 677 } 678 679 /* 680 * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp() 681 * and will be released within lookuppnvp(). 682 */ 683 static int 684 smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags, 685 vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, smb_attr_t *attr, cred_t *cred) 686 { 687 int err; 688 689 *vp = NULL; 690 VN_HOLD(dvp); 691 if (rootvp != rootdir) 692 VN_HOLD(rootvp); 693 694 err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred); 695 if ((err == 0) && (attr != NULL)) 696 (void) smb_vop_getattr(*vp, NULL, attr, 0, zone_kcred()); 697 698 return (err); 699 } 700 701 /* 702 * CATIA Translation of a pathname component prior to passing it to lookuppnvp 703 * 704 * If the translated component name contains a '/' NULL is returned. 705 * The caller should treat this as error EILSEQ. It is not valid to 706 * have a directory name with a '/'. 707 */ 708 static char * 709 smb_pathname_catia_v5tov4(smb_request_t *sr, char *name, 710 char *namebuf, int buflen) 711 { 712 char *namep; 713 714 if (SMB_TREE_SUPPORTS_CATIA(sr)) { 715 namep = smb_vop_catia_v5tov4(name, namebuf, buflen); 716 if (strchr(namep, '/') != NULL) 717 return (NULL); 718 return (namep); 719 } 720 721 return (name); 722 } 723 724 /* 725 * CATIA translation of a pathname component after returning from lookuppnvp 726 */ 727 static char * 728 smb_pathname_catia_v4tov5(smb_request_t *sr, char *name, 729 char *namebuf, int buflen) 730 { 731 if (SMB_TREE_SUPPORTS_CATIA(sr)) { 732 smb_vop_catia_v4tov5(name, namebuf, buflen); 733 return (namebuf); 734 } 735 736 return (name); 737 } 738 739 /* 740 * sr - needed to check for case sense 741 * path - non mangled path needed to be looked up from the startvp 742 * startvp - the vnode to start the lookup from 743 * rootvp - the vnode of the root of the filesystem 744 * returns the vnode found when starting at startvp and using the path 745 * 746 * Finds a vnode starting at startvp and parsing the non mangled path 747 */ 748 749 vnode_t * 750 smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp, 751 vnode_t *rootvp) 752 { 753 pathname_t pn; 754 vnode_t *vp = NULL; 755 int lookup_flags = FOLLOW; 756 757 if (SMB_TREE_IS_CASEINSENSITIVE(sr)) 758 lookup_flags |= FIGNORECASE; 759 760 (void) pn_alloc(&pn); 761 762 if (pn_set(&pn, path) == 0) { 763 VN_HOLD(startvp); 764 if (rootvp != rootdir) 765 VN_HOLD(rootvp); 766 767 /* lookuppnvp should release the holds */ 768 if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp, 769 rootvp, startvp, zone_kcred()) != 0) { 770 pn_free(&pn); 771 return (NULL); 772 } 773 } 774 775 pn_free(&pn); 776 return (vp); 777 } 778 779 /* 780 * smb_pathname_init 781 * Parse path: pname\\fname:sname:stype 782 * 783 * Elements of the smb_pathname_t structure are allocated using request 784 * specific storage and will be free'd when the sr is destroyed. 785 * 786 * Populate pn structure elements with the individual elements 787 * of pn->pn_path. pn->pn_sname will contain the whole stream name 788 * including the stream type and preceding colon: :sname:%DATA 789 * pn_stype will point to the stream type within pn_sname. 790 * 791 * If the pname element is missing pn_pname will be set to NULL. 792 * If any other element is missing the pointer in pn will be NULL. 793 */ 794 void 795 smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path) 796 { 797 char *pname, *fname, *sname; 798 int len; 799 800 bzero(pn, sizeof (smb_pathname_t)); 801 pn->pn_path = smb_pathname_strdup(sr, path); 802 803 smb_pathname_preprocess(sr, pn); 804 805 /* parse pn->pn_path into its constituent parts */ 806 pname = pn->pn_path; 807 808 /* 809 * Split the string between the directory and filename. 810 * Either part may be empty. 811 * 812 * Fill in pn->pn_pname (the path name) 813 */ 814 fname = strrchr(pname, '\\'); 815 if (fname != NULL) { 816 if (fname == pname) { 817 /* 818 * Last '/' is at start of string. 819 * No directory part (dir is root) 820 */ 821 pn->pn_pname = NULL; 822 } else { 823 /* 824 * Directory part ends at the last '/' 825 * Temporarily truncate and copy 826 */ 827 *fname = '\0'; 828 pn->pn_pname = 829 smb_pathname_strdup(sr, pname); 830 *fname = '\\'; 831 } 832 ++fname; 833 /* fname is just after the '/' */ 834 } else { 835 /* 836 * No '/' at all in the string. 837 * It's all filename 838 */ 839 fname = pname; 840 pn->pn_pname = NULL; 841 } 842 843 /* 844 * Find end of the filename part of the string, 845 * which may be the null terminator, or may be 846 * the start of the optional :sname suffix. 847 */ 848 sname = strchr(fname, ':'); 849 if (sname == NULL) { 850 /* 851 * No :sname suffix. We're done. 852 */ 853 pn->pn_fname = smb_pathname_strdup(sr, fname); 854 return; 855 } 856 857 /* 858 * We have a stream name, and maybe a stream type. 859 * Can't use smb_is_stream_name(fname) here because 860 * we need to allow sname="::$DATA" 861 */ 862 if (sname == fname) { 863 /* 864 * The ":sname" part is at the start of 865 * the file name, which means that the 866 * file name is "" and this pathname 867 * refers to a stream on the directory. 868 */ 869 pn->pn_fname = NULL; 870 } else { 871 /* 872 * The filename part ends at the ':' 873 * Temporarily truncate and copy 874 */ 875 *sname = '\0'; 876 pn->pn_fname = smb_pathname_strdup(sr, fname); 877 *sname = ':'; 878 } 879 880 /* 881 * Special case "::$DATA" which "points to" 882 * the "unnamed" stream (the file itself). 883 * Basically ignore the "::$DATA" 884 */ 885 if (strcasecmp(sname, "::$DATA") == 0) { 886 ASSERT(sname >= pname && 887 sname < (pname + strlen(pname))); 888 *sname = '\0'; 889 return; 890 } 891 892 /* 893 * sname points to ":sname:stype" in pn_path 894 * If ":stype" is missing, add it, then set 895 * pn_stype to point after the 2nd ':' 896 * 897 * Caller knows pn_stype is NOT allocated. 898 * Allocations here are free'd via smb_srm_fini 899 */ 900 pn->pn_sname = smb_pathname_strdup(sr, sname); 901 pn->pn_stype = strchr(pn->pn_sname + 1, ':'); 902 if (pn->pn_stype) { 903 (void) smb_strupr(pn->pn_stype); 904 } else { 905 len = strlen(pn->pn_sname); 906 pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA"); 907 pn->pn_stype = pn->pn_sname + len; 908 } 909 ++pn->pn_stype; 910 } 911 912 /* 913 * smb_pathname_preprocess 914 * 915 * Perform common pre-processing of pn->pn_path: 916 * - if the pn_path is blank, set it to '\\' 917 * - perform unicode wildcard converstion. 918 * - convert any '/' to '\\' 919 * - eliminate duplicate slashes 920 * - remove trailing slashes 921 * - quota directory specific pre-processing 922 */ 923 static void 924 smb_pathname_preprocess(smb_request_t *sr, smb_pathname_t *pn) 925 { 926 char *p; 927 928 /* treat empty path as "\\" */ 929 if (strlen(pn->pn_path) == 0) { 930 pn->pn_path = smb_pathname_strdup(sr, "\\"); 931 return; 932 } 933 934 if (sr->session->dialect < NT_LM_0_12) 935 smb_convert_wildcards(pn->pn_path); 936 937 /* treat '/' as '\\' */ 938 (void) strsubst(pn->pn_path, '/', '\\'); 939 940 (void) strcanon(pn->pn_path, "\\"); 941 942 /* remove trailing '\\' */ 943 p = pn->pn_path + strlen(pn->pn_path) - 1; 944 if ((p != pn->pn_path) && (*p == '\\')) 945 *p = '\0'; 946 947 smb_pathname_preprocess_quota(sr, pn); 948 smb_pathname_preprocess_adminshare(sr, pn); 949 } 950 951 /* 952 * smb_pathname_preprocess_quota 953 * 954 * There is a special file required by windows so that the quota 955 * tab will be displayed by windows clients. This is created in 956 * a special directory, $EXTEND, at the root of the shared file 957 * system. To hide this directory prepend a '.' (dot). 958 */ 959 static void 960 smb_pathname_preprocess_quota(smb_request_t *sr, smb_pathname_t *pn) 961 { 962 char *name = "$EXTEND"; 963 char *new_name = ".$EXTEND"; 964 char *p, *slash; 965 int len; 966 967 if (!smb_node_is_vfsroot(sr->tid_tree->t_snode)) 968 return; 969 970 p = pn->pn_path; 971 972 /* ignore any initial "\\" */ 973 p += strspn(p, "\\"); 974 if (smb_strcasecmp(p, name, strlen(name)) != 0) 975 return; 976 977 p += strlen(name); 978 if ((*p != ':') && (*p != '\\') && (*p != '\0')) 979 return; 980 981 slash = (pn->pn_path[0] == '\\') ? "\\" : ""; 982 len = strlen(pn->pn_path) + 2; 983 pn->pn_path = smb_srm_alloc(sr, len); 984 (void) snprintf(pn->pn_path, len, "%s%s%s", slash, new_name, p); 985 (void) smb_strupr(pn->pn_path); 986 } 987 988 /* 989 * smb_pathname_preprocess_adminshare 990 * 991 * Convert any path with share name "C$" or "c$" (Admin share) in to lower case. 992 */ 993 static void 994 smb_pathname_preprocess_adminshare(smb_request_t *sr, smb_pathname_t *pn) 995 { 996 if (strcasecmp(sr->tid_tree->t_sharename, "c$") == 0) 997 (void) smb_strlwr(pn->pn_path); 998 } 999 1000 /* 1001 * smb_pathname_strdup 1002 * 1003 * Duplicate NULL terminated string s. 1004 * 1005 * The new string is allocated using request specific storage and will 1006 * be free'd when the sr is destroyed. 1007 */ 1008 static char * 1009 smb_pathname_strdup(smb_request_t *sr, const char *s) 1010 { 1011 char *s2; 1012 size_t n; 1013 1014 n = strlen(s) + 1; 1015 s2 = smb_srm_zalloc(sr, n); 1016 (void) strlcpy(s2, s, n); 1017 return (s2); 1018 } 1019 1020 /* 1021 * smb_pathname_strcat 1022 * 1023 * Reallocate NULL terminated string s1 to accommodate 1024 * concatenating NULL terminated string s2. 1025 * Append s2 and return resulting NULL terminated string. 1026 * 1027 * The string buffer is reallocated using request specific 1028 * storage and will be free'd when the sr is destroyed. 1029 */ 1030 static char * 1031 smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2) 1032 { 1033 size_t n; 1034 1035 n = strlen(s1) + strlen(s2) + 1; 1036 s1 = smb_srm_rezalloc(sr, s1, n); 1037 (void) strlcat(s1, s2, n); 1038 return (s1); 1039 } 1040 1041 /* 1042 * smb_pathname_validate 1043 * 1044 * Perform basic validation of pn: 1045 * - If first component of pn->path is ".." -> PATH_SYNTAX_BAD 1046 * - If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID 1047 * - If fname is "." -> INVALID_OBJECT_NAME 1048 * 1049 * On unix .. at the root of a file system links to the root. Thus 1050 * an attempt to lookup "/../../.." will be the same as looking up "/" 1051 * CIFs clients expect the above to result in 1052 * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible 1053 * (and questionable if it's desirable) to deal with all cases 1054 * but paths beginning with \\.. are handled. 1055 * 1056 * Returns: B_TRUE if pn is valid, 1057 * otherwise returns B_FALSE and sets error status in sr. 1058 * 1059 * XXX: Get rid of smbsr_error calls for SMB2 1060 */ 1061 boolean_t 1062 smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn) 1063 { 1064 char *path = pn->pn_path; 1065 1066 /* ignore any initial "\\" */ 1067 path += strspn(path, "\\"); 1068 1069 /* If first component of path is ".." -> PATH_SYNTAX_BAD */ 1070 if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) { 1071 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 1072 ERRDOS, ERROR_BAD_PATHNAME); 1073 return (B_FALSE); 1074 } 1075 1076 /* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */ 1077 if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) { 1078 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1079 ERRDOS, ERROR_INVALID_NAME); 1080 return (B_FALSE); 1081 } 1082 1083 /* If fname is "." -> OBJECT_NAME_INVALID */ 1084 if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) { 1085 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1086 ERRDOS, ERROR_INVALID_NAME); 1087 return (B_FALSE); 1088 } 1089 1090 return (B_TRUE); 1091 } 1092 1093 /* 1094 * smb_validate_dirname 1095 * 1096 * smb_pathname_validate() should have already been performed on pn. 1097 * 1098 * Very basic directory name validation: checks for colons in a path. 1099 * Need to skip the drive prefix since it contains a colon. 1100 * 1101 * Returns: B_TRUE if the name is valid, 1102 * otherwise returns B_FALSE and sets error status in sr. 1103 */ 1104 boolean_t 1105 smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn) 1106 { 1107 char *name; 1108 char *path = pn->pn_path; 1109 1110 if ((name = path) != 0) { 1111 name += strspn(name, "\\"); 1112 1113 if (strchr(name, ':') != 0) { 1114 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 1115 ERRDOS, ERROR_INVALID_NAME); 1116 return (B_FALSE); 1117 } 1118 } 1119 1120 if (pn->pn_sname) 1121 return (smb_validate_stream_name(sr, pn)); 1122 1123 return (B_TRUE); 1124 } 1125 1126 /* 1127 * smb_validate_object_name 1128 * 1129 * smb_pathname_validate() should have already been pertformed on pn. 1130 * 1131 * Very basic file name validation. 1132 * For filenames, we check for names of the form "AAAn:". Names that 1133 * contain three characters, a single digit and a colon (:) are reserved 1134 * as DOS device names, i.e. "COM1:". 1135 * Stream name validation is handed off to smb_validate_stream_name 1136 * 1137 * Returns: B_TRUE if pn->pn_fname is valid, 1138 * otherwise returns B_FALSE and sets error status in sr. 1139 */ 1140 boolean_t 1141 smb_validate_object_name(smb_request_t *sr, smb_pathname_t *pn) 1142 { 1143 if (pn->pn_fname && 1144 strlen(pn->pn_fname) == 5 && 1145 smb_isdigit(pn->pn_fname[3]) && 1146 pn->pn_fname[4] == ':') { 1147 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1148 ERRDOS, ERROR_INVALID_NAME); 1149 return (B_FALSE); 1150 } 1151 1152 if (pn->pn_sname) 1153 return (smb_validate_stream_name(sr, pn)); 1154 1155 return (B_TRUE); 1156 } 1157 1158 /* 1159 * smb_stream_parse_name 1160 * 1161 * smb_stream_parse_name should only be called for a path that 1162 * contains a valid named stream. Path validation should have 1163 * been performed before this function is called, typically by 1164 * calling smb_is_stream_name() just before this. 1165 * 1166 * Find the last component of path and split it into filename 1167 * and stream name. 1168 * 1169 * On return the named stream type will be present. The stream 1170 * type defaults to ":$DATA", if it has not been defined 1171 * For example, 'stream' contains :<sname>:$DATA 1172 * 1173 * Output args: filename, stream both MAXNAMELEN 1174 */ 1175 void 1176 smb_stream_parse_name(char *path, char *filename, char *stream) 1177 { 1178 char *fname, *sname, *stype; 1179 size_t flen, slen; 1180 1181 ASSERT(path); 1182 ASSERT(filename); 1183 ASSERT(stream); 1184 1185 fname = strrchr(path, '\\'); 1186 fname = (fname == NULL) ? path : fname + 1; 1187 sname = strchr(fname, ':'); 1188 /* Caller makes sure there is a ':' in path. */ 1189 VERIFY(sname != NULL); 1190 /* LINTED: possible ptrdiff_t overflow */ 1191 flen = sname - fname; 1192 slen = strlen(sname); 1193 1194 if (flen > (MAXNAMELEN-1)) 1195 flen = (MAXNAMELEN-1); 1196 (void) strncpy(filename, fname, flen); 1197 filename[flen] = '\0'; 1198 1199 if (slen > (MAXNAMELEN-1)) 1200 slen = (MAXNAMELEN-1); 1201 (void) strncpy(stream, sname, slen); 1202 stream[slen] = '\0'; 1203 1204 /* Add a "stream type" if there isn't one. */ 1205 stype = strchr(stream + 1, ':'); 1206 if (stype == NULL) 1207 (void) strlcat(stream, ":$DATA", MAXNAMELEN); 1208 else 1209 (void) smb_strupr(stype); 1210 } 1211 1212 /* 1213 * smb_is_stream_name 1214 * 1215 * Determines if 'path' specifies a named stream. 1216 * 1217 * path is a NULL terminated string which could be a stream path. 1218 * [pathname/]fname[:stream_name[:stream_type]] 1219 * 1220 * - If there is no colon in the path or it's the last char 1221 * then it's not a stream name 1222 * 1223 * - '::' is a non-stream and is commonly used by Windows to designate 1224 * the unamed stream in the form "::$DATA" 1225 */ 1226 boolean_t 1227 smb_is_stream_name(char *path) 1228 { 1229 char *colonp; 1230 1231 if (path == NULL) 1232 return (B_FALSE); 1233 1234 colonp = strchr(path, ':'); 1235 if ((colonp == NULL) || (*(colonp+1) == '\0')) 1236 return (B_FALSE); 1237 1238 if (strstr(path, "::")) 1239 return (B_FALSE); 1240 1241 return (B_TRUE); 1242 } 1243 1244 /* 1245 * Is this stream node a "restricted" type? 1246 */ 1247 boolean_t 1248 smb_strname_restricted(char *strname) 1249 { 1250 char *stype; 1251 1252 stype = strrchr(strname, ':'); 1253 if (stype == NULL) 1254 return (B_FALSE); 1255 1256 /* 1257 * Only ":$CA" is restricted (for now). 1258 */ 1259 if (strcmp(stype, ":$CA") == 0) 1260 return (B_TRUE); 1261 1262 return (B_FALSE); 1263 } 1264 1265 /* 1266 * smb_validate_stream_name 1267 * 1268 * B_FALSE will be returned, and the error status ser in the sr, if: 1269 * - the path is not a stream name 1270 * - a path is specified but the fname is ommitted. 1271 * - the stream_type is specified but not valid. 1272 * 1273 * Note: the stream type is case-insensitive. 1274 */ 1275 boolean_t 1276 smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn) 1277 { 1278 static char *strmtype[] = { 1279 "$CA", 1280 "$DATA", 1281 "$INDEX_ALLOCATION" 1282 }; 1283 int i; 1284 1285 ASSERT(pn); 1286 ASSERT(pn->pn_sname); 1287 1288 if (pn->pn_stype != NULL) { 1289 for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) { 1290 if (strcasecmp(pn->pn_stype, strmtype[i]) == 0) 1291 return (B_TRUE); 1292 } 1293 1294 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1295 ERRDOS, ERROR_INVALID_NAME); 1296 return (B_FALSE); 1297 } 1298 1299 return (B_TRUE); 1300 } 1301 1302 /* 1303 * valid DFS I/O path: 1304 * 1305 * \server-or-domain\share 1306 * \server-or-domain\share\path 1307 * 1308 * All the returned errors by this function needs to be 1309 * checked against Windows. 1310 */ 1311 static int 1312 smb_pathname_dfs_preprocess(smb_request_t *sr, char *path, size_t pathsz) 1313 { 1314 smb_unc_t unc; 1315 char *linkpath; 1316 int rc; 1317 1318 if (sr->tid_tree == NULL) 1319 return (0); 1320 1321 if ((rc = smb_unc_init(path, &unc)) != 0) 1322 return (rc); 1323 1324 if (smb_strcasecmp(unc.unc_share, sr->tid_tree->t_sharename, 0)) { 1325 smb_unc_free(&unc); 1326 return (EINVAL); 1327 } 1328 1329 linkpath = unc.unc_path; 1330 (void) snprintf(path, pathsz, "/%s", (linkpath) ? linkpath : ""); 1331 1332 smb_unc_free(&unc); 1333 return (0); 1334 } 1335