smb_conn.c revision 9c9af2590af49bb395bc8d2eace0f2d4ea16d165
/*
* Copyright (c) 2000-2001 Boris Popov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Boris Popov.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: smb_conn.c,v 1.27.166.1 2005/05/27 02:35:29 lindak Exp $
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Connection engine.
*/
#include <sys/socketvar.h>
#include <sys/cred_impl.h>
#ifdef APPLE
#include <sys/smb_apple.h>
#include <sys/smb_iconv.h>
#else
#include <netsmb/smb_osdep.h>
#endif
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_tran.h>
#include <netsmb/smb_pass.h>
static struct smb_connobj smb_vclist;
#ifdef APPLE
static void smb_sm_lockvclist(void);
static void smb_sm_unlockvclist(void);
#endif
/* smb_dup_sockaddr moved to smb_tran.c */
int
smb_sm_init(void)
{
return (0);
}
int
smb_sm_idle(void)
{
int error = 0;
SMBSDEBUG("%d connections still active\n",
}
return (error);
}
void
smb_sm_done(void)
{
/*
* XXX Q4BP why are we not iterating on smb_vclist here?
* Because the caller has just called smb_sm_idle() to
* make sure we have no VCs before calling this.
*/
}
/*
* Find a VC identified by the info in vcspec,
* and return it with a "hold", but not locked.
*/
/*ARGSUSED*/
static int
struct smb_vcspec *vcspec,
{
struct smb_connobj *co;
/* var, head, next_field */
/*
* Some things we can check without
* holding the lock (those that are
* set at creation and never change).
*/
/* VCs in other zones are invisibile. */
continue;
/* Also segregate by owner. */
continue;
/* XXX: we ignore the group. Remove vc_gid? */
/* server */
continue;
/* domain+user */
continue;
continue;
/* No new references allowed when _GONE is set */
goto unlock_continue;
goto unlock_continue;
/*
* Success! (Found one we can use)
* Return with it held, unlocked.
* In-line smb_vc_hold here.
*/
co->co_usecount++;
return (0);
/* keep looking. */
}
return (ENOENT);
}
int
struct smb_vcspec *vcspec,
{
int error;
/* Return if smb_sm_lookupvc fails */
if (error != 0)
return (error);
/* Ingore any VC that's not active. */
return (ENOENT);
}
/* Active VC. Return it held. */
return (error);
}
int
struct smb_vcspec *vcspec,
{
top:
if (error) {
/* The VC was not found. Create? */
return (error);
}
if (error) {
/* Could not create? Unusual. */
return (error);
}
/* Note: co_usecount == 1 */
created = 1;
} else
created = 0;
if (created == 0) {
/*
* Found an existing VC. Reuse it, but first,
* wait for any other thread doing setup, etc.
* Note: We hold a reference on the VC.
*/
error = 0;
break;
if (tmo == 0) {
break;
}
}
/* Interrupted? */
if (error)
goto out;
/*
* Was there a vc_kill while we waited?
* If so, this VC is gone. Start over.
*/
goto top;
}
/*
* The possible states here are:
* SMBIOD_ST_VCACTIVE, SMBIOD_ST_DEAD
*
* SMBIOD_ST_VCACTIVE is the normal case,
* where found a connection ready to use.
*
* We may find vc_state == SMBIOD_ST_DEAD
* if a previous session has disconnected.
* In this case, we'd like to reconnect,
* so take over setting up this VC as if
* this thread had created it.
*/
created = 1;
/* Will signal vc_statechg below */
}
}
if (created) {
/*
* We have a NEW VC, held, but not locked.
*/
case SMBIOD_ST_NOTCONN:
/* XXX: Save credentials of caller here? */
/* FALLTHROUGH */
case SMBIOD_ST_RECONNECT:
if (error)
break;
/* FALLTHROUGH */
case SMBIOD_ST_TRANACTIVE:
/* XXX: Just pass vcspec instead? */
vcp->vc_intoklen = 0;
if (error)
break;
/* FALLTHROUGH */
case SMBIOD_ST_NEGOACTIVE:
case SMBIOD_ST_SSNSETUP:
case SMBIOD_ST_VCACTIVE:
/* We can (re)use this VC. */
error = 0;
break;
default:
break;
}
if (error) {
/*
* Leave the VC in a state that allows the
* next open to attempt a new connection.
* This call does the cv_broadcast too,
* so that's in the else part.
*/
} else {
}
}
out:
if (error) {
/*
* Undo the hold from lookupvc,
* or destroy if from vc_create.
*/
} else {
/* Return it held. */
}
return (error);
}
int
struct smb_vcspec *vcspec,
{
int error;
/*
* We have a VC, held, but not locked.
*
* Code from smb_iod_ssnsetup,
* with lots of rework.
*/
case SMBIOD_ST_NEGOACTIVE:
/*
* This is the state we normally find.
* Calling _setup AGAIN to update the
* flags, security info, etc.
*/
if (error)
break;
/* FALLTHROUGH */
case SMBIOD_ST_SSNSETUP:
/* XXX: Just pass vcspec instead? */
vcp->vc_intoklen = 0;
if (error)
break;
/* OK, start the reader thread... */
if (error)
break;
/* FALLTHROUGH */
case SMBIOD_ST_VCACTIVE:
/* We can (re)use this VC. */
error = 0;
break;
default:
break;
}
return (error);
}
int
struct smb_sharespec *shspec,
{
int error;
/*
* The wait for vc_state in smb_sm_negotiate
* _should_ get us a VC in the right state.
*/
return (ENOTCONN);
}
if (error) {
/* The share was not found. Create? */
return (error);
}
if (error) {
/* Could not create? Unusual. */
return (error);
}
/* Note: co_usecount == 1 */
}
/*
* We have a share, held, but not locked.
* Make it connected...
*/
if (!smb_share_valid(ssp))
if (error) {
/*
* Undo hold from lookupshare,
* or destroy if from _create.
*/
} else {
/* Return it held. */
}
return (error);
}
/*
* Common code for connection object
*/
/*ARGSUSED*/
void
{
}
/*
* Called just before free of an object
* of which smb_connobj is a part, i.e.
* _vc_free, _share_free, also sm_done.
*/
void
{
}
static void
struct smb_connobj *parent,
struct smb_connobj *child)
{
/*
* Set the child's pointer to the parent.
* No references yet, so no need to lock.
*/
/*
* Add the child to the parent's list of
* children, and in-line smb_co_hold
*/
parent->co_usecount++;
}
void
{
cp->co_usecount++;
}
/*
* Called via smb_vc_rele, smb_share_rele
*/
void
{
struct smb_connobj *parent;
int old_flags;
co->co_usecount--;
return;
}
co->co_usecount = 0;
/*
* This list of children should be empty now.
* Check this while we're still linked, so
* we have a better chance of debugging.
*/
/*
* OK, this element is going away.
*
* We need to drop the lock on this CO so we can take the
* parent CO lock. The _GONE flag prevents this CO from
* getting new references before we can unlink it from the
* parent list.
*
* The _GONE flag is also used to ensure that the co_gone
* function is called only once. Note that smb_co_kill may
* do this before we get here. If we find that the _GONE
* flag was not already set, then call the co_gone hook
* (smb_share_gone, smb_vc_gone) which will disconnect
* the share or the VC, respectively.
*
* Note the old: smb_co_gone(co, scred);
* is now in-line here.
*/
/*
* If we have a parent (only smb_vclist does not)
* then unlink from parent's list of children.
* We have the only reference to the child.
*/
if (parent) {
}
}
/*
* Now it's safe to free the CO
*/
}
/*
* Finally, if the CO had a parent, decrement
* the parent's hold count for the lost child.
*/
if (parent) {
/*
* Recursive call here (easier for debugging).
* Can only go two levels.
*/
}
}
/*
* Do just the first part of what co_gone does,
* i.e. tree disconnect, or disconnect a VC.
* This is used to forcibly close things.
*/
void
{
int old_flags;
/*
* Do the same "call only once" logic here as in
* smb_co_rele, though it's probably not possible
* for this to be called after smb_co_rele.
*/
/* XXX: Walk list of children and kill those too? */
}
/*
* Session implementation
*/
/*
* This sets the fields that are allowed to change
* when doing a reconnect. Many others are set in
* smb_vc_create and never change afterwards.
* Don't want domain or user to change here.
*/
int
{
/* Just save all the SMBVOPT_ options. */
if (is_ss) {
/* Called from smb_sm_ssnsetup */
/*
* Get p/w hashes from the keychain.
* The password in vcspec->pass is
* fiction, so don't store it.
*/
return (error);
}
/*
* Note: this can be called more than once
* for a given vcp, so free the old strings.
*/
/*
* Don't store the cleartext password
* unless the minauth value was changed
* to allow use of cleartext passwords.
* (By default, this is not allowed.)
*/
if (minauth == SMBVOPT_MINAUTH_NONE)
/* Compute LM and NTLM hashes. */
}
/* Success! */
error = 0;
return (error);
}
/*ARGSUSED*/
int
{
static char objtype[] = "smb_vc";
int error = 0;
/*
* smb_usr_ioc2vcspec, so at this point
* we know the user has right to create
*/
/*
* I think SMB_UID_UNKNOWN is not the correct
* initial value for vc_smbuid. See the long
* comment in smb_iod_sendrq()
*/
/*
* These identify the connection.
*/
#ifdef NOICONVSUPPORT
/*
* REVISIT
*/
if (error)
goto errout;
if (error)
goto errout;
&vcp->vc_toserver);
if (error)
goto errout;
&vcp->vc_tolocal);
if (error)
goto errout;
}
#endif /* NOICONVSUPPORT */
/* This fills in vcp->vc_tdata */
goto errout;
/* Success! */
return (0);
/*
* This will destroy the new vc.
* See: smb_vc_free
*/
return (error);
}
void
{
}
void
{
}
void
{
}
/*
* Normally called via smb_vc_rele()
* after co_usecount drops to zero.
* Also called via: smb_vc_kill()
*
* Shutdown the VC to this server,
* invalidate shares linked with it.
*/
/*ARGSUSED*/
static void
{
/*
* Was smb_vc_disconnect(vcp);
*/
/* Note: smb_iod_destroy in vc_free */
}
static void
{
/*
* The VC has no more references, so
* no locks should be needed here.
* Make sure the IOD is gone.
*/
}
}
/*
* We are not using the iconv routines here. So commenting them for now.
* REVISIT.
*/
#ifdef NOTYETDEFINED
if (vcp->vc_tolower)
if (vcp->vc_toupper)
if (vcp->vc_tolocal)
if (vcp->vc_toserver)
#endif
}
/*
* Lookup share in the given VC. Share referenced and locked on return.
* VC expected to be locked on entry and will be left locked on exit.
*/
/*ARGSUSED*/
int
{
struct smb_connobj *co;
/* var, head, next_field */
/* No new refs if _GONE is set. */
continue;
/* This has a hold, so no need to lock it. */
goto found;
}
return (ENOENT);
/* Return it with a hold. */
return (0);
}
static char smb_emptypass[] = "";
const char *
{
return (smb_emptypass);
}
{
uint16_t r;
return (r);
}
/*
* Get a pointer to the IP address suitable for passing to Trusted
* Extensions find_tpc() routine. Used by smbfs_mount_label_policy().
* Compare this code to nfs_mount_label_policy() if problems arise.
* Without support for direct CIFS-over-TCP, we should always see
* an AF_NETBIOS sockaddr here.
*/
void *
{
case AF_NETBIOS: {
struct sockaddr_nb *snb;
*ipvers = IPV4_VERSION;
/*LINTED*/
return ((void *)&snb->snb_ipaddr);
}
case AF_INET: {
struct sockaddr_in *sin;
*ipvers = IPV4_VERSION;
/*LINTED*/
}
case AF_INET6: {
struct sockaddr_in6 *sin6;
*ipvers = IPV6_VERSION;
/*LINTED*/
}
default:
SMBSDEBUG("invalid address family %d\n",
*ipvers = 0;
return (NULL);
}
}
/*
* Share implementation
*/
/*
* Allocate share structure and attach it to the given VC
* Connection expected to be locked on entry. Share will be returned
* in locked state.
*/
/*ARGSUSED*/
int
{
static char objtype[] = "smb_ss";
return (0);
}
/*
* Normally called via smb_share_rele()
* after co_usecount drops to zero.
*/
static void
{
}
/*
* Normally called via smb_share_rele()
* after co_usecount drops to zero.
* Also called via: smb_share_kill()
*/
static void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
/*
* Returns NON-zero if the share is valid.
* Called with the share locked.
*/
int
{
return (0);
SMBIODEBUG("found TID unknown\n");
}
SMBIODEBUG("wrong genid\n");
}
}
/*
* Connect (or reconnect) a share object.
* Called with the share locked.
*/
int
{
int error;
SMBIODEBUG("alread connected?");
return (0);
}
/*
* Wait for completion of any state changes
* that might be underway.
*/
ssp->ss_conn_waiters++;
ssp->ss_conn_waiters--;
if (tmo == 0) {
/* Interrupt! */
return (EINTR);
}
}
/* Did someone else do it for us? */
return (0);
/*
* OK, we'll do the work.
*/
/* Drop the lock while doing the call. */
if (!error)
/* They can all go ahead! */
if (ssp->ss_conn_waiters)
return (error);
}
const char *
{
return (smb_emptypass);
}
int
smb_share_count(void)
{
int nshares = 0;
/* VCs in other zones are invisibile. */
continue;
/* var, head, next_field */
nshares++;
}
}
return (nshares);
}
/*
* Solaris zones support
*/
/*ARGSUSED*/
void
{
/* good place for a breakpoint */
DEBUG_ENTER("lingering VC");
}
/*
* On zone shutdown, kill any IOD threads still running in this zone.
*/
/* ARGSUSED */
void
{
struct smb_connobj *co;
continue;
/*
* This will close the connection, and
* cause the IOD thread to terminate.
*/
}
}
/*
* On zone destroy, kill any IOD threads and free all resources they used.
*/
/* ARGSUSED */
void
{
struct smb_connobj *co;
/*
* We will repeat what should have already happened
* in zone_shutdown to make things go away.
*
* There should have been an smb_vc_rele call
* by now for all VCs in the zone. If not,
* there's probably more we needed to do in
* the shutdown call.
*/
SMBERROR("%d connections still active\n",
}
/* var, head, next_field */
continue;
/* Debugging */
}
}