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