smb_conn.c revision 160436
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 160436 2006-07-17 16:12:59Z jhb $"); 39116189Sobrien 4075374Sbp#include <sys/param.h> 4175374Sbp#include <sys/systm.h> 4275374Sbp#include <sys/kernel.h> 4375374Sbp#include <sys/malloc.h> 4475374Sbp#include <sys/proc.h> 4575374Sbp#include <sys/lock.h> 4675374Sbp#include <sys/sysctl.h> 4775374Sbp#include <sys/socketvar.h> 4875374Sbp 4975374Sbp#include <sys/iconv.h> 5075374Sbp 5175374Sbp#include <netsmb/smb.h> 5275374Sbp#include <netsmb/smb_subr.h> 5375374Sbp#include <netsmb/smb_conn.h> 5475374Sbp#include <netsmb/smb_tran.h> 5575374Sbp#include <netsmb/smb_trantcp.h> 5675374Sbp 5775374Sbpstatic struct smb_connobj smb_vclist; 5875374Sbpstatic int smb_vcnext = 1; /* next unique id for VC */ 5975374Sbp 6075374SbpSYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW, NULL, "SMB protocol"); 6175374Sbp 62151897SrwatsonMALLOC_DEFINE(M_SMBCONN, "smb_conn", "SMB connection"); 6375374Sbp 6475374Sbpstatic void smb_co_init(struct smb_connobj *cp, int level, char *objname, 6587192Sbp struct thread *td); 6675374Sbpstatic void smb_co_done(struct smb_connobj *cp); 6787192Sbpstatic int smb_co_lockstatus(struct smb_connobj *cp, struct thread *td); 6875374Sbp 6975374Sbpstatic int smb_vc_disconnect(struct smb_vc *vcp); 7075374Sbpstatic void smb_vc_free(struct smb_connobj *cp); 7175374Sbpstatic void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred); 7275374Sbpstatic smb_co_free_t smb_share_free; 7375374Sbpstatic smb_co_gone_t smb_share_gone; 7475374Sbp 7575374Sbpstatic int smb_sysctl_treedump(SYSCTL_HANDLER_ARGS); 7675374Sbp 7775374SbpSYSCTL_PROC(_net_smb, OID_AUTO, treedump, CTLFLAG_RD | CTLTYPE_OPAQUE, 7875374Sbp NULL, 0, smb_sysctl_treedump, "S,treedump", "Requester tree"); 7975374Sbp 8075374Sbpint 8175374Sbpsmb_sm_init(void) 8275374Sbp{ 8375374Sbp 8487192Sbp smb_co_init(&smb_vclist, SMBL_SM, "smbsm", curthread); 8587192Sbp smb_co_unlock(&smb_vclist, 0, curthread); 8675374Sbp return 0; 8775374Sbp} 8875374Sbp 8975374Sbpint 9075374Sbpsmb_sm_done(void) 9175374Sbp{ 9275374Sbp 9375374Sbp /* XXX: hold the mutex */ 9475374Sbp if (smb_vclist.co_usecount > 1) { 9575374Sbp SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1); 9675374Sbp return EBUSY; 9775374Sbp } 98146179Speadar lockmgr(&smb_vclist.co_lock, LK_DRAIN, 0, curthread); 9975374Sbp smb_co_done(&smb_vclist); 10075374Sbp return 0; 10175374Sbp} 10275374Sbp 10375374Sbpstatic int 10487192Sbpsmb_sm_lockvclist(int flags, struct thread *td) 10575374Sbp{ 10675374Sbp 10787192Sbp return smb_co_lock(&smb_vclist, flags | LK_CANRECURSE, td); 10875374Sbp} 10975374Sbp 11075374Sbpstatic void 11187192Sbpsmb_sm_unlockvclist(struct thread *td) 11275374Sbp{ 11375374Sbp 11487192Sbp smb_co_unlock(&smb_vclist, LK_RELEASE, td); 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{ 12187192Sbp struct thread *td = scred->scr_td; 122132780Skan struct smb_connobj *scp; 12375374Sbp struct smb_vc *vcp; 12475374Sbp int exact = 1; 12575374Sbp int error; 12675374Sbp 12775374Sbp vcspec->shspec = shspec; 12875374Sbp error = ENOENT; 129132780Skan vcp = NULL; 130132780Skan SMBCO_FOREACH(scp, &smb_vclist) { 131132780Skan vcp = (struct smb_vc *)scp; 13287192Sbp error = smb_vc_lock(vcp, LK_EXCLUSIVE, td); 13375374Sbp if (error) 13475374Sbp continue; 13575374Sbp 136119376Smarcel if ((vcp->obj.co_flags & SMBV_PRIVATE) || 137119376Smarcel !CONNADDREQ(vcp->vc_paddr, vcspec->sap) || 138119376Smarcel strcmp(vcp->vc_username, vcspec->username) != 0) 139119376Smarcel goto err1; 140119376Smarcel if (vcspec->owner != SMBM_ANY_OWNER) { 141119376Smarcel if (vcp->vc_uid != vcspec->owner) 142119376Smarcel goto err1; 143119376Smarcel } else 144119376Smarcel exact = 0; 145119376Smarcel if (vcspec->group != SMBM_ANY_GROUP) { 146119376Smarcel if (vcp->vc_grp != vcspec->group) 147119376Smarcel goto err1; 148119376Smarcel } else 149119376Smarcel exact = 0; 150119376Smarcel if (vcspec->mode & SMBM_EXACT) { 151119376Smarcel if (!exact || (vcspec->mode & SMBM_MASK) != 152119376Smarcel vcp->vc_mode) 153119376Smarcel goto err1; 154119376Smarcel } 155119376Smarcel if (smb_vc_access(vcp, scred, vcspec->mode) != 0) 156119376Smarcel goto err1; 157119376Smarcel vcspec->ssp = NULL; 158119376Smarcel if (shspec) { 159119376Smarcel error = (int)smb_vc_lookupshare(vcp, shspec, scred, 160119376Smarcel &vcspec->ssp); 161119376Smarcel if (error) 162119376Smarcel goto fail; 163119376Smarcel } 164119376Smarcel error = 0; 165119376Smarcel break; 166119376Smarcel err1: 167119376Smarcel error = 1; 168119376Smarcel fail: 169119376Smarcel smb_vc_unlock(vcp, 0, td); 17075374Sbp } 17175374Sbp if (vcp) { 17287192Sbp smb_vc_ref(vcp); 17375374Sbp *vcpp = vcp; 17475374Sbp } 175119376Smarcel return (error); 17675374Sbp} 17775374Sbp 17875374Sbpint 17975374Sbpsmb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, 18075374Sbp struct smb_cred *scred, struct smb_vc **vcpp) 18175374Sbp{ 18287192Sbp struct thread *td = scred->scr_td; 18375374Sbp struct smb_vc *vcp; 18475374Sbp struct smb_share *ssp = NULL; 18575374Sbp int error; 18675374Sbp 18775374Sbp *vcpp = vcp = NULL; 18875374Sbp 18987192Sbp error = smb_sm_lockvclist(LK_EXCLUSIVE, td); 19075374Sbp if (error) 19175374Sbp return error; 19275374Sbp error = smb_sm_lookupint(vcspec, shspec, scred, vcpp); 19375374Sbp if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) { 19487192Sbp smb_sm_unlockvclist(td); 19575374Sbp return error; 19675374Sbp } 19775374Sbp error = smb_sm_lookupint(vcspec, NULL, scred, &vcp); 19875374Sbp if (error) { 19975374Sbp error = smb_vc_create(vcspec, scred, &vcp); 20075374Sbp if (error) 20175374Sbp goto out; 20275374Sbp error = smb_vc_connect(vcp, scred); 20375374Sbp if (error) 20475374Sbp goto out; 20575374Sbp } 20675374Sbp if (shspec == NULL) 20775374Sbp goto out; 20875374Sbp error = smb_share_create(vcp, shspec, scred, &ssp); 20975374Sbp if (error) 21075374Sbp goto out; 21175374Sbp error = smb_smb_treeconnect(ssp, scred); 21275374Sbp if (error == 0) 21375374Sbp vcspec->ssp = ssp; 21475374Sbp else 21575374Sbp smb_share_put(ssp, scred); 21675374Sbpout: 21787192Sbp smb_sm_unlockvclist(td); 21875374Sbp if (error == 0) 21975374Sbp *vcpp = vcp; 22075374Sbp else if (vcp) 22175374Sbp smb_vc_put(vcp, scred); 22275374Sbp return error; 22375374Sbp} 22475374Sbp 22575374Sbp/* 22675374Sbp * Common code for connection object 22775374Sbp */ 22875374Sbpstatic void 22987192Sbpsmb_co_init(struct smb_connobj *cp, int level, char *objname, struct thread *td) 23075374Sbp{ 23175374Sbp SLIST_INIT(&cp->co_children); 23275374Sbp smb_sl_init(&cp->co_interlock, objname); 23375374Sbp lockinit(&cp->co_lock, PZERO, objname, 0, 0); 23475374Sbp cp->co_level = level; 23575374Sbp cp->co_usecount = 1; 236160436Sjhb if (smb_co_lock(cp, LK_EXCLUSIVE, td) != 0) 237160436Sjhb panic("smb_co_init: lock failed"); 23875374Sbp} 23975374Sbp 24075374Sbpstatic void 24175374Sbpsmb_co_done(struct smb_connobj *cp) 24275374Sbp{ 24375374Sbp smb_sl_destroy(&cp->co_interlock); 244146179Speadar lockmgr(&cp->co_lock, LK_RELEASE, 0, curthread); 24575374Sbp lockdestroy(&cp->co_lock); 24675374Sbp} 24775374Sbp 24875374Sbpstatic void 24975374Sbpsmb_co_gone(struct smb_connobj *cp, struct smb_cred *scred) 25075374Sbp{ 25175374Sbp struct smb_connobj *parent; 25275374Sbp 25375374Sbp if (cp->co_gone) 25475374Sbp cp->co_gone(cp, scred); 25575374Sbp parent = cp->co_parent; 25675374Sbp if (parent) { 25787192Sbp smb_co_lock(parent, LK_EXCLUSIVE, scred->scr_td); 25875374Sbp SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next); 25975374Sbp smb_co_put(parent, scred); 26075374Sbp } 26175374Sbp if (cp->co_free) 26275374Sbp cp->co_free(cp); 26375374Sbp} 26475374Sbp 26575374Sbpvoid 26687192Sbpsmb_co_ref(struct smb_connobj *cp) 26775374Sbp{ 26875374Sbp 26975374Sbp SMB_CO_LOCK(cp); 27075374Sbp cp->co_usecount++; 27175374Sbp SMB_CO_UNLOCK(cp); 27275374Sbp} 27375374Sbp 27475374Sbpvoid 27575374Sbpsmb_co_rele(struct smb_connobj *cp, struct smb_cred *scred) 27675374Sbp{ 27787192Sbp struct thread *td = scred->scr_td; 27875374Sbp 27975374Sbp SMB_CO_LOCK(cp); 28075374Sbp if (cp->co_usecount > 1) { 28175374Sbp cp->co_usecount--; 28275374Sbp SMB_CO_UNLOCK(cp); 28375374Sbp return; 28475374Sbp } 28575374Sbp if (cp->co_usecount == 0) { 28675374Sbp SMBERROR("negative use_count for object %d", cp->co_level); 28775374Sbp SMB_CO_UNLOCK(cp); 28875374Sbp return; 28975374Sbp } 29075374Sbp cp->co_usecount--; 29175374Sbp cp->co_flags |= SMBO_GONE; 29275374Sbp 29387192Sbp lockmgr(&cp->co_lock, LK_DRAIN | LK_INTERLOCK, &cp->co_interlock, td); 29475374Sbp smb_co_gone(cp, scred); 29575374Sbp} 29675374Sbp 29775374Sbpint 29875374Sbpsmb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred) 29975374Sbp{ 30075374Sbp int error; 30175374Sbp 30275374Sbp if ((flags & LK_INTERLOCK) == 0) 30375374Sbp SMB_CO_LOCK(cp); 30475374Sbp cp->co_usecount++; 30587192Sbp error = smb_co_lock(cp, flags | LK_INTERLOCK, scred->scr_td); 30675374Sbp if (error) { 30775374Sbp SMB_CO_LOCK(cp); 30875374Sbp cp->co_usecount--; 30975374Sbp SMB_CO_UNLOCK(cp); 31075374Sbp return error; 31175374Sbp } 31275374Sbp return 0; 31375374Sbp} 31475374Sbp 31575374Sbpvoid 31675374Sbpsmb_co_put(struct smb_connobj *cp, struct smb_cred *scred) 31775374Sbp{ 31887192Sbp struct thread *td = scred->scr_td; 31975374Sbp 32075374Sbp SMB_CO_LOCK(cp); 32175374Sbp if (cp->co_usecount > 1) { 32275374Sbp cp->co_usecount--; 32375374Sbp } else if (cp->co_usecount == 1) { 32475374Sbp cp->co_usecount--; 32575374Sbp cp->co_flags |= SMBO_GONE; 32675374Sbp } else { 32775374Sbp SMBERROR("negative usecount"); 32875374Sbp } 32987192Sbp lockmgr(&cp->co_lock, LK_RELEASE | LK_INTERLOCK, &cp->co_interlock, td); 33075374Sbp if ((cp->co_flags & SMBO_GONE) == 0) 33175374Sbp return; 33287192Sbp lockmgr(&cp->co_lock, LK_DRAIN, NULL, td); 33375374Sbp smb_co_gone(cp, scred); 33475374Sbp} 33575374Sbp 33675374Sbpint 33787192Sbpsmb_co_lockstatus(struct smb_connobj *cp, struct thread *td) 33875374Sbp{ 33987192Sbp return lockstatus(&cp->co_lock, td); 34075374Sbp} 34175374Sbp 34275374Sbpint 34387192Sbpsmb_co_lock(struct smb_connobj *cp, int flags, struct thread *td) 34475374Sbp{ 34575374Sbp 34675374Sbp if (cp->co_flags & SMBO_GONE) 34775374Sbp return EINVAL; 34875374Sbp if ((flags & LK_TYPE_MASK) == 0) 34975374Sbp flags |= LK_EXCLUSIVE; 35087192Sbp if (smb_co_lockstatus(cp, td) == LK_EXCLUSIVE && 35175374Sbp (flags & LK_CANRECURSE) == 0) { 35275374Sbp SMBERROR("recursive lock for object %d\n", cp->co_level); 35375374Sbp return 0; 35475374Sbp } 35587192Sbp return lockmgr(&cp->co_lock, flags, &cp->co_interlock, td); 35675374Sbp} 35775374Sbp 35875374Sbpvoid 35987192Sbpsmb_co_unlock(struct smb_connobj *cp, int flags, struct thread *td) 36075374Sbp{ 36187192Sbp (void)lockmgr(&cp->co_lock, flags | LK_RELEASE, &cp->co_interlock, td); 36275374Sbp} 36375374Sbp 36475374Sbpstatic void 36575374Sbpsmb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child) 36675374Sbp{ 36787192Sbp KASSERT(smb_co_lockstatus(parent, curthread) == LK_EXCLUSIVE, ("smb_co_addchild: parent not locked")); 36887192Sbp KASSERT(smb_co_lockstatus(child, curthread) == LK_EXCLUSIVE, ("smb_co_addchild: child not locked")); 36975374Sbp 37087192Sbp smb_co_ref(parent); 37175374Sbp SLIST_INSERT_HEAD(&parent->co_children, child, co_next); 37275374Sbp child->co_parent = parent; 37375374Sbp} 37475374Sbp 37575374Sbp/* 37675374Sbp * Session implementation 37775374Sbp */ 37875374Sbp 37975374Sbpint 38075374Sbpsmb_vc_create(struct smb_vcspec *vcspec, 38175374Sbp struct smb_cred *scred, struct smb_vc **vcpp) 38275374Sbp{ 38375374Sbp struct smb_vc *vcp; 38487192Sbp struct thread *td = scred->scr_td; 38575374Sbp struct ucred *cred = scred->scr_cred; 38675374Sbp uid_t uid = vcspec->owner; 38775374Sbp gid_t gid = vcspec->group; 38875374Sbp uid_t realuid = cred->cr_uid; 38975374Sbp char *domain = vcspec->domain; 39075374Sbp int error, isroot; 39175374Sbp 39275374Sbp isroot = smb_suser(cred) == 0; 39375374Sbp /* 39475374Sbp * Only superuser can create VCs with different uid and gid 39575374Sbp */ 39675374Sbp if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) 39775374Sbp return EPERM; 39875374Sbp if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) 39975374Sbp return EPERM; 40075374Sbp 401111119Simp vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK); 40287192Sbp smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc", td); 40375374Sbp vcp->obj.co_free = smb_vc_free; 40475374Sbp vcp->obj.co_gone = smb_vc_gone; 40575374Sbp vcp->vc_number = smb_vcnext++; 40675374Sbp vcp->vc_timo = SMB_DEFRQTIMO; 40775374Sbp vcp->vc_smbuid = SMB_UID_UNKNOWN; 40875374Sbp vcp->vc_mode = vcspec->rights & SMBM_MASK; 40975374Sbp vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE); 41075374Sbp vcp->vc_tdesc = &smb_tran_nbtcp_desc; 411124087Stjr vcp->vc_seqno = 0; 412124087Stjr vcp->vc_mackey = NULL; 413124087Stjr vcp->vc_mackeylen = 0; 41475374Sbp 41575374Sbp if (uid == SMBM_ANY_OWNER) 41675374Sbp uid = realuid; 41775374Sbp if (gid == SMBM_ANY_GROUP) 41875374Sbp gid = cred->cr_groups[0]; 41975374Sbp vcp->vc_uid = uid; 42075374Sbp vcp->vc_grp = gid; 42175374Sbp 42275374Sbp smb_sl_init(&vcp->vc_stlock, "vcstlock"); 423119376Smarcel error = ENOMEM; 42475374Sbp 425126425Srwatson vcp->vc_paddr = sodupsockaddr(vcspec->sap, M_WAITOK); 426119376Smarcel if (vcp->vc_paddr == NULL) 427119376Smarcel goto fail; 428126425Srwatson vcp->vc_laddr = sodupsockaddr(vcspec->lap, M_WAITOK); 429119376Smarcel if (vcp->vc_laddr == NULL) 430119376Smarcel goto fail; 431119376Smarcel vcp->vc_pass = smb_strdup(vcspec->pass); 432119376Smarcel if (vcp->vc_pass == NULL) 433119376Smarcel goto fail; 434119376Smarcel vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : 435119376Smarcel "NODOMAIN"); 436119376Smarcel if (vcp->vc_domain == NULL) 437119376Smarcel goto fail; 438119376Smarcel vcp->vc_srvname = smb_strdup(vcspec->srvname); 439119376Smarcel if (vcp->vc_srvname == NULL) 440119376Smarcel goto fail; 441119376Smarcel vcp->vc_username = smb_strdup(vcspec->username); 442119376Smarcel if (vcp->vc_username == NULL) 443119376Smarcel goto fail; 444119376Smarcel error = (int)iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower); 445119376Smarcel if (error) 446119376Smarcel goto fail; 447119376Smarcel error = (int)iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper); 448119376Smarcel if (error) 449119376Smarcel goto fail; 450119376Smarcel if (vcspec->servercs[0]) { 451119376Smarcel error = (int)iconv_open(vcspec->servercs, vcspec->localcs, 452119376Smarcel &vcp->vc_toserver); 453119376Smarcel if (error) 454119376Smarcel goto fail; 455119376Smarcel error = (int)iconv_open(vcspec->localcs, vcspec->servercs, 456119376Smarcel &vcp->vc_tolocal); 457119376Smarcel if (error) 458119376Smarcel goto fail; 459119376Smarcel } 460119376Smarcel error = (int)smb_iod_create(vcp); 461119376Smarcel if (error) 462119376Smarcel goto fail; 463119376Smarcel *vcpp = vcp; 464119376Smarcel smb_co_addchild(&smb_vclist, VCTOCP(vcp)); 465119376Smarcel return (0); 46675374Sbp 467119376Smarcel fail: 468119376Smarcel smb_vc_put(vcp, scred); 469119376Smarcel return (error); 47075374Sbp} 47175374Sbp 47275374Sbpstatic void 47375374Sbpsmb_vc_free(struct smb_connobj *cp) 47475374Sbp{ 47575374Sbp struct smb_vc *vcp = CPTOVC(cp); 47675374Sbp 47775374Sbp if (vcp->vc_iod) 47875374Sbp smb_iod_destroy(vcp->vc_iod); 47975374Sbp SMB_STRFREE(vcp->vc_username); 48075374Sbp SMB_STRFREE(vcp->vc_srvname); 48175374Sbp SMB_STRFREE(vcp->vc_pass); 48275374Sbp SMB_STRFREE(vcp->vc_domain); 483124087Stjr if (vcp->vc_mackey) 484124087Stjr free(vcp->vc_mackey, M_SMBTEMP); 48575374Sbp if (vcp->vc_paddr) 48675374Sbp free(vcp->vc_paddr, M_SONAME); 48775374Sbp if (vcp->vc_laddr) 48875374Sbp free(vcp->vc_laddr, M_SONAME); 48975374Sbp if (vcp->vc_tolower) 49075374Sbp iconv_close(vcp->vc_tolower); 49175374Sbp if (vcp->vc_toupper) 49275374Sbp iconv_close(vcp->vc_toupper); 49375374Sbp if (vcp->vc_tolocal) 49475374Sbp iconv_close(vcp->vc_tolocal); 49575374Sbp if (vcp->vc_toserver) 49675374Sbp iconv_close(vcp->vc_toserver); 49775374Sbp smb_co_done(VCTOCP(vcp)); 49875374Sbp smb_sl_destroy(&vcp->vc_stlock); 49975374Sbp free(vcp, M_SMBCONN); 50075374Sbp} 50175374Sbp 50275374Sbp/* 50375374Sbp * Called when use count of VC dropped to zero. 50475374Sbp * VC should be locked on enter with LK_DRAIN. 50575374Sbp */ 50675374Sbpstatic void 50775374Sbpsmb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred) 50875374Sbp{ 50975374Sbp struct smb_vc *vcp = CPTOVC(cp); 51075374Sbp 51175374Sbp smb_vc_disconnect(vcp); 51275374Sbp} 51375374Sbp 51475374Sbpvoid 51587192Sbpsmb_vc_ref(struct smb_vc *vcp) 51675374Sbp{ 51787192Sbp smb_co_ref(VCTOCP(vcp)); 51875374Sbp} 51975374Sbp 52075374Sbpvoid 52175374Sbpsmb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred) 52275374Sbp{ 52375374Sbp smb_co_rele(VCTOCP(vcp), scred); 52475374Sbp} 52575374Sbp 52675374Sbpint 52775374Sbpsmb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred) 52875374Sbp{ 52975374Sbp return smb_co_get(VCTOCP(vcp), flags, scred); 53075374Sbp} 53175374Sbp 53275374Sbpvoid 53375374Sbpsmb_vc_put(struct smb_vc *vcp, struct smb_cred *scred) 53475374Sbp{ 53575374Sbp smb_co_put(VCTOCP(vcp), scred); 53675374Sbp} 53775374Sbp 53875374Sbpint 53987192Sbpsmb_vc_lock(struct smb_vc *vcp, int flags, struct thread *td) 54075374Sbp{ 54187192Sbp return smb_co_lock(VCTOCP(vcp), flags, td); 54275374Sbp} 54375374Sbp 54475374Sbpvoid 54587192Sbpsmb_vc_unlock(struct smb_vc *vcp, int flags, struct thread *td) 54675374Sbp{ 54787192Sbp smb_co_unlock(VCTOCP(vcp), flags, td); 54875374Sbp} 54975374Sbp 55075374Sbpint 55175374Sbpsmb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode) 55275374Sbp{ 55375374Sbp struct ucred *cred = scred->scr_cred; 55475374Sbp 55575374Sbp if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid) 55675374Sbp return 0; 55775374Sbp mode >>= 3; 55875374Sbp if (!groupmember(vcp->vc_grp, cred)) 55975374Sbp mode >>= 3; 56075374Sbp return (vcp->vc_mode & mode) == mode ? 0 : EACCES; 56175374Sbp} 56275374Sbp 56375374Sbpstatic int 56475374Sbpsmb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp) 56575374Sbp{ 56675374Sbp int exact = 1; 56775374Sbp 56875374Sbp if (strcmp(ssp->ss_name, dp->name) != 0) 56975374Sbp return 1; 57075374Sbp if (dp->owner != SMBM_ANY_OWNER) { 57175374Sbp if (ssp->ss_uid != dp->owner) 57275374Sbp return 1; 57375374Sbp } else 57475374Sbp exact = 0; 57575374Sbp if (dp->group != SMBM_ANY_GROUP) { 57675374Sbp if (ssp->ss_grp != dp->group) 57775374Sbp return 1; 57875374Sbp } else 57975374Sbp exact = 0; 58075374Sbp 58175374Sbp if (dp->mode & SMBM_EXACT) { 58275374Sbp if (!exact) 58375374Sbp return 1; 58475374Sbp return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1; 58575374Sbp } 58675374Sbp if (smb_share_access(ssp, dp->scred, dp->mode) != 0) 58775374Sbp return 1; 58875374Sbp return 0; 58975374Sbp} 59075374Sbp 59175374Sbp/* 59275374Sbp * Lookup share in the given VC. Share referenced and locked on return. 59375374Sbp * VC expected to be locked on entry and will be left locked on exit. 59475374Sbp */ 59575374Sbpint 59675374Sbpsmb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp, 59775374Sbp struct smb_cred *scred, struct smb_share **sspp) 59875374Sbp{ 59987192Sbp struct thread *td = scred->scr_td; 600132780Skan struct smb_connobj *scp = NULL; 60175374Sbp struct smb_share *ssp = NULL; 60275374Sbp int error; 60375374Sbp 60475374Sbp *sspp = NULL; 60575374Sbp dp->scred = scred; 606132780Skan SMBCO_FOREACH(scp, VCTOCP(vcp)) { 607132780Skan ssp = (struct smb_share *)scp; 60887192Sbp error = smb_share_lock(ssp, LK_EXCLUSIVE, td); 60975374Sbp if (error) 61075374Sbp continue; 61175374Sbp if (smb_vc_cmpshare(ssp, dp) == 0) 61275374Sbp break; 61387192Sbp smb_share_unlock(ssp, 0, td); 61475374Sbp } 61575374Sbp if (ssp) { 61687192Sbp smb_share_ref(ssp); 61775374Sbp *sspp = ssp; 61875374Sbp error = 0; 61975374Sbp } else 62075374Sbp error = ENOENT; 62175374Sbp return error; 62275374Sbp} 62375374Sbp 62475374Sbpint 62575374Sbpsmb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred) 62675374Sbp{ 62775374Sbp 62875374Sbp return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); 62975374Sbp} 63075374Sbp 63175374Sbp/* 63275374Sbp * Destroy VC to server, invalidate shares linked with it. 63375374Sbp * Transport should be locked on entry. 63475374Sbp */ 63575374Sbpint 63675374Sbpsmb_vc_disconnect(struct smb_vc *vcp) 63775374Sbp{ 63875374Sbp 63975374Sbp smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL); 64075374Sbp return 0; 64175374Sbp} 64275374Sbp 64375374Sbpstatic char smb_emptypass[] = ""; 64475374Sbp 64575374Sbpconst char * 64675374Sbpsmb_vc_getpass(struct smb_vc *vcp) 64775374Sbp{ 64875374Sbp if (vcp->vc_pass) 64975374Sbp return vcp->vc_pass; 65075374Sbp return smb_emptypass; 65175374Sbp} 65275374Sbp 65375374Sbpstatic int 65475374Sbpsmb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip) 65575374Sbp{ 65675374Sbp bzero(vip, sizeof(struct smb_vc_info)); 65775374Sbp vip->itype = SMB_INFO_VC; 65875374Sbp vip->usecount = vcp->obj.co_usecount; 65975374Sbp vip->uid = vcp->vc_uid; 66075374Sbp vip->gid = vcp->vc_grp; 66175374Sbp vip->mode = vcp->vc_mode; 66275374Sbp vip->flags = vcp->obj.co_flags; 66375374Sbp vip->sopt = vcp->vc_sopt; 66475374Sbp vip->iodstate = vcp->vc_iod->iod_state; 66575374Sbp bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey)); 66675374Sbp snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname); 66775374Sbp snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username); 66875374Sbp return 0; 66975374Sbp} 67075374Sbp 67175374Sbpu_short 67275374Sbpsmb_vc_nextmid(struct smb_vc *vcp) 67375374Sbp{ 67475374Sbp u_short r; 67575374Sbp 67675374Sbp SMB_CO_LOCK(&vcp->obj); 67775374Sbp r = vcp->vc_mid++; 67875374Sbp SMB_CO_UNLOCK(&vcp->obj); 67975374Sbp return r; 68075374Sbp} 68175374Sbp 68275374Sbp/* 68375374Sbp * Share implementation 68475374Sbp */ 68575374Sbp/* 68675374Sbp * Allocate share structure and attach it to the given VC 68775374Sbp * Connection expected to be locked on entry. Share will be returned 68875374Sbp * in locked state. 68975374Sbp */ 69075374Sbpint 69175374Sbpsmb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, 69275374Sbp struct smb_cred *scred, struct smb_share **sspp) 69375374Sbp{ 69475374Sbp struct smb_share *ssp; 69587192Sbp struct thread *td = scred->scr_td; 69675374Sbp struct ucred *cred = scred->scr_cred; 69775374Sbp uid_t realuid = cred->cr_uid; 69875374Sbp uid_t uid = shspec->owner; 69975374Sbp gid_t gid = shspec->group; 70075374Sbp int error, isroot; 70175374Sbp 70275374Sbp isroot = smb_suser(cred) == 0; 70375374Sbp /* 70475374Sbp * Only superuser can create shares with different uid and gid 70575374Sbp */ 70675374Sbp if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) 70775374Sbp return EPERM; 70875374Sbp if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) 70975374Sbp return EPERM; 71075374Sbp error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); 71175374Sbp if (!error) { 71275374Sbp smb_share_put(ssp, scred); 71375374Sbp return EEXIST; 71475374Sbp } 71575374Sbp if (uid == SMBM_ANY_OWNER) 71675374Sbp uid = realuid; 71775374Sbp if (gid == SMBM_ANY_GROUP) 71875374Sbp gid = cred->cr_groups[0]; 719111119Simp ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK); 72087192Sbp smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss", td); 72175374Sbp ssp->obj.co_free = smb_share_free; 72275374Sbp ssp->obj.co_gone = smb_share_gone; 72375374Sbp smb_sl_init(&ssp->ss_stlock, "ssstlock"); 72475374Sbp ssp->ss_name = smb_strdup(shspec->name); 72575374Sbp if (shspec->pass && shspec->pass[0]) 72675374Sbp ssp->ss_pass = smb_strdup(shspec->pass); 72775374Sbp ssp->ss_type = shspec->stype; 72875374Sbp ssp->ss_tid = SMB_TID_UNKNOWN; 72975374Sbp ssp->ss_uid = uid; 73075374Sbp ssp->ss_grp = gid; 73175374Sbp ssp->ss_mode = shspec->rights & SMBM_MASK; 73275374Sbp smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); 73375374Sbp *sspp = ssp; 73475374Sbp return 0; 73575374Sbp} 73675374Sbp 73775374Sbpstatic void 73875374Sbpsmb_share_free(struct smb_connobj *cp) 73975374Sbp{ 74075374Sbp struct smb_share *ssp = CPTOSS(cp); 74175374Sbp 74275374Sbp SMB_STRFREE(ssp->ss_name); 74375374Sbp SMB_STRFREE(ssp->ss_pass); 74475374Sbp smb_sl_destroy(&ssp->ss_stlock); 74575374Sbp smb_co_done(SSTOCP(ssp)); 74675374Sbp free(ssp, M_SMBCONN); 74775374Sbp} 74875374Sbp 74975374Sbpstatic void 75075374Sbpsmb_share_gone(struct smb_connobj *cp, struct smb_cred *scred) 75175374Sbp{ 75275374Sbp struct smb_share *ssp = CPTOSS(cp); 75375374Sbp 75475374Sbp smb_smb_treedisconnect(ssp, scred); 75575374Sbp} 75675374Sbp 75775374Sbpvoid 75887192Sbpsmb_share_ref(struct smb_share *ssp) 75975374Sbp{ 76087192Sbp smb_co_ref(SSTOCP(ssp)); 76175374Sbp} 76275374Sbp 76375374Sbpvoid 76475374Sbpsmb_share_rele(struct smb_share *ssp, struct smb_cred *scred) 76575374Sbp{ 76675374Sbp smb_co_rele(SSTOCP(ssp), scred); 76775374Sbp} 76875374Sbp 76975374Sbpint 77075374Sbpsmb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred) 77175374Sbp{ 77275374Sbp return smb_co_get(SSTOCP(ssp), flags, scred); 77375374Sbp} 77475374Sbp 77575374Sbpvoid 77675374Sbpsmb_share_put(struct smb_share *ssp, struct smb_cred *scred) 77775374Sbp{ 77875374Sbp smb_co_put(SSTOCP(ssp), scred); 77975374Sbp} 78075374Sbp 78175374Sbpint 78287192Sbpsmb_share_lock(struct smb_share *ssp, int flags, struct thread *td) 78375374Sbp{ 78487192Sbp return smb_co_lock(SSTOCP(ssp), flags, td); 78575374Sbp} 78675374Sbp 78775374Sbpvoid 78887192Sbpsmb_share_unlock(struct smb_share *ssp, int flags, struct thread *td) 78975374Sbp{ 79087192Sbp smb_co_unlock(SSTOCP(ssp), flags, td); 79175374Sbp} 79275374Sbp 79375374Sbpint 79475374Sbpsmb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode) 79575374Sbp{ 79675374Sbp struct ucred *cred = scred->scr_cred; 79775374Sbp 79875374Sbp if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid) 79975374Sbp return 0; 80075374Sbp mode >>= 3; 80175374Sbp if (!groupmember(ssp->ss_grp, cred)) 80275374Sbp mode >>= 3; 80375374Sbp return (ssp->ss_mode & mode) == mode ? 0 : EACCES; 80475374Sbp} 80575374Sbp 80675374Sbpvoid 80775374Sbpsmb_share_invalidate(struct smb_share *ssp) 80875374Sbp{ 80975374Sbp ssp->ss_tid = SMB_TID_UNKNOWN; 81075374Sbp} 81175374Sbp 81275374Sbpint 81375374Sbpsmb_share_valid(struct smb_share *ssp) 81475374Sbp{ 81575374Sbp return ssp->ss_tid != SMB_TID_UNKNOWN && 81675374Sbp ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid; 81775374Sbp} 81875374Sbp 81975374Sbpconst char* 82075374Sbpsmb_share_getpass(struct smb_share *ssp) 82175374Sbp{ 82275374Sbp struct smb_vc *vcp; 82375374Sbp 82475374Sbp if (ssp->ss_pass) 82575374Sbp return ssp->ss_pass; 82675374Sbp vcp = SSTOVC(ssp); 82775374Sbp if (vcp->vc_pass) 82875374Sbp return vcp->vc_pass; 82975374Sbp return smb_emptypass; 83075374Sbp} 83175374Sbp 83275374Sbpstatic int 83375374Sbpsmb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip) 83475374Sbp{ 83575374Sbp bzero(sip, sizeof(struct smb_share_info)); 83675374Sbp sip->itype = SMB_INFO_SHARE; 83775374Sbp sip->usecount = ssp->obj.co_usecount; 83875374Sbp sip->tid = ssp->ss_tid; 83975374Sbp sip->type= ssp->ss_type; 84075374Sbp sip->uid = ssp->ss_uid; 84175374Sbp sip->gid = ssp->ss_grp; 84275374Sbp sip->mode= ssp->ss_mode; 84375374Sbp sip->flags = ssp->obj.co_flags; 84475374Sbp snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name); 84575374Sbp return 0; 84675374Sbp} 84775374Sbp 84875374Sbp/* 84975374Sbp * Dump an entire tree into sysctl call 85075374Sbp */ 85175374Sbpstatic int 85275374Sbpsmb_sysctl_treedump(SYSCTL_HANDLER_ARGS) 85375374Sbp{ 85487192Sbp struct thread *td = req->td; 85575374Sbp struct smb_cred scred; 856132780Skan struct smb_connobj *scp1, *scp2; 85775374Sbp struct smb_vc *vcp; 85875374Sbp struct smb_share *ssp; 85975374Sbp struct smb_vc_info vci; 86075374Sbp struct smb_share_info ssi; 86175374Sbp int error, itype; 86275374Sbp 86391406Sjhb smb_makescred(&scred, td, td->td_ucred); 864126253Struckman error = sysctl_wire_old_buffer(req, 0); 865126253Struckman if (error) 866126253Struckman return (error); 86787192Sbp error = smb_sm_lockvclist(LK_SHARED, td); 86875374Sbp if (error) 86975374Sbp return error; 870132780Skan SMBCO_FOREACH(scp1, &smb_vclist) { 871132780Skan vcp = (struct smb_vc *)scp1; 87287192Sbp error = smb_vc_lock(vcp, LK_SHARED, td); 87375374Sbp if (error) 87475374Sbp continue; 87575374Sbp smb_vc_getinfo(vcp, &vci); 87675374Sbp error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info)); 87775374Sbp if (error) { 87887192Sbp smb_vc_unlock(vcp, 0, td); 87975374Sbp break; 88075374Sbp } 881132780Skan SMBCO_FOREACH(scp2, VCTOCP(vcp)) { 882132780Skan ssp = (struct smb_share *)scp2; 88387192Sbp error = smb_share_lock(ssp, LK_SHARED, td); 88475374Sbp if (error) { 88575374Sbp error = 0; 88675374Sbp continue; 88775374Sbp } 88875374Sbp smb_share_getinfo(ssp, &ssi); 88987192Sbp smb_share_unlock(ssp, 0, td); 89075374Sbp error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info)); 89175374Sbp if (error) 89275374Sbp break; 89375374Sbp } 89487192Sbp smb_vc_unlock(vcp, 0, td); 89575374Sbp if (error) 89675374Sbp break; 89775374Sbp } 89875374Sbp if (!error) { 89975374Sbp itype = SMB_INFO_NONE; 90075374Sbp error = SYSCTL_OUT(req, &itype, sizeof(itype)); 90175374Sbp } 90287192Sbp smb_sm_unlockvclist(td); 90375374Sbp return error; 90475374Sbp} 905