smb_conn.c revision 75374
175374Sbp/*
275374Sbp * Copyright (c) 2000-2001 Boris Popov
375374Sbp * All rights reserved.
475374Sbp *
575374Sbp * Redistribution and use in source and binary forms, with or without
675374Sbp * modification, are permitted provided that the following conditions
775374Sbp * are met:
875374Sbp * 1. Redistributions of source code must retain the above copyright
975374Sbp *    notice, this list of conditions and the following disclaimer.
1075374Sbp * 2. Redistributions in binary form must reproduce the above copyright
1175374Sbp *    notice, this list of conditions and the following disclaimer in the
1275374Sbp *    documentation and/or other materials provided with the distribution.
1375374Sbp * 3. All advertising materials mentioning features or use of this software
1475374Sbp *    must display the following acknowledgement:
1575374Sbp *    This product includes software developed by Boris Popov.
1675374Sbp * 4. Neither the name of the author nor the names of any co-contributors
1775374Sbp *    may be used to endorse or promote products derived from this software
1875374Sbp *    without specific prior written permission.
1975374Sbp *
2075374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2175374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2275374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2375374Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2475374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2575374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2675374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2775374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2875374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2975374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3075374Sbp * SUCH DAMAGE.
3175374Sbp *
3275374Sbp * $FreeBSD: head/sys/netsmb/smb_conn.c 75374 2001-04-10 07:59:06Z bp $
3375374Sbp */
3475374Sbp
3575374Sbp/*
3675374Sbp * Connection engine.
3775374Sbp */
3875374Sbp
3975374Sbp#include <sys/param.h>
4075374Sbp#include <sys/systm.h>
4175374Sbp#include <sys/kernel.h>
4275374Sbp#include <sys/malloc.h>
4375374Sbp#include <sys/proc.h>
4475374Sbp#include <sys/lock.h>
4575374Sbp#include <sys/sysctl.h>
4675374Sbp#include <sys/socketvar.h>
4775374Sbp
4875374Sbp#include <sys/iconv.h>
4975374Sbp
5075374Sbp#include <netsmb/smb.h>
5175374Sbp#include <netsmb/smb_subr.h>
5275374Sbp#include <netsmb/smb_conn.h>
5375374Sbp#include <netsmb/smb_tran.h>
5475374Sbp#include <netsmb/smb_trantcp.h>
5575374Sbp
5675374Sbpstatic struct smb_connobj smb_vclist;
5775374Sbpstatic int smb_vcnext = 1;	/* next unique id for VC */
5875374Sbp
5975374Sbpextern struct linker_set sysctl_net_smb;
6075374Sbp
6175374SbpSYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW, NULL, "SMB protocol");
6275374Sbp
6375374SbpMALLOC_DEFINE(M_SMBCONN, "SMB conn", "SMB connection");
6475374Sbp
6575374Sbpstatic void smb_co_init(struct smb_connobj *cp, int level, char *objname,
6675374Sbp	struct proc *p);
6775374Sbpstatic void smb_co_done(struct smb_connobj *cp);
6875374Sbpstatic int  smb_co_lockstatus(struct smb_connobj *cp, struct proc *p);
6975374Sbp
7075374Sbpstatic int  smb_vc_disconnect(struct smb_vc *vcp);
7175374Sbpstatic void smb_vc_free(struct smb_connobj *cp);
7275374Sbpstatic void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred);
7375374Sbpstatic smb_co_free_t smb_share_free;
7475374Sbpstatic smb_co_gone_t smb_share_gone;
7575374Sbp
7675374Sbpstatic int  smb_sysctl_treedump(SYSCTL_HANDLER_ARGS);
7775374Sbp
7875374SbpSYSCTL_PROC(_net_smb, OID_AUTO, treedump, CTLFLAG_RD | CTLTYPE_OPAQUE,
7975374Sbp	    NULL, 0, smb_sysctl_treedump, "S,treedump", "Requester tree");
8075374Sbp
8175374Sbpint
8275374Sbpsmb_sm_init(void)
8375374Sbp{
8475374Sbp
8575374Sbp	smb_co_init(&smb_vclist, SMBL_SM, "smbsm", curproc);
8675374Sbp	smb_co_unlock(&smb_vclist, 0, curproc);
8775374Sbp	return 0;
8875374Sbp}
8975374Sbp
9075374Sbpint
9175374Sbpsmb_sm_done(void)
9275374Sbp{
9375374Sbp
9475374Sbp	/* XXX: hold the mutex */
9575374Sbp	if (smb_vclist.co_usecount > 1) {
9675374Sbp		SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1);
9775374Sbp		return EBUSY;
9875374Sbp	}
9975374Sbp	smb_co_done(&smb_vclist);
10075374Sbp	return 0;
10175374Sbp}
10275374Sbp
10375374Sbpstatic int
10475374Sbpsmb_sm_lockvclist(int flags, struct proc *p)
10575374Sbp{
10675374Sbp
10775374Sbp	return smb_co_lock(&smb_vclist, flags | LK_CANRECURSE, p);
10875374Sbp}
10975374Sbp
11075374Sbpstatic void
11175374Sbpsmb_sm_unlockvclist(struct proc *p)
11275374Sbp{
11375374Sbp
11475374Sbp	smb_co_unlock(&smb_vclist, LK_RELEASE, p);
11575374Sbp}
11675374Sbp
11775374Sbpstatic int
11875374Sbpsmb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
11975374Sbp	struct smb_cred *scred,	struct smb_vc **vcpp)
12075374Sbp{
12175374Sbp	struct proc *p = scred->scr_p;
12275374Sbp	struct smb_vc *vcp;
12375374Sbp	int exact = 1;
12475374Sbp	int error;
12575374Sbp
12675374Sbp	vcspec->shspec = shspec;
12775374Sbp	error = ENOENT;
12875374Sbp	SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) {
12975374Sbp		error = smb_vc_lock(vcp, LK_EXCLUSIVE, p);
13075374Sbp		if (error)
13175374Sbp			continue;
13275374Sbp		itry {
13375374Sbp			if ((vcp->obj.co_flags & SMBV_PRIVATE) ||
13475374Sbp			    !CONNADDREQ(vcp->vc_paddr, vcspec->sap) ||
13575374Sbp			    strcmp(vcp->vc_username, vcspec->username) != 0)
13675374Sbp				ithrow(1);
13775374Sbp			if (vcspec->owner != SMBM_ANY_OWNER) {
13875374Sbp				if (vcp->vc_uid != vcspec->owner)
13975374Sbp					ithrow(1);
14075374Sbp			} else
14175374Sbp				exact = 0;
14275374Sbp			if (vcspec->group != SMBM_ANY_GROUP) {
14375374Sbp				if (vcp->vc_grp != vcspec->group)
14475374Sbp					ithrow(1);
14575374Sbp			} else
14675374Sbp				exact = 0;
14775374Sbp
14875374Sbp			if (vcspec->mode & SMBM_EXACT) {
14975374Sbp				if (!exact ||
15075374Sbp				    (vcspec->mode & SMBM_MASK) != vcp->vc_mode)
15175374Sbp					ithrow(1);
15275374Sbp			}
15375374Sbp			if (smb_vc_access(vcp, scred, vcspec->mode) != 0)
15475374Sbp				ithrow(1);
15575374Sbp			vcspec->ssp = NULL;
15675374Sbp			if (shspec)
15775374Sbp				ithrow(smb_vc_lookupshare(vcp, shspec, scred, &vcspec->ssp));
15875374Sbp			error = 0;
15975374Sbp			break;
16075374Sbp		} icatch(error) {
16175374Sbp			smb_vc_unlock(vcp, 0, p);
16275374Sbp		} ifinally {
16375374Sbp		} iendtry;
16475374Sbp		if (error == 0)
16575374Sbp			break;
16675374Sbp	}
16775374Sbp	if (vcp) {
16875374Sbp		smb_vc_ref(vcp, p);
16975374Sbp		*vcpp = vcp;
17075374Sbp	}
17175374Sbp	return error;
17275374Sbp}
17375374Sbp
17475374Sbpint
17575374Sbpsmb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
17675374Sbp	struct smb_cred *scred,	struct smb_vc **vcpp)
17775374Sbp{
17875374Sbp	struct proc *p = scred->scr_p;
17975374Sbp	struct smb_vc *vcp;
18075374Sbp	struct smb_share *ssp = NULL;
18175374Sbp	int error;
18275374Sbp
18375374Sbp	*vcpp = vcp = NULL;
18475374Sbp
18575374Sbp	error = smb_sm_lockvclist(LK_EXCLUSIVE, p);
18675374Sbp	if (error)
18775374Sbp		return error;
18875374Sbp	error = smb_sm_lookupint(vcspec, shspec, scred, vcpp);
18975374Sbp	if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) {
19075374Sbp		smb_sm_unlockvclist(p);
19175374Sbp		return error;
19275374Sbp	}
19375374Sbp	error = smb_sm_lookupint(vcspec, NULL, scred, &vcp);
19475374Sbp	if (error) {
19575374Sbp		error = smb_vc_create(vcspec, scred, &vcp);
19675374Sbp		if (error)
19775374Sbp			goto out;
19875374Sbp		error = smb_vc_connect(vcp, scred);
19975374Sbp		if (error)
20075374Sbp			goto out;
20175374Sbp	}
20275374Sbp	if (shspec == NULL)
20375374Sbp		goto out;
20475374Sbp	error = smb_share_create(vcp, shspec, scred, &ssp);
20575374Sbp	if (error)
20675374Sbp		goto out;
20775374Sbp	error = smb_smb_treeconnect(ssp, scred);
20875374Sbp	if (error == 0)
20975374Sbp		vcspec->ssp = ssp;
21075374Sbp	else
21175374Sbp		smb_share_put(ssp, scred);
21275374Sbpout:
21375374Sbp	smb_sm_unlockvclist(p);
21475374Sbp	if (error == 0)
21575374Sbp		*vcpp = vcp;
21675374Sbp	else if (vcp)
21775374Sbp		smb_vc_put(vcp, scred);
21875374Sbp	return error;
21975374Sbp}
22075374Sbp
22175374Sbp/*
22275374Sbp * Common code for connection object
22375374Sbp */
22475374Sbpstatic void
22575374Sbpsmb_co_init(struct smb_connobj *cp, int level, char *objname, struct proc *p)
22675374Sbp{
22775374Sbp	SLIST_INIT(&cp->co_children);
22875374Sbp	smb_sl_init(&cp->co_interlock, objname);
22975374Sbp	lockinit(&cp->co_lock, PZERO, objname, 0, 0);
23075374Sbp	cp->co_level = level;
23175374Sbp	cp->co_usecount = 1;
23275374Sbp	KASSERT(smb_co_lock(cp, LK_EXCLUSIVE, p) == 0, ("smb_co_init: lock failed"));
23375374Sbp}
23475374Sbp
23575374Sbpstatic void
23675374Sbpsmb_co_done(struct smb_connobj *cp)
23775374Sbp{
23875374Sbp	smb_sl_destroy(&cp->co_interlock);
23975374Sbp	lockdestroy(&cp->co_lock);
24075374Sbp}
24175374Sbp
24275374Sbpstatic void
24375374Sbpsmb_co_gone(struct smb_connobj *cp, struct smb_cred *scred)
24475374Sbp{
24575374Sbp	struct smb_connobj *parent;
24675374Sbp
24775374Sbp	if (cp->co_gone)
24875374Sbp		cp->co_gone(cp, scred);
24975374Sbp	parent = cp->co_parent;
25075374Sbp	if (parent) {
25175374Sbp		smb_co_lock(parent, LK_EXCLUSIVE, scred->scr_p);
25275374Sbp		SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next);
25375374Sbp		smb_co_put(parent, scred);
25475374Sbp	}
25575374Sbp	if (cp->co_free)
25675374Sbp		cp->co_free(cp);
25775374Sbp}
25875374Sbp
25975374Sbpvoid
26075374Sbpsmb_co_ref(struct smb_connobj *cp, struct proc *p)
26175374Sbp{
26275374Sbp
26375374Sbp	SMB_CO_LOCK(cp);
26475374Sbp	cp->co_usecount++;
26575374Sbp	SMB_CO_UNLOCK(cp);
26675374Sbp}
26775374Sbp
26875374Sbpvoid
26975374Sbpsmb_co_rele(struct smb_connobj *cp, struct smb_cred *scred)
27075374Sbp{
27175374Sbp	struct proc *p = scred->scr_p;
27275374Sbp
27375374Sbp	SMB_CO_LOCK(cp);
27475374Sbp	if (cp->co_usecount > 1) {
27575374Sbp		cp->co_usecount--;
27675374Sbp		SMB_CO_UNLOCK(cp);
27775374Sbp		return;
27875374Sbp	}
27975374Sbp	if (cp->co_usecount == 0) {
28075374Sbp		SMBERROR("negative use_count for object %d", cp->co_level);
28175374Sbp		SMB_CO_UNLOCK(cp);
28275374Sbp		return;
28375374Sbp	}
28475374Sbp	cp->co_usecount--;
28575374Sbp	cp->co_flags |= SMBO_GONE;
28675374Sbp
28775374Sbp	lockmgr(&cp->co_lock, LK_DRAIN | LK_INTERLOCK, &cp->co_interlock, p);
28875374Sbp	smb_co_gone(cp, scred);
28975374Sbp}
29075374Sbp
29175374Sbpint
29275374Sbpsmb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred)
29375374Sbp{
29475374Sbp	int error;
29575374Sbp
29675374Sbp	if ((flags & LK_INTERLOCK) == 0)
29775374Sbp		SMB_CO_LOCK(cp);
29875374Sbp	cp->co_usecount++;
29975374Sbp	error = smb_co_lock(cp, flags | LK_INTERLOCK, scred->scr_p);
30075374Sbp	if (error) {
30175374Sbp		SMB_CO_LOCK(cp);
30275374Sbp		cp->co_usecount--;
30375374Sbp		SMB_CO_UNLOCK(cp);
30475374Sbp		return error;
30575374Sbp	}
30675374Sbp	return 0;
30775374Sbp}
30875374Sbp
30975374Sbpvoid
31075374Sbpsmb_co_put(struct smb_connobj *cp, struct smb_cred *scred)
31175374Sbp{
31275374Sbp	struct proc *p = scred->scr_p;
31375374Sbp	int flags;
31475374Sbp
31575374Sbp	flags = LK_RELEASE;
31675374Sbp	SMB_CO_LOCK(cp);
31775374Sbp	if (cp->co_usecount > 1) {
31875374Sbp		cp->co_usecount--;
31975374Sbp	} else if (cp->co_usecount == 1) {
32075374Sbp		cp->co_usecount--;
32175374Sbp		cp->co_flags |= SMBO_GONE;
32275374Sbp		flags = LK_DRAIN;
32375374Sbp	} else {
32475374Sbp		SMBERROR("negative usecount");
32575374Sbp	}
32675374Sbp	lockmgr(&cp->co_lock, LK_RELEASE | LK_INTERLOCK, &cp->co_interlock, p);
32775374Sbp	if ((cp->co_flags & SMBO_GONE) == 0)
32875374Sbp		return;
32975374Sbp	lockmgr(&cp->co_lock, LK_DRAIN, NULL, p);
33075374Sbp	smb_co_gone(cp, scred);
33175374Sbp}
33275374Sbp
33375374Sbpint
33475374Sbpsmb_co_lockstatus(struct smb_connobj *cp, struct proc *p)
33575374Sbp{
33675374Sbp	return lockstatus(&cp->co_lock, p);
33775374Sbp}
33875374Sbp
33975374Sbpint
34075374Sbpsmb_co_lock(struct smb_connobj *cp, int flags, struct proc *p)
34175374Sbp{
34275374Sbp
34375374Sbp	if (cp->co_flags & SMBO_GONE)
34475374Sbp		return EINVAL;
34575374Sbp	if ((flags & LK_TYPE_MASK) == 0)
34675374Sbp		flags |= LK_EXCLUSIVE;
34775374Sbp	if (smb_co_lockstatus(cp, p) == LK_EXCLUSIVE &&
34875374Sbp	    (flags & LK_CANRECURSE) == 0) {
34975374Sbp		SMBERROR("recursive lock for object %d\n", cp->co_level);
35075374Sbp		return 0;
35175374Sbp	}
35275374Sbp	return lockmgr(&cp->co_lock, flags, &cp->co_interlock, p);
35375374Sbp}
35475374Sbp
35575374Sbpvoid
35675374Sbpsmb_co_unlock(struct smb_connobj *cp, int flags, struct proc *p)
35775374Sbp{
35875374Sbp	(void)lockmgr(&cp->co_lock, flags | LK_RELEASE, &cp->co_interlock, p);
35975374Sbp}
36075374Sbp
36175374Sbpstatic void
36275374Sbpsmb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child)
36375374Sbp{
36475374Sbp	KASSERT(smb_co_lockstatus(parent, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: parent not locked"));
36575374Sbp	KASSERT(smb_co_lockstatus(child, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: child not locked"));
36675374Sbp
36775374Sbp	smb_co_ref(parent, curproc);
36875374Sbp	SLIST_INSERT_HEAD(&parent->co_children, child, co_next);
36975374Sbp	child->co_parent = parent;
37075374Sbp}
37175374Sbp
37275374Sbp/*
37375374Sbp * Session implementation
37475374Sbp */
37575374Sbp
37675374Sbpint
37775374Sbpsmb_vc_create(struct smb_vcspec *vcspec,
37875374Sbp	struct smb_cred *scred, struct smb_vc **vcpp)
37975374Sbp{
38075374Sbp	struct smb_vc *vcp;
38175374Sbp	struct proc *p = scred->scr_p;
38275374Sbp	struct ucred *cred = scred->scr_cred;
38375374Sbp	uid_t uid = vcspec->owner;
38475374Sbp	gid_t gid = vcspec->group;
38575374Sbp	uid_t realuid = cred->cr_uid;
38675374Sbp	char *domain = vcspec->domain;
38775374Sbp	int error, isroot;
38875374Sbp
38975374Sbp	isroot = smb_suser(cred) == 0;
39075374Sbp	/*
39175374Sbp	 * Only superuser can create VCs with different uid and gid
39275374Sbp	 */
39375374Sbp	if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
39475374Sbp		return EPERM;
39575374Sbp	if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
39675374Sbp		return EPERM;
39775374Sbp
39875374Sbp	vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK);
39975374Sbp	smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc", p);
40075374Sbp	vcp->obj.co_free = smb_vc_free;
40175374Sbp	vcp->obj.co_gone = smb_vc_gone;
40275374Sbp	vcp->vc_number = smb_vcnext++;
40375374Sbp	vcp->vc_timo = SMB_DEFRQTIMO;
40475374Sbp	vcp->vc_smbuid = SMB_UID_UNKNOWN;
40575374Sbp	vcp->vc_mode = vcspec->rights & SMBM_MASK;
40675374Sbp	vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE);
40775374Sbp	vcp->vc_tdesc = &smb_tran_nbtcp_desc;
40875374Sbp
40975374Sbp	if (uid == SMBM_ANY_OWNER)
41075374Sbp		uid = realuid;
41175374Sbp	if (gid == SMBM_ANY_GROUP)
41275374Sbp		gid = cred->cr_groups[0];
41375374Sbp	vcp->vc_uid = uid;
41475374Sbp	vcp->vc_grp = gid;
41575374Sbp
41675374Sbp	smb_sl_init(&vcp->vc_stlock, "vcstlock");
41775374Sbp	error = 0;
41875374Sbp	itry {
41975374Sbp		vcp->vc_paddr = dup_sockaddr(vcspec->sap, 1);
42075374Sbp		ierror(vcp->vc_paddr == NULL, ENOMEM);
42175374Sbp
42275374Sbp		vcp->vc_laddr = dup_sockaddr(vcspec->lap, 1);
42375374Sbp		ierror(vcp->vc_laddr == NULL, ENOMEM);
42475374Sbp
42575374Sbp		ierror((vcp->vc_pass = smb_strdup(vcspec->pass)) == NULL, ENOMEM);
42675374Sbp
42775374Sbp		vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : "NODOMAIN");
42875374Sbp		ierror(vcp->vc_domain == NULL, ENOMEM);
42975374Sbp
43075374Sbp		ierror((vcp->vc_srvname = smb_strdup(vcspec->srvname)) == NULL, ENOMEM);
43175374Sbp		ierror((vcp->vc_username = smb_strdup(vcspec->username)) == NULL, ENOMEM);
43275374Sbp
43375374Sbp		ithrow(iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower));
43475374Sbp		ithrow(iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper));
43575374Sbp		if (vcspec->servercs[0]) {
43675374Sbp			ithrow(iconv_open(vcspec->servercs, vcspec->localcs,
43775374Sbp			    &vcp->vc_toserver));
43875374Sbp			ithrow(iconv_open(vcspec->localcs, vcspec->servercs,
43975374Sbp			    &vcp->vc_tolocal));
44075374Sbp		}
44175374Sbp
44275374Sbp		ithrow(smb_iod_create(vcp));
44375374Sbp		*vcpp = vcp;
44475374Sbp		smb_co_addchild(&smb_vclist, VCTOCP(vcp));
44575374Sbp	} icatch(error) {
44675374Sbp		smb_vc_put(vcp, scred);
44775374Sbp	} ifinally {
44875374Sbp	} iendtry;
44975374Sbp	return error;
45075374Sbp}
45175374Sbp
45275374Sbpstatic void
45375374Sbpsmb_vc_free(struct smb_connobj *cp)
45475374Sbp{
45575374Sbp	struct smb_vc *vcp = CPTOVC(cp);
45675374Sbp
45775374Sbp	if (vcp->vc_iod)
45875374Sbp		smb_iod_destroy(vcp->vc_iod);
45975374Sbp	SMB_STRFREE(vcp->vc_username);
46075374Sbp	SMB_STRFREE(vcp->vc_srvname);
46175374Sbp	SMB_STRFREE(vcp->vc_pass);
46275374Sbp	SMB_STRFREE(vcp->vc_domain);
46375374Sbp	if (vcp->vc_paddr)
46475374Sbp		free(vcp->vc_paddr, M_SONAME);
46575374Sbp	if (vcp->vc_laddr)
46675374Sbp		free(vcp->vc_laddr, M_SONAME);
46775374Sbp	if (vcp->vc_tolower)
46875374Sbp		iconv_close(vcp->vc_tolower);
46975374Sbp	if (vcp->vc_toupper)
47075374Sbp		iconv_close(vcp->vc_toupper);
47175374Sbp	if (vcp->vc_tolocal)
47275374Sbp		iconv_close(vcp->vc_tolocal);
47375374Sbp	if (vcp->vc_toserver)
47475374Sbp		iconv_close(vcp->vc_toserver);
47575374Sbp	smb_co_done(VCTOCP(vcp));
47675374Sbp	smb_sl_destroy(&vcp->vc_stlock);
47775374Sbp	free(vcp, M_SMBCONN);
47875374Sbp}
47975374Sbp
48075374Sbp/*
48175374Sbp * Called when use count of VC dropped to zero.
48275374Sbp * VC should be locked on enter with LK_DRAIN.
48375374Sbp */
48475374Sbpstatic void
48575374Sbpsmb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred)
48675374Sbp{
48775374Sbp	struct smb_vc *vcp = CPTOVC(cp);
48875374Sbp
48975374Sbp	smb_vc_disconnect(vcp);
49075374Sbp}
49175374Sbp
49275374Sbpvoid
49375374Sbpsmb_vc_ref(struct smb_vc *vcp, struct proc *p)
49475374Sbp{
49575374Sbp	smb_co_ref(VCTOCP(vcp), p);
49675374Sbp}
49775374Sbp
49875374Sbpvoid
49975374Sbpsmb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred)
50075374Sbp{
50175374Sbp	smb_co_rele(VCTOCP(vcp), scred);
50275374Sbp}
50375374Sbp
50475374Sbpint
50575374Sbpsmb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred)
50675374Sbp{
50775374Sbp	return smb_co_get(VCTOCP(vcp), flags, scred);
50875374Sbp}
50975374Sbp
51075374Sbpvoid
51175374Sbpsmb_vc_put(struct smb_vc *vcp, struct smb_cred *scred)
51275374Sbp{
51375374Sbp	smb_co_put(VCTOCP(vcp), scred);
51475374Sbp}
51575374Sbp
51675374Sbpint
51775374Sbpsmb_vc_lock(struct smb_vc *vcp, int flags, struct proc *p)
51875374Sbp{
51975374Sbp	return smb_co_lock(VCTOCP(vcp), flags, p);
52075374Sbp}
52175374Sbp
52275374Sbpvoid
52375374Sbpsmb_vc_unlock(struct smb_vc *vcp, int flags, struct proc *p)
52475374Sbp{
52575374Sbp	smb_co_unlock(VCTOCP(vcp), flags, p);
52675374Sbp}
52775374Sbp
52875374Sbpint
52975374Sbpsmb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode)
53075374Sbp{
53175374Sbp	struct ucred *cred = scred->scr_cred;
53275374Sbp
53375374Sbp	if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid)
53475374Sbp		return 0;
53575374Sbp	mode >>= 3;
53675374Sbp	if (!groupmember(vcp->vc_grp, cred))
53775374Sbp		mode >>= 3;
53875374Sbp	return (vcp->vc_mode & mode) == mode ? 0 : EACCES;
53975374Sbp}
54075374Sbp
54175374Sbpstatic int
54275374Sbpsmb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp)
54375374Sbp{
54475374Sbp	int exact = 1;
54575374Sbp
54675374Sbp	if (strcmp(ssp->ss_name, dp->name) != 0)
54775374Sbp		return 1;
54875374Sbp	if (dp->owner != SMBM_ANY_OWNER) {
54975374Sbp		if (ssp->ss_uid != dp->owner)
55075374Sbp			return 1;
55175374Sbp	} else
55275374Sbp		exact = 0;
55375374Sbp	if (dp->group != SMBM_ANY_GROUP) {
55475374Sbp		if (ssp->ss_grp != dp->group)
55575374Sbp			return 1;
55675374Sbp	} else
55775374Sbp		exact = 0;
55875374Sbp
55975374Sbp	if (dp->mode & SMBM_EXACT) {
56075374Sbp		if (!exact)
56175374Sbp			return 1;
56275374Sbp		return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1;
56375374Sbp	}
56475374Sbp	if (smb_share_access(ssp, dp->scred, dp->mode) != 0)
56575374Sbp		return 1;
56675374Sbp	return 0;
56775374Sbp}
56875374Sbp
56975374Sbp/*
57075374Sbp * Lookup share in the given VC. Share referenced and locked on return.
57175374Sbp * VC expected to be locked on entry and will be left locked on exit.
57275374Sbp */
57375374Sbpint
57475374Sbpsmb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp,
57575374Sbp	struct smb_cred *scred,	struct smb_share **sspp)
57675374Sbp{
57775374Sbp	struct proc *p = scred->scr_p;
57875374Sbp	struct smb_share *ssp = NULL;
57975374Sbp	int error;
58075374Sbp
58175374Sbp	*sspp = NULL;
58275374Sbp	dp->scred = scred;
58375374Sbp	SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) {
58475374Sbp		error = smb_share_lock(ssp, LK_EXCLUSIVE, p);
58575374Sbp		if (error)
58675374Sbp			continue;
58775374Sbp		if (smb_vc_cmpshare(ssp, dp) == 0)
58875374Sbp			break;
58975374Sbp		smb_share_unlock(ssp, 0, p);
59075374Sbp	}
59175374Sbp	if (ssp) {
59275374Sbp		smb_share_ref(ssp, p);
59375374Sbp		*sspp = ssp;
59475374Sbp		error = 0;
59575374Sbp	} else
59675374Sbp		error = ENOENT;
59775374Sbp	return error;
59875374Sbp}
59975374Sbp
60075374Sbpint
60175374Sbpsmb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred)
60275374Sbp{
60375374Sbp
60475374Sbp	return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
60575374Sbp}
60675374Sbp
60775374Sbp/*
60875374Sbp * Destroy VC to server, invalidate shares linked with it.
60975374Sbp * Transport should be locked on entry.
61075374Sbp */
61175374Sbpint
61275374Sbpsmb_vc_disconnect(struct smb_vc *vcp)
61375374Sbp{
61475374Sbp
61575374Sbp	smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL);
61675374Sbp	return 0;
61775374Sbp}
61875374Sbp
61975374Sbpstatic char smb_emptypass[] = "";
62075374Sbp
62175374Sbpconst char *
62275374Sbpsmb_vc_getpass(struct smb_vc *vcp)
62375374Sbp{
62475374Sbp	if (vcp->vc_pass)
62575374Sbp		return vcp->vc_pass;
62675374Sbp	return smb_emptypass;
62775374Sbp}
62875374Sbp
62975374Sbpstatic int
63075374Sbpsmb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip)
63175374Sbp{
63275374Sbp	bzero(vip, sizeof(struct smb_vc_info));
63375374Sbp	vip->itype = SMB_INFO_VC;
63475374Sbp	vip->usecount = vcp->obj.co_usecount;
63575374Sbp	vip->uid = vcp->vc_uid;
63675374Sbp	vip->gid = vcp->vc_grp;
63775374Sbp	vip->mode = vcp->vc_mode;
63875374Sbp	vip->flags = vcp->obj.co_flags;
63975374Sbp	vip->sopt = vcp->vc_sopt;
64075374Sbp	vip->iodstate = vcp->vc_iod->iod_state;
64175374Sbp	bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey));
64275374Sbp	snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname);
64375374Sbp	snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username);
64475374Sbp	return 0;
64575374Sbp}
64675374Sbp
64775374Sbpu_short
64875374Sbpsmb_vc_nextmid(struct smb_vc *vcp)
64975374Sbp{
65075374Sbp	u_short r;
65175374Sbp
65275374Sbp	SMB_CO_LOCK(&vcp->obj);
65375374Sbp	r = vcp->vc_mid++;
65475374Sbp	SMB_CO_UNLOCK(&vcp->obj);
65575374Sbp	return r;
65675374Sbp}
65775374Sbp
65875374Sbp/*
65975374Sbp * Share implementation
66075374Sbp */
66175374Sbp/*
66275374Sbp * Allocate share structure and attach it to the given VC
66375374Sbp * Connection expected to be locked on entry. Share will be returned
66475374Sbp * in locked state.
66575374Sbp */
66675374Sbpint
66775374Sbpsmb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
66875374Sbp	struct smb_cred *scred, struct smb_share **sspp)
66975374Sbp{
67075374Sbp	struct smb_share *ssp;
67175374Sbp	struct proc *p = scred->scr_p;
67275374Sbp	struct ucred *cred = scred->scr_cred;
67375374Sbp	uid_t realuid = cred->cr_uid;
67475374Sbp	uid_t uid = shspec->owner;
67575374Sbp	gid_t gid = shspec->group;
67675374Sbp	int error, isroot;
67775374Sbp
67875374Sbp	isroot = smb_suser(cred) == 0;
67975374Sbp	/*
68075374Sbp	 * Only superuser can create shares with different uid and gid
68175374Sbp	 */
68275374Sbp	if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
68375374Sbp		return EPERM;
68475374Sbp	if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
68575374Sbp		return EPERM;
68675374Sbp	error = smb_vc_lookupshare(vcp, shspec, scred, &ssp);
68775374Sbp	if (!error) {
68875374Sbp		smb_share_put(ssp, scred);
68975374Sbp		return EEXIST;
69075374Sbp	}
69175374Sbp	if (uid == SMBM_ANY_OWNER)
69275374Sbp		uid = realuid;
69375374Sbp	if (gid == SMBM_ANY_GROUP)
69475374Sbp		gid = cred->cr_groups[0];
69575374Sbp	ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK);
69675374Sbp	smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss", p);
69775374Sbp	ssp->obj.co_free = smb_share_free;
69875374Sbp	ssp->obj.co_gone = smb_share_gone;
69975374Sbp	smb_sl_init(&ssp->ss_stlock, "ssstlock");
70075374Sbp	ssp->ss_name = smb_strdup(shspec->name);
70175374Sbp	if (shspec->pass && shspec->pass[0])
70275374Sbp		ssp->ss_pass = smb_strdup(shspec->pass);
70375374Sbp	ssp->ss_type = shspec->stype;
70475374Sbp	ssp->ss_tid = SMB_TID_UNKNOWN;
70575374Sbp	ssp->ss_uid = uid;
70675374Sbp	ssp->ss_grp = gid;
70775374Sbp	ssp->ss_mode = shspec->rights & SMBM_MASK;
70875374Sbp	smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
70975374Sbp	*sspp = ssp;
71075374Sbp	return 0;
71175374Sbp}
71275374Sbp
71375374Sbpstatic void
71475374Sbpsmb_share_free(struct smb_connobj *cp)
71575374Sbp{
71675374Sbp	struct smb_share *ssp = CPTOSS(cp);
71775374Sbp
71875374Sbp	SMB_STRFREE(ssp->ss_name);
71975374Sbp	SMB_STRFREE(ssp->ss_pass);
72075374Sbp	smb_sl_destroy(&ssp->ss_stlock);
72175374Sbp	smb_co_done(SSTOCP(ssp));
72275374Sbp	free(ssp, M_SMBCONN);
72375374Sbp}
72475374Sbp
72575374Sbpstatic void
72675374Sbpsmb_share_gone(struct smb_connobj *cp, struct smb_cred *scred)
72775374Sbp{
72875374Sbp	struct smb_share *ssp = CPTOSS(cp);
72975374Sbp
73075374Sbp	smb_smb_treedisconnect(ssp, scred);
73175374Sbp}
73275374Sbp
73375374Sbpvoid
73475374Sbpsmb_share_ref(struct smb_share *ssp, struct proc *p)
73575374Sbp{
73675374Sbp	smb_co_ref(SSTOCP(ssp), p);
73775374Sbp}
73875374Sbp
73975374Sbpvoid
74075374Sbpsmb_share_rele(struct smb_share *ssp, struct smb_cred *scred)
74175374Sbp{
74275374Sbp	smb_co_rele(SSTOCP(ssp), scred);
74375374Sbp}
74475374Sbp
74575374Sbpint
74675374Sbpsmb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred)
74775374Sbp{
74875374Sbp	return smb_co_get(SSTOCP(ssp), flags, scred);
74975374Sbp}
75075374Sbp
75175374Sbpvoid
75275374Sbpsmb_share_put(struct smb_share *ssp, struct smb_cred *scred)
75375374Sbp{
75475374Sbp	smb_co_put(SSTOCP(ssp), scred);
75575374Sbp}
75675374Sbp
75775374Sbpint
75875374Sbpsmb_share_lock(struct smb_share *ssp, int flags, struct proc *p)
75975374Sbp{
76075374Sbp	return smb_co_lock(SSTOCP(ssp), flags, p);
76175374Sbp}
76275374Sbp
76375374Sbpvoid
76475374Sbpsmb_share_unlock(struct smb_share *ssp, int flags, struct proc *p)
76575374Sbp{
76675374Sbp	smb_co_unlock(SSTOCP(ssp), flags, p);
76775374Sbp}
76875374Sbp
76975374Sbpint
77075374Sbpsmb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode)
77175374Sbp{
77275374Sbp	struct ucred *cred = scred->scr_cred;
77375374Sbp
77475374Sbp	if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid)
77575374Sbp		return 0;
77675374Sbp	mode >>= 3;
77775374Sbp	if (!groupmember(ssp->ss_grp, cred))
77875374Sbp		mode >>= 3;
77975374Sbp	return (ssp->ss_mode & mode) == mode ? 0 : EACCES;
78075374Sbp}
78175374Sbp
78275374Sbpvoid
78375374Sbpsmb_share_invalidate(struct smb_share *ssp)
78475374Sbp{
78575374Sbp	ssp->ss_tid = SMB_TID_UNKNOWN;
78675374Sbp}
78775374Sbp
78875374Sbpint
78975374Sbpsmb_share_valid(struct smb_share *ssp)
79075374Sbp{
79175374Sbp	return ssp->ss_tid != SMB_TID_UNKNOWN &&
79275374Sbp	    ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid;
79375374Sbp}
79475374Sbp
79575374Sbpconst char*
79675374Sbpsmb_share_getpass(struct smb_share *ssp)
79775374Sbp{
79875374Sbp	struct smb_vc *vcp;
79975374Sbp
80075374Sbp	if (ssp->ss_pass)
80175374Sbp		return ssp->ss_pass;
80275374Sbp	vcp = SSTOVC(ssp);
80375374Sbp	if (vcp->vc_pass)
80475374Sbp		return vcp->vc_pass;
80575374Sbp	return smb_emptypass;
80675374Sbp}
80775374Sbp
80875374Sbpstatic int
80975374Sbpsmb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip)
81075374Sbp{
81175374Sbp	bzero(sip, sizeof(struct smb_share_info));
81275374Sbp	sip->itype = SMB_INFO_SHARE;
81375374Sbp	sip->usecount = ssp->obj.co_usecount;
81475374Sbp	sip->tid  = ssp->ss_tid;
81575374Sbp	sip->type= ssp->ss_type;
81675374Sbp	sip->uid = ssp->ss_uid;
81775374Sbp	sip->gid = ssp->ss_grp;
81875374Sbp	sip->mode= ssp->ss_mode;
81975374Sbp	sip->flags = ssp->obj.co_flags;
82075374Sbp	snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name);
82175374Sbp	return 0;
82275374Sbp}
82375374Sbp
82475374Sbp/*
82575374Sbp * Dump an entire tree into sysctl call
82675374Sbp */
82775374Sbpstatic int
82875374Sbpsmb_sysctl_treedump(SYSCTL_HANDLER_ARGS)
82975374Sbp{
83075374Sbp	struct proc *p = req->p;
83175374Sbp	struct smb_cred scred;
83275374Sbp	struct smb_vc *vcp;
83375374Sbp	struct smb_share *ssp;
83475374Sbp	struct smb_vc_info vci;
83575374Sbp	struct smb_share_info ssi;
83675374Sbp	int error, itype;
83775374Sbp
83875374Sbp	smb_makescred(&scred, p, p->p_ucred);
83975374Sbp	error = smb_sm_lockvclist(LK_SHARED, p);
84075374Sbp	if (error)
84175374Sbp		return error;
84275374Sbp	SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) {
84375374Sbp		error = smb_vc_lock(vcp, LK_SHARED, p);
84475374Sbp		if (error)
84575374Sbp			continue;
84675374Sbp		smb_vc_getinfo(vcp, &vci);
84775374Sbp		error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info));
84875374Sbp		if (error) {
84975374Sbp			smb_vc_unlock(vcp, 0, p);
85075374Sbp			break;
85175374Sbp		}
85275374Sbp		SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) {
85375374Sbp			error = smb_share_lock(ssp, LK_SHARED, p);
85475374Sbp			if (error) {
85575374Sbp				error = 0;
85675374Sbp				continue;
85775374Sbp			}
85875374Sbp			smb_share_getinfo(ssp, &ssi);
85975374Sbp			smb_share_unlock(ssp, 0, p);
86075374Sbp			error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info));
86175374Sbp			if (error)
86275374Sbp				break;
86375374Sbp		}
86475374Sbp		smb_vc_unlock(vcp, 0, p);
86575374Sbp		if (error)
86675374Sbp			break;
86775374Sbp	}
86875374Sbp	if (!error) {
86975374Sbp		itype = SMB_INFO_NONE;
87075374Sbp		error = SYSCTL_OUT(req, &itype, sizeof(itype));
87175374Sbp	}
87275374Sbp	smb_sm_unlockvclist(p);
87375374Sbp	return error;
87475374Sbp}
875