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