Home | History | Annotate | Download | only in smbsrv
      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