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