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