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