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