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_incl.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 36 uint32_t 37 smb_is_executable(char *path) 38 { 39 char extension[5]; 40 int len = strlen(path); 41 42 if ((len >= 4) && (path[len - 4] == '.')) { 43 (void) strcpy(extension, &path[len - 3]); 44 (void) utf8_strupr(extension); 45 46 if (strcmp(extension, "EXE") == 0) 47 return (NODE_FLAGS_EXECUTABLE); 48 49 if (strcmp(extension, "COM") == 0) 50 return (NODE_FLAGS_EXECUTABLE); 51 52 if (strcmp(extension, "DLL") == 0) 53 return (NODE_FLAGS_EXECUTABLE); 54 55 if (strcmp(extension, "SYM") == 0) 56 return (NODE_FLAGS_EXECUTABLE); 57 } 58 59 return (0); 60 } 61 62 /* 63 * smbd_fs_query 64 * 65 * Upon success, the caller will need to call smb_node_release() on 66 * fqi.last_snode (if it isn't already set to NULL by this routine) and 67 * and fqi.dir_snode. These pointers will not be used after the caller 68 * is done with them and should be released immediately. (The position 69 * of smb_fqi in a union in the smb_request structure makes it difficult 70 * to free these pointers at smb_request deallocation time.) 71 * 72 * If smbd_fs_query() returns error, no smb_nodes will need to be released 73 * by callers as a result of references taken in this routine, and 74 * fqi.last_snode and fqi.dir_snode will be set to NULL. 75 */ 76 77 int 78 smbd_fs_query(smb_request_t *sr, smb_fqi_t *fqi, int fqm) 79 { 80 int rc; 81 82 fqi->last_comp_was_found = 0; 83 84 rc = smb_pathname_reduce(sr, sr->user_cr, fqi->path, 85 sr->tid_tree->t_snode, sr->tid_tree->t_snode, &fqi->dir_snode, 86 fqi->last_comp); 87 88 if (rc) 89 return (rc); 90 91 rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, 92 sr->tid_tree->t_snode, fqi->dir_snode, fqi->last_comp, 93 &fqi->last_snode, &fqi->last_attr); 94 95 if (rc == 0) { 96 fqi->last_comp_was_found = 1; 97 (void) strcpy(fqi->last_comp_od, 98 fqi->last_snode->od_name); 99 100 if (fqm == FQM_PATH_MUST_NOT_EXIST) { 101 smb_node_release(fqi->dir_snode); 102 smb_node_release(fqi->last_snode); 103 SMB_NULL_FQI_NODES(*fqi); 104 return (EEXIST); 105 } 106 107 return (0); 108 } 109 110 if (fqm == FQM_PATH_MUST_EXIST) { 111 smb_node_release(fqi->dir_snode); 112 SMB_NULL_FQI_NODES(*fqi); 113 return (rc); 114 } 115 116 if (rc == ENOENT) { 117 fqi->last_snode = NULL; 118 return (0); 119 } 120 121 smb_node_release(fqi->dir_snode); 122 SMB_NULL_FQI_NODES(*fqi); 123 124 return (rc); 125 } 126 127 /* 128 * smb_pathname_reduce 129 * 130 * smb_pathname_reduce() takes a path and returns the smb_node for the 131 * second-to-last component of the path. It also returns the name of the last 132 * component. Pointers for both of these fields must be supplied by the caller. 133 * 134 * Upon success, 0 is returned. 135 * 136 * Upon error, *dir_node will be set to 0. 137 * 138 * *sr (in) 139 * --- 140 * smb_request structure pointer 141 * 142 * *cred (in) 143 * ----- 144 * credential 145 * 146 * *path (in) 147 * ----- 148 * pathname to be looked up 149 * 150 * *share_root_node (in) 151 * ---------------- 152 * File operations which are share-relative should pass sr->tid_tree->t_snode. 153 * If the call is not for a share-relative operation, this parameter must be 0 154 * (e.g. the call from smbsr_setup_share()). (Such callers will have path 155 * operations done using root_smb_node.) This parameter is used to determine 156 * whether mount points can be crossed. 157 * 158 * share_root_node should have at least one reference on it. This reference 159 * will stay intact throughout this routine. 160 * 161 * *cur_node (in) 162 * --------- 163 * The smb_node for the current directory (for relative paths). 164 * cur_node should have at least one reference on it. 165 * This reference will stay intact throughout this routine. 166 * 167 * **dir_node (out) 168 * ---------- 169 * Directory for the penultimate component of the original path. 170 * (Note that this is not the same as the parent directory of the ultimate 171 * target in the case of a link.) 172 * 173 * The directory smb_node is returned held. The caller will need to release 174 * the hold or otherwise make sure it will get released (e.g. in a destroy 175 * routine if made part of a global structure). 176 * 177 * last_component (out) 178 * -------------- 179 * The last component of the path. (This may be different from the name of any 180 * link target to which the last component may resolve.) 181 * 182 * 183 * ____________________________ 184 * 185 * The CIFS server lookup path needs to have logic equivalent to that of 186 * smb_fsop_lookup(), smb_vop_lookup() and other smb_vop_*() routines in the 187 * following areas: 188 * 189 * - non-traversal of child mounts (handled by smb_pathname_reduce) 190 * - unmangling (handled in smb_pathname) 191 * - "chroot" behavior of share root (handled by lookuppnvp) 192 * 193 * In addition, it needs to replace backslashes with forward slashes. It also 194 * ensures that link processing is done correctly, and that directory 195 * information requested by the caller is correctly returned (i.e. for paths 196 * with a link in the last component, the directory information of the 197 * link and not the target needs to be returned). 198 */ 199 200 int 201 smb_pathname_reduce( 202 smb_request_t *sr, 203 cred_t *cred, 204 const char *path, 205 smb_node_t *share_root_node, 206 smb_node_t *cur_node, 207 smb_node_t **dir_node, 208 char *last_component) 209 { 210 smb_node_t *root_node; 211 pathname_t ppn; 212 char *usepath; 213 int lookup_flags = FOLLOW; 214 int trailing_slash = 0; 215 int err = 0; 216 int len; 217 smb_node_t *vss_cur_node; 218 smb_node_t *vss_root_node; 219 smb_node_t *local_cur_node; 220 smb_node_t *local_root_node; 221 222 ASSERT(dir_node); 223 ASSERT(last_component); 224 225 *dir_node = NULL; 226 *last_component = '\0'; 227 vss_cur_node = NULL; 228 vss_root_node = NULL; 229 230 if (sr && sr->tid_tree) { 231 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) 232 return (EACCES); 233 } 234 235 if (SMB_TREE_IS_CASEINSENSITIVE(sr)) 236 lookup_flags |= FIGNORECASE; 237 238 if (path == NULL) 239 return (EINVAL); 240 241 if (*path == '\0') 242 return (ENOENT); 243 244 usepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 245 246 if ((len = strlcpy(usepath, path, MAXPATHLEN)) >= MAXPATHLEN) { 247 kmem_free(usepath, MAXPATHLEN); 248 return (ENAMETOOLONG); 249 } 250 251 (void) strsubst(usepath, '\\', '/'); 252 253 if (share_root_node) 254 root_node = share_root_node; 255 else 256 root_node = sr->sr_server->si_root_smb_node; 257 258 if (cur_node == NULL) 259 cur_node = root_node; 260 261 local_cur_node = cur_node; 262 local_root_node = root_node; 263 264 if (sr && (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH)) { 265 err = smb_vss_lookup_nodes(sr, root_node, cur_node, 266 usepath, &vss_cur_node, &vss_root_node); 267 268 if (err != 0) { 269 kmem_free(usepath, MAXPATHLEN); 270 return (err); 271 } 272 273 len = strlen(usepath); 274 local_cur_node = vss_cur_node; 275 local_root_node = vss_root_node; 276 } 277 278 if (usepath[len - 1] == '/') 279 trailing_slash = 1; 280 281 (void) strcanon(usepath, "/"); 282 283 (void) pn_alloc(&ppn); 284 285 if ((err = pn_set(&ppn, usepath)) != 0) { 286 (void) pn_free(&ppn); 287 kmem_free(usepath, MAXPATHLEN); 288 if (vss_cur_node != NULL) 289 (void) smb_node_release(vss_cur_node); 290 if (vss_root_node != NULL) 291 (void) smb_node_release(vss_root_node); 292 return (err); 293 } 294 295 /* 296 * If a path does not have a trailing slash, strip off the 297 * last component. (We only need to return an smb_node for 298 * the second to last component; a name is returned for the 299 * last component.) 300 */ 301 302 if (trailing_slash) { 303 (void) strlcpy(last_component, ".", MAXNAMELEN); 304 } else { 305 (void) pn_setlast(&ppn); 306 (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN); 307 ppn.pn_path[0] = '\0'; 308 } 309 310 if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) { 311 smb_node_ref(local_cur_node); 312 *dir_node = local_cur_node; 313 } else { 314 err = smb_pathname(sr, ppn.pn_buf, lookup_flags, 315 local_root_node, local_cur_node, NULL, dir_node, cred); 316 } 317 318 (void) pn_free(&ppn); 319 kmem_free(usepath, MAXPATHLEN); 320 321 /* 322 * Prevent access to anything outside of the share root, except 323 * when mapping a share because that may require traversal from 324 * / to a mounted file system. share_root_node is NULL when 325 * mapping a share. 326 * 327 * Note that we disregard whether the traversal of the path went 328 * outside of the file system and then came back (say via a link). 329 */ 330 331 if ((err == 0) && share_root_node) { 332 if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) 333 err = EACCES; 334 } 335 336 if (err) { 337 if (*dir_node) { 338 (void) smb_node_release(*dir_node); 339 *dir_node = NULL; 340 } 341 *last_component = 0; 342 } 343 344 if (vss_cur_node != NULL) 345 (void) smb_node_release(vss_cur_node); 346 if (vss_root_node != NULL) 347 (void) smb_node_release(vss_root_node); 348 349 return (err); 350 } 351 352 /* 353 * smb_pathname() 354 * wrapper to lookuppnvp(). Handles name unmangling. 355 * 356 * *dir_node is the true directory of the target *node. 357 * 358 * If any component but the last in the path is not found, ENOTDIR instead of 359 * ENOENT will be returned. 360 * 361 * Path components are processed one at a time so that smb_nodes can be 362 * created for each component. This allows the dir_snode field in the 363 * smb_node to be properly populated. 364 * 365 * Because of the above, links are also processed in this routine 366 * (i.e., we do not pass the FOLLOW flag to lookuppnvp()). This 367 * will allow smb_nodes to be created for each component of a link. 368 * 369 * Mangle checking is per component. If a name is mangled, when the 370 * unmangled name is passed to smb_pathname_lookup() do not pass 371 * FIGNORECASE, since the unmangled name is the real on-disk name. 372 * Otherwise pass FIGNORECASE if it's set in flags. This will cause the 373 * file system to return "first match" in the event of a case collision. 374 * 375 * If CATIA character translation is enabled it is applied to each 376 * component before passing the component to smb_pathname_lookup(). 377 * After smb_pathname_lookup() the reverse translation is applied. 378 */ 379 380 int 381 smb_pathname(smb_request_t *sr, char *path, int flags, 382 smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node, 383 smb_node_t **ret_node, cred_t *cred) 384 { 385 char *component, *real_name, *namep; 386 pathname_t pn, rpn, upn, link_pn; 387 smb_node_t *dnode, *fnode; 388 vnode_t *rootvp, *vp; 389 smb_attr_t attr; 390 size_t pathleft; 391 int err = 0; 392 int nlink = 0; 393 int local_flags; 394 char namebuf[MAXNAMELEN]; 395 396 if (path == NULL) 397 return (EINVAL); 398 399 ASSERT(root_node); 400 ASSERT(cur_node); 401 ASSERT(ret_node); 402 403 *ret_node = NULL; 404 405 if (dir_node) 406 *dir_node = NULL; 407 408 (void) pn_alloc(&upn); 409 410 if ((err = pn_set(&upn, path)) != 0) { 411 (void) pn_free(&upn); 412 return (err); 413 } 414 415 (void) pn_alloc(&pn); 416 (void) pn_alloc(&rpn); 417 418 component = kmem_alloc(MAXNAMELEN, KM_SLEEP); 419 real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 420 421 fnode = NULL; 422 dnode = cur_node; 423 smb_node_ref(dnode); 424 rootvp = root_node->vp; 425 426 while ((pathleft = pn_pathleft(&upn)) != 0) { 427 if (fnode) { 428 smb_node_release(dnode); 429 dnode = fnode; 430 fnode = NULL; 431 } 432 433 if ((err = pn_getcomponent(&upn, component)) != 0) 434 break; 435 436 if ((namep = smb_pathname_catia_v5tov4(sr, component, 437 namebuf, sizeof (namebuf))) == NULL) { 438 err = EILSEQ; 439 break; 440 } 441 442 if ((err = pn_set(&pn, namep)) != 0) 443 break; 444 445 local_flags = flags & FIGNORECASE; 446 err = smb_pathname_lookup(&pn, &rpn, local_flags, 447 &vp, rootvp, dnode->vp, cred); 448 449 if (err) { 450 if (smb_maybe_mangled_name(component) == 0) 451 break; 452 453 if ((err = smb_unmangle_name(dnode, component, 454 real_name, MAXNAMELEN)) != 0) 455 break; 456 457 if ((namep = smb_pathname_catia_v5tov4(sr, real_name, 458 namebuf, sizeof (namebuf))) == NULL) { 459 err = EILSEQ; 460 break; 461 } 462 463 if ((err = pn_set(&pn, namep)) != 0) 464 break; 465 466 local_flags = 0; 467 err = smb_pathname_lookup(&pn, &rpn, local_flags, 468 &vp, rootvp, dnode->vp, cred); 469 if (err) 470 break; 471 } 472 473 if ((vp->v_type == VLNK) && 474 ((flags & FOLLOW) || pn_pathleft(&upn))) { 475 476 if (++nlink > MAXSYMLINKS) { 477 err = ELOOP; 478 VN_RELE(vp); 479 break; 480 } 481 482 (void) pn_alloc(&link_pn); 483 err = pn_getsymlink(vp, &link_pn, cred); 484 VN_RELE(vp); 485 486 if (err == 0) { 487 if (pn_pathleft(&link_pn) == 0) 488 (void) pn_set(&link_pn, "."); 489 err = pn_insert(&upn, &link_pn, 490 strlen(component)); 491 } 492 pn_free(&link_pn); 493 494 if (err) 495 break; 496 497 if (upn.pn_pathlen == 0) { 498 err = ENOENT; 499 break; 500 } 501 502 if (upn.pn_path[0] == '/') { 503 fnode = root_node; 504 smb_node_ref(fnode); 505 } 506 507 if (pn_fixslash(&upn)) 508 flags |= FOLLOW; 509 510 } else { 511 if (flags & FIGNORECASE) { 512 if (strcmp(rpn.pn_path, "/") != 0) 513 pn_setlast(&rpn); 514 namep = rpn.pn_path; 515 } else { 516 namep = pn.pn_path; 517 } 518 519 namep = smb_pathname_catia_v4tov5(sr, namep, 520 namebuf, sizeof (namebuf)); 521 522 fnode = smb_node_lookup(sr, NULL, cred, vp, namep, 523 dnode, NULL, &attr); 524 VN_RELE(vp); 525 526 if (fnode == NULL) { 527 err = ENOMEM; 528 break; 529 } 530 } 531 532 while (upn.pn_path[0] == '/') { 533 upn.pn_path++; 534 upn.pn_pathlen--; 535 } 536 537 } 538 539 if ((pathleft) && (err == ENOENT)) 540 err = ENOTDIR; 541 542 if (err) { 543 if (fnode) 544 smb_node_release(fnode); 545 if (dnode) 546 smb_node_release(dnode); 547 } else { 548 *ret_node = fnode; 549 550 if (dir_node) 551 *dir_node = dnode; 552 else 553 smb_node_release(dnode); 554 } 555 556 kmem_free(component, MAXNAMELEN); 557 kmem_free(real_name, MAXNAMELEN); 558 (void) pn_free(&pn); 559 (void) pn_free(&rpn); 560 (void) pn_free(&upn); 561 562 return (err); 563 } 564 565 /* 566 * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp() 567 * and will be released within lookuppnvp(). 568 */ 569 static int 570 smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags, 571 vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, cred_t *cred) 572 { 573 int err; 574 575 *vp = NULL; 576 VN_HOLD(dvp); 577 if (rootvp != rootdir) 578 VN_HOLD(rootvp); 579 580 err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred); 581 return (err); 582 } 583 584 /* 585 * CATIA Translation of a pathname component prior to passing it to lookuppnvp 586 * 587 * If the translated component name contains a '/' NULL is returned. 588 * The caller should treat this as error EILSEQ. It is not valid to 589 * have a directory name with a '/'. 590 */ 591 static char * 592 smb_pathname_catia_v5tov4(smb_request_t *sr, char *name, 593 char *namebuf, int buflen) 594 { 595 char *namep; 596 597 if (SMB_TREE_SUPPORTS_CATIA(sr)) { 598 namep = smb_vop_catia_v5tov4(name, namebuf, buflen); 599 if (strchr(namep, '/') != NULL) 600 return (NULL); 601 return (namep); 602 } 603 604 return (name); 605 } 606 607 /* 608 * CATIA translation of a pathname component after returning from lookuppnvp 609 */ 610 static char * 611 smb_pathname_catia_v4tov5(smb_request_t *sr, char *name, 612 char *namebuf, int buflen) 613 { 614 if (SMB_TREE_SUPPORTS_CATIA(sr)) { 615 smb_vop_catia_v4tov5(name, namebuf, buflen); 616 return (namebuf); 617 } 618 619 return (name); 620 } 621 622 /* 623 * sr - needed to check for case sense 624 * path - non mangled path needed to be looked up from the startvp 625 * startvp - the vnode to start the lookup from 626 * rootvp - the vnode of the root of the filesystem 627 * returns the vnode found when starting at startvp and using the path 628 * 629 * Finds a vnode starting at startvp and parsing the non mangled path 630 */ 631 632 vnode_t * 633 smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp, 634 vnode_t *rootvp) 635 { 636 pathname_t pn; 637 vnode_t *vp = NULL; 638 int lookup_flags = FOLLOW; 639 640 if (SMB_TREE_IS_CASEINSENSITIVE(sr)) 641 lookup_flags |= FIGNORECASE; 642 643 (void) pn_alloc(&pn); 644 645 if (pn_set(&pn, path) == 0) { 646 VN_HOLD(rootvp); 647 if (rootvp != rootdir) 648 VN_HOLD(rootvp); 649 650 /* lookuppnvp should release the holds */ 651 if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp, 652 rootvp, startvp, kcred) != 0) { 653 pn_free(&pn); 654 return (NULL); 655 } 656 } 657 658 pn_free(&pn); 659 return (vp); 660 } 661