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