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