smb_conn.c revision 184568
1139823Simp/*- 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 3375374Sbp/* 3475374Sbp * Connection engine. 3575374Sbp */ 3675374Sbp 37116189Sobrien#include <sys/cdefs.h> 38116189Sobrien__FBSDID("$FreeBSD: head/sys/netsmb/smb_conn.c 184568 2008-11-02 20:22:24Z rwatson $"); 39116189Sobrien 4075374Sbp#include <sys/param.h> 4175374Sbp#include <sys/systm.h> 4275374Sbp#include <sys/kernel.h> 4375374Sbp#include <sys/malloc.h> 44164033Srwatson#include <sys/priv.h> 4575374Sbp#include <sys/proc.h> 4675374Sbp#include <sys/lock.h> 4775374Sbp#include <sys/sysctl.h> 4875374Sbp#include <sys/socketvar.h> 4975374Sbp 5075374Sbp#include <sys/iconv.h> 5175374Sbp 5275374Sbp#include <netsmb/smb.h> 5375374Sbp#include <netsmb/smb_subr.h> 5475374Sbp#include <netsmb/smb_conn.h> 5575374Sbp#include <netsmb/smb_tran.h> 5675374Sbp#include <netsmb/smb_trantcp.h> 5775374Sbp 5875374Sbpstatic struct smb_connobj smb_vclist; 5975374Sbpstatic int smb_vcnext = 1; /* next unique id for VC */ 6075374Sbp 6175374SbpSYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW, NULL, "SMB protocol"); 6275374Sbp 63151897SrwatsonMALLOC_DEFINE(M_SMBCONN, "smb_conn", "SMB connection"); 6475374Sbp 65176518Sattiliostatic void smb_co_init(struct smb_connobj *cp, int level, char *ilockname, 66176518Sattilio char *lockname, struct thread *td); 6775374Sbpstatic void smb_co_done(struct smb_connobj *cp); 6887192Sbpstatic int smb_co_lockstatus(struct smb_connobj *cp, struct thread *td); 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 85176518Sattilio smb_co_init(&smb_vclist, SMBL_SM, "smbsm ilock", "smbsm", curthread); 8687192Sbp smb_co_unlock(&smb_vclist, 0, curthread); 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 } 99175635Sattilio lockmgr(&smb_vclist.co_lock, LK_DRAIN, 0); 10075374Sbp smb_co_done(&smb_vclist); 10175374Sbp return 0; 10275374Sbp} 10375374Sbp 10475374Sbpstatic int 10587192Sbpsmb_sm_lockvclist(int flags, struct thread *td) 10675374Sbp{ 10775374Sbp 10887192Sbp return smb_co_lock(&smb_vclist, flags | LK_CANRECURSE, td); 10975374Sbp} 11075374Sbp 11175374Sbpstatic void 11287192Sbpsmb_sm_unlockvclist(struct thread *td) 11375374Sbp{ 11475374Sbp 11587192Sbp smb_co_unlock(&smb_vclist, LK_RELEASE, td); 11675374Sbp} 11775374Sbp 11875374Sbpstatic int 11975374Sbpsmb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, 12075374Sbp struct smb_cred *scred, struct smb_vc **vcpp) 12175374Sbp{ 12287192Sbp struct thread *td = scred->scr_td; 123132780Skan struct smb_connobj *scp; 12475374Sbp struct smb_vc *vcp; 12575374Sbp int exact = 1; 12675374Sbp int error; 12775374Sbp 12875374Sbp vcspec->shspec = shspec; 12975374Sbp error = ENOENT; 130132780Skan vcp = NULL; 131132780Skan SMBCO_FOREACH(scp, &smb_vclist) { 132132780Skan vcp = (struct smb_vc *)scp; 13387192Sbp error = smb_vc_lock(vcp, LK_EXCLUSIVE, td); 13475374Sbp if (error) 13575374Sbp continue; 13675374Sbp 137119376Smarcel if ((vcp->obj.co_flags & SMBV_PRIVATE) || 138119376Smarcel !CONNADDREQ(vcp->vc_paddr, vcspec->sap) || 139119376Smarcel strcmp(vcp->vc_username, vcspec->username) != 0) 140119376Smarcel goto err1; 141119376Smarcel if (vcspec->owner != SMBM_ANY_OWNER) { 142119376Smarcel if (vcp->vc_uid != vcspec->owner) 143119376Smarcel goto err1; 144119376Smarcel } else 145119376Smarcel exact = 0; 146119376Smarcel if (vcspec->group != SMBM_ANY_GROUP) { 147119376Smarcel if (vcp->vc_grp != vcspec->group) 148119376Smarcel goto err1; 149119376Smarcel } else 150119376Smarcel exact = 0; 151119376Smarcel if (vcspec->mode & SMBM_EXACT) { 152119376Smarcel if (!exact || (vcspec->mode & SMBM_MASK) != 153119376Smarcel vcp->vc_mode) 154119376Smarcel goto err1; 155119376Smarcel } 156119376Smarcel if (smb_vc_access(vcp, scred, vcspec->mode) != 0) 157119376Smarcel goto err1; 158119376Smarcel vcspec->ssp = NULL; 159119376Smarcel if (shspec) { 160119376Smarcel error = (int)smb_vc_lookupshare(vcp, shspec, scred, 161119376Smarcel &vcspec->ssp); 162119376Smarcel if (error) 163119376Smarcel goto fail; 164119376Smarcel } 165119376Smarcel error = 0; 166119376Smarcel break; 167119376Smarcel err1: 168119376Smarcel error = 1; 169119376Smarcel fail: 170119376Smarcel smb_vc_unlock(vcp, 0, td); 17175374Sbp } 17275374Sbp if (vcp) { 17387192Sbp smb_vc_ref(vcp); 17475374Sbp *vcpp = vcp; 17575374Sbp } 176119376Smarcel return (error); 17775374Sbp} 17875374Sbp 17975374Sbpint 18075374Sbpsmb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, 18175374Sbp struct smb_cred *scred, struct smb_vc **vcpp) 18275374Sbp{ 18387192Sbp struct thread *td = scred->scr_td; 18475374Sbp struct smb_vc *vcp; 18575374Sbp struct smb_share *ssp = NULL; 18675374Sbp int error; 18775374Sbp 18875374Sbp *vcpp = vcp = NULL; 18975374Sbp 19087192Sbp error = smb_sm_lockvclist(LK_EXCLUSIVE, td); 19175374Sbp if (error) 19275374Sbp return error; 19375374Sbp error = smb_sm_lookupint(vcspec, shspec, scred, vcpp); 19475374Sbp if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) { 19587192Sbp smb_sm_unlockvclist(td); 19675374Sbp return error; 19775374Sbp } 19875374Sbp error = smb_sm_lookupint(vcspec, NULL, scred, &vcp); 19975374Sbp if (error) { 20075374Sbp error = smb_vc_create(vcspec, scred, &vcp); 20175374Sbp if (error) 20275374Sbp goto out; 20375374Sbp error = smb_vc_connect(vcp, scred); 20475374Sbp if (error) 20575374Sbp goto out; 20675374Sbp } 20775374Sbp if (shspec == NULL) 20875374Sbp goto out; 20975374Sbp error = smb_share_create(vcp, shspec, scred, &ssp); 21075374Sbp if (error) 21175374Sbp goto out; 21275374Sbp error = smb_smb_treeconnect(ssp, scred); 21375374Sbp if (error == 0) 21475374Sbp vcspec->ssp = ssp; 21575374Sbp else 21675374Sbp smb_share_put(ssp, scred); 21775374Sbpout: 21887192Sbp smb_sm_unlockvclist(td); 21975374Sbp if (error == 0) 22075374Sbp *vcpp = vcp; 221184568Srwatson else if (vcp) { 222184568Srwatson smb_vc_lock(vcp, LK_EXCLUSIVE, scred->scr_td); 22375374Sbp smb_vc_put(vcp, scred); 224184568Srwatson } 22575374Sbp return error; 22675374Sbp} 22775374Sbp 22875374Sbp/* 22975374Sbp * Common code for connection object 23075374Sbp */ 23175374Sbpstatic void 232176518Sattiliosmb_co_init(struct smb_connobj *cp, int level, char *ilockname, char *lockname, 233176518Sattilio struct thread *td) 23475374Sbp{ 23575374Sbp SLIST_INIT(&cp->co_children); 236176518Sattilio smb_sl_init(&cp->co_interlock, ilockname); 237176518Sattilio lockinit(&cp->co_lock, PZERO, lockname, 0, 0); 23875374Sbp cp->co_level = level; 23975374Sbp cp->co_usecount = 1; 240160436Sjhb if (smb_co_lock(cp, LK_EXCLUSIVE, td) != 0) 241160436Sjhb panic("smb_co_init: lock failed"); 24275374Sbp} 24375374Sbp 24475374Sbpstatic void 24575374Sbpsmb_co_done(struct smb_connobj *cp) 24675374Sbp{ 24775374Sbp smb_sl_destroy(&cp->co_interlock); 248175635Sattilio lockmgr(&cp->co_lock, LK_RELEASE, 0); 24975374Sbp lockdestroy(&cp->co_lock); 25075374Sbp} 25175374Sbp 25275374Sbpstatic void 25375374Sbpsmb_co_gone(struct smb_connobj *cp, struct smb_cred *scred) 25475374Sbp{ 25575374Sbp struct smb_connobj *parent; 25675374Sbp 25775374Sbp if (cp->co_gone) 25875374Sbp cp->co_gone(cp, scred); 25975374Sbp parent = cp->co_parent; 26075374Sbp if (parent) { 26187192Sbp smb_co_lock(parent, LK_EXCLUSIVE, scred->scr_td); 26275374Sbp SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next); 26375374Sbp smb_co_put(parent, scred); 26475374Sbp } 26575374Sbp if (cp->co_free) 26675374Sbp cp->co_free(cp); 26775374Sbp} 26875374Sbp 26975374Sbpvoid 27087192Sbpsmb_co_ref(struct smb_connobj *cp) 27175374Sbp{ 27275374Sbp 27375374Sbp SMB_CO_LOCK(cp); 27475374Sbp cp->co_usecount++; 27575374Sbp SMB_CO_UNLOCK(cp); 27675374Sbp} 27775374Sbp 27875374Sbpvoid 27975374Sbpsmb_co_rele(struct smb_connobj *cp, struct smb_cred *scred) 28075374Sbp{ 28175374Sbp 28275374Sbp SMB_CO_LOCK(cp); 28375374Sbp if (cp->co_usecount > 1) { 28475374Sbp cp->co_usecount--; 28575374Sbp SMB_CO_UNLOCK(cp); 28675374Sbp return; 28775374Sbp } 28875374Sbp if (cp->co_usecount == 0) { 28975374Sbp SMBERROR("negative use_count for object %d", cp->co_level); 29075374Sbp SMB_CO_UNLOCK(cp); 29175374Sbp return; 29275374Sbp } 29375374Sbp cp->co_usecount--; 29475374Sbp cp->co_flags |= SMBO_GONE; 29575374Sbp 296175635Sattilio lockmgr(&cp->co_lock, LK_DRAIN | LK_INTERLOCK, &cp->co_interlock); 29775374Sbp smb_co_gone(cp, scred); 29875374Sbp} 29975374Sbp 30075374Sbpint 30175374Sbpsmb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred) 30275374Sbp{ 30375374Sbp int error; 30475374Sbp 30575374Sbp if ((flags & LK_INTERLOCK) == 0) 30675374Sbp SMB_CO_LOCK(cp); 30775374Sbp cp->co_usecount++; 30887192Sbp error = smb_co_lock(cp, flags | LK_INTERLOCK, scred->scr_td); 30975374Sbp if (error) { 31075374Sbp SMB_CO_LOCK(cp); 31175374Sbp cp->co_usecount--; 31275374Sbp SMB_CO_UNLOCK(cp); 31375374Sbp return error; 31475374Sbp } 31575374Sbp return 0; 31675374Sbp} 31775374Sbp 31875374Sbpvoid 31975374Sbpsmb_co_put(struct smb_connobj *cp, struct smb_cred *scred) 32075374Sbp{ 32175374Sbp 32275374Sbp SMB_CO_LOCK(cp); 32375374Sbp if (cp->co_usecount > 1) { 32475374Sbp cp->co_usecount--; 32575374Sbp } else if (cp->co_usecount == 1) { 32675374Sbp cp->co_usecount--; 32775374Sbp cp->co_flags |= SMBO_GONE; 32875374Sbp } else { 32975374Sbp SMBERROR("negative usecount"); 33075374Sbp } 331175635Sattilio lockmgr(&cp->co_lock, LK_RELEASE | LK_INTERLOCK, &cp->co_interlock); 33275374Sbp if ((cp->co_flags & SMBO_GONE) == 0) 33375374Sbp return; 334175635Sattilio lockmgr(&cp->co_lock, LK_DRAIN, NULL); 33575374Sbp smb_co_gone(cp, scred); 33675374Sbp} 33775374Sbp 33875374Sbpint 33987192Sbpsmb_co_lockstatus(struct smb_connobj *cp, struct thread *td) 34075374Sbp{ 341176559Sattilio return lockstatus(&cp->co_lock); 34275374Sbp} 34375374Sbp 34475374Sbpint 34587192Sbpsmb_co_lock(struct smb_connobj *cp, int flags, struct thread *td) 34675374Sbp{ 34775374Sbp 34875374Sbp if (cp->co_flags & SMBO_GONE) 34975374Sbp return EINVAL; 35075374Sbp if ((flags & LK_TYPE_MASK) == 0) 35175374Sbp flags |= LK_EXCLUSIVE; 35287192Sbp if (smb_co_lockstatus(cp, td) == LK_EXCLUSIVE && 35375374Sbp (flags & LK_CANRECURSE) == 0) { 35475374Sbp SMBERROR("recursive lock for object %d\n", cp->co_level); 35575374Sbp return 0; 35675374Sbp } 357175635Sattilio return lockmgr(&cp->co_lock, flags, &cp->co_interlock); 35875374Sbp} 35975374Sbp 36075374Sbpvoid 36187192Sbpsmb_co_unlock(struct smb_connobj *cp, int flags, struct thread *td) 36275374Sbp{ 363175635Sattilio (void)lockmgr(&cp->co_lock, flags | LK_RELEASE, &cp->co_interlock); 36475374Sbp} 36575374Sbp 36675374Sbpstatic void 36775374Sbpsmb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child) 36875374Sbp{ 36987192Sbp KASSERT(smb_co_lockstatus(parent, curthread) == LK_EXCLUSIVE, ("smb_co_addchild: parent not locked")); 37087192Sbp KASSERT(smb_co_lockstatus(child, curthread) == LK_EXCLUSIVE, ("smb_co_addchild: child not locked")); 37175374Sbp 37287192Sbp smb_co_ref(parent); 37375374Sbp SLIST_INSERT_HEAD(&parent->co_children, child, co_next); 37475374Sbp child->co_parent = parent; 37575374Sbp} 37675374Sbp 37775374Sbp/* 37875374Sbp * Session implementation 37975374Sbp */ 38075374Sbp 38175374Sbpint 38275374Sbpsmb_vc_create(struct smb_vcspec *vcspec, 38375374Sbp struct smb_cred *scred, struct smb_vc **vcpp) 38475374Sbp{ 38575374Sbp struct smb_vc *vcp; 38687192Sbp struct thread *td = scred->scr_td; 38775374Sbp struct ucred *cred = scred->scr_cred; 38875374Sbp uid_t uid = vcspec->owner; 38975374Sbp gid_t gid = vcspec->group; 39075374Sbp uid_t realuid = cred->cr_uid; 39175374Sbp char *domain = vcspec->domain; 39275374Sbp int error, isroot; 39375374Sbp 39475374Sbp isroot = smb_suser(cred) == 0; 39575374Sbp /* 39675374Sbp * Only superuser can create VCs with different uid and gid 39775374Sbp */ 39875374Sbp if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) 39975374Sbp return EPERM; 40075374Sbp if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) 40175374Sbp return EPERM; 40275374Sbp 403111119Simp vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK); 404176518Sattilio smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc ilock", "smb_vc", td); 40575374Sbp vcp->obj.co_free = smb_vc_free; 40675374Sbp vcp->obj.co_gone = smb_vc_gone; 40775374Sbp vcp->vc_number = smb_vcnext++; 40875374Sbp vcp->vc_timo = SMB_DEFRQTIMO; 40975374Sbp vcp->vc_smbuid = SMB_UID_UNKNOWN; 41075374Sbp vcp->vc_mode = vcspec->rights & SMBM_MASK; 41175374Sbp vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE); 41275374Sbp vcp->vc_tdesc = &smb_tran_nbtcp_desc; 413124087Stjr vcp->vc_seqno = 0; 414124087Stjr vcp->vc_mackey = NULL; 415124087Stjr vcp->vc_mackeylen = 0; 41675374Sbp 41775374Sbp if (uid == SMBM_ANY_OWNER) 41875374Sbp uid = realuid; 41975374Sbp if (gid == SMBM_ANY_GROUP) 42075374Sbp gid = cred->cr_groups[0]; 42175374Sbp vcp->vc_uid = uid; 42275374Sbp vcp->vc_grp = gid; 42375374Sbp 42475374Sbp smb_sl_init(&vcp->vc_stlock, "vcstlock"); 425119376Smarcel error = ENOMEM; 42675374Sbp 427126425Srwatson vcp->vc_paddr = sodupsockaddr(vcspec->sap, M_WAITOK); 428119376Smarcel if (vcp->vc_paddr == NULL) 429119376Smarcel goto fail; 430126425Srwatson vcp->vc_laddr = sodupsockaddr(vcspec->lap, M_WAITOK); 431119376Smarcel if (vcp->vc_laddr == NULL) 432119376Smarcel goto fail; 433119376Smarcel vcp->vc_pass = smb_strdup(vcspec->pass); 434119376Smarcel if (vcp->vc_pass == NULL) 435119376Smarcel goto fail; 436119376Smarcel vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : 437119376Smarcel "NODOMAIN"); 438119376Smarcel if (vcp->vc_domain == NULL) 439119376Smarcel goto fail; 440119376Smarcel vcp->vc_srvname = smb_strdup(vcspec->srvname); 441119376Smarcel if (vcp->vc_srvname == NULL) 442119376Smarcel goto fail; 443119376Smarcel vcp->vc_username = smb_strdup(vcspec->username); 444119376Smarcel if (vcp->vc_username == NULL) 445119376Smarcel goto fail; 446119376Smarcel error = (int)iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower); 447119376Smarcel if (error) 448119376Smarcel goto fail; 449119376Smarcel error = (int)iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper); 450119376Smarcel if (error) 451119376Smarcel goto fail; 452119376Smarcel if (vcspec->servercs[0]) { 453119376Smarcel error = (int)iconv_open(vcspec->servercs, vcspec->localcs, 454119376Smarcel &vcp->vc_toserver); 455119376Smarcel if (error) 456119376Smarcel goto fail; 457119376Smarcel error = (int)iconv_open(vcspec->localcs, vcspec->servercs, 458119376Smarcel &vcp->vc_tolocal); 459119376Smarcel if (error) 460119376Smarcel goto fail; 461119376Smarcel } 462119376Smarcel error = (int)smb_iod_create(vcp); 463119376Smarcel if (error) 464119376Smarcel goto fail; 465119376Smarcel *vcpp = vcp; 466119376Smarcel smb_co_addchild(&smb_vclist, VCTOCP(vcp)); 467119376Smarcel return (0); 46875374Sbp 469119376Smarcel fail: 470119376Smarcel smb_vc_put(vcp, scred); 471119376Smarcel return (error); 47275374Sbp} 47375374Sbp 47475374Sbpstatic void 47575374Sbpsmb_vc_free(struct smb_connobj *cp) 47675374Sbp{ 47775374Sbp struct smb_vc *vcp = CPTOVC(cp); 47875374Sbp 47975374Sbp if (vcp->vc_iod) 48075374Sbp smb_iod_destroy(vcp->vc_iod); 48175374Sbp SMB_STRFREE(vcp->vc_username); 48275374Sbp SMB_STRFREE(vcp->vc_srvname); 48375374Sbp SMB_STRFREE(vcp->vc_pass); 48475374Sbp SMB_STRFREE(vcp->vc_domain); 485124087Stjr if (vcp->vc_mackey) 486124087Stjr free(vcp->vc_mackey, M_SMBTEMP); 48775374Sbp if (vcp->vc_paddr) 48875374Sbp free(vcp->vc_paddr, M_SONAME); 48975374Sbp if (vcp->vc_laddr) 49075374Sbp free(vcp->vc_laddr, M_SONAME); 49175374Sbp if (vcp->vc_tolower) 49275374Sbp iconv_close(vcp->vc_tolower); 49375374Sbp if (vcp->vc_toupper) 49475374Sbp iconv_close(vcp->vc_toupper); 49575374Sbp if (vcp->vc_tolocal) 49675374Sbp iconv_close(vcp->vc_tolocal); 49775374Sbp if (vcp->vc_toserver) 49875374Sbp iconv_close(vcp->vc_toserver); 49975374Sbp smb_co_done(VCTOCP(vcp)); 50075374Sbp smb_sl_destroy(&vcp->vc_stlock); 50175374Sbp free(vcp, M_SMBCONN); 50275374Sbp} 50375374Sbp 50475374Sbp/* 50575374Sbp * Called when use count of VC dropped to zero. 50675374Sbp * VC should be locked on enter with LK_DRAIN. 50775374Sbp */ 50875374Sbpstatic void 50975374Sbpsmb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred) 51075374Sbp{ 51175374Sbp struct smb_vc *vcp = CPTOVC(cp); 51275374Sbp 51375374Sbp smb_vc_disconnect(vcp); 51475374Sbp} 51575374Sbp 51675374Sbpvoid 51787192Sbpsmb_vc_ref(struct smb_vc *vcp) 51875374Sbp{ 51987192Sbp smb_co_ref(VCTOCP(vcp)); 52075374Sbp} 52175374Sbp 52275374Sbpvoid 52375374Sbpsmb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred) 52475374Sbp{ 52575374Sbp smb_co_rele(VCTOCP(vcp), scred); 52675374Sbp} 52775374Sbp 52875374Sbpint 52975374Sbpsmb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred) 53075374Sbp{ 53175374Sbp return smb_co_get(VCTOCP(vcp), flags, scred); 53275374Sbp} 53375374Sbp 53475374Sbpvoid 53575374Sbpsmb_vc_put(struct smb_vc *vcp, struct smb_cred *scred) 53675374Sbp{ 53775374Sbp smb_co_put(VCTOCP(vcp), scred); 53875374Sbp} 53975374Sbp 54075374Sbpint 54187192Sbpsmb_vc_lock(struct smb_vc *vcp, int flags, struct thread *td) 54275374Sbp{ 54387192Sbp return smb_co_lock(VCTOCP(vcp), flags, td); 54475374Sbp} 54575374Sbp 54675374Sbpvoid 54787192Sbpsmb_vc_unlock(struct smb_vc *vcp, int flags, struct thread *td) 54875374Sbp{ 54987192Sbp smb_co_unlock(VCTOCP(vcp), flags, td); 55075374Sbp} 55175374Sbp 55275374Sbpint 55375374Sbpsmb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode) 55475374Sbp{ 55575374Sbp struct ucred *cred = scred->scr_cred; 55675374Sbp 55775374Sbp if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid) 55875374Sbp return 0; 55975374Sbp mode >>= 3; 56075374Sbp if (!groupmember(vcp->vc_grp, cred)) 56175374Sbp mode >>= 3; 56275374Sbp return (vcp->vc_mode & mode) == mode ? 0 : EACCES; 56375374Sbp} 56475374Sbp 56575374Sbpstatic int 56675374Sbpsmb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp) 56775374Sbp{ 56875374Sbp int exact = 1; 56975374Sbp 57075374Sbp if (strcmp(ssp->ss_name, dp->name) != 0) 57175374Sbp return 1; 57275374Sbp if (dp->owner != SMBM_ANY_OWNER) { 57375374Sbp if (ssp->ss_uid != dp->owner) 57475374Sbp return 1; 57575374Sbp } else 57675374Sbp exact = 0; 57775374Sbp if (dp->group != SMBM_ANY_GROUP) { 57875374Sbp if (ssp->ss_grp != dp->group) 57975374Sbp return 1; 58075374Sbp } else 58175374Sbp exact = 0; 58275374Sbp 58375374Sbp if (dp->mode & SMBM_EXACT) { 58475374Sbp if (!exact) 58575374Sbp return 1; 58675374Sbp return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1; 58775374Sbp } 58875374Sbp if (smb_share_access(ssp, dp->scred, dp->mode) != 0) 58975374Sbp return 1; 59075374Sbp return 0; 59175374Sbp} 59275374Sbp 59375374Sbp/* 59475374Sbp * Lookup share in the given VC. Share referenced and locked on return. 59575374Sbp * VC expected to be locked on entry and will be left locked on exit. 59675374Sbp */ 59775374Sbpint 59875374Sbpsmb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp, 59975374Sbp struct smb_cred *scred, struct smb_share **sspp) 60075374Sbp{ 60187192Sbp struct thread *td = scred->scr_td; 602132780Skan struct smb_connobj *scp = NULL; 60375374Sbp struct smb_share *ssp = NULL; 60475374Sbp int error; 60575374Sbp 60675374Sbp *sspp = NULL; 60775374Sbp dp->scred = scred; 608132780Skan SMBCO_FOREACH(scp, VCTOCP(vcp)) { 609132780Skan ssp = (struct smb_share *)scp; 61087192Sbp error = smb_share_lock(ssp, LK_EXCLUSIVE, td); 61175374Sbp if (error) 61275374Sbp continue; 61375374Sbp if (smb_vc_cmpshare(ssp, dp) == 0) 61475374Sbp break; 61587192Sbp smb_share_unlock(ssp, 0, td); 61675374Sbp } 61775374Sbp if (ssp) { 61887192Sbp smb_share_ref(ssp); 61975374Sbp *sspp = ssp; 62075374Sbp error = 0; 62175374Sbp } else 62275374Sbp error = ENOENT; 62375374Sbp return error; 62475374Sbp} 62575374Sbp 62675374Sbpint 62775374Sbpsmb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred) 62875374Sbp{ 62975374Sbp 63075374Sbp return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); 63175374Sbp} 63275374Sbp 63375374Sbp/* 63475374Sbp * Destroy VC to server, invalidate shares linked with it. 63575374Sbp * Transport should be locked on entry. 63675374Sbp */ 63775374Sbpint 63875374Sbpsmb_vc_disconnect(struct smb_vc *vcp) 63975374Sbp{ 64075374Sbp 64175374Sbp smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL); 64275374Sbp return 0; 64375374Sbp} 64475374Sbp 64575374Sbpstatic char smb_emptypass[] = ""; 64675374Sbp 64775374Sbpconst char * 64875374Sbpsmb_vc_getpass(struct smb_vc *vcp) 64975374Sbp{ 65075374Sbp if (vcp->vc_pass) 65175374Sbp return vcp->vc_pass; 65275374Sbp return smb_emptypass; 65375374Sbp} 65475374Sbp 65575374Sbpstatic int 65675374Sbpsmb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip) 65775374Sbp{ 65875374Sbp bzero(vip, sizeof(struct smb_vc_info)); 65975374Sbp vip->itype = SMB_INFO_VC; 66075374Sbp vip->usecount = vcp->obj.co_usecount; 66175374Sbp vip->uid = vcp->vc_uid; 66275374Sbp vip->gid = vcp->vc_grp; 66375374Sbp vip->mode = vcp->vc_mode; 66475374Sbp vip->flags = vcp->obj.co_flags; 66575374Sbp vip->sopt = vcp->vc_sopt; 66675374Sbp vip->iodstate = vcp->vc_iod->iod_state; 66775374Sbp bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey)); 66875374Sbp snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname); 66975374Sbp snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username); 67075374Sbp return 0; 67175374Sbp} 67275374Sbp 67375374Sbpu_short 67475374Sbpsmb_vc_nextmid(struct smb_vc *vcp) 67575374Sbp{ 67675374Sbp u_short r; 67775374Sbp 67875374Sbp SMB_CO_LOCK(&vcp->obj); 67975374Sbp r = vcp->vc_mid++; 68075374Sbp SMB_CO_UNLOCK(&vcp->obj); 68175374Sbp return r; 68275374Sbp} 68375374Sbp 68475374Sbp/* 68575374Sbp * Share implementation 68675374Sbp */ 68775374Sbp/* 68875374Sbp * Allocate share structure and attach it to the given VC 68975374Sbp * Connection expected to be locked on entry. Share will be returned 69075374Sbp * in locked state. 69175374Sbp */ 69275374Sbpint 69375374Sbpsmb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, 69475374Sbp struct smb_cred *scred, struct smb_share **sspp) 69575374Sbp{ 69675374Sbp struct smb_share *ssp; 69787192Sbp struct thread *td = scred->scr_td; 69875374Sbp struct ucred *cred = scred->scr_cred; 69975374Sbp uid_t realuid = cred->cr_uid; 70075374Sbp uid_t uid = shspec->owner; 70175374Sbp gid_t gid = shspec->group; 70275374Sbp int error, isroot; 70375374Sbp 70475374Sbp isroot = smb_suser(cred) == 0; 70575374Sbp /* 70675374Sbp * Only superuser can create shares with different uid and gid 70775374Sbp */ 70875374Sbp if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) 70975374Sbp return EPERM; 71075374Sbp if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) 71175374Sbp return EPERM; 71275374Sbp error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); 71375374Sbp if (!error) { 71475374Sbp smb_share_put(ssp, scred); 71575374Sbp return EEXIST; 71675374Sbp } 71775374Sbp if (uid == SMBM_ANY_OWNER) 71875374Sbp uid = realuid; 71975374Sbp if (gid == SMBM_ANY_GROUP) 72075374Sbp gid = cred->cr_groups[0]; 721111119Simp ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK); 722176518Sattilio smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss ilock", "smbss", td); 72375374Sbp ssp->obj.co_free = smb_share_free; 72475374Sbp ssp->obj.co_gone = smb_share_gone; 72575374Sbp smb_sl_init(&ssp->ss_stlock, "ssstlock"); 72675374Sbp ssp->ss_name = smb_strdup(shspec->name); 72775374Sbp if (shspec->pass && shspec->pass[0]) 72875374Sbp ssp->ss_pass = smb_strdup(shspec->pass); 72975374Sbp ssp->ss_type = shspec->stype; 73075374Sbp ssp->ss_tid = SMB_TID_UNKNOWN; 73175374Sbp ssp->ss_uid = uid; 73275374Sbp ssp->ss_grp = gid; 73375374Sbp ssp->ss_mode = shspec->rights & SMBM_MASK; 73475374Sbp smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); 73575374Sbp *sspp = ssp; 73675374Sbp return 0; 73775374Sbp} 73875374Sbp 73975374Sbpstatic void 74075374Sbpsmb_share_free(struct smb_connobj *cp) 74175374Sbp{ 74275374Sbp struct smb_share *ssp = CPTOSS(cp); 74375374Sbp 74475374Sbp SMB_STRFREE(ssp->ss_name); 74575374Sbp SMB_STRFREE(ssp->ss_pass); 74675374Sbp smb_sl_destroy(&ssp->ss_stlock); 74775374Sbp smb_co_done(SSTOCP(ssp)); 74875374Sbp free(ssp, M_SMBCONN); 74975374Sbp} 75075374Sbp 75175374Sbpstatic void 75275374Sbpsmb_share_gone(struct smb_connobj *cp, struct smb_cred *scred) 75375374Sbp{ 75475374Sbp struct smb_share *ssp = CPTOSS(cp); 75575374Sbp 75675374Sbp smb_smb_treedisconnect(ssp, scred); 75775374Sbp} 75875374Sbp 75975374Sbpvoid 76087192Sbpsmb_share_ref(struct smb_share *ssp) 76175374Sbp{ 76287192Sbp smb_co_ref(SSTOCP(ssp)); 76375374Sbp} 76475374Sbp 76575374Sbpvoid 76675374Sbpsmb_share_rele(struct smb_share *ssp, struct smb_cred *scred) 76775374Sbp{ 76875374Sbp smb_co_rele(SSTOCP(ssp), scred); 76975374Sbp} 77075374Sbp 77175374Sbpint 77275374Sbpsmb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred) 77375374Sbp{ 77475374Sbp return smb_co_get(SSTOCP(ssp), flags, scred); 77575374Sbp} 77675374Sbp 77775374Sbpvoid 77875374Sbpsmb_share_put(struct smb_share *ssp, struct smb_cred *scred) 77975374Sbp{ 78075374Sbp smb_co_put(SSTOCP(ssp), scred); 78175374Sbp} 78275374Sbp 78375374Sbpint 78487192Sbpsmb_share_lock(struct smb_share *ssp, int flags, struct thread *td) 78575374Sbp{ 78687192Sbp return smb_co_lock(SSTOCP(ssp), flags, td); 78775374Sbp} 78875374Sbp 78975374Sbpvoid 79087192Sbpsmb_share_unlock(struct smb_share *ssp, int flags, struct thread *td) 79175374Sbp{ 79287192Sbp smb_co_unlock(SSTOCP(ssp), flags, td); 79375374Sbp} 79475374Sbp 79575374Sbpint 79675374Sbpsmb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode) 79775374Sbp{ 79875374Sbp struct ucred *cred = scred->scr_cred; 79975374Sbp 80075374Sbp if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid) 80175374Sbp return 0; 80275374Sbp mode >>= 3; 80375374Sbp if (!groupmember(ssp->ss_grp, cred)) 80475374Sbp mode >>= 3; 80575374Sbp return (ssp->ss_mode & mode) == mode ? 0 : EACCES; 80675374Sbp} 80775374Sbp 80875374Sbpvoid 80975374Sbpsmb_share_invalidate(struct smb_share *ssp) 81075374Sbp{ 81175374Sbp ssp->ss_tid = SMB_TID_UNKNOWN; 81275374Sbp} 81375374Sbp 81475374Sbpint 81575374Sbpsmb_share_valid(struct smb_share *ssp) 81675374Sbp{ 81775374Sbp return ssp->ss_tid != SMB_TID_UNKNOWN && 81875374Sbp ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid; 81975374Sbp} 82075374Sbp 82175374Sbpconst char* 82275374Sbpsmb_share_getpass(struct smb_share *ssp) 82375374Sbp{ 82475374Sbp struct smb_vc *vcp; 82575374Sbp 82675374Sbp if (ssp->ss_pass) 82775374Sbp return ssp->ss_pass; 82875374Sbp vcp = SSTOVC(ssp); 82975374Sbp if (vcp->vc_pass) 83075374Sbp return vcp->vc_pass; 83175374Sbp return smb_emptypass; 83275374Sbp} 83375374Sbp 83475374Sbpstatic int 83575374Sbpsmb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip) 83675374Sbp{ 83775374Sbp bzero(sip, sizeof(struct smb_share_info)); 83875374Sbp sip->itype = SMB_INFO_SHARE; 83975374Sbp sip->usecount = ssp->obj.co_usecount; 84075374Sbp sip->tid = ssp->ss_tid; 84175374Sbp sip->type= ssp->ss_type; 84275374Sbp sip->uid = ssp->ss_uid; 84375374Sbp sip->gid = ssp->ss_grp; 84475374Sbp sip->mode= ssp->ss_mode; 84575374Sbp sip->flags = ssp->obj.co_flags; 84675374Sbp snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name); 84775374Sbp return 0; 84875374Sbp} 84975374Sbp 85075374Sbp/* 85175374Sbp * Dump an entire tree into sysctl call 85275374Sbp */ 85375374Sbpstatic int 85475374Sbpsmb_sysctl_treedump(SYSCTL_HANDLER_ARGS) 85575374Sbp{ 85687192Sbp struct thread *td = req->td; 85775374Sbp struct smb_cred scred; 858132780Skan struct smb_connobj *scp1, *scp2; 85975374Sbp struct smb_vc *vcp; 86075374Sbp struct smb_share *ssp; 86175374Sbp struct smb_vc_info vci; 86275374Sbp struct smb_share_info ssi; 86375374Sbp int error, itype; 86475374Sbp 86591406Sjhb smb_makescred(&scred, td, td->td_ucred); 866126253Struckman error = sysctl_wire_old_buffer(req, 0); 867126253Struckman if (error) 868126253Struckman return (error); 86987192Sbp error = smb_sm_lockvclist(LK_SHARED, td); 87075374Sbp if (error) 87175374Sbp return error; 872132780Skan SMBCO_FOREACH(scp1, &smb_vclist) { 873132780Skan vcp = (struct smb_vc *)scp1; 87487192Sbp error = smb_vc_lock(vcp, LK_SHARED, td); 87575374Sbp if (error) 87675374Sbp continue; 87775374Sbp smb_vc_getinfo(vcp, &vci); 87875374Sbp error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info)); 87975374Sbp if (error) { 88087192Sbp smb_vc_unlock(vcp, 0, td); 88175374Sbp break; 88275374Sbp } 883132780Skan SMBCO_FOREACH(scp2, VCTOCP(vcp)) { 884132780Skan ssp = (struct smb_share *)scp2; 88587192Sbp error = smb_share_lock(ssp, LK_SHARED, td); 88675374Sbp if (error) { 88775374Sbp error = 0; 88875374Sbp continue; 88975374Sbp } 89075374Sbp smb_share_getinfo(ssp, &ssi); 89187192Sbp smb_share_unlock(ssp, 0, td); 89275374Sbp error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info)); 89375374Sbp if (error) 89475374Sbp break; 89575374Sbp } 89687192Sbp smb_vc_unlock(vcp, 0, td); 89775374Sbp if (error) 89875374Sbp break; 89975374Sbp } 90075374Sbp if (!error) { 90175374Sbp itype = SMB_INFO_NONE; 90275374Sbp error = SYSCTL_OUT(req, &itype, sizeof(itype)); 90375374Sbp } 90487192Sbp smb_sm_unlockvclist(td); 90575374Sbp return error; 90675374Sbp} 907