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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 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 *, 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 39 uint32_t 40 smb_is_executable(char *path) 41 { 42 char extension[5]; 43 int len = strlen(path); 44 45 if ((len >= 4) && (path[len - 4] == '.')) { 46 (void) strcpy(extension, &path[len - 3]); 47 (void) smb_strupr(extension); 48 49 if (strcmp(extension, "EXE") == 0) 50 return (NODE_FLAGS_EXECUTABLE); 51 52 if (strcmp(extension, "COM") == 0) 53 return (NODE_FLAGS_EXECUTABLE); 54 55 if (strcmp(extension, "DLL") == 0) 56 return (NODE_FLAGS_EXECUTABLE); 57 58 if (strcmp(extension, "SYM") == 0) 59 return (NODE_FLAGS_EXECUTABLE); 60 } 61 62 return (0); 63 } 64 65 /* 66 * smb_pathname_reduce 67 * 68 * smb_pathname_reduce() takes a path and returns the smb_node for the 69 * second-to-last component of the path. It also returns the name of the last 70 * component. Pointers for both of these fields must be supplied by the caller. 71 * 72 * Upon success, 0 is returned. 73 * 74 * Upon error, *dir_node will be set to 0. 75 * 76 * *sr (in) 77 * --- 78 * smb_request structure pointer 79 * 80 * *cred (in) 81 * ----- 82 * credential 83 * 84 * *path (in) 85 * ----- 86 * pathname to be looked up 87 * 88 * *share_root_node (in) 89 * ---------------- 90 * File operations which are share-relative should pass sr->tid_tree->t_snode. 91 * If the call is not for a share-relative operation, this parameter must be 0 92 * (e.g. the call from smbsr_setup_share()). (Such callers will have path 93 * operations done using root_smb_node.) This parameter is used to determine 94 * whether mount points can be crossed. 95 * 96 * share_root_node should have at least one reference on it. This reference 97 * will stay intact throughout this routine. 98 * 99 * *cur_node (in) 100 * --------- 101 * The smb_node for the current directory (for relative paths). 102 * cur_node should have at least one reference on it. 103 * This reference will stay intact throughout this routine. 104 * 105 * **dir_node (out) 106 * ---------- 107 * Directory for the penultimate component of the original path. 108 * (Note that this is not the same as the parent directory of the ultimate 109 * target in the case of a link.) 110 * 111 * The directory smb_node is returned held. The caller will need to release 112 * the hold or otherwise make sure it will get released (e.g. in a destroy 113 * routine if made part of a global structure). 114 * 115 * last_component (out) 116 * -------------- 117 * The last component of the path. (This may be different from the name of any 118 * link target to which the last component may resolve.) 119 * 120 * 121 * ____________________________ 122 * 123 * The CIFS server lookup path needs to have logic equivalent to that of 124 * smb_fsop_lookup(), smb_vop_lookup() and other smb_vop_*() routines in the 125 * following areas: 126 * 127 * - non-traversal of child mounts (handled by smb_pathname_reduce) 128 * - unmangling (handled in smb_pathname) 129 * - "chroot" behavior of share root (handled by lookuppnvp) 130 * 131 * In addition, it needs to replace backslashes with forward slashes. It also 132 * ensures that link processing is done correctly, and that directory 133 * information requested by the caller is correctly returned (i.e. for paths 134 * with a link in the last component, the directory information of the 135 * link and not the target needs to be returned). 136 */ 137 138 int 139 smb_pathname_reduce( 140 smb_request_t *sr, 141 cred_t *cred, 142 const char *path, 143 smb_node_t *share_root_node, 144 smb_node_t *cur_node, 145 smb_node_t **dir_node, 146 char *last_component) 147 { 148 smb_node_t *root_node; 149 pathname_t ppn; 150 char *usepath; 151 int lookup_flags = FOLLOW; 152 int trailing_slash = 0; 153 int err = 0; 154 int len; 155 smb_node_t *vss_cur_node; 156 smb_node_t *vss_root_node; 157 smb_node_t *local_cur_node; 158 smb_node_t *local_root_node; 159 160 ASSERT(dir_node); 161 ASSERT(last_component); 162 163 *dir_node = NULL; 164 *last_component = '\0'; 165 vss_cur_node = NULL; 166 vss_root_node = NULL; 167 168 if (sr && sr->tid_tree) { 169 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) 170 return (EACCES); 171 } 172 173 if (SMB_TREE_IS_CASEINSENSITIVE(sr)) 174 lookup_flags |= FIGNORECASE; 175 176 if (path == NULL) 177 return (EINVAL); 178 179 if (*path == '\0') 180 return (ENOENT); 181 182 usepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 183 184 if ((len = strlcpy(usepath, path, MAXPATHLEN)) >= MAXPATHLEN) { 185 kmem_free(usepath, MAXPATHLEN); 186 return (ENAMETOOLONG); 187 } 188 189 (void) strsubst(usepath, '\\', '/'); 190 191 if (share_root_node) 192 root_node = share_root_node; 193 else 194 root_node = sr->sr_server->si_root_smb_node; 195 196 if (cur_node == NULL) 197 cur_node = root_node; 198 199 local_cur_node = cur_node; 200 local_root_node = root_node; 201 202 if (sr && (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH)) { 203 err = smb_vss_lookup_nodes(sr, root_node, cur_node, 204 usepath, &vss_cur_node, &vss_root_node); 205 206 if (err != 0) { 207 kmem_free(usepath, MAXPATHLEN); 208 return (err); 209 } 210 211 len = strlen(usepath); 212 local_cur_node = vss_cur_node; 213 local_root_node = vss_root_node; 214 } 215 216 if (usepath[len - 1] == '/') 217 trailing_slash = 1; 218 219 (void) strcanon(usepath, "/"); 220 221 (void) pn_alloc(&ppn); 222 223 if ((err = pn_set(&ppn, usepath)) != 0) { 224 (void) pn_free(&ppn); 225 kmem_free(usepath, MAXPATHLEN); 226 if (vss_cur_node != NULL) 227 (void) smb_node_release(vss_cur_node); 228 if (vss_root_node != NULL) 229 (void) smb_node_release(vss_root_node); 230 return (err); 231 } 232 233 /* 234 * If a path does not have a trailing slash, strip off the 235 * last component. (We only need to return an smb_node for 236 * the second to last component; a name is returned for the 237 * last component.) 238 */ 239 240 if (trailing_slash) { 241 (void) strlcpy(last_component, ".", MAXNAMELEN); 242 } else { 243 (void) pn_setlast(&ppn); 244 (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN); 245 ppn.pn_path[0] = '\0'; 246 } 247 248 if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) { 249 smb_node_ref(local_cur_node); 250 *dir_node = local_cur_node; 251 } else { 252 err = smb_pathname(sr, ppn.pn_buf, lookup_flags, 253 local_root_node, local_cur_node, NULL, dir_node, cred); 254 } 255 256 (void) pn_free(&ppn); 257 kmem_free(usepath, MAXPATHLEN); 258 259 /* 260 * Prevent access to anything outside of the share root, except 261 * when mapping a share because that may require traversal from 262 * / to a mounted file system. share_root_node is NULL when 263 * mapping a share. 264 * 265 * Note that we disregard whether the traversal of the path went 266 * outside of the file system and then came back (say via a link). 267 */ 268 269 if ((err == 0) && share_root_node) { 270 if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) 271 err = EACCES; 272 } 273 274 if (err) { 275 if (*dir_node) { 276 (void) smb_node_release(*dir_node); 277 *dir_node = NULL; 278 } 279 *last_component = 0; 280 } 281 282 if (vss_cur_node != NULL) 283 (void) smb_node_release(vss_cur_node); 284 if (vss_root_node != NULL) 285 (void) smb_node_release(vss_root_node); 286 287 return (err); 288 } 289 290 /* 291 * smb_pathname() 292 * wrapper to lookuppnvp(). Handles name unmangling. 293 * 294 * *dir_node is the true directory of the target *node. 295 * 296 * If any component but the last in the path is not found, ENOTDIR instead of 297 * ENOENT will be returned. 298 * 299 * Path components are processed one at a time so that smb_nodes can be 300 * created for each component. This allows the n_dnode field in the 301 * smb_node to be properly populated. 302 * 303 * Because of the above, links are also processed in this routine 304 * (i.e., we do not pass the FOLLOW flag to lookuppnvp()). This 305 * will allow smb_nodes to be created for each component of a link. 306 * 307 * Mangle checking is per component. If a name is mangled, when the 308 * unmangled name is passed to smb_pathname_lookup() do not pass 309 * FIGNORECASE, since the unmangled name is the real on-disk name. 310 * Otherwise pass FIGNORECASE if it's set in flags. This will cause the 311 * file system to return "first match" in the event of a case collision. 312 * 313 * If CATIA character translation is enabled it is applied to each 314 * component before passing the component to smb_pathname_lookup(). 315 * After smb_pathname_lookup() the reverse translation is applied. 316 */ 317 318 int 319 smb_pathname(smb_request_t *sr, char *path, int flags, 320 smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node, 321 smb_node_t **ret_node, cred_t *cred) 322 { 323 char *component, *real_name, *namep; 324 pathname_t pn, rpn, upn, link_pn; 325 smb_node_t *dnode, *fnode; 326 vnode_t *rootvp, *vp; 327 size_t pathleft; 328 int err = 0; 329 int nlink = 0; 330 int local_flags; 331 uint32_t abe_flag = 0; 332 char namebuf[MAXNAMELEN]; 333 334 if (path == NULL) 335 return (EINVAL); 336 337 ASSERT(root_node); 338 ASSERT(cur_node); 339 ASSERT(ret_node); 340 341 *ret_node = NULL; 342 343 if (dir_node) 344 *dir_node = NULL; 345 346 (void) pn_alloc(&upn); 347 348 if ((err = pn_set(&upn, path)) != 0) { 349 (void) pn_free(&upn); 350 return (err); 351 } 352 353 if (SMB_TREE_SUPPORTS_ABE(sr)) 354 abe_flag = SMB_ABE; 355 356 (void) pn_alloc(&pn); 357 (void) pn_alloc(&rpn); 358 359 component = kmem_alloc(MAXNAMELEN, KM_SLEEP); 360 real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 361 362 fnode = NULL; 363 dnode = cur_node; 364 smb_node_ref(dnode); 365 rootvp = root_node->vp; 366 367 while ((pathleft = pn_pathleft(&upn)) != 0) { 368 if (fnode) { 369 smb_node_release(dnode); 370 dnode = fnode; 371 fnode = NULL; 372 } 373 374 if ((err = pn_getcomponent(&upn, component)) != 0) 375 break; 376 377 if ((namep = smb_pathname_catia_v5tov4(sr, component, 378 namebuf, sizeof (namebuf))) == NULL) { 379 err = EILSEQ; 380 break; 381 } 382 383 if ((err = pn_set(&pn, namep)) != 0) 384 break; 385 386 local_flags = flags & FIGNORECASE; 387 err = smb_pathname_lookup(&pn, &rpn, local_flags, 388 &vp, rootvp, dnode->vp, cred); 389 390 if (err) { 391 if (smb_maybe_mangled_name(component) == 0) 392 break; 393 394 if ((err = smb_unmangle_name(dnode, component, 395 real_name, MAXNAMELEN, abe_flag)) != 0) 396 break; 397 398 if ((namep = smb_pathname_catia_v5tov4(sr, real_name, 399 namebuf, sizeof (namebuf))) == NULL) { 400 err = EILSEQ; 401 break; 402 } 403 404 if ((err = pn_set(&pn, namep)) != 0) 405 break; 406 407 local_flags = 0; 408 err = smb_pathname_lookup(&pn, &rpn, local_flags, 409 &vp, rootvp, dnode->vp, cred); 410 if (err) 411 break; 412 } 413 414 if ((vp->v_type == VLNK) && 415 ((flags & FOLLOW) || pn_pathleft(&upn))) { 416 417 if (++nlink > MAXSYMLINKS) { 418 err = ELOOP; 419 VN_RELE(vp); 420 break; 421 } 422 423 (void) pn_alloc(&link_pn); 424 err = pn_getsymlink(vp, &link_pn, cred); 425 VN_RELE(vp); 426 427 if (err == 0) { 428 if (pn_pathleft(&link_pn) == 0) 429 (void) pn_set(&link_pn, "."); 430 err = pn_insert(&upn, &link_pn, 431 strlen(component)); 432 } 433 pn_free(&link_pn); 434 435 if (err) 436 break; 437 438 if (upn.pn_pathlen == 0) { 439 err = ENOENT; 440 break; 441 } 442 443 if (upn.pn_path[0] == '/') { 444 fnode = root_node; 445 smb_node_ref(fnode); 446 } 447 448 if (pn_fixslash(&upn)) 449 flags |= FOLLOW; 450 451 } else { 452 if (flags & FIGNORECASE) { 453 if (strcmp(rpn.pn_path, "/") != 0) 454 pn_setlast(&rpn); 455 namep = rpn.pn_path; 456 } else { 457 namep = pn.pn_path; 458 } 459 460 namep = smb_pathname_catia_v4tov5(sr, namep, 461 namebuf, sizeof (namebuf)); 462 463 fnode = smb_node_lookup(sr, NULL, cred, vp, namep, 464 dnode, NULL); 465 VN_RELE(vp); 466 467 if (fnode == NULL) { 468 err = ENOMEM; 469 break; 470 } 471 } 472 473 while (upn.pn_path[0] == '/') { 474 upn.pn_path++; 475 upn.pn_pathlen--; 476 } 477 478 } 479 480 if ((pathleft) && (err == ENOENT)) 481 err = ENOTDIR; 482 483 if (err) { 484 if (fnode) 485 smb_node_release(fnode); 486 if (dnode) 487 smb_node_release(dnode); 488 } else { 489 *ret_node = fnode; 490 491 if (dir_node) 492 *dir_node = dnode; 493 else 494 smb_node_release(dnode); 495 } 496 497 kmem_free(component, MAXNAMELEN); 498 kmem_free(real_name, MAXNAMELEN); 499 (void) pn_free(&pn); 500 (void) pn_free(&rpn); 501 (void) pn_free(&upn); 502 503 return (err); 504 } 505 506 /* 507 * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp() 508 * and will be released within lookuppnvp(). 509 */ 510 static int 511 smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags, 512 vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, cred_t *cred) 513 { 514 int err; 515 516 *vp = NULL; 517 VN_HOLD(dvp); 518 if (rootvp != rootdir) 519 VN_HOLD(rootvp); 520 521 err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred); 522 return (err); 523 } 524 525 /* 526 * CATIA Translation of a pathname component prior to passing it to lookuppnvp 527 * 528 * If the translated component name contains a '/' NULL is returned. 529 * The caller should treat this as error EILSEQ. It is not valid to 530 * have a directory name with a '/'. 531 */ 532 static char * 533 smb_pathname_catia_v5tov4(smb_request_t *sr, char *name, 534 char *namebuf, int buflen) 535 { 536 char *namep; 537 538 if (SMB_TREE_SUPPORTS_CATIA(sr)) { 539 namep = smb_vop_catia_v5tov4(name, namebuf, buflen); 540 if (strchr(namep, '/') != NULL) 541 return (NULL); 542 return (namep); 543 } 544 545 return (name); 546 } 547 548 /* 549 * CATIA translation of a pathname component after returning from lookuppnvp 550 */ 551 static char * 552 smb_pathname_catia_v4tov5(smb_request_t *sr, char *name, 553 char *namebuf, int buflen) 554 { 555 if (SMB_TREE_SUPPORTS_CATIA(sr)) { 556 smb_vop_catia_v4tov5(name, namebuf, buflen); 557 return (namebuf); 558 } 559 560 return (name); 561 } 562 563 /* 564 * sr - needed to check for case sense 565 * path - non mangled path needed to be looked up from the startvp 566 * startvp - the vnode to start the lookup from 567 * rootvp - the vnode of the root of the filesystem 568 * returns the vnode found when starting at startvp and using the path 569 * 570 * Finds a vnode starting at startvp and parsing the non mangled path 571 */ 572 573 vnode_t * 574 smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp, 575 vnode_t *rootvp) 576 { 577 pathname_t pn; 578 vnode_t *vp = NULL; 579 int lookup_flags = FOLLOW; 580 581 if (SMB_TREE_IS_CASEINSENSITIVE(sr)) 582 lookup_flags |= FIGNORECASE; 583 584 (void) pn_alloc(&pn); 585 586 if (pn_set(&pn, path) == 0) { 587 VN_HOLD(startvp); 588 if (rootvp != rootdir) 589 VN_HOLD(rootvp); 590 591 /* lookuppnvp should release the holds */ 592 if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp, 593 rootvp, startvp, kcred) != 0) { 594 pn_free(&pn); 595 return (NULL); 596 } 597 } 598 599 pn_free(&pn); 600 return (vp); 601 } 602 603 /* 604 * smb_pathname_init 605 * Parse path: pname\\fname:sname:stype 606 * 607 * Elements of the smb_pathname_t structure are allocated using request 608 * specific storage and will be free'd when the sr is destroyed. 609 * 610 * Populate pn structure elements with the individual elements 611 * of pn->pn_path. pn->pn_sname will contain the whole stream name 612 * including the stream type and preceding colon: :sname:%DATA 613 * pn_stype will point to the stream type within pn_sname. 614 * 615 * If the pname element is missing pn_pname will be set to "\\". 616 * If any other element is missing the pointer in pn will be NULL. 617 */ 618 void 619 smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path) 620 { 621 char *pname, *fname, *sname; 622 int len; 623 624 bzero(pn, sizeof (smb_pathname_t)); 625 pn->pn_path = smb_pathname_strdup(sr, path); 626 627 smb_pathname_preprocess(sr, pn); 628 629 /* parse pn->pn_path into its constituent parts */ 630 pname = pn->pn_path; 631 fname = strrchr(pn->pn_path, '\\'); 632 633 if (fname) { 634 if (fname == pname) { 635 pn->pn_pname = smb_pathname_strdup(sr, "\\"); 636 } else { 637 *fname = '\0'; 638 pn->pn_pname = 639 smb_pathname_strdup(sr, pname); 640 *fname = '\\'; 641 } 642 ++fname; 643 } else { 644 fname = pname; 645 pn->pn_pname = smb_pathname_strdup(sr, "\\"); 646 } 647 648 if (fname[0] == '\0') { 649 pn->pn_fname = NULL; 650 return; 651 } 652 653 if (!smb_is_stream_name(fname)) { 654 pn->pn_fname = smb_pathname_strdup(sr, fname); 655 return; 656 } 657 658 /* 659 * find sname and stype in fname. 660 * sname can't be NULL smb_is_stream_name checks this 661 */ 662 sname = strchr(fname, ':'); 663 if (sname == fname) 664 fname = NULL; 665 else { 666 *sname = '\0'; 667 pn->pn_fname = 668 smb_pathname_strdup(sr, fname); 669 *sname = ':'; 670 } 671 672 pn->pn_sname = smb_pathname_strdup(sr, sname); 673 pn->pn_stype = strchr(pn->pn_sname + 1, ':'); 674 if (pn->pn_stype) { 675 (void) smb_strupr(pn->pn_stype); 676 } else { 677 len = strlen(pn->pn_sname); 678 pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA"); 679 pn->pn_stype = pn->pn_sname + len; 680 } 681 ++pn->pn_stype; 682 } 683 684 /* 685 * smb_pathname_preprocess 686 * 687 * Perform common pre-processing of pn->pn_path: 688 * - if the pn_path is blank, set it to '\\' 689 * - perform unicode wildcard converstion. 690 * - convert any '/' to '\\' 691 * - eliminate duplicate slashes 692 * - remove trailing slashes 693 */ 694 static void 695 smb_pathname_preprocess(smb_request_t *sr, smb_pathname_t *pn) 696 { 697 char *p; 698 699 /* treat empty path as "\\" */ 700 if (strlen(pn->pn_path) == 0) { 701 pn->pn_path = smb_pathname_strdup(sr, "\\"); 702 return; 703 } 704 705 /* perform unicode wildcard conversion */ 706 smb_convert_wildcards(pn->pn_path); 707 708 /* treat '/' as '\\' */ 709 (void) strsubst(pn->pn_path, '/', '\\'); 710 711 (void) strcanon(pn->pn_path, "\\"); 712 713 /* remove trailing '\\' */ 714 p = pn->pn_path + strlen(pn->pn_path) - 1; 715 if ((p != pn->pn_path) && (*p == '\\')) 716 *p = '\0'; 717 } 718 719 /* 720 * smb_pathname_strdup 721 * 722 * Duplicate NULL terminated string s. 723 * 724 * The new string is allocated using request specific storage and will 725 * be free'd when the sr is destroyed. 726 */ 727 static char * 728 smb_pathname_strdup(smb_request_t *sr, const char *s) 729 { 730 char *s2; 731 size_t n; 732 733 n = strlen(s) + 1; 734 s2 = smb_srm_alloc(sr, n); 735 (void) strlcpy(s2, s, n); 736 return (s2); 737 } 738 739 /* 740 * smb_pathname_strcat 741 * 742 * Reallocate NULL terminated string s1 to accommodate 743 * concatenating NULL terminated string s2. 744 * Append s2 and return resulting NULL terminated string. 745 * 746 * The string buffer is reallocated using request specific 747 * storage and will be free'd when the sr is destroyed. 748 */ 749 static char * 750 smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2) 751 { 752 size_t n; 753 754 n = strlen(s1) + strlen(s2) + 1; 755 s1 = smb_srm_realloc(sr, s1, n); 756 (void) strlcat(s1, s2, n); 757 return (s1); 758 } 759 760 /* 761 * smb_pathname_validate 762 * 763 * Perform basic validation of pn: 764 * - If first component of pn->path is ".." -> PATH_SYNTAX_BAD 765 * - If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID 766 * - If fname is "." -> INVALID_OBJECT_NAME 767 * 768 * On unix .. at the root of a file system links to the root. Thus 769 * an attempt to lookup "/../../.." will be the same as looking up "/" 770 * CIFs clients expect the above to result in 771 * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible 772 * (and questionable if it's desirable) to deal with all cases 773 * but paths beginning with \\.. are handled. 774 * 775 * Returns: B_TRUE if pn is valid, 776 * otherwise returns B_FALSE and sets error status in sr. 777 */ 778 boolean_t 779 smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn) 780 { 781 char *path = pn->pn_path; 782 783 /* ignore any initial "\\" */ 784 path += strspn(path, "\\"); 785 786 /* If first component of path is ".." -> PATH_SYNTAX_BAD */ 787 if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) { 788 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 789 ERRDOS, ERROR_BAD_PATHNAME); 790 return (B_FALSE); 791 } 792 793 /* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */ 794 if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) { 795 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 796 ERRDOS, ERROR_INVALID_NAME); 797 return (B_FALSE); 798 } 799 800 /* If fname is "." -> INVALID_OBJECT_NAME */ 801 if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) { 802 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 803 ERRDOS, ERROR_PATH_NOT_FOUND); 804 return (B_FALSE); 805 } 806 807 return (B_TRUE); 808 } 809 810 /* 811 * smb_validate_dirname 812 * 813 * smb_pathname_validate() should have already been performed on pn. 814 * 815 * Very basic directory name validation: checks for colons in a path. 816 * Need to skip the drive prefix since it contains a colon. 817 * 818 * Returns: B_TRUE if the name is valid, 819 * otherwise returns B_FALSE and sets error status in sr. 820 */ 821 boolean_t 822 smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn) 823 { 824 char *name; 825 char *path = pn->pn_path; 826 827 if ((name = path) != 0) { 828 name += strspn(name, "\\"); 829 830 if (strchr(name, ':') != 0) { 831 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 832 ERRDOS, ERROR_INVALID_NAME); 833 return (B_FALSE); 834 } 835 } 836 837 return (B_TRUE); 838 } 839 840 /* 841 * smb_validate_object_name 842 * 843 * smb_pathname_validate() should have already been pertformed on pn. 844 * 845 * Very basic file name validation. 846 * For filenames, we check for names of the form "AAAn:". Names that 847 * contain three characters, a single digit and a colon (:) are reserved 848 * as DOS device names, i.e. "COM1:". 849 * Stream name validation is handed off to smb_validate_stream_name 850 * 851 * Returns: B_TRUE if pn->pn_fname is valid, 852 * otherwise returns B_FALSE and sets error status in sr. 853 */ 854 boolean_t 855 smb_validate_object_name(smb_request_t *sr, smb_pathname_t *pn) 856 { 857 if (pn->pn_fname && 858 strlen(pn->pn_fname) == 5 && 859 smb_isdigit(pn->pn_fname[3]) && 860 pn->pn_fname[4] == ':') { 861 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 862 ERRDOS, ERROR_INVALID_NAME); 863 return (B_FALSE); 864 } 865 866 if (pn->pn_sname) 867 return (smb_validate_stream_name(sr, pn)); 868 869 return (B_TRUE); 870 } 871 872 /* 873 * smb_stream_parse_name 874 * 875 * smb_stream_parse_name should only be called for a path that 876 * contains a valid named stream. Path validation should have 877 * been performed before this function is called. 878 * 879 * Find the last component of path and split it into filename 880 * and stream name. 881 * 882 * On return the named stream type will be present. The stream 883 * type defaults to ":$DATA", if it has not been defined 884 * For exmaple, 'stream' contains :<sname>:$DATA 885 */ 886 void 887 smb_stream_parse_name(char *path, char *filename, char *stream) 888 { 889 char *fname, *sname, *stype; 890 891 ASSERT(path); 892 ASSERT(filename); 893 ASSERT(stream); 894 895 fname = strrchr(path, '\\'); 896 fname = (fname == NULL) ? path : fname + 1; 897 (void) strlcpy(filename, fname, MAXNAMELEN); 898 899 sname = strchr(filename, ':'); 900 (void) strlcpy(stream, sname, MAXNAMELEN); 901 *sname = '\0'; 902 903 stype = strchr(stream + 1, ':'); 904 if (stype == NULL) 905 (void) strlcat(stream, ":$DATA", MAXNAMELEN); 906 else 907 (void) smb_strupr(stype); 908 } 909 910 /* 911 * smb_is_stream_name 912 * 913 * Determines if 'path' specifies a named stream. 914 * 915 * path is a NULL terminated string which could be a stream path. 916 * [pathname/]fname[:stream_name[:stream_type]] 917 * 918 * - If there is no colon in the path or it's the last char 919 * then it's not a stream name 920 * 921 * - '::' is a non-stream and is commonly used by Windows to designate 922 * the unamed stream in the form "::$DATA" 923 */ 924 boolean_t 925 smb_is_stream_name(char *path) 926 { 927 char *colonp; 928 929 if (path == NULL) 930 return (B_FALSE); 931 932 colonp = strchr(path, ':'); 933 if ((colonp == NULL) || (*(colonp+1) == '\0')) 934 return (B_FALSE); 935 936 if (strstr(path, "::")) 937 return (B_FALSE); 938 939 return (B_TRUE); 940 } 941 942 /* 943 * smb_validate_stream_name 944 * 945 * B_FALSE will be returned, and the error status ser in the sr, if: 946 * - the path is not a stream name 947 * - a path is specified but the fname is ommitted. 948 * - the stream_type is specified but not valid. 949 * 950 * Note: the stream type is case-insensitive. 951 */ 952 boolean_t 953 smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn) 954 { 955 static char *strmtype[] = { 956 "$DATA", 957 "$INDEX_ALLOCATION" 958 }; 959 int i; 960 961 ASSERT(pn); 962 ASSERT(pn->pn_sname); 963 964 if ((!(pn->pn_sname)) || 965 ((pn->pn_pname) && !(pn->pn_fname))) { 966 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 967 ERRDOS, ERROR_INVALID_NAME); 968 return (B_FALSE); 969 } 970 971 972 if (pn->pn_stype != NULL) { 973 for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) { 974 if (strcasecmp(pn->pn_stype, strmtype[i]) == 0) 975 return (B_TRUE); 976 } 977 978 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 979 ERRDOS, ERROR_INVALID_NAME); 980 return (B_FALSE); 981 } 982 983 return (B_TRUE); 984 } 985