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