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