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