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
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);
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)
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 */
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 {
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);
|
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smb_fsops.h>
28 #include <sys/pathname.h>
29 #include <sys/sdt.h>
30
31 static char *smb_pathname_catia_v5tov4(smb_request_t *, char *, char *, int);
32 static char *smb_pathname_catia_v4tov5(smb_request_t *, char *, char *, int);
33 static int smb_pathname_lookup(pathname_t *, pathname_t *, int,
34 vnode_t **, vnode_t *, vnode_t *, smb_attr_t *attr, cred_t *);
35 static char *smb_pathname_strdup(smb_request_t *, const char *);
36 static char *smb_pathname_strcat(smb_request_t *, char *, const char *);
37 static void smb_pathname_preprocess(smb_request_t *, smb_pathname_t *);
38 static void smb_pathname_preprocess_quota(smb_request_t *, smb_pathname_t *);
39 static int smb_pathname_dfs_preprocess(smb_request_t *, char *, size_t);
40 static void smb_pathname_preprocess_adminshare(smb_request_t *,
41 smb_pathname_t *);
42
43
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);
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)
584 }
585
586 namep = smb_pathname_catia_v4tov5(sr, namep,
587 namebuf, sizeof (namebuf));
588
589 fnode = smb_node_lookup(sr, NULL, cred, vp, namep,
590 dnode, NULL);
591 VN_RELE(vp);
592
593 if (fnode == NULL) {
594 err = ENOMEM;
595 break;
596 }
597 }
598
599 while (upn.pn_path[0] == '/') {
600 upn.pn_path++;
601 upn.pn_pathlen--;
602 }
603
604 /*
605 * If the node we looked up is the root of a filesystem,
606 * snapshot the lookup so we can replay this after discovering
607 * the lowest mounted filesystem.
608 */
609 if (mnt_pn != NULL &&
610 (err = VFS_ROOT(fnode->vp->v_vfsp, &fsrootvp)) == 0) {
611 if (fsrootvp == fnode->vp) {
612 mnt_pn->pn_pathlen = pn_pathleft(&upn);
613 mnt_pn->pn_path = mnt_pn->pn_buf +
614 ((ptrdiff_t)upn.pn_path -
615 (ptrdiff_t)upn.pn_buf);
616
617 smb_node_ref(fnode);
618 if (mnt_node != NULL)
619 smb_node_release(mnt_node);
620 mnt_node = fnode;
621
622 }
623 VN_RELE(fsrootvp);
624 }
625 }
626
627 if ((pathleft) && (err == ENOENT))
628 err = ENOTDIR;
629
630 if (mnt_node == NULL)
631 mnt_pn = NULL;
632
633 /*
634 * We always want to return a node when we're doing VSS
635 * (mnt_pn != NULL)
636 */
637 if (mnt_pn == NULL && err != 0) {
638 if (fnode)
639 smb_node_release(fnode);
640 if (dnode)
641 smb_node_release(dnode);
642 } else {
643 if (mnt_pn != NULL) {
644 *ret_node = mnt_node;
645 if (fnode != NULL)
646 smb_node_release(fnode);
647 } else {
648 *ret_node = fnode;
649 }
650
651 if (dir_node)
652 *dir_node = dnode;
653 else
654 smb_node_release(dnode);
655 }
656
657 kmem_free(component, MAXNAMELEN);
658 kmem_free(real_name, MAXNAMELEN);
659 (void) pn_free(&pn);
660 (void) pn_free(&rpn);
661 (void) pn_free(&upn);
662
663 return (err);
664 }
665
666 /*
667 * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp()
668 * and will be released within lookuppnvp().
669 */
998 {
999 char *path = pn->pn_path;
1000
1001 /* ignore any initial "\\" */
1002 path += strspn(path, "\\");
1003
1004 /* If first component of path is ".." -> PATH_SYNTAX_BAD */
1005 if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) {
1006 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
1007 ERRDOS, ERROR_BAD_PATHNAME);
1008 return (B_FALSE);
1009 }
1010
1011 /* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */
1012 if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) {
1013 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1014 ERRDOS, ERROR_INVALID_NAME);
1015 return (B_FALSE);
1016 }
1017
1018 /* If fname is "." -> OBJECT_NAME_INVALID */
1019 if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) {
1020 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1021 ERRDOS, ERROR_INVALID_NAME);
1022 return (B_FALSE);
1023 }
1024
1025 return (B_TRUE);
1026 }
1027
1028 /*
1029 * smb_validate_dirname
1030 *
1031 * smb_pathname_validate() should have already been performed on pn.
1032 *
1033 * Very basic directory name validation: checks for colons in a path.
1034 * Need to skip the drive prefix since it contains a colon.
1035 *
1036 * Returns: B_TRUE if the name is valid,
1037 * otherwise returns B_FALSE and sets error status in sr.
1038 */
1039 boolean_t
1040 smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn)
1041 {
1075 if (pn->pn_fname &&
1076 strlen(pn->pn_fname) == 5 &&
1077 smb_isdigit(pn->pn_fname[3]) &&
1078 pn->pn_fname[4] == ':') {
1079 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1080 ERRDOS, ERROR_INVALID_NAME);
1081 return (B_FALSE);
1082 }
1083
1084 if (pn->pn_sname)
1085 return (smb_validate_stream_name(sr, pn));
1086
1087 return (B_TRUE);
1088 }
1089
1090 /*
1091 * smb_stream_parse_name
1092 *
1093 * smb_stream_parse_name should only be called for a path that
1094 * contains a valid named stream. Path validation should have
1095 * been performed before this function is called, typically by
1096 * calling smb_is_stream_name() just before this.
1097 *
1098 * Find the last component of path and split it into filename
1099 * and stream name.
1100 *
1101 * On return the named stream type will be present. The stream
1102 * type defaults to ":$DATA", if it has not been defined
1103 * For example, 'stream' contains :<sname>:$DATA
1104 *
1105 * Output args: filename, stream both MAXNAMELEN
1106 */
1107 void
1108 smb_stream_parse_name(char *path, char *filename, char *stream)
1109 {
1110 char *fname, *sname, *stype;
1111 size_t flen, slen;
1112
1113 ASSERT(path);
1114 ASSERT(filename);
1115 ASSERT(stream);
1116
1117 fname = strrchr(path, '\\');
1118 fname = (fname == NULL) ? path : fname + 1;
1119 sname = strchr(fname, ':');
1120 /* Caller makes sure there is a ':' in path. */
1121 VERIFY(sname != NULL);
1122 /* LINTED: possible ptrdiff_t overflow */
1123 flen = sname - fname;
1124 slen = strlen(sname);
1125
1126 if (flen > (MAXNAMELEN-1))
1127 flen = (MAXNAMELEN-1);
1128 (void) strncpy(filename, fname, flen);
1129 filename[flen] = '\0';
1130
1131 if (slen > (MAXNAMELEN-1))
1132 slen = (MAXNAMELEN-1);
1133 (void) strncpy(stream, sname, slen);
1134 stream[slen] = '\0';
1135
1136 /* Add a "stream type" if there isn't one. */
1137 stype = strchr(stream + 1, ':');
1138 if (stype == NULL)
1139 (void) strlcat(stream, ":$DATA", MAXNAMELEN);
1140 else
1141 (void) smb_strupr(stype);
1142 }
1143
1144 /*
1145 * smb_is_stream_name
1146 *
1147 * Determines if 'path' specifies a named stream.
1148 *
1149 * path is a NULL terminated string which could be a stream path.
1150 * [pathname/]fname[:stream_name[:stream_type]]
1151 *
1152 * - If there is no colon in the path or it's the last char
1153 * then it's not a stream name
1154 *
1155 * - '::' is a non-stream and is commonly used by Windows to designate
1156 * the unamed stream in the form "::$DATA"
1157 */
1158 boolean_t
1159 smb_is_stream_name(char *path)
1160 {
1161 char *colonp;
1162
1163 if (path == NULL)
1164 return (B_FALSE);
1165
1166 colonp = strchr(path, ':');
1167 if ((colonp == NULL) || (*(colonp+1) == '\0'))
1168 return (B_FALSE);
1169
1170 if (strstr(path, "::"))
1171 return (B_FALSE);
1172
1173 return (B_TRUE);
1174 }
1175
1176 /*
1177 * Is this stream node a "restricted" type?
1178 */
1179 boolean_t
1180 smb_strname_restricted(char *strname)
1181 {
1182 char *stype;
1183
1184 stype = strrchr(strname, ':');
1185 if (stype == NULL)
1186 return (B_FALSE);
1187
1188 /*
1189 * Only ":$CA" is restricted (for now).
1190 */
1191 if (strcmp(stype, ":$CA") == 0)
1192 return (B_TRUE);
1193
1194 return (B_FALSE);
1195 }
1196
1197 /*
1198 * smb_validate_stream_name
1199 *
1200 * B_FALSE will be returned, and the error status ser in the sr, if:
1201 * - the path is not a stream name
1202 * - a path is specified but the fname is ommitted.
1203 * - the stream_type is specified but not valid.
1204 *
1205 * Note: the stream type is case-insensitive.
1206 */
1207 boolean_t
1208 smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
1209 {
1210 static char *strmtype[] = {
1211 "$CA",
1212 "$DATA",
1213 "$INDEX_ALLOCATION"
1214 };
1215 int i;
1216
1217 ASSERT(pn);
1218 ASSERT(pn->pn_sname);
1219
1220 if ((!(pn->pn_sname)) ||
1221 ((pn->pn_pname) && !(pn->pn_fname))) {
1222 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1223 ERRDOS, ERROR_INVALID_NAME);
1224 return (B_FALSE);
1225 }
1226
1227
1228 if (pn->pn_stype != NULL) {
1229 for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
1230 if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
1231 return (B_TRUE);
|