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