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 2021 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 fname = strrchr(pn->pn_path, '\\'); 808 809 if (fname) { 810 if (fname == pname) { 811 pn->pn_pname = NULL; 812 } else { 813 *fname = '\0'; 814 pn->pn_pname = 815 smb_pathname_strdup(sr, pname); 816 *fname = '\\'; 817 } 818 ++fname; 819 } else { 820 fname = pname; 821 pn->pn_pname = NULL; 822 } 823 824 if (fname[0] == '\0') { 825 pn->pn_fname = NULL; 826 return; 827 } 828 829 if (!smb_is_stream_name(fname)) { 830 pn->pn_fname = smb_pathname_strdup(sr, fname); 831 return; 832 } 833 834 /* 835 * find sname and stype in fname. 836 * sname can't be NULL smb_is_stream_name checks this 837 */ 838 sname = strchr(fname, ':'); 839 if (sname == fname) 840 fname = NULL; 841 else { 842 *sname = '\0'; 843 pn->pn_fname = 844 smb_pathname_strdup(sr, fname); 845 *sname = ':'; 846 } 847 848 pn->pn_sname = smb_pathname_strdup(sr, sname); 849 pn->pn_stype = strchr(pn->pn_sname + 1, ':'); 850 if (pn->pn_stype) { 851 (void) smb_strupr(pn->pn_stype); 852 } else { 853 len = strlen(pn->pn_sname); 854 pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA"); 855 pn->pn_stype = pn->pn_sname + len; 856 } 857 ++pn->pn_stype; 858 } 859 860 /* 861 * smb_pathname_preprocess 862 * 863 * Perform common pre-processing of pn->pn_path: 864 * - if the pn_path is blank, set it to '\\' 865 * - perform unicode wildcard converstion. 866 * - convert any '/' to '\\' 867 * - eliminate duplicate slashes 868 * - remove trailing slashes 869 * - quota directory specific pre-processing 870 */ 871 static void 872 smb_pathname_preprocess(smb_request_t *sr, smb_pathname_t *pn) 873 { 874 char *p; 875 876 /* treat empty path as "\\" */ 877 if (strlen(pn->pn_path) == 0) { 878 pn->pn_path = smb_pathname_strdup(sr, "\\"); 879 return; 880 } 881 882 if (sr->session->dialect < NT_LM_0_12) 883 smb_convert_wildcards(pn->pn_path); 884 885 /* treat '/' as '\\' */ 886 (void) strsubst(pn->pn_path, '/', '\\'); 887 888 (void) strcanon(pn->pn_path, "\\"); 889 890 /* remove trailing '\\' */ 891 p = pn->pn_path + strlen(pn->pn_path) - 1; 892 if ((p != pn->pn_path) && (*p == '\\')) 893 *p = '\0'; 894 895 smb_pathname_preprocess_quota(sr, pn); 896 smb_pathname_preprocess_adminshare(sr, pn); 897 } 898 899 /* 900 * smb_pathname_preprocess_quota 901 * 902 * There is a special file required by windows so that the quota 903 * tab will be displayed by windows clients. This is created in 904 * a special directory, $EXTEND, at the root of the shared file 905 * system. To hide this directory prepend a '.' (dot). 906 */ 907 static void 908 smb_pathname_preprocess_quota(smb_request_t *sr, smb_pathname_t *pn) 909 { 910 char *name = "$EXTEND"; 911 char *new_name = ".$EXTEND"; 912 char *p, *slash; 913 int len; 914 915 if (!smb_node_is_vfsroot(sr->tid_tree->t_snode)) 916 return; 917 918 p = pn->pn_path; 919 920 /* ignore any initial "\\" */ 921 p += strspn(p, "\\"); 922 if (smb_strcasecmp(p, name, strlen(name)) != 0) 923 return; 924 925 p += strlen(name); 926 if ((*p != ':') && (*p != '\\') && (*p != '\0')) 927 return; 928 929 slash = (pn->pn_path[0] == '\\') ? "\\" : ""; 930 len = strlen(pn->pn_path) + 2; 931 pn->pn_path = smb_srm_alloc(sr, len); 932 (void) snprintf(pn->pn_path, len, "%s%s%s", slash, new_name, p); 933 (void) smb_strupr(pn->pn_path); 934 } 935 936 /* 937 * smb_pathname_preprocess_adminshare 938 * 939 * Convert any path with share name "C$" or "c$" (Admin share) in to lower case. 940 */ 941 static void 942 smb_pathname_preprocess_adminshare(smb_request_t *sr, smb_pathname_t *pn) 943 { 944 if (strcasecmp(sr->tid_tree->t_sharename, "c$") == 0) 945 (void) smb_strlwr(pn->pn_path); 946 } 947 948 /* 949 * smb_pathname_strdup 950 * 951 * Duplicate NULL terminated string s. 952 * 953 * The new string is allocated using request specific storage and will 954 * be free'd when the sr is destroyed. 955 */ 956 static char * 957 smb_pathname_strdup(smb_request_t *sr, const char *s) 958 { 959 char *s2; 960 size_t n; 961 962 n = strlen(s) + 1; 963 s2 = smb_srm_zalloc(sr, n); 964 (void) strlcpy(s2, s, n); 965 return (s2); 966 } 967 968 /* 969 * smb_pathname_strcat 970 * 971 * Reallocate NULL terminated string s1 to accommodate 972 * concatenating NULL terminated string s2. 973 * Append s2 and return resulting NULL terminated string. 974 * 975 * The string buffer is reallocated using request specific 976 * storage and will be free'd when the sr is destroyed. 977 */ 978 static char * 979 smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2) 980 { 981 size_t n; 982 983 n = strlen(s1) + strlen(s2) + 1; 984 s1 = smb_srm_rezalloc(sr, s1, n); 985 (void) strlcat(s1, s2, n); 986 return (s1); 987 } 988 989 /* 990 * smb_pathname_validate 991 * 992 * Perform basic validation of pn: 993 * - If first component of pn->path is ".." -> PATH_SYNTAX_BAD 994 * - If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID 995 * - If fname is "." -> INVALID_OBJECT_NAME 996 * 997 * On unix .. at the root of a file system links to the root. Thus 998 * an attempt to lookup "/../../.." will be the same as looking up "/" 999 * CIFs clients expect the above to result in 1000 * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible 1001 * (and questionable if it's desirable) to deal with all cases 1002 * but paths beginning with \\.. are handled. 1003 * 1004 * Returns: B_TRUE if pn is valid, 1005 * otherwise returns B_FALSE and sets error status in sr. 1006 * 1007 * XXX: Get rid of smbsr_error calls for SMB2 1008 */ 1009 boolean_t 1010 smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn) 1011 { 1012 char *path = pn->pn_path; 1013 1014 /* ignore any initial "\\" */ 1015 path += strspn(path, "\\"); 1016 1017 /* If first component of path is ".." -> PATH_SYNTAX_BAD */ 1018 if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) { 1019 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 1020 ERRDOS, ERROR_BAD_PATHNAME); 1021 return (B_FALSE); 1022 } 1023 1024 /* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */ 1025 if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) { 1026 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1027 ERRDOS, ERROR_INVALID_NAME); 1028 return (B_FALSE); 1029 } 1030 1031 /* If fname is "." -> OBJECT_NAME_INVALID */ 1032 if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) { 1033 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1034 ERRDOS, ERROR_INVALID_NAME); 1035 return (B_FALSE); 1036 } 1037 1038 return (B_TRUE); 1039 } 1040 1041 /* 1042 * smb_validate_dirname 1043 * 1044 * smb_pathname_validate() should have already been performed on pn. 1045 * 1046 * Very basic directory name validation: checks for colons in a path. 1047 * Need to skip the drive prefix since it contains a colon. 1048 * 1049 * Returns: B_TRUE if the name is valid, 1050 * otherwise returns B_FALSE and sets error status in sr. 1051 */ 1052 boolean_t 1053 smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn) 1054 { 1055 char *name; 1056 char *path = pn->pn_path; 1057 1058 if ((name = path) != 0) { 1059 name += strspn(name, "\\"); 1060 1061 if (strchr(name, ':') != 0) { 1062 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 1063 ERRDOS, ERROR_INVALID_NAME); 1064 return (B_FALSE); 1065 } 1066 } 1067 1068 return (B_TRUE); 1069 } 1070 1071 /* 1072 * smb_validate_object_name 1073 * 1074 * smb_pathname_validate() should have already been pertformed on pn. 1075 * 1076 * Very basic file name validation. 1077 * For filenames, we check for names of the form "AAAn:". Names that 1078 * contain three characters, a single digit and a colon (:) are reserved 1079 * as DOS device names, i.e. "COM1:". 1080 * Stream name validation is handed off to smb_validate_stream_name 1081 * 1082 * Returns: B_TRUE if pn->pn_fname is valid, 1083 * otherwise returns B_FALSE and sets error status in sr. 1084 */ 1085 boolean_t 1086 smb_validate_object_name(smb_request_t *sr, smb_pathname_t *pn) 1087 { 1088 if (pn->pn_fname && 1089 strlen(pn->pn_fname) == 5 && 1090 smb_isdigit(pn->pn_fname[3]) && 1091 pn->pn_fname[4] == ':') { 1092 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1093 ERRDOS, ERROR_INVALID_NAME); 1094 return (B_FALSE); 1095 } 1096 1097 if (pn->pn_sname) 1098 return (smb_validate_stream_name(sr, pn)); 1099 1100 return (B_TRUE); 1101 } 1102 1103 /* 1104 * smb_stream_parse_name 1105 * 1106 * smb_stream_parse_name should only be called for a path that 1107 * contains a valid named stream. Path validation should have 1108 * been performed before this function is called, typically by 1109 * calling smb_is_stream_name() just before this. 1110 * 1111 * Find the last component of path and split it into filename 1112 * and stream name. 1113 * 1114 * On return the named stream type will be present. The stream 1115 * type defaults to ":$DATA", if it has not been defined 1116 * For example, 'stream' contains :<sname>:$DATA 1117 * 1118 * Output args: filename, stream both MAXNAMELEN 1119 */ 1120 void 1121 smb_stream_parse_name(char *path, char *filename, char *stream) 1122 { 1123 char *fname, *sname, *stype; 1124 size_t flen, slen; 1125 1126 ASSERT(path); 1127 ASSERT(filename); 1128 ASSERT(stream); 1129 1130 fname = strrchr(path, '\\'); 1131 fname = (fname == NULL) ? path : fname + 1; 1132 sname = strchr(fname, ':'); 1133 /* Caller makes sure there is a ':' in path. */ 1134 VERIFY(sname != NULL); 1135 /* LINTED: possible ptrdiff_t overflow */ 1136 flen = sname - fname; 1137 slen = strlen(sname); 1138 1139 if (flen > (MAXNAMELEN-1)) 1140 flen = (MAXNAMELEN-1); 1141 (void) strncpy(filename, fname, flen); 1142 filename[flen] = '\0'; 1143 1144 if (slen > (MAXNAMELEN-1)) 1145 slen = (MAXNAMELEN-1); 1146 (void) strncpy(stream, sname, slen); 1147 stream[slen] = '\0'; 1148 1149 /* Add a "stream type" if there isn't one. */ 1150 stype = strchr(stream + 1, ':'); 1151 if (stype == NULL) 1152 (void) strlcat(stream, ":$DATA", MAXNAMELEN); 1153 else 1154 (void) smb_strupr(stype); 1155 } 1156 1157 /* 1158 * smb_is_stream_name 1159 * 1160 * Determines if 'path' specifies a named stream. 1161 * 1162 * path is a NULL terminated string which could be a stream path. 1163 * [pathname/]fname[:stream_name[:stream_type]] 1164 * 1165 * - If there is no colon in the path or it's the last char 1166 * then it's not a stream name 1167 * 1168 * - '::' is a non-stream and is commonly used by Windows to designate 1169 * the unamed stream in the form "::$DATA" 1170 */ 1171 boolean_t 1172 smb_is_stream_name(char *path) 1173 { 1174 char *colonp; 1175 1176 if (path == NULL) 1177 return (B_FALSE); 1178 1179 colonp = strchr(path, ':'); 1180 if ((colonp == NULL) || (*(colonp+1) == '\0')) 1181 return (B_FALSE); 1182 1183 if (strstr(path, "::")) 1184 return (B_FALSE); 1185 1186 return (B_TRUE); 1187 } 1188 1189 /* 1190 * Is this stream node a "restricted" type? 1191 */ 1192 boolean_t 1193 smb_strname_restricted(char *strname) 1194 { 1195 char *stype; 1196 1197 stype = strrchr(strname, ':'); 1198 if (stype == NULL) 1199 return (B_FALSE); 1200 1201 /* 1202 * Only ":$CA" is restricted (for now). 1203 */ 1204 if (strcmp(stype, ":$CA") == 0) 1205 return (B_TRUE); 1206 1207 return (B_FALSE); 1208 } 1209 1210 /* 1211 * smb_validate_stream_name 1212 * 1213 * B_FALSE will be returned, and the error status ser in the sr, if: 1214 * - the path is not a stream name 1215 * - a path is specified but the fname is ommitted. 1216 * - the stream_type is specified but not valid. 1217 * 1218 * Note: the stream type is case-insensitive. 1219 */ 1220 boolean_t 1221 smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn) 1222 { 1223 static char *strmtype[] = { 1224 "$CA", 1225 "$DATA", 1226 "$INDEX_ALLOCATION" 1227 }; 1228 int i; 1229 1230 ASSERT(pn); 1231 ASSERT(pn->pn_sname); 1232 1233 if ((!(pn->pn_sname)) || 1234 ((pn->pn_pname) && !(pn->pn_fname))) { 1235 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1236 ERRDOS, ERROR_INVALID_NAME); 1237 return (B_FALSE); 1238 } 1239 1240 1241 if (pn->pn_stype != NULL) { 1242 for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) { 1243 if (strcasecmp(pn->pn_stype, strmtype[i]) == 0) 1244 return (B_TRUE); 1245 } 1246 1247 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 1248 ERRDOS, ERROR_INVALID_NAME); 1249 return (B_FALSE); 1250 } 1251 1252 return (B_TRUE); 1253 } 1254 1255 /* 1256 * valid DFS I/O path: 1257 * 1258 * \server-or-domain\share 1259 * \server-or-domain\share\path 1260 * 1261 * All the returned errors by this function needs to be 1262 * checked against Windows. 1263 */ 1264 static int 1265 smb_pathname_dfs_preprocess(smb_request_t *sr, char *path, size_t pathsz) 1266 { 1267 smb_unc_t unc; 1268 char *linkpath; 1269 int rc; 1270 1271 if (sr->tid_tree == NULL) 1272 return (0); 1273 1274 if ((rc = smb_unc_init(path, &unc)) != 0) 1275 return (rc); 1276 1277 if (smb_strcasecmp(unc.unc_share, sr->tid_tree->t_sharename, 0)) { 1278 smb_unc_free(&unc); 1279 return (EINVAL); 1280 } 1281 1282 linkpath = unc.unc_path; 1283 (void) snprintf(path, pathsz, "/%s", (linkpath) ? linkpath : ""); 1284 1285 smb_unc_free(&unc); 1286 return (0); 1287 } 1288