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