1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smb_fsops.h>
28 #include <sys/pathname.h>
29 #include <sys/sdt.h>
30
31 static char *smb_pathname_catia_v5tov4(smb_request_t *, char *, char *, int);
32 static char *smb_pathname_catia_v4tov5(smb_request_t *, char *, char *, int);
33 static int smb_pathname_lookup(pathname_t *, pathname_t *, int,
34 vnode_t **, vnode_t *, vnode_t *, smb_attr_t *attr, cred_t *);
35 static char *smb_pathname_strdup(smb_request_t *, const char *);
36 static char *smb_pathname_strcat(smb_request_t *, char *, const char *);
37 static void smb_pathname_preprocess(smb_request_t *, smb_pathname_t *);
38 static void smb_pathname_preprocess_quota(smb_request_t *, smb_pathname_t *);
39 static int smb_pathname_dfs_preprocess(smb_request_t *, char *, size_t);
40 static void smb_pathname_preprocess_adminshare(smb_request_t *,
41 smb_pathname_t *);
42
43
44 uint32_t
smb_is_executable(char * path)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
smb_pathname_reduce(smb_request_t * sr,cred_t * cred,const char * path,smb_node_t * share_root_node,smb_node_t * cur_node,smb_node_t ** dir_node,char * last_component)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
smb_pathname(smb_request_t * sr,char * path,int flags,smb_node_t * root_node,smb_node_t * cur_node,smb_node_t ** dir_node,smb_node_t ** ret_node,cred_t * cred)358 smb_pathname(smb_request_t *sr, char *path, int flags,
359 smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node,
360 smb_node_t **ret_node, cred_t *cred)
361 {
362 char *component, *real_name, *namep;
363 pathname_t pn, rpn, upn, link_pn;
364 smb_node_t *dnode, *fnode;
365 smb_attr_t attr;
366 vnode_t *rootvp, *vp;
367 size_t pathleft;
368 int err = 0;
369 int nlink = 0;
370 int local_flags;
371 uint32_t abe_flag = 0;
372 char namebuf[MAXNAMELEN];
373
374 if (path == NULL)
375 return (EINVAL);
376
377 ASSERT(root_node);
378 ASSERT(cur_node);
379 ASSERT(ret_node);
380
381 *ret_node = NULL;
382
383 if (dir_node)
384 *dir_node = NULL;
385
386 (void) pn_alloc_sz(&upn, SMB_MAXPATHLEN);
387
388 if ((err = pn_set(&upn, path)) != 0) {
389 (void) pn_free(&upn);
390 return (err);
391 }
392
393 if (SMB_TREE_SUPPORTS_ABE(sr))
394 abe_flag = SMB_ABE;
395
396 (void) pn_alloc(&pn);
397 (void) pn_alloc(&rpn);
398
399 component = kmem_alloc(MAXNAMELEN, KM_SLEEP);
400 real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
401
402 fnode = NULL;
403 dnode = cur_node;
404 smb_node_ref(dnode);
405 rootvp = root_node->vp;
406
407 while ((pathleft = pn_pathleft(&upn)) != 0) {
408 if (fnode) {
409 smb_node_release(dnode);
410 dnode = fnode;
411 fnode = NULL;
412 }
413
414 if ((err = pn_getcomponent(&upn, component)) != 0)
415 break;
416
417 if ((namep = smb_pathname_catia_v5tov4(sr, component,
418 namebuf, sizeof (namebuf))) == NULL) {
419 err = EILSEQ;
420 break;
421 }
422
423 if ((err = pn_set(&pn, namep)) != 0)
424 break;
425
426 local_flags = flags & FIGNORECASE;
427 err = smb_pathname_lookup(&pn, &rpn, local_flags,
428 &vp, rootvp, dnode->vp, &attr, cred);
429
430 if (err) {
431 if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
432 !smb_maybe_mangled(component))
433 break;
434
435 if ((err = smb_unmangle(dnode, component,
436 real_name, MAXNAMELEN, abe_flag)) != 0)
437 break;
438
439 if ((namep = smb_pathname_catia_v5tov4(sr, real_name,
440 namebuf, sizeof (namebuf))) == NULL) {
441 err = EILSEQ;
442 break;
443 }
444
445 if ((err = pn_set(&pn, namep)) != 0)
446 break;
447
448 local_flags = 0;
449 err = smb_pathname_lookup(&pn, &rpn, local_flags,
450 &vp, rootvp, dnode->vp, &attr, cred);
451 if (err)
452 break;
453 }
454
455 /*
456 * This check MUST be done before symlink check
457 * since a reparse point is of type VLNK but should
458 * not be handled like a regular symlink.
459 */
460 if (attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) {
461 err = EREMOTE;
462 VN_RELE(vp);
463 break;
464 }
465
466 if ((vp->v_type == VLNK) &&
467 ((flags & FOLLOW) || pn_pathleft(&upn))) {
468
469 if (++nlink > MAXSYMLINKS) {
470 err = ELOOP;
471 VN_RELE(vp);
472 break;
473 }
474
475 (void) pn_alloc(&link_pn);
476 err = pn_getsymlink(vp, &link_pn, cred);
477 VN_RELE(vp);
478
479 if (err == 0) {
480 if (pn_pathleft(&link_pn) == 0)
481 (void) pn_set(&link_pn, ".");
482 err = pn_insert(&upn, &link_pn,
483 strlen(component));
484 }
485 pn_free(&link_pn);
486
487 if (err)
488 break;
489
490 if (upn.pn_pathlen == 0) {
491 err = ENOENT;
492 break;
493 }
494
495 if (upn.pn_path[0] == '/') {
496 fnode = root_node;
497 smb_node_ref(fnode);
498 }
499
500 if (pn_fixslash(&upn))
501 flags |= FOLLOW;
502
503 } else {
504 if (flags & FIGNORECASE) {
505 if (strcmp(rpn.pn_path, "/") != 0)
506 pn_setlast(&rpn);
507 namep = rpn.pn_path;
508 } else {
509 namep = pn.pn_path;
510 }
511
512 namep = smb_pathname_catia_v4tov5(sr, namep,
513 namebuf, sizeof (namebuf));
514
515 fnode = smb_node_lookup(sr, NULL, cred, vp, namep,
516 dnode, NULL);
517 VN_RELE(vp);
518
519 if (fnode == NULL) {
520 err = ENOMEM;
521 break;
522 }
523 }
524
525 while (upn.pn_path[0] == '/') {
526 upn.pn_path++;
527 upn.pn_pathlen--;
528 }
529
530 }
531
532 if ((pathleft) && (err == ENOENT))
533 err = ENOTDIR;
534
535 if (err) {
536 if (fnode)
537 smb_node_release(fnode);
538 if (dnode)
539 smb_node_release(dnode);
540 } else {
541 *ret_node = fnode;
542
543 if (dir_node)
544 *dir_node = dnode;
545 else
546 smb_node_release(dnode);
547 }
548
549 kmem_free(component, MAXNAMELEN);
550 kmem_free(real_name, MAXNAMELEN);
551 (void) pn_free(&pn);
552 (void) pn_free(&rpn);
553 (void) pn_free(&upn);
554
555 return (err);
556 }
557
558 /*
559 * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp()
560 * and will be released within lookuppnvp().
561 */
562 static int
smb_pathname_lookup(pathname_t * pn,pathname_t * rpn,int flags,vnode_t ** vp,vnode_t * rootvp,vnode_t * dvp,smb_attr_t * attr,cred_t * cred)563 smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags,
564 vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, smb_attr_t *attr, cred_t *cred)
565 {
566 int err;
567
568 *vp = NULL;
569 VN_HOLD(dvp);
570 if (rootvp != rootdir)
571 VN_HOLD(rootvp);
572
573 err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred);
574 if ((err == 0) && (attr != NULL))
575 (void) smb_vop_getattr(*vp, NULL, attr, 0, zone_kcred());
576
577 return (err);
578 }
579
580 /*
581 * CATIA Translation of a pathname component prior to passing it to lookuppnvp
582 *
583 * If the translated component name contains a '/' NULL is returned.
584 * The caller should treat this as error EILSEQ. It is not valid to
585 * have a directory name with a '/'.
586 */
587 static char *
smb_pathname_catia_v5tov4(smb_request_t * sr,char * name,char * namebuf,int buflen)588 smb_pathname_catia_v5tov4(smb_request_t *sr, char *name,
589 char *namebuf, int buflen)
590 {
591 char *namep;
592
593 if (SMB_TREE_SUPPORTS_CATIA(sr)) {
594 namep = smb_vop_catia_v5tov4(name, namebuf, buflen);
595 if (strchr(namep, '/') != NULL)
596 return (NULL);
597 return (namep);
598 }
599
600 return (name);
601 }
602
603 /*
604 * CATIA translation of a pathname component after returning from lookuppnvp
605 */
606 static char *
smb_pathname_catia_v4tov5(smb_request_t * sr,char * name,char * namebuf,int buflen)607 smb_pathname_catia_v4tov5(smb_request_t *sr, char *name,
608 char *namebuf, int buflen)
609 {
610 if (SMB_TREE_SUPPORTS_CATIA(sr)) {
611 smb_vop_catia_v4tov5(name, namebuf, buflen);
612 return (namebuf);
613 }
614
615 return (name);
616 }
617
618 /*
619 * sr - needed to check for case sense
620 * path - non mangled path needed to be looked up from the startvp
621 * startvp - the vnode to start the lookup from
622 * rootvp - the vnode of the root of the filesystem
623 * returns the vnode found when starting at startvp and using the path
624 *
625 * Finds a vnode starting at startvp and parsing the non mangled path
626 */
627
628 vnode_t *
smb_lookuppathvptovp(smb_request_t * sr,char * path,vnode_t * startvp,vnode_t * rootvp)629 smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp,
630 vnode_t *rootvp)
631 {
632 pathname_t pn;
633 vnode_t *vp = NULL;
634 int lookup_flags = FOLLOW;
635
636 if (SMB_TREE_IS_CASEINSENSITIVE(sr))
637 lookup_flags |= FIGNORECASE;
638
639 (void) pn_alloc(&pn);
640
641 if (pn_set(&pn, path) == 0) {
642 VN_HOLD(startvp);
643 if (rootvp != rootdir)
644 VN_HOLD(rootvp);
645
646 /* lookuppnvp should release the holds */
647 if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp,
648 rootvp, startvp, zone_kcred()) != 0) {
649 pn_free(&pn);
650 return (NULL);
651 }
652 }
653
654 pn_free(&pn);
655 return (vp);
656 }
657
658 /*
659 * smb_pathname_init
660 * Parse path: pname\\fname:sname:stype
661 *
662 * Elements of the smb_pathname_t structure are allocated using request
663 * specific storage and will be free'd when the sr is destroyed.
664 *
665 * Populate pn structure elements with the individual elements
666 * of pn->pn_path. pn->pn_sname will contain the whole stream name
667 * including the stream type and preceding colon: :sname:%DATA
668 * pn_stype will point to the stream type within pn_sname.
669 *
670 * If the pname element is missing pn_pname will be set to NULL.
671 * If any other element is missing the pointer in pn will be NULL.
672 */
673 void
smb_pathname_init(smb_request_t * sr,smb_pathname_t * pn,char * path)674 smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path)
675 {
676 char *pname, *fname, *sname;
677 int len;
678
679 bzero(pn, sizeof (smb_pathname_t));
680 pn->pn_path = smb_pathname_strdup(sr, path);
681
682 smb_pathname_preprocess(sr, pn);
683
684 /* parse pn->pn_path into its constituent parts */
685 pname = pn->pn_path;
686 fname = strrchr(pn->pn_path, '\\');
687
688 if (fname) {
689 if (fname == pname) {
690 pn->pn_pname = NULL;
691 } else {
692 *fname = '\0';
693 pn->pn_pname =
694 smb_pathname_strdup(sr, pname);
695 *fname = '\\';
696 }
697 ++fname;
698 } else {
699 fname = pname;
700 pn->pn_pname = NULL;
701 }
702
703 if (fname[0] == '\0') {
704 pn->pn_fname = NULL;
705 return;
706 }
707
708 if (!smb_is_stream_name(fname)) {
709 pn->pn_fname = smb_pathname_strdup(sr, fname);
710 return;
711 }
712
713 /*
714 * find sname and stype in fname.
715 * sname can't be NULL smb_is_stream_name checks this
716 */
717 sname = strchr(fname, ':');
718 if (sname == fname)
719 fname = NULL;
720 else {
721 *sname = '\0';
722 pn->pn_fname =
723 smb_pathname_strdup(sr, fname);
724 *sname = ':';
725 }
726
727 pn->pn_sname = smb_pathname_strdup(sr, sname);
728 pn->pn_stype = strchr(pn->pn_sname + 1, ':');
729 if (pn->pn_stype) {
730 (void) smb_strupr(pn->pn_stype);
731 } else {
732 len = strlen(pn->pn_sname);
733 pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA");
734 pn->pn_stype = pn->pn_sname + len;
735 }
736 ++pn->pn_stype;
737 }
738
739 /*
740 * smb_pathname_preprocess
741 *
742 * Perform common pre-processing of pn->pn_path:
743 * - if the pn_path is blank, set it to '\\'
744 * - perform unicode wildcard converstion.
745 * - convert any '/' to '\\'
746 * - eliminate duplicate slashes
747 * - remove trailing slashes
748 * - quota directory specific pre-processing
749 */
750 static void
smb_pathname_preprocess(smb_request_t * sr,smb_pathname_t * pn)751 smb_pathname_preprocess(smb_request_t *sr, smb_pathname_t *pn)
752 {
753 char *p;
754
755 /* treat empty path as "\\" */
756 if (strlen(pn->pn_path) == 0) {
757 pn->pn_path = smb_pathname_strdup(sr, "\\");
758 return;
759 }
760
761 if (sr->session->dialect < NT_LM_0_12)
762 smb_convert_wildcards(pn->pn_path);
763
764 /* treat '/' as '\\' */
765 (void) strsubst(pn->pn_path, '/', '\\');
766
767 (void) strcanon(pn->pn_path, "\\");
768
769 /* remove trailing '\\' */
770 p = pn->pn_path + strlen(pn->pn_path) - 1;
771 if ((p != pn->pn_path) && (*p == '\\'))
772 *p = '\0';
773
774 smb_pathname_preprocess_quota(sr, pn);
775 smb_pathname_preprocess_adminshare(sr, pn);
776 }
777
778 /*
779 * smb_pathname_preprocess_quota
780 *
781 * There is a special file required by windows so that the quota
782 * tab will be displayed by windows clients. This is created in
783 * a special directory, $EXTEND, at the root of the shared file
784 * system. To hide this directory prepend a '.' (dot).
785 */
786 static void
smb_pathname_preprocess_quota(smb_request_t * sr,smb_pathname_t * pn)787 smb_pathname_preprocess_quota(smb_request_t *sr, smb_pathname_t *pn)
788 {
789 char *name = "$EXTEND";
790 char *new_name = ".$EXTEND";
791 char *p, *slash;
792 int len;
793
794 if (!smb_node_is_vfsroot(sr->tid_tree->t_snode))
795 return;
796
797 p = pn->pn_path;
798
799 /* ignore any initial "\\" */
800 p += strspn(p, "\\");
801 if (smb_strcasecmp(p, name, strlen(name)) != 0)
802 return;
803
804 p += strlen(name);
805 if ((*p != ':') && (*p != '\\') && (*p != '\0'))
806 return;
807
808 slash = (pn->pn_path[0] == '\\') ? "\\" : "";
809 len = strlen(pn->pn_path) + 2;
810 pn->pn_path = smb_srm_alloc(sr, len);
811 (void) snprintf(pn->pn_path, len, "%s%s%s", slash, new_name, p);
812 (void) smb_strupr(pn->pn_path);
813 }
814
815 /*
816 * smb_pathname_preprocess_adminshare
817 *
818 * Convert any path with share name "C$" or "c$" (Admin share) in to lower case.
819 */
820 static void
smb_pathname_preprocess_adminshare(smb_request_t * sr,smb_pathname_t * pn)821 smb_pathname_preprocess_adminshare(smb_request_t *sr, smb_pathname_t *pn)
822 {
823 if (strcasecmp(sr->tid_tree->t_sharename, "c$") == 0)
824 (void) smb_strlwr(pn->pn_path);
825 }
826
827 /*
828 * smb_pathname_strdup
829 *
830 * Duplicate NULL terminated string s.
831 *
832 * The new string is allocated using request specific storage and will
833 * be free'd when the sr is destroyed.
834 */
835 static char *
smb_pathname_strdup(smb_request_t * sr,const char * s)836 smb_pathname_strdup(smb_request_t *sr, const char *s)
837 {
838 char *s2;
839 size_t n;
840
841 n = strlen(s) + 1;
842 s2 = smb_srm_zalloc(sr, n);
843 (void) strlcpy(s2, s, n);
844 return (s2);
845 }
846
847 /*
848 * smb_pathname_strcat
849 *
850 * Reallocate NULL terminated string s1 to accommodate
851 * concatenating NULL terminated string s2.
852 * Append s2 and return resulting NULL terminated string.
853 *
854 * The string buffer is reallocated using request specific
855 * storage and will be free'd when the sr is destroyed.
856 */
857 static char *
smb_pathname_strcat(smb_request_t * sr,char * s1,const char * s2)858 smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2)
859 {
860 size_t n;
861
862 n = strlen(s1) + strlen(s2) + 1;
863 s1 = smb_srm_rezalloc(sr, s1, n);
864 (void) strlcat(s1, s2, n);
865 return (s1);
866 }
867
868 /*
869 * smb_pathname_validate
870 *
871 * Perform basic validation of pn:
872 * - If first component of pn->path is ".." -> PATH_SYNTAX_BAD
873 * - If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID
874 * - If fname is "." -> INVALID_OBJECT_NAME
875 *
876 * On unix .. at the root of a file system links to the root. Thus
877 * an attempt to lookup "/../../.." will be the same as looking up "/"
878 * CIFs clients expect the above to result in
879 * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible
880 * (and questionable if it's desirable) to deal with all cases
881 * but paths beginning with \\.. are handled.
882 *
883 * Returns: B_TRUE if pn is valid,
884 * otherwise returns B_FALSE and sets error status in sr.
885 *
886 * XXX: Get rid of smbsr_error calls for SMB2
887 */
888 boolean_t
smb_pathname_validate(smb_request_t * sr,smb_pathname_t * pn)889 smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn)
890 {
891 char *path = pn->pn_path;
892
893 /* ignore any initial "\\" */
894 path += strspn(path, "\\");
895
896 /* If first component of path is ".." -> PATH_SYNTAX_BAD */
897 if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) {
898 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
899 ERRDOS, ERROR_BAD_PATHNAME);
900 return (B_FALSE);
901 }
902
903 /* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */
904 if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) {
905 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
906 ERRDOS, ERROR_INVALID_NAME);
907 return (B_FALSE);
908 }
909
910 /* If fname is "." -> INVALID_OBJECT_NAME */
911 if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) {
912 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
913 ERRDOS, ERROR_PATH_NOT_FOUND);
914 return (B_FALSE);
915 }
916
917 return (B_TRUE);
918 }
919
920 /*
921 * smb_validate_dirname
922 *
923 * smb_pathname_validate() should have already been performed on pn.
924 *
925 * Very basic directory name validation: checks for colons in a path.
926 * Need to skip the drive prefix since it contains a colon.
927 *
928 * Returns: B_TRUE if the name is valid,
929 * otherwise returns B_FALSE and sets error status in sr.
930 */
931 boolean_t
smb_validate_dirname(smb_request_t * sr,smb_pathname_t * pn)932 smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn)
933 {
934 char *name;
935 char *path = pn->pn_path;
936
937 if ((name = path) != 0) {
938 name += strspn(name, "\\");
939
940 if (strchr(name, ':') != 0) {
941 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
942 ERRDOS, ERROR_INVALID_NAME);
943 return (B_FALSE);
944 }
945 }
946
947 return (B_TRUE);
948 }
949
950 /*
951 * smb_validate_object_name
952 *
953 * smb_pathname_validate() should have already been pertformed on pn.
954 *
955 * Very basic file name validation.
956 * For filenames, we check for names of the form "AAAn:". Names that
957 * contain three characters, a single digit and a colon (:) are reserved
958 * as DOS device names, i.e. "COM1:".
959 * Stream name validation is handed off to smb_validate_stream_name
960 *
961 * Returns: B_TRUE if pn->pn_fname is valid,
962 * otherwise returns B_FALSE and sets error status in sr.
963 */
964 boolean_t
smb_validate_object_name(smb_request_t * sr,smb_pathname_t * pn)965 smb_validate_object_name(smb_request_t *sr, smb_pathname_t *pn)
966 {
967 if (pn->pn_fname &&
968 strlen(pn->pn_fname) == 5 &&
969 smb_isdigit(pn->pn_fname[3]) &&
970 pn->pn_fname[4] == ':') {
971 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
972 ERRDOS, ERROR_INVALID_NAME);
973 return (B_FALSE);
974 }
975
976 if (pn->pn_sname)
977 return (smb_validate_stream_name(sr, pn));
978
979 return (B_TRUE);
980 }
981
982 /*
983 * smb_stream_parse_name
984 *
985 * smb_stream_parse_name should only be called for a path that
986 * contains a valid named stream. Path validation should have
987 * been performed before this function is called.
988 *
989 * Find the last component of path and split it into filename
990 * and stream name.
991 *
992 * On return the named stream type will be present. The stream
993 * type defaults to ":$DATA", if it has not been defined
994 * For exmaple, 'stream' contains :<sname>:$DATA
995 */
996 void
smb_stream_parse_name(char * path,char * filename,char * stream)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
smb_is_stream_name(char * path)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
smb_validate_stream_name(smb_request_t * sr,smb_pathname_t * pn)1063 smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
1064 {
1065 static char *strmtype[] = {
1066 "$DATA",
1067 "$INDEX_ALLOCATION"
1068 };
1069 int i;
1070
1071 ASSERT(pn);
1072 ASSERT(pn->pn_sname);
1073
1074 if ((!(pn->pn_sname)) ||
1075 ((pn->pn_pname) && !(pn->pn_fname))) {
1076 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1077 ERRDOS, ERROR_INVALID_NAME);
1078 return (B_FALSE);
1079 }
1080
1081
1082 if (pn->pn_stype != NULL) {
1083 for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
1084 if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
1085 return (B_TRUE);
1086 }
1087
1088 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1089 ERRDOS, ERROR_INVALID_NAME);
1090 return (B_FALSE);
1091 }
1092
1093 return (B_TRUE);
1094 }
1095
1096 /*
1097 * valid DFS I/O path:
1098 *
1099 * \server-or-domain\share
1100 * \server-or-domain\share\path
1101 *
1102 * All the returned errors by this function needs to be
1103 * checked against Windows.
1104 */
1105 static int
smb_pathname_dfs_preprocess(smb_request_t * sr,char * path,size_t pathsz)1106 smb_pathname_dfs_preprocess(smb_request_t *sr, char *path, size_t pathsz)
1107 {
1108 smb_unc_t unc;
1109 char *linkpath;
1110 int rc;
1111
1112 if (sr->tid_tree == NULL)
1113 return (0);
1114
1115 if ((rc = smb_unc_init(path, &unc)) != 0)
1116 return (rc);
1117
1118 if (smb_strcasecmp(unc.unc_share, sr->tid_tree->t_sharename, 0)) {
1119 smb_unc_free(&unc);
1120 return (EINVAL);
1121 }
1122
1123 linkpath = unc.unc_path;
1124 (void) snprintf(path, pathsz, "/%s", (linkpath) ? linkpath : "");
1125
1126 smb_unc_free(&unc);
1127 return (0);
1128 }
1129