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 2012 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(MAXPATHLEN, KM_SLEEP);
188
189 if ((len = strlcpy(usepath, path, MAXPATHLEN)) >= MAXPATHLEN) {
190 kmem_free(usepath, MAXPATHLEN);
191 return (ENAMETOOLONG);
192 }
193
194 (void) strsubst(usepath, '\\', '/');
195
196 if (share_root_node)
197 root_node = share_root_node;
198 else
199 root_node = sr->sr_server->si_root_smb_node;
200
201 if (cur_node == NULL)
202 cur_node = root_node;
203
204 local_cur_node = cur_node;
205 local_root_node = root_node;
206
207 if (SMB_TREE_IS_DFSROOT(sr) && (sr->smb_flg2 & SMB_FLAGS2_DFS)) {
208 err = smb_pathname_dfs_preprocess(sr, usepath, MAXPATHLEN);
209 if (err != 0) {
210 kmem_free(usepath, MAXPATHLEN);
211 return (err);
212 }
213 len = strlen(usepath);
214 }
215
216 if (sr && (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH)) {
217 err = smb_vss_lookup_nodes(sr, root_node, cur_node,
218 usepath, &vss_cur_node, &vss_root_node);
219
220 if (err != 0) {
221 kmem_free(usepath, MAXPATHLEN);
222 return (err);
223 }
224
225 len = strlen(usepath);
226 local_cur_node = vss_cur_node;
227 local_root_node = vss_root_node;
228 }
229
230 if (usepath[len - 1] == '/')
231 trailing_slash = 1;
232
233 (void) strcanon(usepath, "/");
234
235 (void) pn_alloc(&ppn);
236
237 if ((err = pn_set(&ppn, usepath)) != 0) {
238 (void) pn_free(&ppn);
239 kmem_free(usepath, MAXPATHLEN);
240 if (vss_cur_node != NULL)
241 (void) smb_node_release(vss_cur_node);
242 if (vss_root_node != NULL)
243 (void) smb_node_release(vss_root_node);
244 return (err);
245 }
246
247 /*
248 * If a path does not have a trailing slash, strip off the
249 * last component. (We only need to return an smb_node for
250 * the second to last component; a name is returned for the
251 * last component.)
252 */
253
254 if (trailing_slash) {
255 (void) strlcpy(last_component, ".", MAXNAMELEN);
256 } else {
257 (void) pn_setlast(&ppn);
258 (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN);
259 ppn.pn_path[0] = '\0';
260 }
261
262 if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) {
263 smb_node_ref(local_cur_node);
264 *dir_node = local_cur_node;
265 } else {
266 err = smb_pathname(sr, ppn.pn_buf, lookup_flags,
267 local_root_node, local_cur_node, NULL, dir_node, cred);
268 }
269
270 (void) pn_free(&ppn);
271 kmem_free(usepath, MAXPATHLEN);
272
273 /*
274 * Prevent traversal to another file system if mount point
275 * traversal is disabled.
276 *
277 * Note that we disregard whether the traversal of the path went
278 * outside of the file system and then came back (say via a link).
279 * This means that only symlinks that are expressed relatively to
280 * the share root work.
281 *
282 * share_root_node is NULL when mapping a share, so we disregard
283 * that case.
284 */
285
286 if ((err == 0) && share_root_node) {
287 if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) {
288 err = EACCES;
289 if ((sr) && (sr)->tid_tree &&
290 smb_tree_has_feature((sr)->tid_tree,
291 SMB_TREE_TRAVERSE_MOUNTS))
292 err = 0;
293 }
294 }
295
296 if (err) {
297 if (*dir_node) {
298 (void) smb_node_release(*dir_node);
299 *dir_node = NULL;
300 }
301 *last_component = 0;
302 }
303
304 if (vss_cur_node != NULL)
305 (void) smb_node_release(vss_cur_node);
306 if (vss_root_node != NULL)
307 (void) smb_node_release(vss_root_node);
308
309 return (err);
310 }
311
312 /*
313 * smb_pathname()
314 * wrapper to lookuppnvp(). Handles name unmangling.
315 *
316 * *dir_node is the true directory of the target *node.
317 *
318 * If any component but the last in the path is not found, ENOTDIR instead of
319 * ENOENT will be returned.
320 *
321 * Path components are processed one at a time so that smb_nodes can be
322 * created for each component. This allows the n_dnode field in the
323 * smb_node to be properly populated.
324 *
325 * Because of the above, links are also processed in this routine
326 * (i.e., we do not pass the FOLLOW flag to lookuppnvp()). This
327 * will allow smb_nodes to be created for each component of a link.
328 *
329 * Mangle checking is per component. If a name is mangled, when the
330 * unmangled name is passed to smb_pathname_lookup() do not pass
331 * FIGNORECASE, since the unmangled name is the real on-disk name.
332 * Otherwise pass FIGNORECASE if it's set in flags. This will cause the
333 * file system to return "first match" in the event of a case collision.
334 *
335 * If CATIA character translation is enabled it is applied to each
336 * component before passing the component to smb_pathname_lookup().
337 * After smb_pathname_lookup() the reverse translation is applied.
338 */
339
340 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)341 smb_pathname(smb_request_t *sr, char *path, int flags,
342 smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node,
343 smb_node_t **ret_node, cred_t *cred)
344 {
345 char *component, *real_name, *namep;
346 pathname_t pn, rpn, upn, link_pn;
347 smb_node_t *dnode, *fnode;
348 smb_attr_t attr;
349 vnode_t *rootvp, *vp;
350 size_t pathleft;
351 int err = 0;
352 int nlink = 0;
353 int local_flags;
354 uint32_t abe_flag = 0;
355 char namebuf[MAXNAMELEN];
356
357 if (path == NULL)
358 return (EINVAL);
359
360 ASSERT(root_node);
361 ASSERT(cur_node);
362 ASSERT(ret_node);
363
364 *ret_node = NULL;
365
366 if (dir_node)
367 *dir_node = NULL;
368
369 (void) pn_alloc(&upn);
370
371 if ((err = pn_set(&upn, path)) != 0) {
372 (void) pn_free(&upn);
373 return (err);
374 }
375
376 if (SMB_TREE_SUPPORTS_ABE(sr))
377 abe_flag = SMB_ABE;
378
379 (void) pn_alloc(&pn);
380 (void) pn_alloc(&rpn);
381
382 component = kmem_alloc(MAXNAMELEN, KM_SLEEP);
383 real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
384
385 fnode = NULL;
386 dnode = cur_node;
387 smb_node_ref(dnode);
388 rootvp = root_node->vp;
389
390 while ((pathleft = pn_pathleft(&upn)) != 0) {
391 if (fnode) {
392 smb_node_release(dnode);
393 dnode = fnode;
394 fnode = NULL;
395 }
396
397 if ((err = pn_getcomponent(&upn, component)) != 0)
398 break;
399
400 if ((namep = smb_pathname_catia_v5tov4(sr, component,
401 namebuf, sizeof (namebuf))) == NULL) {
402 err = EILSEQ;
403 break;
404 }
405
406 if ((err = pn_set(&pn, namep)) != 0)
407 break;
408
409 local_flags = flags & FIGNORECASE;
410 err = smb_pathname_lookup(&pn, &rpn, local_flags,
411 &vp, rootvp, dnode->vp, &attr, cred);
412
413 if (err) {
414 if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
415 !smb_maybe_mangled(component))
416 break;
417
418 if ((err = smb_unmangle(dnode, component,
419 real_name, MAXNAMELEN, abe_flag)) != 0)
420 break;
421
422 if ((namep = smb_pathname_catia_v5tov4(sr, real_name,
423 namebuf, sizeof (namebuf))) == NULL) {
424 err = EILSEQ;
425 break;
426 }
427
428 if ((err = pn_set(&pn, namep)) != 0)
429 break;
430
431 local_flags = 0;
432 err = smb_pathname_lookup(&pn, &rpn, local_flags,
433 &vp, rootvp, dnode->vp, &attr, cred);
434 if (err)
435 break;
436 }
437
438 /*
439 * This check MUST be done before symlink check
440 * since a reparse point is of type VLNK but should
441 * not be handled like a regular symlink.
442 */
443 if (attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) {
444 err = EREMOTE;
445 VN_RELE(vp);
446 break;
447 }
448
449 if ((vp->v_type == VLNK) &&
450 ((flags & FOLLOW) || pn_pathleft(&upn))) {
451
452 if (++nlink > MAXSYMLINKS) {
453 err = ELOOP;
454 VN_RELE(vp);
455 break;
456 }
457
458 (void) pn_alloc(&link_pn);
459 err = pn_getsymlink(vp, &link_pn, cred);
460 VN_RELE(vp);
461
462 if (err == 0) {
463 if (pn_pathleft(&link_pn) == 0)
464 (void) pn_set(&link_pn, ".");
465 err = pn_insert(&upn, &link_pn,
466 strlen(component));
467 }
468 pn_free(&link_pn);
469
470 if (err)
471 break;
472
473 if (upn.pn_pathlen == 0) {
474 err = ENOENT;
475 break;
476 }
477
478 if (upn.pn_path[0] == '/') {
479 fnode = root_node;
480 smb_node_ref(fnode);
481 }
482
483 if (pn_fixslash(&upn))
484 flags |= FOLLOW;
485
486 } else {
487 if (flags & FIGNORECASE) {
488 if (strcmp(rpn.pn_path, "/") != 0)
489 pn_setlast(&rpn);
490 namep = rpn.pn_path;
491 } else {
492 namep = pn.pn_path;
493 }
494
495 namep = smb_pathname_catia_v4tov5(sr, namep,
496 namebuf, sizeof (namebuf));
497
498 fnode = smb_node_lookup(sr, NULL, cred, vp, namep,
499 dnode, NULL);
500 VN_RELE(vp);
501
502 if (fnode == NULL) {
503 err = ENOMEM;
504 break;
505 }
506 }
507
508 while (upn.pn_path[0] == '/') {
509 upn.pn_path++;
510 upn.pn_pathlen--;
511 }
512
513 }
514
515 if ((pathleft) && (err == ENOENT))
516 err = ENOTDIR;
517
518 if (err) {
519 if (fnode)
520 smb_node_release(fnode);
521 if (dnode)
522 smb_node_release(dnode);
523 } else {
524 *ret_node = fnode;
525
526 if (dir_node)
527 *dir_node = dnode;
528 else
529 smb_node_release(dnode);
530 }
531
532 kmem_free(component, MAXNAMELEN);
533 kmem_free(real_name, MAXNAMELEN);
534 (void) pn_free(&pn);
535 (void) pn_free(&rpn);
536 (void) pn_free(&upn);
537
538 return (err);
539 }
540
541 /*
542 * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp()
543 * and will be released within lookuppnvp().
544 */
545 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)546 smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags,
547 vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, smb_attr_t *attr, cred_t *cred)
548 {
549 int err;
550
551 *vp = NULL;
552 VN_HOLD(dvp);
553 if (rootvp != rootdir)
554 VN_HOLD(rootvp);
555
556 err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred);
557 if ((err == 0) && (attr != NULL))
558 (void) smb_vop_getattr(*vp, NULL, attr, 0, zone_kcred());
559
560 return (err);
561 }
562
563 /*
564 * CATIA Translation of a pathname component prior to passing it to lookuppnvp
565 *
566 * If the translated component name contains a '/' NULL is returned.
567 * The caller should treat this as error EILSEQ. It is not valid to
568 * have a directory name with a '/'.
569 */
570 static char *
smb_pathname_catia_v5tov4(smb_request_t * sr,char * name,char * namebuf,int buflen)571 smb_pathname_catia_v5tov4(smb_request_t *sr, char *name,
572 char *namebuf, int buflen)
573 {
574 char *namep;
575
576 if (SMB_TREE_SUPPORTS_CATIA(sr)) {
577 namep = smb_vop_catia_v5tov4(name, namebuf, buflen);
578 if (strchr(namep, '/') != NULL)
579 return (NULL);
580 return (namep);
581 }
582
583 return (name);
584 }
585
586 /*
587 * CATIA translation of a pathname component after returning from lookuppnvp
588 */
589 static char *
smb_pathname_catia_v4tov5(smb_request_t * sr,char * name,char * namebuf,int buflen)590 smb_pathname_catia_v4tov5(smb_request_t *sr, char *name,
591 char *namebuf, int buflen)
592 {
593 if (SMB_TREE_SUPPORTS_CATIA(sr)) {
594 smb_vop_catia_v4tov5(name, namebuf, buflen);
595 return (namebuf);
596 }
597
598 return (name);
599 }
600
601 /*
602 * sr - needed to check for case sense
603 * path - non mangled path needed to be looked up from the startvp
604 * startvp - the vnode to start the lookup from
605 * rootvp - the vnode of the root of the filesystem
606 * returns the vnode found when starting at startvp and using the path
607 *
608 * Finds a vnode starting at startvp and parsing the non mangled path
609 */
610
611 vnode_t *
smb_lookuppathvptovp(smb_request_t * sr,char * path,vnode_t * startvp,vnode_t * rootvp)612 smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp,
613 vnode_t *rootvp)
614 {
615 pathname_t pn;
616 vnode_t *vp = NULL;
617 int lookup_flags = FOLLOW;
618
619 if (SMB_TREE_IS_CASEINSENSITIVE(sr))
620 lookup_flags |= FIGNORECASE;
621
622 (void) pn_alloc(&pn);
623
624 if (pn_set(&pn, path) == 0) {
625 VN_HOLD(startvp);
626 if (rootvp != rootdir)
627 VN_HOLD(rootvp);
628
629 /* lookuppnvp should release the holds */
630 if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp,
631 rootvp, startvp, zone_kcred()) != 0) {
632 pn_free(&pn);
633 return (NULL);
634 }
635 }
636
637 pn_free(&pn);
638 return (vp);
639 }
640
641 /*
642 * smb_pathname_init
643 * Parse path: pname\\fname:sname:stype
644 *
645 * Elements of the smb_pathname_t structure are allocated using request
646 * specific storage and will be free'd when the sr is destroyed.
647 *
648 * Populate pn structure elements with the individual elements
649 * of pn->pn_path. pn->pn_sname will contain the whole stream name
650 * including the stream type and preceding colon: :sname:%DATA
651 * pn_stype will point to the stream type within pn_sname.
652 *
653 * If the pname element is missing pn_pname will be set to NULL.
654 * If any other element is missing the pointer in pn will be NULL.
655 */
656 void
smb_pathname_init(smb_request_t * sr,smb_pathname_t * pn,char * path)657 smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path)
658 {
659 char *pname, *fname, *sname;
660 int len;
661
662 bzero(pn, sizeof (smb_pathname_t));
663 pn->pn_path = smb_pathname_strdup(sr, path);
664
665 smb_pathname_preprocess(sr, pn);
666
667 /* parse pn->pn_path into its constituent parts */
668 pname = pn->pn_path;
669 fname = strrchr(pn->pn_path, '\\');
670
671 if (fname) {
672 if (fname == pname) {
673 pn->pn_pname = NULL;
674 } else {
675 *fname = '\0';
676 pn->pn_pname =
677 smb_pathname_strdup(sr, pname);
678 *fname = '\\';
679 }
680 ++fname;
681 } else {
682 fname = pname;
683 pn->pn_pname = NULL;
684 }
685
686 if (fname[0] == '\0') {
687 pn->pn_fname = NULL;
688 return;
689 }
690
691 if (!smb_is_stream_name(fname)) {
692 pn->pn_fname = smb_pathname_strdup(sr, fname);
693 return;
694 }
695
696 /*
697 * find sname and stype in fname.
698 * sname can't be NULL smb_is_stream_name checks this
699 */
700 sname = strchr(fname, ':');
701 if (sname == fname)
702 fname = NULL;
703 else {
704 *sname = '\0';
705 pn->pn_fname =
706 smb_pathname_strdup(sr, fname);
707 *sname = ':';
708 }
709
710 pn->pn_sname = smb_pathname_strdup(sr, sname);
711 pn->pn_stype = strchr(pn->pn_sname + 1, ':');
712 if (pn->pn_stype) {
713 (void) smb_strupr(pn->pn_stype);
714 } else {
715 len = strlen(pn->pn_sname);
716 pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA");
717 pn->pn_stype = pn->pn_sname + len;
718 }
719 ++pn->pn_stype;
720 }
721
722 /*
723 * smb_pathname_preprocess
724 *
725 * Perform common pre-processing of pn->pn_path:
726 * - if the pn_path is blank, set it to '\\'
727 * - perform unicode wildcard converstion.
728 * - convert any '/' to '\\'
729 * - eliminate duplicate slashes
730 * - remove trailing slashes
731 * - quota directory specific pre-processing
732 */
733 static void
smb_pathname_preprocess(smb_request_t * sr,smb_pathname_t * pn)734 smb_pathname_preprocess(smb_request_t *sr, smb_pathname_t *pn)
735 {
736 char *p;
737
738 /* treat empty path as "\\" */
739 if (strlen(pn->pn_path) == 0) {
740 pn->pn_path = smb_pathname_strdup(sr, "\\");
741 return;
742 }
743
744 if (sr->session->dialect < NT_LM_0_12)
745 smb_convert_wildcards(pn->pn_path);
746
747 /* treat '/' as '\\' */
748 (void) strsubst(pn->pn_path, '/', '\\');
749
750 (void) strcanon(pn->pn_path, "\\");
751
752 /* remove trailing '\\' */
753 p = pn->pn_path + strlen(pn->pn_path) - 1;
754 if ((p != pn->pn_path) && (*p == '\\'))
755 *p = '\0';
756
757 smb_pathname_preprocess_quota(sr, pn);
758 smb_pathname_preprocess_adminshare(sr, pn);
759 }
760
761 /*
762 * smb_pathname_preprocess_quota
763 *
764 * There is a special file required by windows so that the quota
765 * tab will be displayed by windows clients. This is created in
766 * a special directory, $EXTEND, at the root of the shared file
767 * system. To hide this directory prepend a '.' (dot).
768 */
769 static void
smb_pathname_preprocess_quota(smb_request_t * sr,smb_pathname_t * pn)770 smb_pathname_preprocess_quota(smb_request_t *sr, smb_pathname_t *pn)
771 {
772 char *name = "$EXTEND";
773 char *new_name = ".$EXTEND";
774 char *p, *slash;
775 int len;
776
777 if (!smb_node_is_vfsroot(sr->tid_tree->t_snode))
778 return;
779
780 p = pn->pn_path;
781
782 /* ignore any initial "\\" */
783 p += strspn(p, "\\");
784 if (smb_strcasecmp(p, name, strlen(name)) != 0)
785 return;
786
787 p += strlen(name);
788 if ((*p != ':') && (*p != '\\') && (*p != '\0'))
789 return;
790
791 slash = (pn->pn_path[0] == '\\') ? "\\" : "";
792 len = strlen(pn->pn_path) + 2;
793 pn->pn_path = smb_srm_alloc(sr, len);
794 (void) snprintf(pn->pn_path, len, "%s%s%s", slash, new_name, p);
795 (void) smb_strupr(pn->pn_path);
796 }
797
798 /*
799 * smb_pathname_preprocess_adminshare
800 *
801 * Convert any path with share name "C$" or "c$" (Admin share) in to lower case.
802 */
803 static void
smb_pathname_preprocess_adminshare(smb_request_t * sr,smb_pathname_t * pn)804 smb_pathname_preprocess_adminshare(smb_request_t *sr, smb_pathname_t *pn)
805 {
806 if (strcasecmp(sr->tid_tree->t_sharename, "c$") == 0)
807 (void) smb_strlwr(pn->pn_path);
808 }
809
810 /*
811 * smb_pathname_strdup
812 *
813 * Duplicate NULL terminated string s.
814 *
815 * The new string is allocated using request specific storage and will
816 * be free'd when the sr is destroyed.
817 */
818 static char *
smb_pathname_strdup(smb_request_t * sr,const char * s)819 smb_pathname_strdup(smb_request_t *sr, const char *s)
820 {
821 char *s2;
822 size_t n;
823
824 n = strlen(s) + 1;
825 s2 = smb_srm_zalloc(sr, n);
826 (void) strlcpy(s2, s, n);
827 return (s2);
828 }
829
830 /*
831 * smb_pathname_strcat
832 *
833 * Reallocate NULL terminated string s1 to accommodate
834 * concatenating NULL terminated string s2.
835 * Append s2 and return resulting NULL terminated string.
836 *
837 * The string buffer is reallocated using request specific
838 * storage and will be free'd when the sr is destroyed.
839 */
840 static char *
smb_pathname_strcat(smb_request_t * sr,char * s1,const char * s2)841 smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2)
842 {
843 size_t n;
844
845 n = strlen(s1) + strlen(s2) + 1;
846 s1 = smb_srm_rezalloc(sr, s1, n);
847 (void) strlcat(s1, s2, n);
848 return (s1);
849 }
850
851 /*
852 * smb_pathname_validate
853 *
854 * Perform basic validation of pn:
855 * - If first component of pn->path is ".." -> PATH_SYNTAX_BAD
856 * - If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID
857 * - If fname is "." -> INVALID_OBJECT_NAME
858 *
859 * On unix .. at the root of a file system links to the root. Thus
860 * an attempt to lookup "/../../.." will be the same as looking up "/"
861 * CIFs clients expect the above to result in
862 * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible
863 * (and questionable if it's desirable) to deal with all cases
864 * but paths beginning with \\.. are handled.
865 *
866 * Returns: B_TRUE if pn is valid,
867 * otherwise returns B_FALSE and sets error status in sr.
868 */
869 boolean_t
smb_pathname_validate(smb_request_t * sr,smb_pathname_t * pn)870 smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn)
871 {
872 char *path = pn->pn_path;
873
874 /* ignore any initial "\\" */
875 path += strspn(path, "\\");
876
877 /* If first component of path is ".." -> PATH_SYNTAX_BAD */
878 if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) {
879 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
880 ERRDOS, ERROR_BAD_PATHNAME);
881 return (B_FALSE);
882 }
883
884 /* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */
885 if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) {
886 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
887 ERRDOS, ERROR_INVALID_NAME);
888 return (B_FALSE);
889 }
890
891 /* If fname is "." -> INVALID_OBJECT_NAME */
892 if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) {
893 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
894 ERRDOS, ERROR_PATH_NOT_FOUND);
895 return (B_FALSE);
896 }
897
898 return (B_TRUE);
899 }
900
901 /*
902 * smb_validate_dirname
903 *
904 * smb_pathname_validate() should have already been performed on pn.
905 *
906 * Very basic directory name validation: checks for colons in a path.
907 * Need to skip the drive prefix since it contains a colon.
908 *
909 * Returns: B_TRUE if the name is valid,
910 * otherwise returns B_FALSE and sets error status in sr.
911 */
912 boolean_t
smb_validate_dirname(smb_request_t * sr,smb_pathname_t * pn)913 smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn)
914 {
915 char *name;
916 char *path = pn->pn_path;
917
918 if ((name = path) != 0) {
919 name += strspn(name, "\\");
920
921 if (strchr(name, ':') != 0) {
922 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
923 ERRDOS, ERROR_INVALID_NAME);
924 return (B_FALSE);
925 }
926 }
927
928 return (B_TRUE);
929 }
930
931 /*
932 * smb_validate_object_name
933 *
934 * smb_pathname_validate() should have already been pertformed on pn.
935 *
936 * Very basic file name validation.
937 * For filenames, we check for names of the form "AAAn:". Names that
938 * contain three characters, a single digit and a colon (:) are reserved
939 * as DOS device names, i.e. "COM1:".
940 * Stream name validation is handed off to smb_validate_stream_name
941 *
942 * Returns: B_TRUE if pn->pn_fname is valid,
943 * otherwise returns B_FALSE and sets error status in sr.
944 */
945 boolean_t
smb_validate_object_name(smb_request_t * sr,smb_pathname_t * pn)946 smb_validate_object_name(smb_request_t *sr, smb_pathname_t *pn)
947 {
948 if (pn->pn_fname &&
949 strlen(pn->pn_fname) == 5 &&
950 smb_isdigit(pn->pn_fname[3]) &&
951 pn->pn_fname[4] == ':') {
952 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
953 ERRDOS, ERROR_INVALID_NAME);
954 return (B_FALSE);
955 }
956
957 if (pn->pn_sname)
958 return (smb_validate_stream_name(sr, pn));
959
960 return (B_TRUE);
961 }
962
963 /*
964 * smb_stream_parse_name
965 *
966 * smb_stream_parse_name should only be called for a path that
967 * contains a valid named stream. Path validation should have
968 * been performed before this function is called.
969 *
970 * Find the last component of path and split it into filename
971 * and stream name.
972 *
973 * On return the named stream type will be present. The stream
974 * type defaults to ":$DATA", if it has not been defined
975 * For exmaple, 'stream' contains :<sname>:$DATA
976 */
977 void
smb_stream_parse_name(char * path,char * filename,char * stream)978 smb_stream_parse_name(char *path, char *filename, char *stream)
979 {
980 char *fname, *sname, *stype;
981
982 ASSERT(path);
983 ASSERT(filename);
984 ASSERT(stream);
985
986 fname = strrchr(path, '\\');
987 fname = (fname == NULL) ? path : fname + 1;
988 (void) strlcpy(filename, fname, MAXNAMELEN);
989
990 sname = strchr(filename, ':');
991 (void) strlcpy(stream, sname, MAXNAMELEN);
992 *sname = '\0';
993
994 stype = strchr(stream + 1, ':');
995 if (stype == NULL)
996 (void) strlcat(stream, ":$DATA", MAXNAMELEN);
997 else
998 (void) smb_strupr(stype);
999 }
1000
1001 /*
1002 * smb_is_stream_name
1003 *
1004 * Determines if 'path' specifies a named stream.
1005 *
1006 * path is a NULL terminated string which could be a stream path.
1007 * [pathname/]fname[:stream_name[:stream_type]]
1008 *
1009 * - If there is no colon in the path or it's the last char
1010 * then it's not a stream name
1011 *
1012 * - '::' is a non-stream and is commonly used by Windows to designate
1013 * the unamed stream in the form "::$DATA"
1014 */
1015 boolean_t
smb_is_stream_name(char * path)1016 smb_is_stream_name(char *path)
1017 {
1018 char *colonp;
1019
1020 if (path == NULL)
1021 return (B_FALSE);
1022
1023 colonp = strchr(path, ':');
1024 if ((colonp == NULL) || (*(colonp+1) == '\0'))
1025 return (B_FALSE);
1026
1027 if (strstr(path, "::"))
1028 return (B_FALSE);
1029
1030 return (B_TRUE);
1031 }
1032
1033 /*
1034 * smb_validate_stream_name
1035 *
1036 * B_FALSE will be returned, and the error status ser in the sr, if:
1037 * - the path is not a stream name
1038 * - a path is specified but the fname is ommitted.
1039 * - the stream_type is specified but not valid.
1040 *
1041 * Note: the stream type is case-insensitive.
1042 */
1043 boolean_t
smb_validate_stream_name(smb_request_t * sr,smb_pathname_t * pn)1044 smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
1045 {
1046 static char *strmtype[] = {
1047 "$DATA",
1048 "$INDEX_ALLOCATION"
1049 };
1050 int i;
1051
1052 ASSERT(pn);
1053 ASSERT(pn->pn_sname);
1054
1055 if ((!(pn->pn_sname)) ||
1056 ((pn->pn_pname) && !(pn->pn_fname))) {
1057 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1058 ERRDOS, ERROR_INVALID_NAME);
1059 return (B_FALSE);
1060 }
1061
1062
1063 if (pn->pn_stype != NULL) {
1064 for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
1065 if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
1066 return (B_TRUE);
1067 }
1068
1069 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1070 ERRDOS, ERROR_INVALID_NAME);
1071 return (B_FALSE);
1072 }
1073
1074 return (B_TRUE);
1075 }
1076
1077 /*
1078 * valid DFS I/O path:
1079 *
1080 * \server-or-domain\share
1081 * \server-or-domain\share\path
1082 *
1083 * All the returned errors by this function needs to be
1084 * checked against Windows.
1085 */
1086 static int
smb_pathname_dfs_preprocess(smb_request_t * sr,char * path,size_t pathsz)1087 smb_pathname_dfs_preprocess(smb_request_t *sr, char *path, size_t pathsz)
1088 {
1089 smb_unc_t unc;
1090 char *linkpath;
1091 int rc;
1092
1093 if (sr->tid_tree == NULL)
1094 return (0);
1095
1096 if ((rc = smb_unc_init(path, &unc)) != 0)
1097 return (rc);
1098
1099 if (smb_strcasecmp(unc.unc_share, sr->tid_tree->t_sharename, 0)) {
1100 smb_unc_free(&unc);
1101 return (EINVAL);
1102 }
1103
1104 linkpath = unc.unc_path;
1105 (void) snprintf(path, pathsz, "/%s", (linkpath) ? linkpath : "");
1106
1107 smb_unc_free(&unc);
1108 return (0);
1109 }
1110