]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netsmb/smb_conn.c
zfs: merge openzfs/zfs@95f71c019
[FreeBSD/FreeBSD.git] / sys / netsmb / smb_conn.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2000-2001 Boris Popov
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Connection engine.
31  */
32
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/priv.h>
39 #include <sys/proc.h>
40 #include <sys/lock.h>
41 #include <sys/sysctl.h>
42 #include <sys/socketvar.h>
43
44 #include <sys/iconv.h>
45
46 #include <netsmb/smb.h>
47 #include <netsmb/smb_subr.h>
48 #include <netsmb/smb_conn.h>
49 #include <netsmb/smb_tran.h>
50 #include <netsmb/smb_trantcp.h>
51
52 static struct smb_connobj smb_vclist;
53 static int smb_vcnext = 1;      /* next unique id for VC */
54
55 SYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
56     "SMB protocol");
57
58 static MALLOC_DEFINE(M_SMBCONN, "smb_conn", "SMB connection");
59
60 static void smb_co_init(struct smb_connobj *cp, int level, char *ilockname,
61     char *lockname);
62 static void smb_co_done(struct smb_connobj *cp);
63 static int  smb_vc_disconnect(struct smb_vc *vcp);
64 static void smb_vc_free(struct smb_connobj *cp);
65 static void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred);
66 static smb_co_free_t smb_share_free;
67 static smb_co_gone_t smb_share_gone;
68
69 static int  smb_sysctl_treedump(SYSCTL_HANDLER_ARGS);
70
71 SYSCTL_PROC(_net_smb, OID_AUTO, treedump,
72     CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE,
73     NULL, 0, smb_sysctl_treedump, "S,treedump",
74     "Requester tree");
75
76 int
77 smb_sm_init(void)
78 {
79
80         smb_co_init(&smb_vclist, SMBL_SM, "smbsm ilock", "smbsm");
81         sx_xlock(&smb_vclist.co_interlock);
82         smb_co_unlock(&smb_vclist);
83         sx_unlock(&smb_vclist.co_interlock);
84         return 0;
85 }
86
87 int
88 smb_sm_done(void)
89 {
90
91         /* XXX: hold the mutex */
92         if (smb_vclist.co_usecount > 1) {
93                 SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1);
94                 return EBUSY;
95         }
96         smb_co_done(&smb_vclist);
97         return 0;
98 }
99
100 static int
101 smb_sm_lockvclist(void)
102 {
103         int error;
104
105         sx_xlock(&smb_vclist.co_interlock);
106         error = smb_co_lock(&smb_vclist);
107         sx_unlock(&smb_vclist.co_interlock);
108
109         return error;
110 }
111
112 static void
113 smb_sm_unlockvclist(void)
114 {
115
116         sx_xlock(&smb_vclist.co_interlock);
117         smb_co_unlock(&smb_vclist);
118         sx_unlock(&smb_vclist.co_interlock);
119 }
120
121 static int
122 smb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
123         struct smb_cred *scred, struct smb_vc **vcpp)
124 {
125         struct smb_connobj *scp;
126         struct smb_vc *vcp;
127         int exact = 1;
128         int error;
129
130         vcspec->shspec = shspec;
131         error = ENOENT;
132         vcp = NULL;
133         SMBCO_FOREACH(scp, &smb_vclist) {
134                 vcp = (struct smb_vc *)scp;
135                 error = smb_vc_lock(vcp);
136                 if (error)
137                         continue;
138
139                 if ((vcp->obj.co_flags & SMBV_PRIVATE) ||
140                     !CONNADDREQ(vcp->vc_paddr, vcspec->sap) ||
141                     strcmp(vcp->vc_username, vcspec->username) != 0)
142                         goto err1;
143                 if (vcspec->owner != SMBM_ANY_OWNER) {
144                         if (vcp->vc_uid != vcspec->owner)
145                                 goto err1;
146                 } else
147                         exact = 0;
148                 if (vcspec->group != SMBM_ANY_GROUP) {
149                         if (vcp->vc_grp != vcspec->group)
150                                 goto err1;
151                 } else
152                         exact = 0;
153                 if (vcspec->mode & SMBM_EXACT) {
154                         if (!exact || (vcspec->mode & SMBM_MASK) !=
155                             vcp->vc_mode)
156                                 goto err1;
157                 }
158                 if (smb_vc_access(vcp, scred, vcspec->mode) != 0)
159                         goto err1;
160                 vcspec->ssp = NULL;
161                 if (shspec) {
162                         error = (int)smb_vc_lookupshare(vcp, shspec, scred,
163                             &vcspec->ssp);
164                         if (error)
165                                 goto fail;
166                 }
167                 error = 0;
168                 break;
169         err1:
170                 error = 1;
171         fail:
172                 smb_vc_unlock(vcp);
173         }
174         if (vcp) {
175                 smb_vc_ref(vcp);
176                 *vcpp = vcp;
177         }
178         return (error);
179 }
180
181 int
182 smb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
183         struct smb_cred *scred, struct smb_vc **vcpp)
184 {
185         struct smb_vc *vcp;
186         struct smb_share *ssp = NULL;
187         int error;
188
189         *vcpp = vcp = NULL;
190
191         error = smb_sm_lockvclist();
192         if (error)
193                 return error;
194         error = smb_sm_lookupint(vcspec, shspec, scred, vcpp);
195         if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) {
196                 smb_sm_unlockvclist();
197                 return error;
198         }
199         error = smb_sm_lookupint(vcspec, NULL, scred, &vcp);
200         if (error) {
201                 error = smb_vc_create(vcspec, scred, &vcp);
202                 if (error)
203                         goto out;
204                 error = smb_vc_connect(vcp, scred);
205                 if (error)
206                         goto out;
207         }
208         if (shspec == NULL)
209                 goto out;
210         error = smb_share_create(vcp, shspec, scred, &ssp);
211         if (error)
212                 goto out;
213         error = smb_smb_treeconnect(ssp, scred);
214         if (error == 0)
215                 vcspec->ssp = ssp;
216         else
217                 smb_share_put(ssp, scred);
218 out:
219         smb_sm_unlockvclist();
220         if (error == 0)
221                 *vcpp = vcp;
222         else if (vcp) {
223                 smb_vc_lock(vcp);
224                 smb_vc_put(vcp, scred);
225         }
226         return error;
227 }
228
229 /*
230  * Common code for connection object
231  */
232 static void
233 smb_co_init(struct smb_connobj *cp, int level, char *ilockname, char *lockname)
234 {
235         SLIST_INIT(&cp->co_children);
236         sx_init_flags(&cp->co_interlock, ilockname, SX_RECURSE);
237         cv_init(&cp->co_lock, "smblock");
238         cp->co_lockcnt = 0;
239         cp->co_locker = NULL;
240         cp->co_level = level;
241         cp->co_usecount = 1;
242         sx_xlock(&cp->co_interlock);
243         smb_co_lock(cp);
244         sx_unlock(&cp->co_interlock);
245 }
246
247 static void
248 smb_co_done(struct smb_connobj *cp)
249 {
250
251         sx_destroy(&cp->co_interlock);
252         cv_destroy(&cp->co_lock);
253         cp->co_locker = NULL;
254         cp->co_flags = 0;
255         cp->co_lockcnt = 0;
256 }
257
258 static void
259 smb_co_gone(struct smb_connobj *cp, struct smb_cred *scred)
260 {
261         struct smb_connobj *parent;
262
263         if (cp->co_gone)
264                 cp->co_gone(cp, scred);
265         parent = cp->co_parent;
266         if (parent) {
267                 sx_xlock(&parent->co_interlock);
268                 smb_co_lock(parent);
269                 sx_unlock(&parent->co_interlock);
270                 SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next);
271                 smb_co_put(parent, scred);
272         }
273         if (cp->co_free)
274                 cp->co_free(cp);
275 }
276
277 void
278 smb_co_ref(struct smb_connobj *cp)
279 {
280
281         sx_xlock(&cp->co_interlock);
282         cp->co_usecount++;
283         sx_unlock(&cp->co_interlock);
284 }
285
286 void
287 smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred)
288 {
289
290         sx_xlock(&cp->co_interlock);
291         smb_co_unlock(cp);
292         if (cp->co_usecount > 1) {
293                 cp->co_usecount--;
294                 sx_unlock(&cp->co_interlock);
295                 return;
296         }
297         if (cp->co_usecount == 0) {
298                 SMBERROR("negative use_count for object %d", cp->co_level);
299                 sx_unlock(&cp->co_interlock);
300                 return;
301         }
302         cp->co_usecount--;
303         cp->co_flags |= SMBO_GONE;
304         sx_unlock(&cp->co_interlock);
305         smb_co_gone(cp, scred);
306 }
307
308 int
309 smb_co_get(struct smb_connobj *cp, struct smb_cred *scred)
310 {
311         int error;
312
313         MPASS(sx_xholder(&cp->co_interlock) == curthread);
314         cp->co_usecount++;
315         error = smb_co_lock(cp);
316         if (error) 
317                 cp->co_usecount--;
318         return error;
319 }
320
321 void
322 smb_co_put(struct smb_connobj *cp, struct smb_cred *scred)
323 {
324
325         sx_xlock(&cp->co_interlock);
326         if (cp->co_usecount > 1) {
327                 cp->co_usecount--;
328         } else if (cp->co_usecount == 1) {
329                 cp->co_usecount--;
330                 cp->co_flags |= SMBO_GONE;
331         } else {
332                 SMBERROR("negative usecount");
333         }
334         smb_co_unlock(cp);
335         sx_unlock(&cp->co_interlock);
336         if ((cp->co_flags & SMBO_GONE) == 0)
337                 return;
338         smb_co_gone(cp, scred);
339 }
340
341 int
342 smb_co_lock(struct smb_connobj *cp)
343 {
344
345         MPASS(sx_xholder(&cp->co_interlock) == curthread); 
346         for (;;) {
347                 if (cp->co_flags & SMBO_GONE)
348                         return EINVAL;
349                 if (cp->co_locker == NULL) {
350                         cp->co_locker = curthread;
351                         return 0;
352                 }
353                 if (cp->co_locker == curthread) {
354                         cp->co_lockcnt++;
355                         return 0;
356                 }
357                 cv_wait(&cp->co_lock, &cp->co_interlock);
358         }
359 }
360
361 void
362 smb_co_unlock(struct smb_connobj *cp)
363 {
364
365         MPASS(sx_xholder(&cp->co_interlock) == curthread); 
366         MPASS(cp->co_locker == curthread);
367         if (cp->co_lockcnt != 0) {
368                 cp->co_lockcnt--;
369                 return;
370         }
371         cp->co_locker = NULL;
372         cv_signal(&cp->co_lock);
373 }
374
375 static void
376 smb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child)
377 {
378
379         smb_co_ref(parent);
380         SLIST_INSERT_HEAD(&parent->co_children, child, co_next);
381         child->co_parent = parent;
382 }
383
384 /*
385  * Session implementation
386  */
387
388 int
389 smb_vc_create(struct smb_vcspec *vcspec,
390         struct smb_cred *scred, struct smb_vc **vcpp)
391 {
392         struct smb_vc *vcp;
393         struct ucred *cred = scred->scr_cred;
394         uid_t uid = vcspec->owner;
395         gid_t gid = vcspec->group;
396         uid_t realuid = cred->cr_uid;
397         char *domain = vcspec->domain;
398         int error, isroot;
399
400         isroot = smb_suser(cred) == 0;
401         /*
402          * Only superuser can create VCs with different uid and gid
403          */
404         if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
405                 return EPERM;
406         if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
407                 return EPERM;
408
409         vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK);
410         smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc ilock", "smb_vc");
411         vcp->obj.co_free = smb_vc_free;
412         vcp->obj.co_gone = smb_vc_gone;
413         vcp->vc_number = smb_vcnext++;
414         vcp->vc_timo = SMB_DEFRQTIMO;
415         vcp->vc_smbuid = SMB_UID_UNKNOWN;
416         vcp->vc_mode = vcspec->rights & SMBM_MASK;
417         vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE);
418         vcp->vc_tdesc = &smb_tran_nbtcp_desc;
419         vcp->vc_seqno = 0;
420         vcp->vc_mackey = NULL;
421         vcp->vc_mackeylen = 0;
422
423         if (uid == SMBM_ANY_OWNER)
424                 uid = realuid;
425         if (gid == SMBM_ANY_GROUP)
426                 gid = cred->cr_groups[0];
427         vcp->vc_uid = uid;
428         vcp->vc_grp = gid;
429
430         smb_sl_init(&vcp->vc_stlock, "vcstlock");
431         error = ENOMEM;
432
433         vcp->vc_paddr = sodupsockaddr(vcspec->sap, M_WAITOK);
434         if (vcp->vc_paddr == NULL)
435                 goto fail;
436         vcp->vc_laddr = sodupsockaddr(vcspec->lap, M_WAITOK);
437         if (vcp->vc_laddr == NULL)
438                 goto fail;
439         vcp->vc_pass = smb_strdup(vcspec->pass);
440         if (vcp->vc_pass == NULL)
441                 goto fail;
442         vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain :
443             "NODOMAIN");
444         if (vcp->vc_domain == NULL)
445                 goto fail;
446         vcp->vc_srvname = smb_strdup(vcspec->srvname);
447         if (vcp->vc_srvname == NULL)
448                 goto fail;
449         vcp->vc_username = smb_strdup(vcspec->username);
450         if (vcp->vc_username == NULL)
451                 goto fail;
452         error = (int)iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower);
453         if (error)
454                 goto fail;
455         error = (int)iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper);
456         if (error)
457                 goto fail;
458         if (vcspec->servercs[0]) {
459                 error = (int)iconv_open(vcspec->servercs, vcspec->localcs,
460                     &vcp->vc_cp_toserver);
461                 if (error)
462                         goto fail;
463                 error = (int)iconv_open(vcspec->localcs, vcspec->servercs,
464                     &vcp->vc_cp_tolocal);
465                 if (error)
466                         goto fail;
467                 vcp->vc_toserver = vcp->vc_cp_toserver;
468                 vcp->vc_tolocal = vcp->vc_cp_tolocal;
469                 iconv_add(ENCODING_UNICODE, ENCODING_UNICODE, SMB_UNICODE_NAME);
470                 iconv_add(ENCODING_UNICODE, SMB_UNICODE_NAME, ENCODING_UNICODE);
471                 error = (int)iconv_open(SMB_UNICODE_NAME, vcspec->localcs,
472                     &vcp->vc_ucs_toserver);
473                 if (!error) {
474                         error = (int)iconv_open(vcspec->localcs, SMB_UNICODE_NAME,
475                             &vcp->vc_ucs_tolocal);
476                 }
477                 if (error) {
478                         if (vcp->vc_ucs_toserver)
479                                 iconv_close(vcp->vc_ucs_toserver);
480                         vcp->vc_ucs_toserver = NULL;
481                         vcp->vc_ucs_tolocal = NULL;
482                 }
483         }
484         error = (int)smb_iod_create(vcp);
485         if (error)
486                 goto fail;
487         *vcpp = vcp;
488         smb_co_addchild(&smb_vclist, VCTOCP(vcp));
489         return (0);
490
491  fail:
492         smb_vc_put(vcp, scred);
493         return (error);
494 }
495
496 static void
497 smb_vc_free(struct smb_connobj *cp)
498 {
499         struct smb_vc *vcp = CPTOVC(cp);
500
501         if (vcp->vc_iod)
502                 smb_iod_destroy(vcp->vc_iod);
503         SMB_STRFREE(vcp->vc_username);
504         SMB_STRFREE(vcp->vc_srvname);
505         SMB_STRFREE(vcp->vc_pass);
506         SMB_STRFREE(vcp->vc_domain);
507         if (vcp->vc_mackey)
508                 free(vcp->vc_mackey, M_SMBTEMP);
509         if (vcp->vc_paddr)
510                 free(vcp->vc_paddr, M_SONAME);
511         if (vcp->vc_laddr)
512                 free(vcp->vc_laddr, M_SONAME);
513         if (vcp->vc_tolower)
514                 iconv_close(vcp->vc_tolower);
515         if (vcp->vc_toupper)
516                 iconv_close(vcp->vc_toupper);
517         if (vcp->vc_tolocal)
518                 vcp->vc_tolocal = NULL;
519         if (vcp->vc_toserver)
520                 vcp->vc_toserver = NULL;
521         if (vcp->vc_cp_tolocal)
522                 iconv_close(vcp->vc_cp_tolocal);
523         if (vcp->vc_cp_toserver)
524                 iconv_close(vcp->vc_cp_toserver);
525         if (vcp->vc_ucs_tolocal)
526                 iconv_close(vcp->vc_ucs_tolocal);
527         if (vcp->vc_ucs_toserver)
528                 iconv_close(vcp->vc_ucs_toserver);
529         smb_co_done(VCTOCP(vcp));
530         smb_sl_destroy(&vcp->vc_stlock);
531         free(vcp, M_SMBCONN);
532 }
533
534 /*
535  * Called when use count of VC dropped to zero.
536  */
537 static void
538 smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred)
539 {
540         struct smb_vc *vcp = CPTOVC(cp);
541
542         smb_vc_disconnect(vcp);
543 }
544
545 void
546 smb_vc_ref(struct smb_vc *vcp)
547 {
548         smb_co_ref(VCTOCP(vcp));
549 }
550
551 void
552 smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred)
553 {
554         smb_co_rele(VCTOCP(vcp), scred);
555 }
556
557 int
558 smb_vc_get(struct smb_vc *vcp, struct smb_cred *scred)
559 {
560         struct smb_connobj *cp;
561         int error;
562
563         cp = VCTOCP(vcp);
564         sx_xlock(&cp->co_interlock);
565         error = smb_co_get(cp, scred);
566         sx_unlock(&cp->co_interlock);
567         return error;
568 }
569
570 void
571 smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred)
572 {
573         smb_co_put(VCTOCP(vcp), scred);
574 }
575
576 int
577 smb_vc_lock(struct smb_vc *vcp)
578 {
579         struct smb_connobj *cp;
580         int error;
581
582         cp = VCTOCP(vcp);
583         sx_xlock(&cp->co_interlock);
584         error = smb_co_lock(cp);
585         sx_unlock(&cp->co_interlock);
586         return error;
587 }
588
589 void
590 smb_vc_unlock(struct smb_vc *vcp)
591 {
592
593         struct smb_connobj *cp;
594
595         cp = VCTOCP(vcp);
596         sx_xlock(&cp->co_interlock);
597         smb_co_unlock(cp);
598         sx_unlock(&cp->co_interlock);
599 }
600
601 int
602 smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode)
603 {
604         struct ucred *cred = scred->scr_cred;
605
606         if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid)
607                 return 0;
608         mode >>= 3;
609         if (!groupmember(vcp->vc_grp, cred))
610                 mode >>= 3;
611         return (vcp->vc_mode & mode) == mode ? 0 : EACCES;
612 }
613
614 static int
615 smb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp)
616 {
617         int exact = 1;
618
619         if (strcmp(ssp->ss_name, dp->name) != 0)
620                 return 1;
621         if (dp->owner != SMBM_ANY_OWNER) {
622                 if (ssp->ss_uid != dp->owner)
623                         return 1;
624         } else
625                 exact = 0;
626         if (dp->group != SMBM_ANY_GROUP) {
627                 if (ssp->ss_grp != dp->group)
628                         return 1;
629         } else
630                 exact = 0;
631
632         if (dp->mode & SMBM_EXACT) {
633                 if (!exact)
634                         return 1;
635                 return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1;
636         }
637         if (smb_share_access(ssp, dp->scred, dp->mode) != 0)
638                 return 1;
639         return 0;
640 }
641
642 /*
643  * Lookup share in the given VC. Share referenced and locked on return.
644  * VC expected to be locked on entry and will be left locked on exit.
645  */
646 int
647 smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp,
648         struct smb_cred *scred, struct smb_share **sspp)
649 {
650         struct smb_connobj *scp = NULL;
651         struct smb_share *ssp = NULL;
652         int error;
653
654         *sspp = NULL;
655         dp->scred = scred;
656         SMBCO_FOREACH(scp, VCTOCP(vcp)) {
657                 ssp = (struct smb_share *)scp;
658                 error = smb_share_lock(ssp);
659                 if (error)
660                         continue;
661                 if (smb_vc_cmpshare(ssp, dp) == 0)
662                         break;
663                 smb_share_unlock(ssp);
664         }
665         if (ssp) {
666                 smb_share_ref(ssp);
667                 *sspp = ssp;
668                 error = 0;
669         } else
670                 error = ENOENT;
671         return error;
672 }
673
674 int
675 smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred)
676 {
677
678         return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
679 }
680
681 /*
682  * Destroy VC to server, invalidate shares linked with it.
683  * Transport should be locked on entry.
684  */
685 int
686 smb_vc_disconnect(struct smb_vc *vcp)
687 {
688
689         if (vcp->vc_iod != NULL)
690                 smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT |
691                     SMBIOD_EV_SYNC, NULL);
692         return 0;
693 }
694
695 static char smb_emptypass[] = "";
696
697 const char *
698 smb_vc_getpass(struct smb_vc *vcp)
699 {
700         if (vcp->vc_pass)
701                 return vcp->vc_pass;
702         return smb_emptypass;
703 }
704
705 static int
706 smb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip)
707 {
708         bzero(vip, sizeof(struct smb_vc_info));
709         vip->itype = SMB_INFO_VC;
710         vip->usecount = vcp->obj.co_usecount;
711         vip->uid = vcp->vc_uid;
712         vip->gid = vcp->vc_grp;
713         vip->mode = vcp->vc_mode;
714         vip->flags = vcp->obj.co_flags;
715         vip->sopt = vcp->vc_sopt;
716         vip->iodstate = vcp->vc_iod->iod_state;
717         bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey));
718         snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname);
719         snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username);
720         return 0;
721 }
722
723 u_short
724 smb_vc_nextmid(struct smb_vc *vcp)
725 {
726         u_short r;
727
728         sx_xlock(&vcp->obj.co_interlock);
729         r = vcp->vc_mid++;
730         sx_unlock(&vcp->obj.co_interlock);
731         return r;
732 }
733
734 /*
735  * Share implementation
736  */
737 /*
738  * Allocate share structure and attach it to the given VC
739  * Connection expected to be locked on entry. Share will be returned
740  * in locked state.
741  */
742 int
743 smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
744         struct smb_cred *scred, struct smb_share **sspp)
745 {
746         struct smb_share *ssp;
747         struct ucred *cred = scred->scr_cred;
748         uid_t realuid = cred->cr_uid;
749         uid_t uid = shspec->owner;
750         gid_t gid = shspec->group;
751         int error, isroot;
752
753         isroot = smb_suser(cred) == 0;
754         /*
755          * Only superuser can create shares with different uid and gid
756          */
757         if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
758                 return EPERM;
759         if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
760                 return EPERM;
761         error = smb_vc_lookupshare(vcp, shspec, scred, &ssp);
762         if (!error) {
763                 smb_share_put(ssp, scred);
764                 return EEXIST;
765         }
766         if (uid == SMBM_ANY_OWNER)
767                 uid = realuid;
768         if (gid == SMBM_ANY_GROUP)
769                 gid = cred->cr_groups[0];
770         ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK);
771         smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss ilock", "smbss");
772         ssp->obj.co_free = smb_share_free;
773         ssp->obj.co_gone = smb_share_gone;
774         smb_sl_init(&ssp->ss_stlock, "ssstlock");
775         ssp->ss_name = smb_strdup(shspec->name);
776         if (shspec->pass && shspec->pass[0])
777                 ssp->ss_pass = smb_strdup(shspec->pass);
778         ssp->ss_type = shspec->stype;
779         ssp->ss_tid = SMB_TID_UNKNOWN;
780         ssp->ss_uid = uid;
781         ssp->ss_grp = gid;
782         ssp->ss_mode = shspec->rights & SMBM_MASK;
783         smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
784         *sspp = ssp;
785         return 0;
786 }
787
788 static void
789 smb_share_free(struct smb_connobj *cp)
790 {
791         struct smb_share *ssp = CPTOSS(cp);
792
793         SMB_STRFREE(ssp->ss_name);
794         SMB_STRFREE(ssp->ss_pass);
795         smb_sl_destroy(&ssp->ss_stlock);
796         smb_co_done(SSTOCP(ssp));
797         free(ssp, M_SMBCONN);
798 }
799
800 static void
801 smb_share_gone(struct smb_connobj *cp, struct smb_cred *scred)
802 {
803         struct smb_share *ssp = CPTOSS(cp);
804
805         smb_smb_treedisconnect(ssp, scred);
806 }
807
808 void
809 smb_share_ref(struct smb_share *ssp)
810 {
811         smb_co_ref(SSTOCP(ssp));
812 }
813
814 void
815 smb_share_rele(struct smb_share *ssp, struct smb_cred *scred)
816 {
817         smb_co_rele(SSTOCP(ssp), scred);
818 }
819
820 int
821 smb_share_get(struct smb_share *ssp, struct smb_cred *scred)
822 {
823         struct smb_connobj *cp = SSTOCP(ssp);
824         int error;
825
826         sx_xlock(&cp->co_interlock);
827         error = smb_co_get(cp, scred);
828         sx_unlock(&cp->co_interlock);
829         return error;
830 }
831
832 void
833 smb_share_put(struct smb_share *ssp, struct smb_cred *scred)
834 {
835
836         smb_co_put(SSTOCP(ssp), scred);
837 }
838
839 int
840 smb_share_lock(struct smb_share *ssp)
841 {
842         struct smb_connobj *cp;
843         int error;
844
845         cp = SSTOCP(ssp);
846         sx_xlock(&cp->co_interlock);
847         error = smb_co_lock(cp);
848         sx_unlock(&cp->co_interlock);
849         return error;
850 }
851
852 void
853 smb_share_unlock(struct smb_share *ssp)
854 {
855         struct smb_connobj *cp;
856
857         cp = SSTOCP(ssp);
858         sx_xlock(&cp->co_interlock);
859         smb_co_unlock(cp);
860         sx_unlock(&cp->co_interlock);
861 }
862
863 int
864 smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode)
865 {
866         struct ucred *cred = scred->scr_cred;
867
868         if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid)
869                 return 0;
870         mode >>= 3;
871         if (!groupmember(ssp->ss_grp, cred))
872                 mode >>= 3;
873         return (ssp->ss_mode & mode) == mode ? 0 : EACCES;
874 }
875
876 void
877 smb_share_invalidate(struct smb_share *ssp)
878 {
879         ssp->ss_tid = SMB_TID_UNKNOWN;
880 }
881
882 int
883 smb_share_valid(struct smb_share *ssp)
884 {
885         return ssp->ss_tid != SMB_TID_UNKNOWN &&
886             ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid;
887 }
888
889 const char*
890 smb_share_getpass(struct smb_share *ssp)
891 {
892         struct smb_vc *vcp;
893
894         if (ssp->ss_pass)
895                 return ssp->ss_pass;
896         vcp = SSTOVC(ssp);
897         if (vcp->vc_pass)
898                 return vcp->vc_pass;
899         return smb_emptypass;
900 }
901
902 static int
903 smb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip)
904 {
905         bzero(sip, sizeof(struct smb_share_info));
906         sip->itype = SMB_INFO_SHARE;
907         sip->usecount = ssp->obj.co_usecount;
908         sip->tid  = ssp->ss_tid;
909         sip->type= ssp->ss_type;
910         sip->uid = ssp->ss_uid;
911         sip->gid = ssp->ss_grp;
912         sip->mode= ssp->ss_mode;
913         sip->flags = ssp->obj.co_flags;
914         snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name);
915         return 0;
916 }
917
918 /*
919  * Dump an entire tree into sysctl call
920  */
921 static int
922 smb_sysctl_treedump(SYSCTL_HANDLER_ARGS)
923 {
924         struct smb_connobj *scp1, *scp2;
925         struct smb_vc *vcp;
926         struct smb_share *ssp;
927         struct smb_vc_info vci;
928         struct smb_share_info ssi;
929         int error, itype;
930
931         error = sysctl_wire_old_buffer(req, 0);
932         if (error)
933                 return (error);
934         error = smb_sm_lockvclist();
935         if (error)
936                 return error;
937         SMBCO_FOREACH(scp1, &smb_vclist) {
938                 vcp = (struct smb_vc *)scp1;
939                 error = smb_vc_lock(vcp);
940                 if (error)
941                         continue;
942                 smb_vc_getinfo(vcp, &vci);
943                 error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info));
944                 if (error) {
945                         smb_vc_unlock(vcp);
946                         break;
947                 }
948                 SMBCO_FOREACH(scp2, VCTOCP(vcp)) {
949                         ssp = (struct smb_share *)scp2;
950                         error = smb_share_lock(ssp);
951                         if (error) {
952                                 error = 0;
953                                 continue;
954                         }
955                         smb_share_getinfo(ssp, &ssi);
956                         smb_share_unlock(ssp);
957                         error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info));
958                         if (error)
959                                 break;
960                 }
961                 smb_vc_unlock(vcp);
962                 if (error)
963                         break;
964         }
965         if (!error) {
966                 itype = SMB_INFO_NONE;
967                 error = SYSCTL_OUT(req, &itype, sizeof(itype));
968         }
969         smb_sm_unlockvclist();
970         return error;
971 }