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