xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_pathname.c (revision 05876af472c3264c8a0734aef9cae317325aadf2)
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 2017 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 		/* We want the DOS attributes. */
427 		bzero(&attr, sizeof (attr));
428 		attr.sa_mask = SMB_AT_DOSATTR;
429 
430 		local_flags = flags & FIGNORECASE;
431 		err = smb_pathname_lookup(&pn, &rpn, local_flags,
432 		    &vp, rootvp, dnode->vp, &attr, cred);
433 
434 		if (err) {
435 			if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
436 			    !smb_maybe_mangled(component))
437 				break;
438 
439 			if ((err = smb_unmangle(dnode, component,
440 			    real_name, MAXNAMELEN, abe_flag)) != 0)
441 				break;
442 
443 			if ((namep = smb_pathname_catia_v5tov4(sr, real_name,
444 			    namebuf, sizeof (namebuf))) == NULL) {
445 				err = EILSEQ;
446 				break;
447 			}
448 
449 			if ((err = pn_set(&pn, namep)) != 0)
450 				break;
451 
452 			local_flags = 0;
453 			err = smb_pathname_lookup(&pn, &rpn, local_flags,
454 			    &vp, rootvp, dnode->vp, &attr, cred);
455 			if (err)
456 				break;
457 		}
458 
459 		/*
460 		 * This check MUST be done before symlink check
461 		 * since a reparse point is of type VLNK but should
462 		 * not be handled like a regular symlink.
463 		 */
464 		if (attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) {
465 			err = EREMOTE;
466 			VN_RELE(vp);
467 			break;
468 		}
469 
470 		if ((vp->v_type == VLNK) &&
471 		    ((flags & FOLLOW) || pn_pathleft(&upn))) {
472 
473 			if (++nlink > MAXSYMLINKS) {
474 				err = ELOOP;
475 				VN_RELE(vp);
476 				break;
477 			}
478 
479 			(void) pn_alloc(&link_pn);
480 			err = pn_getsymlink(vp, &link_pn, cred);
481 			VN_RELE(vp);
482 
483 			if (err == 0) {
484 				if (pn_pathleft(&link_pn) == 0)
485 					(void) pn_set(&link_pn, ".");
486 				err = pn_insert(&upn, &link_pn,
487 				    strlen(component));
488 			}
489 			pn_free(&link_pn);
490 
491 			if (err)
492 				break;
493 
494 			if (upn.pn_pathlen == 0) {
495 				err = ENOENT;
496 				break;
497 			}
498 
499 			if (upn.pn_path[0] == '/') {
500 				fnode = root_node;
501 				smb_node_ref(fnode);
502 			}
503 
504 			if (pn_fixslash(&upn))
505 				flags |= FOLLOW;
506 
507 		} else {
508 			if (flags & FIGNORECASE) {
509 				if (strcmp(rpn.pn_path, "/") != 0)
510 					pn_setlast(&rpn);
511 				namep = rpn.pn_path;
512 			} else {
513 				namep = pn.pn_path;
514 			}
515 
516 			namep = smb_pathname_catia_v4tov5(sr, namep,
517 			    namebuf, sizeof (namebuf));
518 
519 			fnode = smb_node_lookup(sr, NULL, cred, vp, namep,
520 			    dnode, NULL);
521 			VN_RELE(vp);
522 
523 			if (fnode == NULL) {
524 				err = ENOMEM;
525 				break;
526 			}
527 		}
528 
529 		while (upn.pn_path[0] == '/') {
530 			upn.pn_path++;
531 			upn.pn_pathlen--;
532 		}
533 
534 	}
535 
536 	if ((pathleft) && (err == ENOENT))
537 		err = ENOTDIR;
538 
539 	if (err) {
540 		if (fnode)
541 			smb_node_release(fnode);
542 		if (dnode)
543 			smb_node_release(dnode);
544 	} else {
545 		*ret_node = fnode;
546 
547 		if (dir_node)
548 			*dir_node = dnode;
549 		else
550 			smb_node_release(dnode);
551 	}
552 
553 	kmem_free(component, MAXNAMELEN);
554 	kmem_free(real_name, MAXNAMELEN);
555 	(void) pn_free(&pn);
556 	(void) pn_free(&rpn);
557 	(void) pn_free(&upn);
558 
559 	return (err);
560 }
561 
562 /*
563  * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp()
564  * and will be released within lookuppnvp().
565  */
566 static int
567 smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags,
568     vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, smb_attr_t *attr, cred_t *cred)
569 {
570 	int err;
571 
572 	*vp = NULL;
573 	VN_HOLD(dvp);
574 	if (rootvp != rootdir)
575 		VN_HOLD(rootvp);
576 
577 	err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred);
578 	if ((err == 0) && (attr != NULL))
579 		(void) smb_vop_getattr(*vp, NULL, attr, 0, zone_kcred());
580 
581 	return (err);
582 }
583 
584 /*
585  * CATIA Translation of a pathname component prior to passing it to lookuppnvp
586  *
587  * If the translated component name contains a '/' NULL is returned.
588  * The caller should treat this as error EILSEQ. It is not valid to
589  * have a directory name with a '/'.
590  */
591 static char *
592 smb_pathname_catia_v5tov4(smb_request_t *sr, char *name,
593     char *namebuf, int buflen)
594 {
595 	char *namep;
596 
597 	if (SMB_TREE_SUPPORTS_CATIA(sr)) {
598 		namep = smb_vop_catia_v5tov4(name, namebuf, buflen);
599 		if (strchr(namep, '/') != NULL)
600 			return (NULL);
601 		return (namep);
602 	}
603 
604 	return (name);
605 }
606 
607 /*
608  * CATIA translation of a pathname component after returning from lookuppnvp
609  */
610 static char *
611 smb_pathname_catia_v4tov5(smb_request_t *sr, char *name,
612     char *namebuf, int buflen)
613 {
614 	if (SMB_TREE_SUPPORTS_CATIA(sr)) {
615 		smb_vop_catia_v4tov5(name, namebuf, buflen);
616 		return (namebuf);
617 	}
618 
619 	return (name);
620 }
621 
622 /*
623  * sr - needed to check for case sense
624  * path - non mangled path needed to be looked up from the startvp
625  * startvp - the vnode to start the lookup from
626  * rootvp - the vnode of the root of the filesystem
627  * returns the vnode found when starting at startvp and using the path
628  *
629  * Finds a vnode starting at startvp and parsing the non mangled path
630  */
631 
632 vnode_t *
633 smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp,
634     vnode_t *rootvp)
635 {
636 	pathname_t pn;
637 	vnode_t *vp = NULL;
638 	int lookup_flags = FOLLOW;
639 
640 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
641 		lookup_flags |= FIGNORECASE;
642 
643 	(void) pn_alloc(&pn);
644 
645 	if (pn_set(&pn, path) == 0) {
646 		VN_HOLD(startvp);
647 		if (rootvp != rootdir)
648 			VN_HOLD(rootvp);
649 
650 		/* lookuppnvp should release the holds */
651 		if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp,
652 		    rootvp, startvp, zone_kcred()) != 0) {
653 			pn_free(&pn);
654 			return (NULL);
655 		}
656 	}
657 
658 	pn_free(&pn);
659 	return (vp);
660 }
661 
662 /*
663  * smb_pathname_init
664  * Parse path: pname\\fname:sname:stype
665  *
666  * Elements of the smb_pathname_t structure are allocated using request
667  * specific storage and will be free'd when the sr is destroyed.
668  *
669  * Populate pn structure elements with the individual elements
670  * of pn->pn_path. pn->pn_sname will contain the whole stream name
671  * including the stream type and preceding colon: :sname:%DATA
672  * pn_stype will point to the stream type within pn_sname.
673  *
674  * If the pname element is missing pn_pname will be set to NULL.
675  * If any other element is missing the pointer in pn will be NULL.
676  */
677 void
678 smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path)
679 {
680 	char *pname, *fname, *sname;
681 	int len;
682 
683 	bzero(pn, sizeof (smb_pathname_t));
684 	pn->pn_path = smb_pathname_strdup(sr, path);
685 
686 	smb_pathname_preprocess(sr, pn);
687 
688 	/* parse pn->pn_path into its constituent parts */
689 	pname = pn->pn_path;
690 	fname = strrchr(pn->pn_path, '\\');
691 
692 	if (fname) {
693 		if (fname == pname) {
694 			pn->pn_pname = NULL;
695 		} else {
696 			*fname = '\0';
697 			pn->pn_pname =
698 			    smb_pathname_strdup(sr, pname);
699 			*fname = '\\';
700 		}
701 		++fname;
702 	} else {
703 		fname = pname;
704 		pn->pn_pname = NULL;
705 	}
706 
707 	if (fname[0] == '\0') {
708 		pn->pn_fname = NULL;
709 		return;
710 	}
711 
712 	if (!smb_is_stream_name(fname)) {
713 		pn->pn_fname = smb_pathname_strdup(sr, fname);
714 		return;
715 	}
716 
717 	/*
718 	 * find sname and stype in fname.
719 	 * sname can't be NULL smb_is_stream_name checks this
720 	 */
721 	sname = strchr(fname, ':');
722 	if (sname == fname)
723 		fname = NULL;
724 	else {
725 		*sname = '\0';
726 		pn->pn_fname =
727 		    smb_pathname_strdup(sr, fname);
728 		*sname = ':';
729 	}
730 
731 	pn->pn_sname = smb_pathname_strdup(sr, sname);
732 	pn->pn_stype = strchr(pn->pn_sname + 1, ':');
733 	if (pn->pn_stype) {
734 		(void) smb_strupr(pn->pn_stype);
735 	} else {
736 		len = strlen(pn->pn_sname);
737 		pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA");
738 		pn->pn_stype = pn->pn_sname + len;
739 	}
740 	++pn->pn_stype;
741 }
742 
743 /*
744  * smb_pathname_preprocess
745  *
746  * Perform common pre-processing of pn->pn_path:
747  * - if the pn_path is blank, set it to '\\'
748  * - perform unicode wildcard converstion.
749  * - convert any '/' to '\\'
750  * - eliminate duplicate slashes
751  * - remove trailing slashes
752  * - quota directory specific pre-processing
753  */
754 static void
755 smb_pathname_preprocess(smb_request_t *sr, smb_pathname_t *pn)
756 {
757 	char *p;
758 
759 	/* treat empty path as "\\" */
760 	if (strlen(pn->pn_path) == 0) {
761 		pn->pn_path = smb_pathname_strdup(sr, "\\");
762 		return;
763 	}
764 
765 	if (sr->session->dialect < NT_LM_0_12)
766 		smb_convert_wildcards(pn->pn_path);
767 
768 	/* treat '/' as '\\' */
769 	(void) strsubst(pn->pn_path, '/', '\\');
770 
771 	(void) strcanon(pn->pn_path, "\\");
772 
773 	/* remove trailing '\\' */
774 	p = pn->pn_path + strlen(pn->pn_path) - 1;
775 	if ((p != pn->pn_path) && (*p == '\\'))
776 		*p = '\0';
777 
778 	smb_pathname_preprocess_quota(sr, pn);
779 	smb_pathname_preprocess_adminshare(sr, pn);
780 }
781 
782 /*
783  * smb_pathname_preprocess_quota
784  *
785  * There is a special file required by windows so that the quota
786  * tab will be displayed by windows clients. This is created in
787  * a special directory, $EXTEND, at the root of the shared file
788  * system. To hide this directory prepend a '.' (dot).
789  */
790 static void
791 smb_pathname_preprocess_quota(smb_request_t *sr, smb_pathname_t *pn)
792 {
793 	char *name = "$EXTEND";
794 	char *new_name = ".$EXTEND";
795 	char *p, *slash;
796 	int len;
797 
798 	if (!smb_node_is_vfsroot(sr->tid_tree->t_snode))
799 		return;
800 
801 	p = pn->pn_path;
802 
803 	/* ignore any initial "\\" */
804 	p += strspn(p, "\\");
805 	if (smb_strcasecmp(p, name, strlen(name)) != 0)
806 		return;
807 
808 	p += strlen(name);
809 	if ((*p != ':') && (*p != '\\') && (*p != '\0'))
810 		return;
811 
812 	slash = (pn->pn_path[0] == '\\') ? "\\" : "";
813 	len = strlen(pn->pn_path) + 2;
814 	pn->pn_path = smb_srm_alloc(sr, len);
815 	(void) snprintf(pn->pn_path, len, "%s%s%s", slash, new_name, p);
816 	(void) smb_strupr(pn->pn_path);
817 }
818 
819 /*
820  * smb_pathname_preprocess_adminshare
821  *
822  * Convert any path with share name "C$" or "c$" (Admin share) in to lower case.
823  */
824 static void
825 smb_pathname_preprocess_adminshare(smb_request_t *sr, smb_pathname_t *pn)
826 {
827 	if (strcasecmp(sr->tid_tree->t_sharename, "c$") == 0)
828 		(void) smb_strlwr(pn->pn_path);
829 }
830 
831 /*
832  * smb_pathname_strdup
833  *
834  * Duplicate NULL terminated string s.
835  *
836  * The new string is allocated using request specific storage and will
837  * be free'd when the sr is destroyed.
838  */
839 static char *
840 smb_pathname_strdup(smb_request_t *sr, const char *s)
841 {
842 	char *s2;
843 	size_t n;
844 
845 	n = strlen(s) + 1;
846 	s2 = smb_srm_zalloc(sr, n);
847 	(void) strlcpy(s2, s, n);
848 	return (s2);
849 }
850 
851 /*
852  * smb_pathname_strcat
853  *
854  * Reallocate NULL terminated string s1 to accommodate
855  * concatenating  NULL terminated string s2.
856  * Append s2 and return resulting NULL terminated string.
857  *
858  * The string buffer is reallocated using request specific
859  * storage and will be free'd when the sr is destroyed.
860  */
861 static char *
862 smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2)
863 {
864 	size_t n;
865 
866 	n = strlen(s1) + strlen(s2) + 1;
867 	s1 = smb_srm_rezalloc(sr, s1, n);
868 	(void) strlcat(s1, s2, n);
869 	return (s1);
870 }
871 
872 /*
873  * smb_pathname_validate
874  *
875  * Perform basic validation of pn:
876  * - If first component of pn->path is ".." -> PATH_SYNTAX_BAD
877  * - If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID
878  * - If fname is "." -> INVALID_OBJECT_NAME
879  *
880  * On unix .. at the root of a file system links to the root. Thus
881  * an attempt to lookup "/../../.." will be the same as looking up "/"
882  * CIFs clients expect the above to result in
883  * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible
884  * (and questionable if it's desirable) to deal with all cases
885  * but paths beginning with \\.. are handled.
886  *
887  * Returns: B_TRUE if pn is valid,
888  *          otherwise returns B_FALSE and sets error status in sr.
889  *
890  * XXX: Get rid of smbsr_error calls for SMB2
891  */
892 boolean_t
893 smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn)
894 {
895 	char *path = pn->pn_path;
896 
897 	/* ignore any initial "\\" */
898 	path += strspn(path, "\\");
899 
900 	/* If first component of path is ".." -> PATH_SYNTAX_BAD */
901 	if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) {
902 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
903 		    ERRDOS, ERROR_BAD_PATHNAME);
904 		return (B_FALSE);
905 	}
906 
907 	/* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */
908 	if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) {
909 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
910 		    ERRDOS, ERROR_INVALID_NAME);
911 		return (B_FALSE);
912 	}
913 
914 	/* If fname is "." -> OBJECT_NAME_INVALID */
915 	if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) {
916 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
917 		    ERRDOS, ERROR_INVALID_NAME);
918 		return (B_FALSE);
919 	}
920 
921 	return (B_TRUE);
922 }
923 
924 /*
925  * smb_validate_dirname
926  *
927  * smb_pathname_validate() should have already been performed on pn.
928  *
929  * Very basic directory name validation:  checks for colons in a path.
930  * Need to skip the drive prefix since it contains a colon.
931  *
932  * Returns: B_TRUE if the name is valid,
933  *          otherwise returns B_FALSE and sets error status in sr.
934  */
935 boolean_t
936 smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn)
937 {
938 	char *name;
939 	char *path = pn->pn_path;
940 
941 	if ((name = path) != 0) {
942 		name += strspn(name, "\\");
943 
944 		if (strchr(name, ':') != 0) {
945 			smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
946 			    ERRDOS, ERROR_INVALID_NAME);
947 			return (B_FALSE);
948 		}
949 	}
950 
951 	return (B_TRUE);
952 }
953 
954 /*
955  * smb_validate_object_name
956  *
957  * smb_pathname_validate() should have already been pertformed on pn.
958  *
959  * Very basic file name validation.
960  * For filenames, we check for names of the form "AAAn:". Names that
961  * contain three characters, a single digit and a colon (:) are reserved
962  * as DOS device names, i.e. "COM1:".
963  * Stream name validation is handed off to smb_validate_stream_name
964  *
965  * Returns: B_TRUE if pn->pn_fname is valid,
966  *          otherwise returns B_FALSE and sets error status in sr.
967  */
968 boolean_t
969 smb_validate_object_name(smb_request_t *sr, smb_pathname_t *pn)
970 {
971 	if (pn->pn_fname &&
972 	    strlen(pn->pn_fname) == 5 &&
973 	    smb_isdigit(pn->pn_fname[3]) &&
974 	    pn->pn_fname[4] == ':') {
975 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
976 		    ERRDOS, ERROR_INVALID_NAME);
977 		return (B_FALSE);
978 	}
979 
980 	if (pn->pn_sname)
981 		return (smb_validate_stream_name(sr, pn));
982 
983 	return (B_TRUE);
984 }
985 
986 /*
987  * smb_stream_parse_name
988  *
989  * smb_stream_parse_name should only be called for a path that
990  * contains a valid named stream.  Path validation should have
991  * been performed before this function is called, typically by
992  * calling smb_is_stream_name() just before this.
993  *
994  * Find the last component of path and split it into filename
995  * and stream name.
996  *
997  * On return the named stream type will be present.  The stream
998  * type defaults to ":$DATA", if it has not been defined
999  * For example, 'stream' contains :<sname>:$DATA
1000  *
1001  * Output args: filename, stream both MAXNAMELEN
1002  */
1003 void
1004 smb_stream_parse_name(char *path, char *filename, char *stream)
1005 {
1006 	char *fname, *sname, *stype;
1007 	size_t flen, slen;
1008 
1009 	ASSERT(path);
1010 	ASSERT(filename);
1011 	ASSERT(stream);
1012 
1013 	fname = strrchr(path, '\\');
1014 	fname = (fname == NULL) ? path : fname + 1;
1015 	sname = strchr(fname, ':');
1016 	/* Caller makes sure there is a ':' in path. */
1017 	VERIFY(sname != NULL);
1018 	/* LINTED: possible ptrdiff_t overflow */
1019 	flen = sname - fname;
1020 	slen = strlen(sname);
1021 
1022 	if (flen > (MAXNAMELEN-1))
1023 		flen = (MAXNAMELEN-1);
1024 	(void) strncpy(filename, fname, flen);
1025 	filename[flen] = '\0';
1026 
1027 	if (slen > (MAXNAMELEN-1))
1028 		slen = (MAXNAMELEN-1);
1029 	(void) strncpy(stream, sname, slen);
1030 	stream[slen] = '\0';
1031 
1032 	/* Add a "stream type" if there isn't one. */
1033 	stype = strchr(stream + 1, ':');
1034 	if (stype == NULL)
1035 		(void) strlcat(stream, ":$DATA", MAXNAMELEN);
1036 	else
1037 		(void) smb_strupr(stype);
1038 }
1039 
1040 /*
1041  * smb_is_stream_name
1042  *
1043  * Determines if 'path' specifies a named stream.
1044  *
1045  * path is a NULL terminated string which could be a stream path.
1046  * [pathname/]fname[:stream_name[:stream_type]]
1047  *
1048  * - If there is no colon in the path or it's the last char
1049  *   then it's not a stream name
1050  *
1051  * - '::' is a non-stream and is commonly used by Windows to designate
1052  *   the unamed stream in the form "::$DATA"
1053  */
1054 boolean_t
1055 smb_is_stream_name(char *path)
1056 {
1057 	char *colonp;
1058 
1059 	if (path == NULL)
1060 		return (B_FALSE);
1061 
1062 	colonp = strchr(path, ':');
1063 	if ((colonp == NULL) || (*(colonp+1) == '\0'))
1064 		return (B_FALSE);
1065 
1066 	if (strstr(path, "::"))
1067 		return (B_FALSE);
1068 
1069 	return (B_TRUE);
1070 }
1071 
1072 /*
1073  * Is this stream node a "restricted" type?
1074  */
1075 boolean_t
1076 smb_strname_restricted(char *strname)
1077 {
1078 	char *stype;
1079 
1080 	stype = strrchr(strname, ':');
1081 	if (stype == NULL)
1082 		return (B_FALSE);
1083 
1084 	/*
1085 	 * Only ":$CA" is restricted (for now).
1086 	 */
1087 	if (strcmp(stype, ":$CA") == 0)
1088 		return (B_TRUE);
1089 
1090 	return (B_FALSE);
1091 }
1092 
1093 /*
1094  * smb_validate_stream_name
1095  *
1096  * B_FALSE will be returned, and the error status ser in the sr, if:
1097  * - the path is not a stream name
1098  * - a path is specified but the fname is ommitted.
1099  * - the stream_type is specified but not valid.
1100  *
1101  * Note: the stream type is case-insensitive.
1102  */
1103 boolean_t
1104 smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
1105 {
1106 	static char *strmtype[] = {
1107 		"$CA",
1108 		"$DATA",
1109 		"$INDEX_ALLOCATION"
1110 	};
1111 	int i;
1112 
1113 	ASSERT(pn);
1114 	ASSERT(pn->pn_sname);
1115 
1116 	if ((!(pn->pn_sname)) ||
1117 	    ((pn->pn_pname) && !(pn->pn_fname))) {
1118 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1119 		    ERRDOS, ERROR_INVALID_NAME);
1120 		return (B_FALSE);
1121 	}
1122 
1123 
1124 	if (pn->pn_stype != NULL) {
1125 		for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
1126 			if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
1127 				return (B_TRUE);
1128 		}
1129 
1130 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1131 		    ERRDOS, ERROR_INVALID_NAME);
1132 		return (B_FALSE);
1133 	}
1134 
1135 	return (B_TRUE);
1136 }
1137 
1138 /*
1139  * valid DFS I/O path:
1140  *
1141  * \server-or-domain\share
1142  * \server-or-domain\share\path
1143  *
1144  * All the returned errors by this function needs to be
1145  * checked against Windows.
1146  */
1147 static int
1148 smb_pathname_dfs_preprocess(smb_request_t *sr, char *path, size_t pathsz)
1149 {
1150 	smb_unc_t unc;
1151 	char *linkpath;
1152 	int rc;
1153 
1154 	if (sr->tid_tree == NULL)
1155 		return (0);
1156 
1157 	if ((rc = smb_unc_init(path, &unc)) != 0)
1158 		return (rc);
1159 
1160 	if (smb_strcasecmp(unc.unc_share, sr->tid_tree->t_sharename, 0)) {
1161 		smb_unc_free(&unc);
1162 		return (EINVAL);
1163 	}
1164 
1165 	linkpath = unc.unc_path;
1166 	(void) snprintf(path, pathsz, "/%s", (linkpath) ? linkpath : "");
1167 
1168 	smb_unc_free(&unc);
1169 	return (0);
1170 }
1171