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 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 /*
28 * General Structures Layout
29 * -------------------------
30 *
31 * This is a simplified diagram showing the relationship between most of the
32 * main structures.
33 *
34 * +-------------------+
35 * | SMB_INFO |
36 * +-------------------+
37 * |
38 * |
39 * v
40 * +-------------------+ +-------------------+ +-------------------+
41 * | SESSION |<----->| SESSION |......| SESSION |
42 * +-------------------+ +-------------------+ +-------------------+
43 * | |
44 * | |
45 * | v
46 * | +-------------------+ +-------------------+ +-------------------+
47 * | | USER |<--->| USER |...| USER |
48 * | +-------------------+ +-------------------+ +-------------------+
49 * |
50 * |
51 * v
52 * +-------------------+ +-------------------+ +-------------------+
53 * | TREE |<----->| TREE |......| TREE |
54 * +-------------------+ +-------------------+ +-------------------+
55 * | |
56 * | |
57 * | v
58 * | +-------+ +-------+ +-------+
59 * | | OFILE |<----->| OFILE |......| OFILE |
60 * | +-------+ +-------+ +-------+
61 * |
62 * |
63 * v
64 * +-------+ +------+ +------+
65 * | ODIR |<----->| ODIR |......| ODIR |
66 * +-------+ +------+ +------+
67 *
68 *
69 * Tree State Machine
70 * ------------------
71 *
72 * +-----------------------------+ T0
73 * | SMB_TREE_STATE_CONNECTED |<----------- Creation/Allocation
74 * +-----------------------------+
75 * |
76 * | T1
77 * |
78 * v
79 * +------------------------------+
80 * | SMB_TREE_STATE_DISCONNECTING |
81 * +------------------------------+
82 * |
83 * | T2
84 * |
85 * v
86 * +-----------------------------+ T3
87 * | SMB_TREE_STATE_DISCONNECTED |----------> Deletion/Free
88 * +-----------------------------+
89 *
90 * SMB_TREE_STATE_CONNECTED
91 *
92 * While in this state:
93 * - The tree is queued in the list of trees of its user.
94 * - References will be given out if the tree is looked up.
95 * - Files under that tree can be accessed.
96 *
97 * SMB_TREE_STATE_DISCONNECTING
98 *
99 * While in this state:
100 * - The tree is queued in the list of trees of its user.
101 * - References will not be given out if the tree is looked up.
102 * - The files and directories open under the tree are being closed.
103 * - The resources associated with the tree remain.
104 *
105 * SMB_TREE_STATE_DISCONNECTED
106 *
107 * While in this state:
108 * - The tree is queued in the list of trees of its user.
109 * - References will not be given out if the tree is looked up.
110 * - The tree has no more files and directories opened.
111 * - The resources associated with the tree remain.
112 *
113 * Transition T0
114 *
115 * This transition occurs in smb_tree_connect(). A new tree is created and
116 * added to the list of trees of a user.
117 *
118 * Transition T1
119 *
120 * This transition occurs in smb_tree_disconnect().
121 *
122 * Transition T2
123 *
124 * This transition occurs in smb_tree_release(). The resources associated
125 * with the tree are freed as well as the tree structure. For the transition
126 * to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and
127 * the reference count be zero.
128 *
129 * Comments
130 * --------
131 *
132 * The state machine of the tree structures is controlled by 3 elements:
133 * - The list of trees of the user it belongs to.
134 * - The mutex embedded in the structure itself.
135 * - The reference count.
136 *
137 * There's a mutex embedded in the tree structure used to protect its fields
138 * and there's a lock embedded in the list of trees of a user. To
139 * increment or to decrement the reference count the mutex must be entered.
140 * To insert the tree into the list of trees of the user and to remove
141 * the tree from it, the lock must be entered in RW_WRITER mode.
142 *
143 * Rules of access to a tree structure:
144 *
145 * 1) In order to avoid deadlocks, when both (mutex and lock of the user
146 * list) have to be entered, the lock must be entered first.
147 *
148 * 2) All actions applied to a tree require a reference count.
149 *
150 * 3) There are 2 ways of getting a reference count: when a tree is
151 * connected and when a tree is looked up.
152 *
153 * It should be noted that the reference count of a tree registers the
154 * number of references to the tree in other structures (such as an smb
155 * request). The reference count is not incremented in these 2 instances:
156 *
157 * 1) The tree is connected. An tree is anchored by his state. If there's
158 * no activity involving a tree currently connected, the reference
159 * count of that tree is zero.
160 *
161 * 2) The tree is queued in the list of trees of the user. The fact of
162 * being queued in that list is NOT registered by incrementing the
163 * reference count.
164 */
165
166 #include <sys/refstr_impl.h>
167 #include <smbsrv/smb_kproto.h>
168 #include <smbsrv/smb_ktypes.h>
169 #include <smbsrv/smb_fsops.h>
170 #include <smbsrv/smb_share.h>
171
172 int smb_tcon_mute = 0;
173
174 uint32_t smb_tree_connect_core(smb_request_t *);
175 uint32_t smb_tree_connect_disk(smb_request_t *, smb_arg_tcon_t *);
176 uint32_t smb_tree_connect_printq(smb_request_t *, smb_arg_tcon_t *);
177 uint32_t smb_tree_connect_ipc(smb_request_t *, smb_arg_tcon_t *);
178 static smb_tree_t *smb_tree_alloc(smb_request_t *, const smb_kshare_t *,
179 smb_node_t *, uint32_t, uint32_t);
180 static boolean_t smb_tree_is_connected_locked(smb_tree_t *);
181 static boolean_t smb_tree_is_disconnected(smb_tree_t *);
182 static char *smb_tree_get_sharename(char *);
183 static int smb_tree_getattr(const smb_kshare_t *, smb_node_t *, smb_tree_t *);
184 static void smb_tree_get_volname(vfs_t *, smb_tree_t *);
185 static void smb_tree_get_flags(const smb_kshare_t *, vfs_t *, smb_tree_t *);
186 static void smb_tree_log(smb_request_t *, const char *, const char *, ...);
187 static void smb_tree_close_odirs(smb_tree_t *, uint16_t);
188 static smb_ofile_t *smb_tree_get_ofile(smb_tree_t *, smb_ofile_t *);
189 static smb_odir_t *smb_tree_get_odir(smb_tree_t *, smb_odir_t *);
190 static void smb_tree_set_execinfo(smb_tree_t *, smb_shr_execinfo_t *, int);
191 static int smb_tree_enum_private(smb_tree_t *, smb_svcenum_t *);
192 static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *);
193 static void smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *);
194 static void smb_tree_netinfo_fini(smb_netconnectinfo_t *);
195
196 uint32_t
smb_tree_connect(smb_request_t * sr)197 smb_tree_connect(smb_request_t *sr)
198 {
199 smb_server_t *sv = sr->sr_server;
200 uint32_t status;
201
202 if (smb_threshold_enter(&sv->sv_tcon_ct) != 0) {
203 return (NT_STATUS_INSUFF_SERVER_RESOURCES);
204 }
205
206 status = smb_tree_connect_core(sr);
207 smb_threshold_exit(&sv->sv_tcon_ct);
208 return (status);
209 }
210
211 /*
212 * Lookup the share name dispatch the appropriate stype handler.
213 * Share names are case insensitive so we map the share name to
214 * lower-case as a convenience for internal processing.
215 *
216 * Valid service values are:
217 * A: Disk share
218 * LPT1: Printer
219 * IPC Named pipe (IPC$ is reserved as the named pipe share).
220 * COMM Communications device
221 * ????? Any type of device (wildcard)
222 */
223 uint32_t
smb_tree_connect_core(smb_request_t * sr)224 smb_tree_connect_core(smb_request_t *sr)
225 {
226 smb_arg_tcon_t *tcon = &sr->sr_tcon;
227 smb_kshare_t *si;
228 char *name;
229 uint32_t status;
230
231 (void) smb_strlwr(tcon->path);
232
233 if ((name = smb_tree_get_sharename(tcon->path)) == NULL) {
234 smb_tree_log(sr, tcon->path, "invalid UNC path");
235 return (NT_STATUS_BAD_NETWORK_NAME);
236 }
237
238 si = smb_kshare_lookup(sr->sr_server, name);
239 if (si == NULL) {
240 smb_tree_log(sr, name, "share not found");
241 return (NT_STATUS_BAD_NETWORK_NAME);
242 }
243
244 if (!strcasecmp(SMB_SHARE_PRINT, name)) {
245 smb_kshare_release(sr->sr_server, si);
246 smb_tree_log(sr, name, "access not permitted");
247 return (NT_STATUS_ACCESS_DENIED);
248 }
249
250 /* NB: name points into tcon->path - don't free it. */
251 tcon->name = name;
252 sr->sr_tcon.si = si;
253
254 switch (si->shr_type & STYPE_MASK) {
255 case STYPE_DISKTREE:
256 status = smb_tree_connect_disk(sr, &sr->sr_tcon);
257 break;
258 case STYPE_IPC:
259 status = smb_tree_connect_ipc(sr, &sr->sr_tcon);
260 break;
261 case STYPE_PRINTQ:
262 status = smb_tree_connect_printq(sr, &sr->sr_tcon);
263 break;
264 default:
265 status = NT_STATUS_BAD_DEVICE_TYPE;
266 break;
267 }
268
269 smb_kshare_release(sr->sr_server, si);
270 sr->sr_tcon.si = NULL;
271
272 return (status);
273 }
274
275 /*
276 * Disconnect a tree.
277 */
278 void
smb_tree_disconnect(smb_tree_t * tree,boolean_t do_exec)279 smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec)
280 {
281 smb_shr_execinfo_t execinfo;
282
283 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
284
285 mutex_enter(&tree->t_mutex);
286 ASSERT(tree->t_refcnt);
287
288 if (smb_tree_is_connected_locked(tree)) {
289 /*
290 * Indicate that the disconnect process has started.
291 */
292 tree->t_state = SMB_TREE_STATE_DISCONNECTING;
293 mutex_exit(&tree->t_mutex);
294
295 if (do_exec) {
296 /*
297 * The files opened under this tree are closed.
298 */
299 smb_ofile_close_all(tree);
300 /*
301 * The directories opened under this tree are closed.
302 */
303 smb_tree_close_odirs(tree, 0);
304 }
305
306 mutex_enter(&tree->t_mutex);
307 tree->t_state = SMB_TREE_STATE_DISCONNECTED;
308 smb_server_dec_trees(tree->t_server);
309 }
310
311 mutex_exit(&tree->t_mutex);
312
313 if (do_exec && (tree->t_state == SMB_TREE_STATE_DISCONNECTED) &&
314 (tree->t_execflags & SMB_EXEC_UNMAP)) {
315
316 smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_UNMAP);
317 (void) smb_kshare_exec(tree->t_server, &execinfo);
318 }
319 }
320
321 /*
322 * Take a reference on a tree.
323 */
324 boolean_t
smb_tree_hold(smb_tree_t * tree)325 smb_tree_hold(
326 smb_tree_t *tree)
327 {
328 SMB_TREE_VALID(tree);
329
330 mutex_enter(&tree->t_mutex);
331
332 if (smb_tree_is_connected_locked(tree)) {
333 tree->t_refcnt++;
334 mutex_exit(&tree->t_mutex);
335 return (B_TRUE);
336 }
337
338 mutex_exit(&tree->t_mutex);
339 return (B_FALSE);
340 }
341
342 /*
343 * Bump the hold count regardless of the tree state. This is used in
344 * some internal code paths where we've already checked that we had a
345 * valid tree connection, and don't want to deal with the possiblity
346 * that the tree state might have changed to disconnecting after our
347 * original hold was taken. It's correct to continue processing a
348 * request even when new requests cannot lookup that tree anymore.
349 */
350 void
smb_tree_hold_internal(smb_tree_t * tree)351 smb_tree_hold_internal(
352 smb_tree_t *tree)
353 {
354 SMB_TREE_VALID(tree);
355
356 mutex_enter(&tree->t_mutex);
357 tree->t_refcnt++;
358 mutex_exit(&tree->t_mutex);
359 }
360
361 /*
362 * Release a reference on a tree. If the tree is disconnected and the
363 * reference count falls to zero, post the object for deletion.
364 * Object deletion is deferred to avoid modifying a list while an
365 * iteration may be in progress.
366 */
367 void
smb_tree_release(smb_tree_t * tree)368 smb_tree_release(
369 smb_tree_t *tree)
370 {
371 SMB_TREE_VALID(tree);
372
373 mutex_enter(&tree->t_mutex);
374 ASSERT(tree->t_refcnt);
375 tree->t_refcnt--;
376
377 /* flush the ofile and odir lists' delete queues */
378 smb_llist_flush(&tree->t_ofile_list);
379 smb_llist_flush(&tree->t_odir_list);
380
381 if (smb_tree_is_disconnected(tree) && (tree->t_refcnt == 0))
382 smb_session_post_tree(tree->t_session, tree);
383
384 mutex_exit(&tree->t_mutex);
385 }
386
387 void
smb_tree_post_ofile(smb_tree_t * tree,smb_ofile_t * of)388 smb_tree_post_ofile(smb_tree_t *tree, smb_ofile_t *of)
389 {
390 SMB_TREE_VALID(tree);
391 SMB_OFILE_VALID(of);
392 ASSERT(of->f_refcnt == 0);
393 ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED);
394 ASSERT(of->f_tree == tree);
395
396 smb_llist_post(&tree->t_ofile_list, of, smb_ofile_delete);
397 }
398
399 void
smb_tree_post_odir(smb_tree_t * tree,smb_odir_t * od)400 smb_tree_post_odir(smb_tree_t *tree, smb_odir_t *od)
401 {
402 SMB_TREE_VALID(tree);
403 SMB_ODIR_VALID(od);
404 ASSERT(od->d_refcnt == 0);
405 ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED);
406 ASSERT(od->d_tree == tree);
407
408 smb_llist_post(&tree->t_odir_list, od, smb_odir_delete);
409 }
410
411 /*
412 * Close ofiles and odirs that match pid.
413 */
414 void
smb_tree_close_pid(smb_tree_t * tree,uint32_t pid)415 smb_tree_close_pid(
416 smb_tree_t *tree,
417 uint32_t pid)
418 {
419 ASSERT(tree);
420 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
421
422 smb_ofile_close_all_by_pid(tree, pid);
423 smb_tree_close_odirs(tree, pid);
424 }
425
426 /*
427 * Check whether or not a tree supports the features identified by flags.
428 */
429 boolean_t
smb_tree_has_feature(smb_tree_t * tree,uint32_t flags)430 smb_tree_has_feature(smb_tree_t *tree, uint32_t flags)
431 {
432 ASSERT(tree);
433 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
434
435 return ((tree->t_flags & flags) == flags);
436 }
437
438 /*
439 * If the enumeration request is for tree data, handle the request
440 * here. Otherwise, pass it on to the ofiles.
441 *
442 * This function should be called with a hold on the tree.
443 */
444 int
smb_tree_enum(smb_tree_t * tree,smb_svcenum_t * svcenum)445 smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum)
446 {
447 smb_ofile_t *of;
448 smb_ofile_t *next;
449 int rc = 0;
450
451 ASSERT(tree);
452 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
453
454 if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE)
455 return (smb_tree_enum_private(tree, svcenum));
456
457 of = smb_tree_get_ofile(tree, NULL);
458 while (of) {
459 ASSERT(of->f_tree == tree);
460
461 rc = smb_ofile_enum(of, svcenum);
462 if (rc != 0) {
463 smb_ofile_release(of);
464 break;
465 }
466
467 next = smb_tree_get_ofile(tree, of);
468 smb_ofile_release(of);
469 of = next;
470 }
471
472 return (rc);
473 }
474
475 /*
476 * Close a file by its unique id.
477 */
478 int
smb_tree_fclose(smb_tree_t * tree,uint32_t uniqid)479 smb_tree_fclose(smb_tree_t *tree, uint32_t uniqid)
480 {
481 smb_ofile_t *of;
482
483 ASSERT(tree);
484 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
485
486 if ((of = smb_ofile_lookup_by_uniqid(tree, uniqid)) == NULL)
487 return (ENOENT);
488
489 if (smb_ofile_disallow_fclose(of)) {
490 smb_ofile_release(of);
491 return (EACCES);
492 }
493
494 smb_ofile_close(of, 0);
495 smb_ofile_release(of);
496 return (0);
497 }
498
499 /* *************************** Static Functions ***************************** */
500
501 #define SHARES_DIR ".zfs/shares/"
502
503 /*
504 * Calculates permissions given by the share's ACL to the
505 * user in the passed request. The default is full access.
506 * If any error occurs, full access is granted.
507 *
508 * Using the vnode of the share path find the root directory
509 * of the mounted file system. Then look to see if there is a
510 * .zfs/shares directory and if there is, lookup the file with
511 * the same name as the share name in it. The ACL set for this
512 * file is the share's ACL which is used for access check here.
513 */
514 static uint32_t
smb_tree_acl_access(smb_request_t * sr,const smb_kshare_t * si,vnode_t * pathvp)515 smb_tree_acl_access(smb_request_t *sr, const smb_kshare_t *si, vnode_t *pathvp)
516 {
517 smb_user_t *user;
518 cred_t *cred;
519 int rc;
520 vfs_t *vfsp;
521 vnode_t *root = NULL;
522 vnode_t *sharevp = NULL;
523 char *sharepath;
524 struct pathname pnp;
525 size_t size;
526 uint32_t access;
527
528 user = sr->uid_user;
529 cred = user->u_cred;
530 access = ACE_ALL_PERMS;
531
532 if (si->shr_flags & SMB_SHRF_AUTOHOME) {
533 /*
534 * An autohome share owner gets full access to the share.
535 * Everyone else is denied access.
536 */
537 if (si->shr_uid != crgetuid(cred))
538 access = 0;
539
540 return (access);
541 }
542
543 /*
544 * The hold on 'root' is released by the lookuppnvp() that follows
545 */
546 vfsp = pathvp->v_vfsp;
547 if (vfsp != NULL)
548 rc = VFS_ROOT(vfsp, &root);
549 else
550 rc = ENOENT;
551
552 if (rc != 0)
553 return (access);
554
555
556 size = sizeof (SHARES_DIR) + strlen(si->shr_name) + 1;
557 sharepath = smb_srm_alloc(sr, size);
558 (void) snprintf(sharepath, size, "%s%s", SHARES_DIR, si->shr_name);
559
560 pn_alloc(&pnp);
561 (void) pn_set(&pnp, sharepath);
562 rc = lookuppnvp(&pnp, NULL, NO_FOLLOW, NULL, &sharevp, rootdir, root,
563 zone_kcred());
564 pn_free(&pnp);
565
566 /*
567 * Now get the effective access value based on cred and ACL values.
568 */
569 if (rc == 0) {
570 smb_vop_eaccess(sharevp, (int *)&access, V_ACE_MASK, NULL,
571 cred);
572 VN_RELE(sharevp);
573 }
574
575 return (access);
576 }
577
578 /*
579 * Performs the following access checks for a disk share:
580 *
581 * - No IPC/anonymous user is allowed
582 *
583 * - If user is Guest, guestok property of the share should be
584 * enabled
585 *
586 * - If this is an Admin share, the user should have administrative
587 * privileges
588 *
589 * - Host based access control lists
590 *
591 * - Share ACL
592 *
593 * Returns the access allowed or 0 if access is denied.
594 */
595 static uint32_t
smb_tree_chkaccess(smb_request_t * sr,smb_kshare_t * shr,vnode_t * vp)596 smb_tree_chkaccess(smb_request_t *sr, smb_kshare_t *shr, vnode_t *vp)
597 {
598 smb_user_t *user = sr->uid_user;
599 char *sharename = shr->shr_name;
600 uint32_t host_access;
601 uint32_t acl_access;
602 uint32_t access;
603
604 if (user->u_flags & SMB_USER_FLAG_ANON) {
605 smb_tree_log(sr, sharename, "access denied: IPC only");
606 return (0);
607 }
608
609 if ((user->u_flags & SMB_USER_FLAG_GUEST) &&
610 ((shr->shr_flags & SMB_SHRF_GUEST_OK) == 0)) {
611 smb_tree_log(sr, sharename, "access denied: guest disabled");
612 return (0);
613 }
614
615 if ((shr->shr_flags & SMB_SHRF_ADMIN) && !smb_user_is_admin(user)) {
616 smb_tree_log(sr, sharename, "access denied: not admin");
617 return (0);
618 }
619
620 host_access = smb_kshare_hostaccess(shr, sr->session);
621 if ((host_access & ACE_ALL_PERMS) == 0) {
622 smb_tree_log(sr, sharename, "access denied: host access");
623 return (0);
624 }
625
626 acl_access = smb_tree_acl_access(sr, shr, vp);
627 if ((acl_access & ACE_ALL_PERMS) == 0) {
628 smb_tree_log(sr, sharename, "access denied: share ACL");
629 return (0);
630 }
631
632 access = host_access & acl_access;
633 if ((access & ACE_ALL_PERMS) == 0) {
634 smb_tree_log(sr, sharename, "access denied");
635 return (0);
636 }
637
638 return (access);
639 }
640
641 /*
642 * Connect a share for use with files and directories.
643 */
644 uint32_t
smb_tree_connect_disk(smb_request_t * sr,smb_arg_tcon_t * tcon)645 smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
646 {
647 char *sharename = tcon->path;
648 const char *any = "?????";
649 smb_user_t *user = sr->uid_user;
650 smb_node_t *dnode = NULL;
651 smb_node_t *snode = NULL;
652 smb_kshare_t *si = tcon->si;
653 char *service = tcon->service;
654 char last_component[MAXNAMELEN];
655 smb_tree_t *tree;
656 int rc;
657 uint32_t access;
658 smb_shr_execinfo_t execinfo;
659
660 ASSERT(user);
661 ASSERT(user->u_cred);
662
663 if (service != NULL &&
664 strcmp(service, any) != 0 &&
665 strcasecmp(service, "A:") != 0) {
666 smb_tree_log(sr, sharename, "invalid service (%s)", service);
667 return (NT_STATUS_BAD_DEVICE_TYPE);
668 }
669
670 /*
671 * Check that the shared directory exists.
672 */
673 rc = smb_pathname_reduce(sr, user->u_cred, si->shr_path, 0, 0, &dnode,
674 last_component);
675 if (rc == 0) {
676 rc = smb_fsop_lookup(sr, user->u_cred, SMB_FOLLOW_LINKS,
677 sr->sr_server->si_root_smb_node, dnode, last_component,
678 &snode);
679
680 smb_node_release(dnode);
681 }
682
683 if (rc) {
684 if (snode)
685 smb_node_release(snode);
686
687 smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
688 return (NT_STATUS_BAD_NETWORK_NAME);
689 }
690
691 if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
692 smb_node_release(snode);
693 return (NT_STATUS_ACCESS_DENIED);
694 }
695
696 /*
697 * Set up the OptionalSupport for this share.
698 */
699 tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
700
701 switch (si->shr_flags & SMB_SHRF_CSC_MASK) {
702 case SMB_SHRF_CSC_DISABLED:
703 tcon->optional_support |= SMB_CSC_CACHE_NONE;
704 break;
705 case SMB_SHRF_CSC_AUTO:
706 tcon->optional_support |= SMB_CSC_CACHE_AUTO_REINT;
707 break;
708 case SMB_SHRF_CSC_VDO:
709 tcon->optional_support |= SMB_CSC_CACHE_VDO;
710 break;
711 case SMB_SHRF_CSC_MANUAL:
712 default:
713 /*
714 * Default to SMB_CSC_CACHE_MANUAL_REINT.
715 */
716 break;
717 }
718
719 /* ABE support */
720 if (si->shr_flags & SMB_SHRF_ABE)
721 tcon->optional_support |=
722 SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM;
723
724 if (si->shr_flags & SMB_SHRF_DFSROOT)
725 tcon->optional_support |= SMB_SHARE_IS_IN_DFS;
726
727 /* if 'smb' zfs property: shortnames=disabled */
728 if (!smb_shortnames)
729 sr->arg.tcon.optional_support |= SMB_UNIQUE_FILE_NAME;
730
731 tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
732
733 smb_node_release(snode);
734
735 if (tree == NULL)
736 return (NT_STATUS_INSUFF_SERVER_RESOURCES);
737
738 if (tree->t_execflags & SMB_EXEC_MAP) {
739 smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_MAP);
740
741 rc = smb_kshare_exec(tree->t_server, &execinfo);
742
743 if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) {
744 smb_tree_disconnect(tree, B_FALSE);
745 smb_tree_release(tree);
746 return (NT_STATUS_ACCESS_DENIED);
747 }
748 }
749
750 sr->tid_tree = tree;
751 sr->smb_tid = tree->t_tid;
752
753 return (0);
754 }
755
756 /*
757 * Shares have both a share and host based access control. The access
758 * granted will be minimum permissions based on both hostaccess
759 * (permissions allowed by host based access) and aclaccess (from the
760 * share ACL).
761 */
762 uint32_t
smb_tree_connect_printq(smb_request_t * sr,smb_arg_tcon_t * tcon)763 smb_tree_connect_printq(smb_request_t *sr, smb_arg_tcon_t *tcon)
764 {
765 char *sharename = tcon->path;
766 const char *any = "?????";
767 smb_user_t *user = sr->uid_user;
768 smb_node_t *dnode = NULL;
769 smb_node_t *snode = NULL;
770 smb_kshare_t *si = tcon->si;
771 char *service = tcon->service;
772 char last_component[MAXNAMELEN];
773 smb_tree_t *tree;
774 int rc;
775 uint32_t access;
776
777 ASSERT(user);
778 ASSERT(user->u_cred);
779
780 if (sr->sr_server->sv_cfg.skc_print_enable == 0) {
781 smb_tree_log(sr, sharename, "printing disabled");
782 return (NT_STATUS_BAD_NETWORK_NAME);
783 }
784
785 if (service != NULL &&
786 strcmp(service, any) != 0 &&
787 strcasecmp(service, "LPT1:") != 0) {
788 smb_tree_log(sr, sharename, "invalid service (%s)", service);
789 return (NT_STATUS_BAD_DEVICE_TYPE);
790 }
791
792 /*
793 * Check that the shared directory exists.
794 */
795 rc = smb_pathname_reduce(sr, user->u_cred, si->shr_path, 0, 0, &dnode,
796 last_component);
797 if (rc == 0) {
798 rc = smb_fsop_lookup(sr, user->u_cred, SMB_FOLLOW_LINKS,
799 sr->sr_server->si_root_smb_node, dnode, last_component,
800 &snode);
801
802 smb_node_release(dnode);
803 }
804
805 if (rc) {
806 if (snode)
807 smb_node_release(snode);
808
809 smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
810 return (NT_STATUS_BAD_NETWORK_NAME);
811 }
812
813 if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
814 smb_node_release(snode);
815 return (NT_STATUS_ACCESS_DENIED);
816 }
817
818 tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
819
820 tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
821
822 smb_node_release(snode);
823
824 if (tree == NULL)
825 return (NT_STATUS_INSUFF_SERVER_RESOURCES);
826
827 sr->tid_tree = tree;
828 sr->smb_tid = tree->t_tid;
829
830 return (0);
831 }
832
833 /*
834 * Connect an IPC share for use with named pipes.
835 */
836 uint32_t
smb_tree_connect_ipc(smb_request_t * sr,smb_arg_tcon_t * tcon)837 smb_tree_connect_ipc(smb_request_t *sr, smb_arg_tcon_t *tcon)
838 {
839 char *name = tcon->path;
840 const char *any = "?????";
841 smb_user_t *user = sr->uid_user;
842 smb_tree_t *tree;
843 smb_kshare_t *si = tcon->si;
844 char *service = tcon->service;
845
846 ASSERT(user);
847
848 if (service != NULL &&
849 strcmp(service, any) != 0 &&
850 strcasecmp(service, "IPC") != 0) {
851 smb_tree_log(sr, name, "invalid service (%s)", service);
852 return (NT_STATUS_BAD_DEVICE_TYPE);
853 }
854
855 if ((user->u_flags & SMB_USER_FLAG_ANON) &&
856 sr->sr_cfg->skc_restrict_anon) {
857 smb_tree_log(sr, name, "access denied: restrict anonymous");
858 return (NT_STATUS_ACCESS_DENIED);
859 }
860
861 tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
862
863 tree = smb_tree_alloc(sr, si, NULL, ACE_ALL_PERMS, 0);
864 if (tree == NULL)
865 return (NT_STATUS_INSUFF_SERVER_RESOURCES);
866
867 sr->tid_tree = tree;
868 sr->smb_tid = tree->t_tid;
869
870 return (0);
871 }
872
873 /*
874 * Allocate a tree.
875 */
876 static smb_tree_t *
smb_tree_alloc(smb_request_t * sr,const smb_kshare_t * si,smb_node_t * snode,uint32_t access,uint32_t execflags)877 smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si,
878 smb_node_t *snode, uint32_t access, uint32_t execflags)
879 {
880 smb_session_t *session = sr->session;
881 smb_tree_t *tree;
882 uint32_t stype = si->shr_type;
883 uint16_t tid;
884
885 if (smb_idpool_alloc(&session->s_tid_pool, &tid))
886 return (NULL);
887
888 tree = kmem_cache_alloc(smb_cache_tree, KM_SLEEP);
889 bzero(tree, sizeof (smb_tree_t));
890
891 tree->t_session = session;
892 tree->t_server = session->s_server;
893
894 /* grab a ref for tree->t_owner */
895 smb_user_hold_internal(sr->uid_user);
896 tree->t_owner = sr->uid_user;
897
898 if (STYPE_ISDSK(stype) || STYPE_ISPRN(stype)) {
899 if (smb_tree_getattr(si, snode, tree) != 0) {
900 smb_idpool_free(&session->s_tid_pool, tid);
901 kmem_cache_free(smb_cache_tree, tree);
902 return (NULL);
903 }
904 }
905
906 if (smb_idpool_constructor(&tree->t_fid_pool)) {
907 smb_idpool_free(&session->s_tid_pool, tid);
908 kmem_cache_free(smb_cache_tree, tree);
909 return (NULL);
910 }
911
912 if (smb_idpool_constructor(&tree->t_odid_pool)) {
913 smb_idpool_destructor(&tree->t_fid_pool);
914 smb_idpool_free(&session->s_tid_pool, tid);
915 kmem_cache_free(smb_cache_tree, tree);
916 return (NULL);
917 }
918
919 smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t),
920 offsetof(smb_ofile_t, f_lnd));
921
922 smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t),
923 offsetof(smb_odir_t, d_lnd));
924
925 (void) strlcpy(tree->t_sharename, si->shr_name,
926 sizeof (tree->t_sharename));
927 (void) strlcpy(tree->t_resource, si->shr_path,
928 sizeof (tree->t_resource));
929
930 mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL);
931
932 tree->t_refcnt = 1;
933 tree->t_tid = tid;
934 tree->t_res_type = stype;
935 tree->t_state = SMB_TREE_STATE_CONNECTED;
936 tree->t_magic = SMB_TREE_MAGIC;
937 tree->t_access = access;
938 tree->t_connect_time = gethrestime_sec();
939 tree->t_execflags = execflags;
940
941 /* if FS is readonly, enforce that here */
942 if (tree->t_flags & SMB_TREE_READONLY)
943 tree->t_access &= ~ACE_ALL_WRITE_PERMS;
944
945 if (STYPE_ISDSK(stype) || STYPE_ISPRN(stype)) {
946 smb_node_ref(snode);
947 tree->t_snode = snode;
948 tree->t_acltype = smb_fsop_acltype(snode);
949 }
950
951 smb_llist_enter(&session->s_tree_list, RW_WRITER);
952 smb_llist_insert_head(&session->s_tree_list, tree);
953 smb_llist_exit(&session->s_tree_list);
954 atomic_inc_32(&session->s_tree_cnt);
955 smb_server_inc_trees(session->s_server);
956 return (tree);
957 }
958
959 /*
960 * Deallocate a tree. The open file and open directory lists should be
961 * empty.
962 *
963 * Remove the tree from the user's tree list before freeing resources
964 * associated with the tree.
965 */
966 void
smb_tree_dealloc(void * arg)967 smb_tree_dealloc(void *arg)
968 {
969 smb_session_t *session;
970 smb_tree_t *tree = (smb_tree_t *)arg;
971
972 SMB_TREE_VALID(tree);
973 ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
974 ASSERT(tree->t_refcnt == 0);
975
976 session = tree->t_session;
977 smb_llist_enter(&session->s_tree_list, RW_WRITER);
978 smb_llist_remove(&session->s_tree_list, tree);
979 smb_idpool_free(&session->s_tid_pool, tree->t_tid);
980 atomic_dec_32(&session->s_tree_cnt);
981 smb_llist_exit(&session->s_tree_list);
982
983 mutex_enter(&tree->t_mutex);
984 mutex_exit(&tree->t_mutex);
985
986 tree->t_magic = (uint32_t)~SMB_TREE_MAGIC;
987
988 if (tree->t_snode)
989 smb_node_release(tree->t_snode);
990
991 mutex_destroy(&tree->t_mutex);
992 smb_llist_destructor(&tree->t_ofile_list);
993 smb_llist_destructor(&tree->t_odir_list);
994 smb_idpool_destructor(&tree->t_fid_pool);
995 smb_idpool_destructor(&tree->t_odid_pool);
996
997 SMB_USER_VALID(tree->t_owner);
998 smb_user_release(tree->t_owner);
999
1000 kmem_cache_free(smb_cache_tree, tree);
1001 }
1002
1003 /*
1004 * Determine whether or not a tree is connected.
1005 * This function must be called with the tree mutex held.
1006 */
1007 static boolean_t
smb_tree_is_connected_locked(smb_tree_t * tree)1008 smb_tree_is_connected_locked(smb_tree_t *tree)
1009 {
1010 switch (tree->t_state) {
1011 case SMB_TREE_STATE_CONNECTED:
1012 return (B_TRUE);
1013
1014 case SMB_TREE_STATE_DISCONNECTING:
1015 case SMB_TREE_STATE_DISCONNECTED:
1016 /*
1017 * The tree exists but being diconnected or destroyed.
1018 */
1019 return (B_FALSE);
1020
1021 default:
1022 ASSERT(0);
1023 return (B_FALSE);
1024 }
1025 }
1026
1027 /*
1028 * Determine whether or not a tree is disconnected.
1029 * This function must be called with the tree mutex held.
1030 */
1031 static boolean_t
smb_tree_is_disconnected(smb_tree_t * tree)1032 smb_tree_is_disconnected(smb_tree_t *tree)
1033 {
1034 switch (tree->t_state) {
1035 case SMB_TREE_STATE_DISCONNECTED:
1036 return (B_TRUE);
1037
1038 case SMB_TREE_STATE_CONNECTED:
1039 case SMB_TREE_STATE_DISCONNECTING:
1040 return (B_FALSE);
1041
1042 default:
1043 ASSERT(0);
1044 return (B_FALSE);
1045 }
1046 }
1047
1048 /*
1049 * Return a pointer to the share name within a share resource path.
1050 *
1051 * The share path may be a Uniform Naming Convention (UNC) string
1052 * (\\server\share) or simply the share name. We validate the UNC
1053 * format but we don't look at the server name.
1054 */
1055 static char *
smb_tree_get_sharename(char * unc_path)1056 smb_tree_get_sharename(char *unc_path)
1057 {
1058 char *sharename = unc_path;
1059
1060 if (sharename[0] == '\\') {
1061 /*
1062 * Looks like a UNC path, validate the format.
1063 */
1064 if (sharename[1] != '\\')
1065 return (NULL);
1066
1067 if ((sharename = strchr(sharename+2, '\\')) == NULL)
1068 return (NULL);
1069
1070 ++sharename;
1071 } else if (strchr(sharename, '\\') != NULL) {
1072 /*
1073 * This should be a share name (no embedded \'s).
1074 */
1075 return (NULL);
1076 }
1077
1078 return (sharename);
1079 }
1080
1081 /*
1082 * Obtain the tree attributes: volume name, typename and flags.
1083 */
1084 static int
smb_tree_getattr(const smb_kshare_t * si,smb_node_t * node,smb_tree_t * tree)1085 smb_tree_getattr(const smb_kshare_t *si, smb_node_t *node, smb_tree_t *tree)
1086 {
1087 vfs_t *vfsp = SMB_NODE_VFS(node);
1088
1089 ASSERT(vfsp);
1090
1091 if (getvfs(&vfsp->vfs_fsid) != vfsp)
1092 return (ESTALE);
1093
1094 smb_tree_get_volname(vfsp, tree);
1095 smb_tree_get_flags(si, vfsp, tree);
1096
1097 VFS_RELE(vfsp);
1098 return (0);
1099 }
1100
1101 /*
1102 * Extract the volume name.
1103 */
1104 static void
smb_tree_get_volname(vfs_t * vfsp,smb_tree_t * tree)1105 smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree)
1106 {
1107 #ifdef _FAKE_KERNEL
1108 _NOTE(ARGUNUSED(vfsp))
1109 (void) strlcpy(tree->t_volume, "fake", SMB_VOLNAMELEN);
1110 #else /* _FAKE_KERNEL */
1111 refstr_t *vfs_mntpoint;
1112 const char *s;
1113 char *name;
1114
1115 vfs_mntpoint = vfs_getmntpoint(vfsp);
1116
1117 s = refstr_value(vfs_mntpoint);
1118 s += strspn(s, "/");
1119 (void) strlcpy(tree->t_volume, s, SMB_VOLNAMELEN);
1120
1121 refstr_rele(vfs_mntpoint);
1122
1123 name = tree->t_volume;
1124 (void) strsep((char **)&name, "/");
1125 #endif /* _FAKE_KERNEL */
1126 }
1127
1128 /*
1129 * Always set "unicode on disk" because we always use utf8 names locally.
1130 * Always set ACL support because the VFS will fake ACLs for file systems
1131 * that don't support them.
1132 *
1133 * Some flags are dependent on the typename, which is also set up here.
1134 * File system types are hardcoded in uts/common/os/vfs_conf.c.
1135 */
1136 static void
smb_tree_get_flags(const smb_kshare_t * si,vfs_t * vfsp,smb_tree_t * tree)1137 smb_tree_get_flags(const smb_kshare_t *si, vfs_t *vfsp, smb_tree_t *tree)
1138 {
1139 smb_session_t *ssn = tree->t_session;
1140 struct vfssw *vswp;
1141
1142 typedef struct smb_mtype {
1143 char *mt_name;
1144 size_t mt_namelen;
1145 uint32_t mt_flags;
1146 } smb_mtype_t;
1147
1148 static smb_mtype_t smb_mtype[] = {
1149 { "zfs", 3, SMB_TREE_QUOTA | SMB_TREE_SPARSE},
1150 { "ufs", 3, 0 },
1151 { "nfs", 3, SMB_TREE_NFS_MOUNTED },
1152 { "tmpfs", 5, SMB_TREE_NO_EXPORT }
1153 };
1154 smb_mtype_t *mtype;
1155 char *name;
1156 uint32_t flags =
1157 SMB_TREE_SUPPORTS_ACLS |
1158 SMB_TREE_UNICODE_ON_DISK;
1159 int i;
1160
1161 if (si->shr_flags & SMB_SHRF_DFSROOT)
1162 flags |= SMB_TREE_DFSROOT;
1163
1164 if (si->shr_flags & SMB_SHRF_CATIA)
1165 flags |= SMB_TREE_CATIA;
1166
1167 if (si->shr_flags & SMB_SHRF_ABE)
1168 flags |= SMB_TREE_ABE;
1169
1170 if (ssn->s_cfg.skc_oplock_enable) {
1171 /* if 'smb' zfs property: oplocks=enabled */
1172 flags |= SMB_TREE_OPLOCKS;
1173 }
1174
1175 /* Global config option for now. Later make per-share. */
1176 if (ssn->s_cfg.skc_traverse_mounts)
1177 flags |= SMB_TREE_TRAVERSE_MOUNTS;
1178
1179 /* if 'smb' zfs property: shortnames=enabled */
1180 if (smb_shortnames)
1181 flags |= SMB_TREE_SHORTNAMES;
1182
1183 if (vfsp->vfs_flag & VFS_RDONLY)
1184 flags |= SMB_TREE_READONLY;
1185
1186 if (vfsp->vfs_flag & VFS_XATTR)
1187 flags |= SMB_TREE_STREAMS;
1188
1189 vswp = vfs_getvfsswbyvfsops(vfs_getops(vfsp));
1190 if (vswp != NULL) {
1191 name = vswp->vsw_name;
1192 vfs_unrefvfssw(vswp);
1193 } else {
1194 name = "?";
1195 }
1196
1197 for (i = 0; i < sizeof (smb_mtype) / sizeof (smb_mtype[0]); ++i) {
1198 mtype = &smb_mtype[i];
1199 if (strncasecmp(name, mtype->mt_name, mtype->mt_namelen) == 0)
1200 flags |= mtype->mt_flags;
1201 }
1202
1203 (void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN);
1204 (void) smb_strupr((char *)tree->t_typename);
1205
1206 if (vfs_has_feature(vfsp, VFSFT_XVATTR))
1207 flags |= SMB_TREE_XVATTR;
1208
1209 if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE))
1210 flags |= SMB_TREE_CASEINSENSITIVE;
1211
1212 if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE))
1213 flags |= SMB_TREE_NO_CASESENSITIVE;
1214
1215 if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS))
1216 flags |= SMB_TREE_DIRENTFLAGS;
1217
1218 if (vfs_has_feature(vfsp, VFSFT_ACLONCREATE))
1219 flags |= SMB_TREE_ACLONCREATE;
1220
1221 if (vfs_has_feature(vfsp, VFSFT_ACEMASKONACCESS))
1222 flags |= SMB_TREE_ACEMASKONACCESS;
1223
1224 DTRACE_PROBE2(smb__tree__flags, uint32_t, flags, char *, name);
1225
1226
1227 tree->t_flags = flags;
1228 }
1229
1230 /*
1231 * Report share access result to syslog.
1232 */
1233 static void
smb_tree_log(smb_request_t * sr,const char * sharename,const char * fmt,...)1234 smb_tree_log(smb_request_t *sr, const char *sharename, const char *fmt, ...)
1235 {
1236 va_list ap;
1237 char buf[128];
1238 smb_user_t *user = sr->uid_user;
1239
1240 ASSERT(user);
1241
1242 if (smb_tcon_mute)
1243 return;
1244
1245 if ((user->u_name) && (strcasecmp(sharename, "IPC$") == 0)) {
1246 /*
1247 * Only report normal users, i.e. ignore W2K misuse
1248 * of the IPC connection by filtering out internal
1249 * names such as nobody and root.
1250 */
1251 if ((strcmp(user->u_name, "root") == 0) ||
1252 (strcmp(user->u_name, "nobody") == 0)) {
1253 return;
1254 }
1255 }
1256
1257 va_start(ap, fmt);
1258 (void) vsnprintf(buf, 128, fmt, ap);
1259 va_end(ap);
1260
1261 cmn_err(CE_NOTE, "smbd[%s\\%s]: %s %s",
1262 user->u_domain, user->u_name, sharename, buf);
1263 }
1264
1265 /*
1266 * smb_tree_lookup_odir
1267 *
1268 * Find the specified odir in the tree's list of odirs, and
1269 * attempt to obtain a hold on the odir.
1270 *
1271 * Returns NULL if odir not found or a hold cannot be obtained.
1272 */
1273 smb_odir_t *
smb_tree_lookup_odir(smb_request_t * sr,uint16_t odid)1274 smb_tree_lookup_odir(smb_request_t *sr, uint16_t odid)
1275 {
1276 smb_odir_t *od;
1277 smb_llist_t *od_list;
1278 smb_tree_t *tree = sr->tid_tree;
1279
1280 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1281
1282 od_list = &tree->t_odir_list;
1283
1284 smb_llist_enter(od_list, RW_READER);
1285 od = smb_llist_head(od_list);
1286 while (od) {
1287 if (od->d_odid == odid)
1288 break;
1289 od = smb_llist_next(od_list, od);
1290 }
1291 if (od == NULL)
1292 goto out;
1293
1294 /*
1295 * Only allow use of a given Search ID with the same UID that
1296 * was used to create it. MS-CIFS 3.3.5.14
1297 */
1298 if (od->d_user != sr->uid_user) {
1299 od = NULL;
1300 goto out;
1301 }
1302 if (!smb_odir_hold(od))
1303 od = NULL;
1304
1305 out:
1306 smb_llist_exit(od_list);
1307 return (od);
1308 }
1309
1310 boolean_t
smb_tree_is_connected(smb_tree_t * tree)1311 smb_tree_is_connected(smb_tree_t *tree)
1312 {
1313 boolean_t rb;
1314
1315 mutex_enter(&tree->t_mutex);
1316 rb = smb_tree_is_connected_locked(tree);
1317 mutex_exit(&tree->t_mutex);
1318 return (rb);
1319 }
1320
1321 /*
1322 * Get the next open ofile in the list. A reference is taken on
1323 * the ofile, which can be released later with smb_ofile_release().
1324 *
1325 * If the specified ofile is NULL, search from the beginning of the
1326 * list. Otherwise, the search starts just after that ofile.
1327 *
1328 * Returns NULL if there are no open files in the list.
1329 */
1330 static smb_ofile_t *
smb_tree_get_ofile(smb_tree_t * tree,smb_ofile_t * of)1331 smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of)
1332 {
1333 smb_llist_t *ofile_list;
1334
1335 ASSERT(tree);
1336 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1337
1338 ofile_list = &tree->t_ofile_list;
1339 smb_llist_enter(ofile_list, RW_READER);
1340
1341 if (of) {
1342 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
1343 of = smb_llist_next(ofile_list, of);
1344 } else {
1345 of = smb_llist_head(ofile_list);
1346 }
1347
1348 while (of) {
1349 if (smb_ofile_hold(of))
1350 break;
1351
1352 of = smb_llist_next(ofile_list, of);
1353 }
1354
1355 smb_llist_exit(ofile_list);
1356 return (of);
1357 }
1358
1359 /*
1360 * smb_tree_get_odir
1361 *
1362 * Find the next odir in the tree's list of odirs, and obtain a
1363 * hold on it.
1364 * If the specified odir is NULL the search starts at the beginning
1365 * of the tree's odir list, otherwise the search starts after the
1366 * specified odir.
1367 */
1368 static smb_odir_t *
smb_tree_get_odir(smb_tree_t * tree,smb_odir_t * od)1369 smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od)
1370 {
1371 smb_llist_t *od_list;
1372
1373 ASSERT(tree);
1374 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1375
1376 od_list = &tree->t_odir_list;
1377 smb_llist_enter(od_list, RW_READER);
1378
1379 if (od) {
1380 ASSERT(od->d_magic == SMB_ODIR_MAGIC);
1381 od = smb_llist_next(od_list, od);
1382 } else {
1383 od = smb_llist_head(od_list);
1384 }
1385
1386 while (od) {
1387 ASSERT(od->d_magic == SMB_ODIR_MAGIC);
1388
1389 if (smb_odir_hold(od))
1390 break;
1391 od = smb_llist_next(od_list, od);
1392 }
1393
1394 smb_llist_exit(od_list);
1395 return (od);
1396 }
1397
1398 /*
1399 * smb_tree_close_odirs
1400 *
1401 * Close all open odirs in the tree's list which were opened by
1402 * the process identified by pid.
1403 * If pid is zero, close all open odirs in the tree's list.
1404 */
1405 static void
smb_tree_close_odirs(smb_tree_t * tree,uint16_t pid)1406 smb_tree_close_odirs(smb_tree_t *tree, uint16_t pid)
1407 {
1408 smb_odir_t *od, *next_od;
1409
1410 ASSERT(tree);
1411 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
1412
1413 od = smb_tree_get_odir(tree, NULL);
1414 while (od) {
1415 ASSERT(od->d_magic == SMB_ODIR_MAGIC);
1416 ASSERT(od->d_tree == tree);
1417
1418 next_od = smb_tree_get_odir(tree, od);
1419 if ((pid == 0) || (od->d_opened_by_pid == pid))
1420 smb_odir_close(od);
1421 smb_odir_release(od);
1422
1423 od = next_od;
1424 }
1425 }
1426
1427 static void
smb_tree_set_execinfo(smb_tree_t * tree,smb_shr_execinfo_t * exec,int exec_type)1428 smb_tree_set_execinfo(smb_tree_t *tree, smb_shr_execinfo_t *exec,
1429 int exec_type)
1430 {
1431 exec->e_sharename = tree->t_sharename;
1432 exec->e_winname = tree->t_owner->u_name;
1433 exec->e_userdom = tree->t_owner->u_domain;
1434 exec->e_srv_ipaddr = tree->t_session->local_ipaddr;
1435 exec->e_cli_ipaddr = tree->t_session->ipaddr;
1436 exec->e_cli_netbiosname = tree->t_session->workstation;
1437 exec->e_uid = crgetuid(tree->t_owner->u_cred);
1438 exec->e_type = exec_type;
1439 }
1440
1441 /*
1442 * Private function to support smb_tree_enum.
1443 */
1444 static int
smb_tree_enum_private(smb_tree_t * tree,smb_svcenum_t * svcenum)1445 smb_tree_enum_private(smb_tree_t *tree, smb_svcenum_t *svcenum)
1446 {
1447 uint8_t *pb;
1448 uint_t nbytes;
1449 int rc;
1450
1451 if (svcenum->se_nskip > 0) {
1452 svcenum->se_nskip--;
1453 return (0);
1454 }
1455
1456 if (svcenum->se_nitems >= svcenum->se_nlimit) {
1457 svcenum->se_nitems = svcenum->se_nlimit;
1458 return (0);
1459 }
1460
1461 pb = &svcenum->se_buf[svcenum->se_bused];
1462 rc = smb_tree_netinfo_encode(tree, pb, svcenum->se_bavail, &nbytes);
1463 if (rc == 0) {
1464 svcenum->se_bavail -= nbytes;
1465 svcenum->se_bused += nbytes;
1466 svcenum->se_nitems++;
1467 }
1468
1469 return (rc);
1470 }
1471
1472 /*
1473 * Encode connection information into a buffer: connection information
1474 * needed in user space to support RPC requests.
1475 */
1476 static int
smb_tree_netinfo_encode(smb_tree_t * tree,uint8_t * buf,size_t buflen,uint32_t * nbytes)1477 smb_tree_netinfo_encode(smb_tree_t *tree, uint8_t *buf, size_t buflen,
1478 uint32_t *nbytes)
1479 {
1480 smb_netconnectinfo_t info;
1481 int rc;
1482
1483 smb_tree_netinfo_init(tree, &info);
1484 rc = smb_netconnectinfo_encode(&info, buf, buflen, nbytes);
1485 smb_tree_netinfo_fini(&info);
1486
1487 return (rc);
1488 }
1489
1490 static void
smb_tree_netinfo_username(smb_tree_t * tree,char ** namestr,uint32_t * namelen)1491 smb_tree_netinfo_username(smb_tree_t *tree, char **namestr, uint32_t *namelen)
1492 {
1493 smb_user_t *user = tree->t_owner;
1494
1495 /*
1496 * u_domain_len and u_name_len include the '\0' in their
1497 * lengths, hence the sum of the two lengths gives us room
1498 * for both the '\\' and '\0' chars.
1499 */
1500 ASSERT(namestr);
1501 ASSERT(namelen);
1502 ASSERT(user->u_domain_len > 0);
1503 ASSERT(user->u_name_len > 0);
1504 *namelen = user->u_domain_len + user->u_name_len;
1505 *namestr = kmem_alloc(*namelen, KM_SLEEP);
1506 (void) snprintf(*namestr, *namelen, "%s\\%s", user->u_domain,
1507 user->u_name);
1508 }
1509
1510 /*
1511 * Note: ci_numusers should be the number of users connected to
1512 * the share rather than the number of references on the tree but
1513 * we don't have a mechanism to track users/share in smbsrv yet.
1514 */
1515 static void
smb_tree_netinfo_init(smb_tree_t * tree,smb_netconnectinfo_t * info)1516 smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *info)
1517 {
1518 ASSERT(tree);
1519
1520 info->ci_id = tree->t_tid;
1521 info->ci_type = tree->t_res_type;
1522 info->ci_numopens = tree->t_open_files;
1523 info->ci_numusers = tree->t_refcnt;
1524 info->ci_time = gethrestime_sec() - tree->t_connect_time;
1525
1526 info->ci_sharelen = strlen(tree->t_sharename) + 1;
1527 info->ci_share = smb_mem_strdup(tree->t_sharename);
1528
1529 smb_tree_netinfo_username(tree, &info->ci_username, &info->ci_namelen);
1530 }
1531
1532 static void
smb_tree_netinfo_fini(smb_netconnectinfo_t * info)1533 smb_tree_netinfo_fini(smb_netconnectinfo_t *info)
1534 {
1535 if (info == NULL)
1536 return;
1537
1538 if (info->ci_username)
1539 kmem_free(info->ci_username, info->ci_namelen);
1540 if (info->ci_share)
1541 smb_mem_free(info->ci_share);
1542
1543 bzero(info, sizeof (smb_netconnectinfo_t));
1544 }
1545