redo security (aka. access control)

Break up monolithic SecurityPlugin API into three parts.

1. Authentication.
2. Authorization.
3. Access Control decision.  Left to ChannelProviders
This commit is contained in:
Michael Davidsaver
2019-01-12 11:48:52 -08:00
parent 13cac5f4cc
commit fa484a883a
19 changed files with 665 additions and 670 deletions

View File

@ -773,6 +773,7 @@ INPUT = ../src/client/pv \
../src/rpcService/pv \
../src/utils/pv \
../src/pva/pv \
../src/remote/pv \
.
# This tag can be used to specify the character encoding of the source files

View File

@ -819,6 +819,7 @@ public:
};
struct PeerInfo; // see pv/security.h
class ChannelRequester;
@ -1133,6 +1134,26 @@ public:
* @param connectionState The new connection state.
*/
virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) = 0;
/**
* @brief Return information on connected peer if applicable.
*
* A server-type ChannelProvider will use this method to discover if a remote client
* has provided credentials which may be used in access control decisions.
*
* Default implementation returns NULL.
*
* isConnected()==true and getPeerInfo()==NULL when the ChannelProvider does not provide
* information about the peer. This should be treated as an unauthenticated, anonymous,
* peer.
*
* The returned instance must not change, and a different instance should be returned
* if/when peer information changes (eg. after reconnect).
*
* May return !NULL when !isConnected(). getPeerInfo() must be called _before_
* testing isConnected() in situations where connection state is being polled.
*/
virtual std::tr1::shared_ptr<const PeerInfo> getPeerInfo();
};
//! Used when ChannelProvider::createChannel() is passed a NULL ChannelRequester

View File

@ -8,9 +8,11 @@
#include <epicsGuard.h>
#include <pv/reftrack.h>
#include <pv/valueBuilder.h>
#include <pv/epicsException.h>
#define epicsExportSharedSymbols
#include <pv/pvAccess.h>
#include <pv/security.h>
namespace pvd = epics::pvData;
@ -418,6 +420,11 @@ ChannelRequester::~ChannelRequester()
REFTRACE_DECREMENT(num_instances);
}
PeerInfo::const_shared_pointer ChannelRequester::getPeerInfo()
{
return PeerInfo::const_shared_pointer();
}
std::string DefaultChannelRequester::getRequesterName() { return "DefaultChannelRequester"; }
void DefaultChannelRequester::channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel)

View File

@ -40,6 +40,8 @@ using namespace std;
using namespace epics::pvData;
using namespace epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
namespace {
struct BreakTransport : TransportSender
{
@ -1087,15 +1089,11 @@ void BlockingTCPTransportCodec::internalClose()
Transport::shared_pointer thisSharedPtr = this->shared_from_this();
_context->getTransportRegistry()->remove(thisSharedPtr);
// TODO sync
if (_securitySession)
_securitySession->close();
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug,
"TCP socket to %s is to be closed.",
inetAddressToString(_socketAddress).c_str());
_socketName.c_str());
}
}
@ -1244,7 +1242,6 @@ BlockingTCPTransportCodec::BlockingTCPTransportCodec(bool serverFlag, const Cont
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
_socketName = ipAddrStr;
}
}
@ -1361,24 +1358,28 @@ bool BlockingTCPTransportCodec::verify(epics::pvData::int32 timeoutMs) {
}
void BlockingTCPTransportCodec::verified(epics::pvData::Status const & status) {
epics::pvData::Lock lock(_verifiedMutex);
epics::pvData::Lock lock(_mutex);
if (IS_LOGGABLE(logLevelDebug) && !status.isOK())
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Failed to verify connection to %s: %s.", ipAddrStr, status.getMessage().c_str());
// TODO stack dump
LOG(logLevelDebug, "Failed to verify connection to %s: %s.", _socketName.c_str(), status.getMessage().c_str());
}
{
Guard G(_mutex);
_verified = status.isSuccess();
}
_verifiedEvent.signal();
}
void BlockingTCPTransportCodec::authNZMessage(epics::pvData::PVField::shared_pointer const & data) {
// TODO sync
if (_securitySession)
_securitySession->messageReceived(data);
void BlockingTCPTransportCodec::authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) {
AuthenticationSession::shared_pointer sess;
{
Guard G(_mutex);
sess = _authSession;
}
if (sess)
sess->messageReceived(data);
else
{
char ipAddrStr[48];
@ -1392,7 +1393,7 @@ class SecurityPluginMessageTransportSender : public TransportSender {
public:
POINTER_DEFINITIONS(SecurityPluginMessageTransportSender);
SecurityPluginMessageTransportSender(PVField::shared_pointer const & data) :
SecurityPluginMessageTransportSender(PVStructure::const_shared_pointer const & data) :
_data(data)
{
}
@ -1405,11 +1406,10 @@ public:
}
private:
PVField::shared_pointer _data;
PVStructure::const_shared_pointer _data;
};
void BlockingTCPTransportCodec::sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data) {
// TODO not optimal since it allocates a new object every time
void BlockingTCPTransportCodec::sendSecurityPluginMessage(epics::pvData::PVStructure::const_shared_pointer const & data) {
SecurityPluginMessageTransportSender::shared_pointer spmts(new SecurityPluginMessageTransportSender(data));
enqueueSendRequest(spmts);
}
@ -1426,7 +1426,7 @@ BlockingServerTCPTransportCodec::BlockingServerTCPTransportCodec(
int32_t receiveBufferSize) :
BlockingTCPTransportCodec(true, context, channel, responseHandler,
sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY),
_lastChannelSID(0), _verifyOrVerified(false), _securityRequired(false)
_lastChannelSID(0), _verifyOrVerified(false)
{
// NOTE: priority not yet known, default priority is used to
//register/unregister
@ -1530,29 +1530,38 @@ void BlockingServerTCPTransportCodec::send(ByteBuffer* buffer,
// TODO
buffer->putShort(0x7FFF);
// list of authNZ plugin names
const Context::securityPlugins_t& securityPlugins = _context->getSecurityPlugins();
vector<string> validSPNames;
validSPNames.reserve(securityPlugins.size());
// list of authNZ plugin names advertised to this client
for (Context::securityPlugins_t::const_iterator iter(securityPlugins.begin());
iter != securityPlugins.end(); iter++)
AuthenticationRegistry::list_t plugins;
AuthenticationRegistry::servers().snapshot(plugins); // copy
std::vector<std::string> validSPNames;
validSPNames.reserve(plugins.size()); // assume all will be valid
PeerInfo info;
info.transport = "pva";
info.peer = _socketName;
info.transportVersion = this->getRevision();
// filter plugins which may be used by this peer
for(AuthenticationRegistry::list_t::iterator it(plugins.begin()), end(plugins.end());
it!=end; ++it)
{
SecurityPlugin::shared_pointer securityPlugin = iter->second;
if (securityPlugin->isValidFor(_socketAddress))
validSPNames.push_back(securityPlugin->getId());
info.authority = it->first;
if(it->second->isValidFor(info))
validSPNames.push_back(it->first);
}
size_t validSPCount = validSPNames.size();
SerializeHelper::writeSize(validSPCount, buffer, this);
for (vector<string>::const_iterator iter =
validSPNames.begin();
iter != validSPNames.end(); iter++)
SerializeHelper::writeSize(validSPNames.size(), buffer, this);
for (vector<string>::const_iterator iter(validSPNames.begin()), end(validSPNames.end());
iter != end; iter++)
{
SerializeHelper::serializeString(*iter, buffer, this);
}
// TODO sync
_securityRequired = (validSPCount > 0);
{
Guard G(_mutex);
advertisedAuthPlugins.swap(validSPNames);
}
// send immediately
control->flush(true);
@ -1564,10 +1573,12 @@ void BlockingServerTCPTransportCodec::send(ByteBuffer* buffer,
//
control->startMessage(CMD_CONNECTION_VALIDATED, 0);
pvData::Status sts;
{
Lock lock(_verificationStatusMutex);
_verificationStatus.serialize(buffer, control);
Lock lock(_mutex);
sts = _verificationStatus;
}
sts.serialize(buffer, control);
// send immediately
control->flush(true);
@ -1600,14 +1611,25 @@ void BlockingServerTCPTransportCodec::internalClose() {
destroyAllChannels();
}
void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status)
void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status,
const std::tr1::shared_ptr<PeerInfo>& peer)
{
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], _socketName.c_str());
}
if (!isVerified()) // TODO sync
bool isVerified;
{
Guard G(_mutex);
isVerified = _verified;
if(status.isSuccess())
_peerInfo = peer;
else
_peerInfo.reset();
}
if (!isVerified)
verified(status);
else if (!status.isSuccess())
{
@ -1620,59 +1642,35 @@ void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Sta
}
}
epics::pvData::Status BlockingServerTCPTransportCodec::invalidSecurityPluginNameStatus(Status::STATUSTYPE_ERROR, "invalid security plug-in name");
void BlockingServerTCPTransportCodec::authNZInitialize(const std::string& securityPluginName,
const epics::pvData::PVField::shared_pointer& data)
const epics::pvData::PVStructure::shared_pointer& data)
{
// check if plug-in name is valid
SecurityPlugin::shared_pointer securityPlugin;
AuthenticationPlugin::shared_pointer plugin(AuthenticationRegistry::servers().lookup(securityPluginName));
// attempting the force use of an un-advertised/non-existant plugin is treated as a protocol error.
// We cheat here by assuming the the registry doesn't often change after server start,
// and don't test if securityPluginName is in advertisedAuthPlugins
if(!plugin)
throw std::runtime_error(_socketName+" failing attempt to select non-existant auth. plugin "+securityPluginName);
Context::securityPlugins_t::const_iterator spIter(_context->getSecurityPlugins().find(securityPluginName));
if (spIter != _context->getSecurityPlugins().end())
securityPlugin = spIter->second;
if (!securityPlugin)
{
if (_securityRequired)
{
verified(invalidSecurityPluginNameStatus);
return;
}
else
{
securityPlugin = NoSecurityPlugin::INSTANCE;
PeerInfo::shared_pointer info(new PeerInfo);
info->peer = _socketName;
info->transport = "pva";
info->transportVersion = getRevision();
info->authority = securityPluginName;
if (!plugin->isValidFor(*info))
verified(pvData::Status::error("invalid security plug-in name"));
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "No security plug-in installed, selecting default plug-in '%s' for PVA client: %s.", securityPlugin->getId().c_str(), _socketName.c_str());
}
}
LOG(logLevelDebug, "Accepted security plug-in '%s' for PVA client: %s.", securityPluginName.c_str(), _socketName.c_str());
}
if (!securityPlugin->isValidFor(_socketAddress))
verified(invalidSecurityPluginNameStatus);
AuthenticationSession::shared_pointer sess(plugin->createSession(info, shared_from_this(), data));
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Accepted security plug-in '%s' for PVA client: %s.", securityPluginName.c_str(), ipAddrStr);
}
try
{
// create session
SecurityPluginControl::shared_pointer spc = std::tr1::dynamic_pointer_cast<SecurityPluginControl>(shared_from_this());
// TODO sync
_securitySession = securityPlugin->createSession(_socketAddress, spc, data);
} catch (SecurityException &se) {
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", securityPluginName.c_str(), _socketName.c_str());
}
Status status(Status::STATUSTYPE_ERROR, se.what());
verified(status);
}
Guard G(_mutex);
_authSessionName = securityPluginName;
_authSession.swap(sess);
}
@ -1894,17 +1892,25 @@ void BlockingClientTCPTransportCodec::send(ByteBuffer* buffer,
// QoS (aka connection priority)
buffer->putShort(getPriority());
// TODO sync
if (_securitySession)
std::string pluginName;
AuthenticationSession::shared_pointer session;
{
Guard G(_mutex);
pluginName = _authSessionName;
session = _authSession;
}
if (session)
{
// selected authNZ plug-in name
SerializeHelper::serializeString(_securitySession->getSecurityPlugin()->getId(), buffer, control);
SerializeHelper::serializeString(_authSessionName, buffer, control);
// optional authNZ plug-in initialization data
SerializationHelper::serializeFull(buffer, control, _securitySession->initializationData());
SerializationHelper::serializeFull(buffer, control, session->initializationData());
}
else
{
//TODO: allowed?
// emptry authNZ plug-in name
SerializeHelper::serializeString("", buffer, control);
@ -1926,38 +1932,68 @@ void BlockingClientTCPTransportCodec::send(ByteBuffer* buffer,
void BlockingClientTCPTransportCodec::authNZInitialize(const std::vector<std::string>& offeredSecurityPlugins)
{
if (!offeredSecurityPlugins.empty())
{
const Context::securityPlugins_t& availableSecurityPlugins(_context->getSecurityPlugins());
AuthenticationRegistry& plugins = AuthenticationRegistry::clients();
std::string selectedName;
AuthenticationPlugin::shared_pointer plugin;
for (vector<string>::const_iterator offeredSP = offeredSecurityPlugins.begin();
offeredSP != offeredSecurityPlugins.end(); offeredSP++)
{
Context::securityPlugins_t::const_iterator spi(availableSecurityPlugins.find(*offeredSP));
if (spi != availableSecurityPlugins.end())
{
SecurityPlugin::shared_pointer securityPlugin = spi->second;
if (securityPlugin->isValidFor(_socketAddress))
{
// create session
SecurityPluginControl::shared_pointer spc = std::tr1::dynamic_pointer_cast<SecurityPluginControl>(shared_from_this());
// because of a missing break; the original SecurityPlugin effectively treated the offered list as being
// in order of increasing preference (last is preferred).
// we continue with this because, hey isn't compatibility fun...
// TODO sync
_securitySession = securityPlugin->createSession(_socketAddress, spc, PVField::shared_pointer());
for(std::vector<std::string>::const_reverse_iterator it(offeredSecurityPlugins.rbegin()), end(offeredSecurityPlugins.rend());
it!=end; ++it)
{
plugin = plugins.lookup(*it);
if(plugin) {
selectedName = *it;
break;
}
}
if(!plugin) {
// mis-match and legacy. some early servers (java?) don't advertise any plugins.
// treat this as anonymous
selectedName = "anonymous";
plugin = plugins.lookup(selectedName);
assert(plugin); // fallback required
}
{
PeerInfo::shared_pointer info(new PeerInfo);
info->peer = _socketName; // this is the server name
info->transport = "pva";
info->transportVersion = getRevision();
info->authority = selectedName;
AuthenticationSession::shared_pointer sess(plugin->createSession(info, shared_from_this(), pvData::PVStructure::shared_pointer()));
Guard G(_mutex);
_authSessionName = selectedName;
_authSession = sess;
}
TransportSender::shared_pointer transportSender = std::tr1::dynamic_pointer_cast<TransportSender>(shared_from_this());
enqueueSendRequest(transportSender);
}
void BlockingClientTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status)
void BlockingClientTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status,
const std::tr1::shared_ptr<PeerInfo>& peer)
{
// noop for client side (server will send ConnectionValidation message)
}
void BlockingClientTCPTransportCodec::verified(epics::pvData::Status const & status)
{
AuthenticationSession::shared_pointer sess;
{
Guard G(_mutex);
sess = _authSession;
}
if(sess)
sess->authenticationComplete(status);
this->BlockingTCPTransportCodec::verified(status);
}
}
}
}

View File

@ -124,14 +124,10 @@ public:
// noop
}
virtual void authNZMessage(epics::pvData::PVField::shared_pointer const & data) OVERRIDE FINAL {
virtual void authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) OVERRIDE FINAL {
// noop
}
virtual std::tr1::shared_ptr<SecuritySession> getSecuritySession() const OVERRIDE FINAL {
return std::tr1::shared_ptr<SecuritySession>();
}
// NOTE: this is not yet used for UDP
virtual void setByteOrder(int byteOrder) OVERRIDE FINAL {
// called from receive thread... or before processing

View File

@ -296,7 +296,7 @@ private:
class BlockingTCPTransportCodec:
public AbstractCodec,
public SecurityPluginControl,
public AuthenticationPluginControl,
public std::tr1::enable_shared_from_this<BlockingTCPTransportCodec>
{
@ -428,18 +428,9 @@ public:
virtual void verified(epics::pvData::Status const & status) OVERRIDE;
bool isVerified() const {
return _verified; // TODO sync
}
virtual void authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) OVERRIDE FINAL;
virtual std::tr1::shared_ptr<SecuritySession> getSecuritySession() const OVERRIDE FINAL {
// TODO sync
return _securitySession;
}
virtual void authNZMessage(epics::pvData::PVField::shared_pointer const & data) OVERRIDE FINAL;
virtual void sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data) OVERRIDE FINAL;
virtual void sendSecurityPluginMessage(epics::pvData::PVStructure::const_shared_pointer const & data) OVERRIDE FINAL;
private:
void receiveThread();
@ -467,7 +458,12 @@ protected:
IntrospectionRegistry _incomingIR;
IntrospectionRegistry _outgoingIR;
SecuritySession::shared_pointer _securitySession;
// active authentication exchange, if any
std::string _authSessionName;
AuthenticationSession::shared_pointer _authSession;
public:
// final info, after authentication complete.
PeerInfo::const_shared_pointer _peerInfo;
private:
@ -476,9 +472,12 @@ private:
epics::pvData::int8 _remoteTransportRevision;
epics::pvData::int16 _priority;
protected:
bool _verified;
epics::pvData::Mutex _verifiedMutex;
epics::pvData::Event _verifiedEvent;
public:
mutable epics::pvData::Mutex _mutex;
};
class BlockingServerTCPTransportCodec :
@ -554,9 +553,10 @@ public:
}
virtual void verified(epics::pvData::Status const & status) OVERRIDE FINAL {
_verificationStatusMutex.lock();
{
epicsGuard<epicsMutex> G(_mutex);
_verificationStatus = status;
_verificationStatusMutex.unlock();
}
BlockingTCPTransportCodec::verified(status);
}
@ -565,9 +565,10 @@ public:
}
void authNZInitialize(const std::string& securityPluginName,
const epics::pvData::PVField::shared_pointer& data);
const epics::pvData::PVStructure::shared_pointer& data);
virtual void authenticationCompleted(epics::pvData::Status const & status) OVERRIDE FINAL;
virtual void authenticationCompleted(epics::pvData::Status const & status,
const std::tr1::shared_ptr<PeerInfo>& peer) OVERRIDE FINAL;
virtual void send(epics::pvData::ByteBuffer* buffer,
TransportSendControl* control) OVERRIDE FINAL;
@ -595,13 +596,10 @@ private:
mutable epics::pvData::Mutex _channelsMutex;
epics::pvData::Status _verificationStatus;
epics::pvData::Mutex _verificationStatusMutex;
bool _verifyOrVerified;
bool _securityRequired;
static epics::pvData::Status invalidSecurityPluginNameStatus;
std::vector<std::string> advertisedAuthPlugins;
};
@ -673,8 +671,10 @@ public:
void authNZInitialize(const std::vector<std::string>& offeredSecurityPlugins);
virtual void authenticationCompleted(epics::pvData::Status const & status) OVERRIDE FINAL;
virtual void authenticationCompleted(epics::pvData::Status const & status,
const std::tr1::shared_ptr<PeerInfo>& peer) OVERRIDE FINAL;
virtual void verified(epics::pvData::Status const & status) OVERRIDE FINAL;
protected:
virtual void internalClose() OVERRIDE FINAL;
@ -719,8 +719,6 @@ private:
* Responsive transport notify.
*/
void responsiveTransport();
epics::pvData::Mutex _mutex;
};
}

View File

@ -291,13 +291,12 @@ public:
* Pass data to the active security plug-in session.
* @param data the data (any data), can be <code>null</code>.
*/
virtual void authNZMessage(epics::pvData::PVField::shared_pointer const & data) = 0;
virtual std::tr1::shared_ptr<SecuritySession> getSecuritySession() const = 0;
virtual void authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) = 0;
};
class Channel;
class SecurityPlugin;
class AuthenticationRegistry;
/**
* Not public IF, used by Transports, etc.
@ -317,14 +316,6 @@ public:
virtual Configuration::const_shared_pointer getConfiguration() = 0;
typedef std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> > securityPlugins_t;
/**
* Get map of available security plug-ins.
* @return the map of available security plug-ins
*/
virtual const securityPlugins_t& getSecurityPlugins() = 0;
///
/// due to ClientContextImpl
///

View File

@ -7,6 +7,72 @@
#ifndef SECURITY_H
#define SECURITY_H
/** @page pva_security PVA Security
*
* Summary of PVA auth. process.
*
@msc
arcgradient = 8;
CP [label="Client Plugin"], CS [label="Client Session"], C [label="Client"], S [label="Server"], SS [label="Server Session"], SP [label="Server Plugin"];
C:>S [label="Opens TCP connection"];
S box SP [label="Server lists plugins"];
S=>SP [label="isValidFor()"];
C<:S [label="CONNECTION_VALIDATION"];
CP box C [label="Client lists plugins"];
CP box C [label="Client select plugin"];
CP<=C [label="createSession()"];
CP>>C [label="Returns Session"];
CS<=C [label="initializationData()"];
CS>>C [label="Initial payload"];
C:>S [label="CONNECTION_VALIDATION"];
S=>SP [label="createSession()"];
S<<SP [label="Returns Session"];
--- [label="Optional (Repeatable)"];
S<=SS [label="sendSecurityPluginMessage()"];
C<:S [label="AUTHZ"];
CS<=C [label="messageReceived()"];
...;
CS=>C [label="sendSecurityPluginMessage()"];
C:>S [label="AUTHZ"];
S=>SS [label="messageReceived()"];
...;
--- [label="Completion"];
S<=SS [label="authenticationCompleted()"];
C<:S [label="CONNECTION_VALIDATED"];
CS<=C [label="authenticationComplete()"];
@endmsc
*
* Ownership
*
@dot
digraph authplugin {
External;
AuthenticationRegistry [shape=box];
AuthenticationPlugin [shape=box];
AuthenticationPluginControl [shape=box];
AuthenticationSession [shape=box];
External -> AuthenticationRegistry;
AuthenticationRegistry -> AuthenticationPlugin;
External -> AuthenticationSession;
AuthenticationSession -> AuthenticationPluginControl [color=green, style=dashed];
External -> AuthenticationPluginControl;
AuthenticationPluginControl -> AuthenticationSession;
AuthenticationPlugin -> AuthenticationSession [style=dashed];
}
@enddot
*
* Locking
*
* All methods of AuthenticationSession are called from a single thread.
* Methods of AuthenticationPlugin and AuthenticationPluginControl can be called
* from any threads.
*
* AuthenticationPluginControl is an Operation, AuthenticationSession is a Requester.
* @see provider_roles_requester_locking
*/
#ifdef epicsExportSharedSymbols
# define securityEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
@ -14,6 +80,7 @@
#include <string>
#include <osiSock.h>
#include <epicsMutex.h>
#include <pv/status.h>
#include <pv/pvData.h>
@ -34,426 +101,166 @@
namespace epics {
namespace pvAccess {
// notify client only on demand, configurable via pvRequest
// add the following method to ChannelRequest:
// void credentialsChanged(std::vector<BitSet> credentials);
// pvAccess message: channel client id, ioid (if invalid then it's for channel) and array of bitSets
// or leave to the plugin?
// when clients gets initial credentialsChanged call before create is called
// and then on each change
class epicsShareClass ChannelSecuritySession {
public:
POINTER_DEFINITIONS(ChannelSecuritySession);
virtual ~ChannelSecuritySession() {}
/// closes this session
virtual void close() = 0;
// for every authroizeCreate... a release() must be called
virtual void release(pvAccessID ioid) = 0;
// bitSet w/ one bit
virtual epics::pvData::Status authorizeCreateChannelProcess(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
virtual epics::pvData::Status authorizeProcess(pvAccessID ioid) = 0;
// bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
virtual epics::pvData::Status authorizeCreateChannelGet(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
virtual epics::pvData::Status authorizeGet(pvAccessID ioid) = 0;
// read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
// write: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
virtual epics::pvData::Status authorizeCreateChannelPut(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
virtual epics::pvData::Status authorizePut(
pvAccessID ioid,
epics::pvData::PVStructure::shared_pointer const & dataToPut,
epics::pvData::BitSet::shared_pointer const & fieldsToPut) = 0;
// read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
// write: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
// process: bitSet w/ one bit (allowed, not allowed)
virtual epics::pvData::Status authorizeCreateChannelPutGet(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
virtual epics::pvData::Status authorizePutGet(
pvAccessID ioid,
epics::pvData::PVStructure::shared_pointer const & dataToPut,
epics::pvData::BitSet::shared_pointer const & fieldsToPut) = 0;
// bitSet w/ one bit
virtual epics::pvData::Status authorizeCreateChannelRPC(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
// one could authorize per operation basis
virtual epics::pvData::Status authorizeRPC(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & arguments) = 0;
// read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
virtual epics::pvData::Status authorizeCreateMonitor(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
virtual epics::pvData::Status authorizeMonitor(pvAccessID ioid) = 0;
// read: bitSet w/ one bit (allowed, not allowed) and rest put/get/set length
virtual epics::pvData::Status authorizeCreateChannelArray(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
// use authorizeGet
virtual epics::pvData::Status authorizePut(pvAccessID ioid, epics::pvData::PVArray::shared_pointer const & dataToPut) = 0;
virtual epics::pvData::Status authorizeSetLength(pvAccessID ioid) = 0;
// introspection authorization
virtual epics::pvData::Status authorizeGetField(pvAccessID ioid, std::string const & subField) = 0;
};
class SecurityPlugin;
class SecurityException: public std::runtime_error {
public:
explicit SecurityException(std::string const & what): std::runtime_error(what) {}
};
class epicsShareClass SecuritySession {
public:
POINTER_DEFINITIONS(SecuritySession);
virtual ~SecuritySession() {}
// optional (can be null) initialization data for the remote party
// client to server
virtual epics::pvData::PVField::shared_pointer initializationData() = 0;
// get parent
virtual std::tr1::shared_ptr<SecurityPlugin> getSecurityPlugin() = 0;
// can be called any time, for any reason
virtual void messageReceived(epics::pvData::PVField::shared_pointer const & data) = 0;
/// closes this session
virtual void close() = 0;
// notification to the client on allowed requests (bitSet, a bit per request)
virtual ChannelSecuritySession::shared_pointer createChannelSession(std::string const & channelName) = 0;
};
class epicsShareClass SecurityPluginControl {
public:
POINTER_DEFINITIONS(SecurityPluginControl);
virtual ~SecurityPluginControl() {}
// can be called any time, for any reason
virtual void sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data) = 0;
// if Status.isSuccess() == false,
// pvAccess will send status to the client and close the connection
// can be called more then once (in case of re-authentication process)
virtual void authenticationCompleted(epics::pvData::Status const & status) = 0;
};
class epicsShareClass SecurityPlugin {
public:
POINTER_DEFINITIONS(SecurityPlugin);
virtual ~SecurityPlugin() {}
/**
* Short, unique name for the plug-in, used to identify the plugin.
* @return the ID.
*/
virtual std::string getId() const = 0;
/**
* Description of the security plug-in.
* @return the description string.
*/
virtual std::string getDescription() const = 0;
/**
* Check whether the remote instance with given network address is
* valid to use this security plug-in to authNZ.
* @param remoteAddress
* @return <code>true</code> if this security plugin can be used for remote instance.
*/
virtual bool isValidFor(osiSockAddr const & remoteAddress) const = 0;
/**
* Create a security session (usually one per transport).
* @param remoteAddress
* @return a new session.
* @throws SecurityException
/** @brief Information provded by a client to a server-type ChannelProvider.
*
* @warning a Ref. loop is created if the SecuritySession stores a pointer to 'control'
* All peers must be identified by a peer name, which may be a network address (IP+port#) and transport.
* Peer names must be unique for a given transport.
*
* Transport names include:
*
* # "local" in-process client. Peer name is optional and arbitrary. Must set local flag.
* # "pva" PVA over TCP. Used by PVA client provider. Peer name is IP address and TCP port number as "XXX.XXX.XXX.XXX:YYYYY".
*
* Authority names include:
*
* "anonymous" - No credentials provided. Must not set identified flag.
* "plain" - Unauthenticated credential.
*/
// authentication must be done immediately when connection is established (timeout 3seconds),
// later on authentication process can be repeated
// the server and the client can exchange (arbitrary number) of messages using SecurityPluginControl.sendMessage()
// the process completion must be notified by calling AuthenticationControl.completed()
virtual SecuritySession::shared_pointer createSession(
osiSockAddr const & remoteAddress,
SecurityPluginControl::shared_pointer const & control,
epics::pvData::PVField::shared_pointer const & data) = 0;
struct epicsShareClass PeerInfo {
POINTER_DEFINITIONS(PeerInfo);
static size_t num_instances;
std::string peer; //!< network address of remote peer. eg. "192.168.1.1:5075".
std::string transport; //!< transport protocol used eg. "pva". Must not be empty.
std::string authority; //!< authentication mechanism used. eg. "anonymous" or "gssapi". Must not be empty.
std::string realm; //!< scope of authority. eg. "mylab.gov"
std::string account; //!< aka. user name
//! NULL or extra authority specific information.
pvData::PVStructure::const_shared_pointer aux;
typedef std::set<std::string> roles_t;
//! Set of strings which may be used to modify access control decisions.
roles_t roles;
unsigned transportVersion; //!< If applicable, the protocol minor version number
// attributes for programatic consumption
bool local; //!< Short-hand for transport=="local"
bool identified; //!< Short-hand for authority!="anonymous"
PeerInfo();
virtual ~PeerInfo();
};
class epicsShareClass NoSecurityPlugin :
public SecurityPlugin,
public SecuritySession,
public ChannelSecuritySession,
public std::tr1::enable_shared_from_this<NoSecurityPlugin> {
protected:
NoSecurityPlugin() {}
public:
POINTER_DEFINITIONS(NoSecurityPlugin);
static NoSecurityPlugin::shared_pointer INSTANCE;
virtual ~NoSecurityPlugin() {}
// optional (can be null) initialization data for the remote party
// client to server
virtual epics::pvData::PVField::shared_pointer initializationData() {
return epics::pvData::PVField::shared_pointer();
}
// get parent
virtual std::tr1::shared_ptr<SecurityPlugin> getSecurityPlugin() {
return shared_from_this();
}
// can be called any time, for any reason
virtual void messageReceived(epics::pvData::PVField::shared_pointer const & data) {
// noop
}
/// closes this session
virtual void close() {
// noop
}
// notification to the client on allowed requests (bitSet, a bit per request)
virtual ChannelSecuritySession::shared_pointer createChannelSession(std::string const & /*channelName*/)
/** A particular authentication exchange. See AuthenticationPlugin::createSession()
*
* @note Must not hold a strong reference to AuthenticationPluginControl
*/
class epicsShareClass AuthenticationSession
{
return shared_from_this();
}
public:
POINTER_DEFINITIONS(AuthenticationSession);
/**
* Short, unique name for the plug-in, used to identify the plugin.
* @return the ID.
virtual ~AuthenticationSession();
//! For client plugins only, call to find the payload returned with CONNECTION_VALIDATION.
//! May return NULL.
virtual epics::pvData::PVStructure::const_shared_pointer initializationData()
{ return epics::pvData::PVStructure::const_shared_pointer(); }
//! Called when an AUTHZ message is recieved from the peer.
//! See AuthenticationPluginControl::sendSecurityPluginMessage().
//! callee accepts ownership of data, which will not be modified.
virtual void messageReceived(epics::pvData::PVStructure::const_shared_pointer const & data) {}
/** For client plugins only. Notification that server has declared the exchange complete.
* @param status Check Status::isSuccess()
* @param peer Final information about pe
*/
virtual std::string getId() const {
return "none";
}
/**
* Description of the security plug-in.
* @return the description string.
*/
virtual std::string getDescription() const {
return "No security plug-in";
}
/**
* Check whether the remote instance with given network address is
* valid to use this security plug-in to authNZ.
* @param remoteAddress
* @return <code>true</code> if this security plugin can be used for remote instance.
*/
virtual bool isValidFor(osiSockAddr const & /*remoteAddress*/) const {
return true;
}
/**
* Create a security session (usually one per transport).
* @param remoteAddress
* @return a new session.
* @throws SecurityException
*/
// authentication must be done immediately when connection is established (timeout 3seconds),
// later on authentication process can be repeated
// the server and the client can exchange (arbitrary number) of messages using SecurityPluginControl.sendMessage()
// the process completion must be notified by calling AuthenticationControl.completed()
virtual SecuritySession::shared_pointer createSession(
osiSockAddr const & /*remoteAddress*/,
SecurityPluginControl::shared_pointer const & control,
epics::pvData::PVField::shared_pointer const & /*data*/) {
control->authenticationCompleted(epics::pvData::Status::Ok);
return shared_from_this();
}
// for every authroizeCreate... a release() must be called
virtual void release(pvAccessID ioid) {
// noop
}
// bitSet w/ one bit
virtual epics::pvData::Status authorizeCreateChannelProcess(
pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) {
return epics::pvData::Status::Ok;
}
virtual epics::pvData::Status authorizeProcess(pvAccessID /*ioid*/) {
return epics::pvData::Status::Ok;
}
// bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
virtual epics::pvData::Status authorizeCreateChannelGet(
pvAccessID /*ioid*/, epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) {
return epics::pvData::Status::Ok;
}
virtual epics::pvData::Status authorizeGet(pvAccessID /*ioid*/) {
return epics::pvData::Status::Ok;
}
// read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
// write: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
virtual epics::pvData::Status authorizeCreateChannelPut(
pvAccessID /*ioid*/, epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) {
return epics::pvData::Status::Ok;
}
virtual epics::pvData::Status authorizePut(
pvAccessID /*ioid*/,
epics::pvData::PVStructure::shared_pointer const & /*dataToPut*/,
epics::pvData::BitSet::shared_pointer const & /*fieldsToPut*/) {
return epics::pvData::Status::Ok;
}
// read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
// write: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
// process: bitSet w/ one bit (allowed, not allowed)
virtual epics::pvData::Status authorizeCreateChannelPutGet(
pvAccessID /*ioid*/, epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) {
return epics::pvData::Status::Ok;
}
virtual epics::pvData::Status authorizePutGet(
pvAccessID /*ioid*/,
epics::pvData::PVStructure::shared_pointer const & /*dataToPut*/,
epics::pvData::BitSet::shared_pointer const & /*fieldsToPut*/) {
return epics::pvData::Status::Ok;
}
// bitSet w/ one bit
virtual epics::pvData::Status authorizeCreateChannelRPC(
pvAccessID /*ioid*/, epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) {
return epics::pvData::Status::Ok;
}
// one could authorize per operation basis
virtual epics::pvData::Status authorizeRPC(
pvAccessID /*ioid*/, epics::pvData::PVStructure::shared_pointer const & /*arguments*/) {
return epics::pvData::Status::Ok;
}
// read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field
virtual epics::pvData::Status authorizeCreateMonitor(
pvAccessID /*ioid*/, epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) {
return epics::pvData::Status::Ok;
}
virtual epics::pvData::Status authorizeMonitor(pvAccessID /*ioid*/) {
return epics::pvData::Status::Ok;
}
// read: bitSet w/ one bit (allowed, not allowed) and rest put/get/set length
virtual epics::pvData::Status authorizeCreateChannelArray(
pvAccessID /*ioid*/, epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) {
return epics::pvData::Status::Ok;
}
// use authorizeGet
virtual epics::pvData::Status authorizePut(
pvAccessID /*ioid*/, epics::pvData::PVArray::shared_pointer const & /*dataToPut*/) {
return epics::pvData::Status::Ok;
}
virtual epics::pvData::Status authorizeSetLength(pvAccessID /*ioid*/) {
return epics::pvData::Status::Ok;
}
// introspection authorization
virtual epics::pvData::Status authorizeGetField(pvAccessID /*ioid*/, std::string const & /*subField*/) {
return epics::pvData::Status::Ok;
}
virtual void authenticationComplete(const epics::pvData::Status& status) {}
};
class epicsShareClass CAClientSecurityPlugin :
public NoSecurityPlugin {
protected:
epics::pvData::PVStructure::shared_pointer m_userAndHost;
CAClientSecurityPlugin();
//! Callbacks for use by AuthenticationSession
class epicsShareClass AuthenticationPluginControl
{
public:
POINTER_DEFINITIONS(CAClientSecurityPlugin);
POINTER_DEFINITIONS(AuthenticationPluginControl);
virtual ~AuthenticationPluginControl();
static CAClientSecurityPlugin::shared_pointer INSTANCE;
//! Send AUTHZ to peer with payload.
//! caller gives up ownership of data, which must not be modified.
virtual void sendSecurityPluginMessage(epics::pvData::PVStructure::const_shared_pointer const & data) = 0;
virtual epics::pvData::PVField::shared_pointer initializationData() {
return m_userAndHost;
}
virtual std::string getId() const {
return "ca";
}
virtual std::string getDescription() const {
return "CA client security plug-in";
}
/** Called by server plugin to indicate the the exchange has completed.
*
* @param status If !status.isSuccess() then the connection will be closed without being used.
* @param peer Partially initialized PeerInfo. See AuthenticationPlugin::createSession().
* PeerInfo::realm and/or PeerInfo::account will now be considered valid.
* Caller transfers ownership to callee, which may modify.
*/
virtual void authenticationCompleted(const epics::pvData::Status& status,
const std::tr1::shared_ptr<PeerInfo>& peer) = 0;
};
class epicsShareClass SecurityPluginRegistry
//! Actor through which authentication exchanges are initiated.
class epicsShareClass AuthenticationPlugin
{
EPICS_NOT_COPYABLE(SecurityPluginRegistry)
public:
POINTER_DEFINITIONS(AuthenticationPlugin);
virtual ~AuthenticationPlugin();
static SecurityPluginRegistry& instance()
/** Allow this plugin to be advertised to a particular peer.
*
* At this point the PeerInfo has only been partially initialized with
* transport/protocol specific information: PeerInfo::peer, PeerInfo::transport, and PeerInfo::transportVersion.
*/
virtual bool isValidFor(const PeerInfo& peer) const { return true; }
/** Begin a new session with a peer.
*
* @param peer Partially initialized PeerInfo. See isValidFor().
* PeerInfo::authority is also set.
* Caller transfers ownership to callee, which may modify.
* @param control callee uses to asynchronously continue, and complete the session.
* @param data Always NULL for client-type plugins. For server-type plugins,
* the result of initializationData() from the peer
*/
virtual std::tr1::shared_ptr<AuthenticationSession> createSession(
const std::tr1::shared_ptr<PeerInfo>& peer,
std::tr1::shared_ptr<AuthenticationPluginControl> const & control,
epics::pvData::PVStructure::shared_pointer const & data) = 0;
};
/** Registry(s) for plugins
*/
class epicsShareClass AuthenticationRegistry
{
static SecurityPluginRegistry thisInstance;
return thisInstance;
}
typedef std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> > securityPlugins_t;
securityPlugins_t& getClientSecurityPlugins()
{
return m_clientSecurityPlugins;
}
securityPlugins_t& getServerSecurityPlugins()
{
return m_serverSecurityPlugins;
}
void installClientSecurityPlugin(std::tr1::shared_ptr<SecurityPlugin> plugin)
{
m_clientSecurityPlugins[plugin->getId()] = plugin;
LOG(epics::pvAccess::logLevelDebug, "Client security plug-in '%s' installed.", plugin->getId().c_str());
}
void installServerSecurityPlugin(std::tr1::shared_ptr<SecurityPlugin> plugin)
{
m_serverSecurityPlugins[plugin->getId()] = plugin;
LOG(epics::pvAccess::logLevelDebug, "Server security plug-in '%s' installed.", plugin->getId().c_str());
}
EPICS_NOT_COPYABLE(AuthenticationRegistry) // would need locking
public:
POINTER_DEFINITIONS(AuthenticationRegistry);
private:
SecurityPluginRegistry();
typedef std::map<int, std::pair<std::string, AuthenticationPlugin::shared_pointer> > map_t;
map_t map;
mutable epicsMutex mutex;
public:
typedef std::vector<map_t::mapped_type> list_t;
securityPlugins_t m_clientSecurityPlugins;
securityPlugins_t m_serverSecurityPlugins;
//! The client side of the conversation
static AuthenticationRegistry& clients();
//! The server side of the conversation
static AuthenticationRegistry& servers();
AuthenticationRegistry() {}
~AuthenticationRegistry();
//! Save a copy of the current registry in order of increasing priority
void snapshot(list_t& plugmap) const;
/** @brief Add a new plugin to this registry.
*
@param prio Order in which plugins are considered. highest is preferred.
@param name Name under which this plugin will be known
@param plugin Plugin instance
*/
void add(int prio, const std::string& name, const AuthenticationPlugin::shared_pointer& plugin);
//! Remove an existing entry. Remove true if the entry was actually removed.
bool remove(const AuthenticationPlugin::shared_pointer& plugin);
//! Fetch a single plugin explicitly by name.
//! @returns NULL if no entry for this name is available.
AuthenticationPlugin::shared_pointer lookup(const std::string& name) const;
};
}

View File

@ -82,7 +82,7 @@ public:
* Serialize optional PVField.
* @param buffer data buffer.
*/
static void serializeFull(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control, epics::pvData::PVField::shared_pointer const & pvField);
static void serializeFull(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control, const epics::pvData::PVField::const_shared_pointer &pvField);
};

View File

@ -6,50 +6,222 @@
#include <osiProcess.h>
#include <epicsThread.h>
#include <epicsGuard.h>
#include <pv/epicsException.h>
#include <pv/reftrack.h>
#define epicsExportSharedSymbols
#include <pv/securityImpl.h>
using namespace epics::pvData;
using namespace epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
NoSecurityPlugin::shared_pointer NoSecurityPlugin::INSTANCE(new NoSecurityPlugin());
namespace {
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
CAClientSecurityPlugin::shared_pointer CAClientSecurityPlugin::INSTANCE(new CAClientSecurityPlugin());
CAClientSecurityPlugin::CAClientSecurityPlugin()
pvd::StructureConstPtr userAndHostStructure(
pvd::FieldBuilder::begin()->
add("user", pvd::pvString)->
add("host", pvd::pvString)->
createStructure()
);
struct SimpleSession : public pva::AuthenticationSession
{
StructureConstPtr userAndHostStructure =
getFieldCreate()->createFieldBuilder()->
add("user", pvString)->
add("host", pvString)->
createStructure();
const pvd::PVStructure::const_shared_pointer initdata;
m_userAndHost = getPVDataCreate()->createPVStructure(userAndHostStructure);
SimpleSession(const pvd::PVStructure::const_shared_pointer& data) :initdata(data) {}
virtual ~SimpleSession() {}
//
// user name
//
virtual epics::pvData::PVStructure::const_shared_pointer initializationData() OVERRIDE FINAL
{ return initdata; }
};
char buffer[256];
struct AnonPlugin : public pva::AuthenticationPlugin
{
const bool server;
std::string userName;
if (osiGetUserName(buffer, sizeof(buffer)) == osiGetUserNameSuccess)
userName = buffer;
// TODO more error handling
AnonPlugin(bool server) :server(server) {}
virtual ~AnonPlugin() {}
m_userAndHost->getSubFieldT<PVString>("user")->put(userName);
//
// host name
//
std::string hostName;
if (gethostname(buffer, sizeof(buffer)) == 0)
hostName = buffer;
// TODO more error handling
m_userAndHost->getSubFieldT<PVString>("host")->put(buffer);
virtual std::tr1::shared_ptr<pva::AuthenticationSession> createSession(
const std::tr1::shared_ptr<pva::PeerInfo>& peer,
std::tr1::shared_ptr<pva::AuthenticationPluginControl> const & control,
epics::pvData::PVStructure::shared_pointer const & data) OVERRIDE FINAL
{
std::tr1::shared_ptr<SimpleSession> sess(new SimpleSession(pvd::PVStructure::const_shared_pointer())); // no init data
if(server) {
peer->identified = false;
peer->account = "anonymous";
control->authenticationCompleted(pvd::Status::Ok, peer);
}
return sess;
}
};
struct CAPlugin : public pva::AuthenticationPlugin
{
const bool server;
// fully const after ctor
const pvd::PVStructurePtr user;
CAPlugin(bool server)
:server(server)
,user(userAndHostStructure->build())
{
std::vector<char> buffer(256u);
if(osiGetUserName(&buffer[0], buffer.size()) != osiGetUserNameSuccess)
throw std::runtime_error("Unable to determine user account name");
buffer[buffer.size()-1] = '\0';
user->getSubFieldT<pvd::PVString>("user")->put(&buffer[0]);
// use of unverified host name is considered deprecated.
// use PeerInfo::peer instead.
if (gethostname(&buffer[0], buffer.size()) != 0)
throw std::runtime_error("Unable to determine host name");
buffer[buffer.size()-1] = '\0';
user->getSubFieldT<pvd::PVString>("host")->put(&buffer[0]);
}
virtual ~CAPlugin() {}
virtual std::tr1::shared_ptr<pva::AuthenticationSession> createSession(
const std::tr1::shared_ptr<pva::PeerInfo>& peer,
std::tr1::shared_ptr<pva::AuthenticationPluginControl> const & control,
epics::pvData::PVStructure::shared_pointer const & data) OVERRIDE FINAL
{
std::tr1::shared_ptr<SimpleSession> sess(new SimpleSession(user)); // no init data
if(server) {
peer->identified = true;
peer->account = data->getSubFieldT<pvd::PVString>("user")->get();
peer->aux = pvd::getPVDataCreate()->createPVStructure(data); // clone to ensure it won't be modified
control->authenticationCompleted(pvd::Status::Ok, peer);
}
return sess;
}
};
} // namespace
namespace epics {
namespace pvAccess {
size_t PeerInfo::num_instances;
PeerInfo::PeerInfo()
:transportVersion(0u)
,local(false)
,identified(false)
{
REFTRACE_INCREMENT(num_instances);
}
PeerInfo::~PeerInfo()
{
REFTRACE_DECREMENT(num_instances);
}
AuthenticationSession::~AuthenticationSession() {}
AuthenticationPluginControl::~AuthenticationPluginControl() {}
AuthenticationPlugin::~AuthenticationPlugin() {}
AuthenticationRegistry::~AuthenticationRegistry() {}
namespace {
struct authGbl_t {
mutable epicsMutex mutex;
AuthenticationRegistry servers, clients;
} *authGbl;
void authGblInit(void *)
{
authGbl = new authGbl_t;
{
AnonPlugin::shared_pointer plugin(new AnonPlugin(true));
authGbl->servers.add(-1024, "anonymous", plugin);
}
{
AnonPlugin::shared_pointer plugin(new AnonPlugin(false));
authGbl->clients.add(-1024, "anonymous", plugin);
}
{
CAPlugin::shared_pointer plugin(new CAPlugin(true));
authGbl->servers.add(0, "ca", plugin);
}
{
CAPlugin::shared_pointer plugin(new CAPlugin(false));
authGbl->clients.add(0, "ca", plugin);
}
epics::registerRefCounter("PeerInfo", &PeerInfo::num_instances);
}
epicsThreadOnceId authGblOnce = EPICS_THREAD_ONCE_INIT;
} // namespace
AuthenticationRegistry& AuthenticationRegistry::clients()
{
epicsThreadOnce(&authGblOnce, &authGblInit, 0);
assert(authGbl);
return authGbl->clients;
}
AuthenticationRegistry& AuthenticationRegistry::servers()
{
epicsThreadOnce(&authGblOnce, &authGblInit, 0);
assert(authGbl);
return authGbl->servers;
}
void AuthenticationRegistry::snapshot(list_t &plugmap) const
{
plugmap.clear();
Guard G(mutex);
plugmap.reserve(map.size());
for(map_t::const_iterator it(map.begin()), end(map.end()); it!=end; ++it) {
plugmap.push_back(it->second);
}
}
void AuthenticationRegistry::add(int prio, const std::string& name,
const AuthenticationPlugin::shared_pointer& plugin)
{
Guard G(mutex);
if(map.find(prio)!=map.end())
THROW_EXCEPTION2(std::logic_error, "Authentication plugin already registered with this priority");
map[prio] = std::make_pair(name, plugin);
}
bool AuthenticationRegistry::remove(const AuthenticationPlugin::shared_pointer& plugin)
{
Guard G(mutex);
for(map_t::iterator it(map.begin()), end(map.end()); it!=end; ++it) {
if(it->second.second==plugin) {
map.erase(it);
return true;
}
}
return false;
}
AuthenticationPlugin::shared_pointer AuthenticationRegistry::lookup(const std::string& name) const
{
Guard G(mutex);
// assuming the number of plugins is small, we don't index by name.
for(map_t::const_iterator it(map.begin()), end(map.end()); it!=end; ++it) {
if(it->second.first==name)
return it->second.second;
}
return AuthenticationPlugin::shared_pointer();
}
void AuthNZHandler::handleResponse(osiSockAddr* responseFrom,
@ -61,15 +233,17 @@ void AuthNZHandler::handleResponse(osiSockAddr* responseFrom,
{
ResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer);
epics::pvData::PVField::shared_pointer data =
SerializationHelper::deserializeFull(payloadBuffer, transport.get());
pvd::PVStructure::shared_pointer data;
{
pvd::PVField::shared_pointer raw(SerializationHelper::deserializeFull(payloadBuffer, transport.get()));
if(raw->getField()->getType()==pvd::structure) {
data = std::tr1::static_pointer_cast<pvd::PVStructure>(raw);
} else {
// was originally possible, but never used
}
}
transport->authNZMessage(data);
}
SecurityPluginRegistry::SecurityPluginRegistry() {
// install CA client security plugin by default
installClientSecurityPlugin(CAClientSecurityPlugin::INSTANCE);
}
}} // namespace epics::pvAccess

View File

@ -81,7 +81,7 @@ void SerializationHelper::serializeStructureFull(ByteBuffer* buffer, Serializabl
serializeFull(buffer, control, pvStructure);
}
void SerializationHelper::serializeFull(ByteBuffer* buffer, SerializableControl* control, PVField::shared_pointer const & pvField)
void SerializationHelper::serializeFull(ByteBuffer* buffer, SerializableControl* control, PVField::const_shared_pointer const & pvField)
{
if (!pvField)
{

View File

@ -4164,9 +4164,6 @@ private:
m_channelSearchManager.reset(new ChannelSearchManager(thisPointer));
// preinitialize security plugins
SecurityPluginRegistry::instance();
// TODO put memory barrier here... (if not already called within a lock?)
// setup UDP transport
@ -4448,11 +4445,6 @@ private:
}
}
const securityPlugins_t& getSecurityPlugins() OVERRIDE FINAL
{
return SecurityPluginRegistry::instance().getClientSecurityPlugins();
}
/**
* Get channel search manager.
* @return channel search manager.

View File

@ -201,14 +201,15 @@ public:
protected:
ServerChannelRequesterImpl(Transport::shared_pointer const & transport,
const std::string channelName,
const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css);
const pvAccessID cid);
public:
virtual ~ServerChannelRequesterImpl() {}
static ChannelRequester::shared_pointer create(ChannelProvider::shared_pointer const & provider,
Transport::shared_pointer const & transport, const std::string channelName,
const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css);
const pvAccessID cid);
virtual void channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel) OVERRIDE FINAL;
virtual void channelStateChange(Channel::shared_pointer const & c, const Channel::ConnectionState isConnected) OVERRIDE FINAL;
virtual std::tr1::shared_ptr<const PeerInfo> getPeerInfo() OVERRIDE FINAL;
virtual std::string getRequesterName() OVERRIDE FINAL;
virtual void message(std::string const & message, epics::pvData::MessageType messageType) OVERRIDE FINAL;
virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) OVERRIDE FINAL;
@ -217,7 +218,6 @@ private:
std::tr1::weak_ptr<detail::BlockingServerTCPTransportCodec> _transport;
const std::string _channelName;
const pvAccessID _cid;
ChannelSecuritySession::shared_pointer const & _css;
epics::pvData::Status _status;
epics::pvData::Mutex _mutex;
};

View File

@ -33,8 +33,7 @@ public:
*/
ServerChannel(Channel::shared_pointer const & channel,
const ChannelRequester::shared_pointer& requester,
pvAccessID cid, pvAccessID sid,
ChannelSecuritySession::shared_pointer const & css);
pvAccessID cid, pvAccessID sid);
~ServerChannel();
const Channel::shared_pointer& getChannel() const { return _channel; }
@ -43,9 +42,6 @@ public:
pvAccessID getSID() const { return _sid; }
ChannelSecuritySession::shared_pointer getChannelSecuritySession() const
{ return _channelSecuritySession; }
void registerRequest(pvAccessID id, const std::tr1::shared_ptr<BaseChannelRequester>& request);
void unregisterRequest(pvAccessID id);
@ -77,8 +73,6 @@ private:
bool _destroyed;
mutable epics::pvData::Mutex _mutex;
const ChannelSecuritySession::shared_pointer _channelSecuritySession;
};
}

View File

@ -51,7 +51,6 @@ public:
Transport::shared_pointer getSearchTransport() OVERRIDE FINAL;
Configuration::const_shared_pointer getConfiguration() OVERRIDE FINAL;
TransportRegistry* getTransportRegistry() OVERRIDE FINAL;
const securityPlugins_t& getSecurityPlugins() OVERRIDE FINAL;
virtual void newServerDetected() OVERRIDE FINAL;

View File

@ -194,15 +194,30 @@ void ServerConnectionValidationHandler::handleResponse(
std::string securityPluginName = SerializeHelper::deserializeString(payloadBuffer, transport.get());
// optional authNZ plug-in initialization data
PVField::shared_pointer data;
if (payloadBuffer->getRemaining())
data = SerializationHelper::deserializeFull(payloadBuffer, transport.get());
PVStructure::shared_pointer data;
if (payloadBuffer->getRemaining()) {
PVField::shared_pointer raw(SerializationHelper::deserializeFull(payloadBuffer, transport.get()));
if(raw && raw->getField()->getType()==structure) {
data = std::tr1::static_pointer_cast<PVStructure>(raw);
} else {
// was originally allowed, but never used
}
}
detail::BlockingServerTCPTransportCodec* casTransport(static_cast<detail::BlockingServerTCPTransportCodec*>(transport.get()));
//TODO: simplify byzantine class heirarchy...
assert(casTransport);
try {
casTransport->authNZInitialize(securityPluginName, data);
}catch(std::exception& e){
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", securityPluginName.c_str(), casTransport->getRemoteName().c_str());
}
casTransport->verified(pvData::Status::error(e.what()));
throw;
}
}
@ -732,30 +747,13 @@ void ServerCreateChannelHandler::handleResponse(osiSockAddr* responseFrom,
return;
}
SecuritySession::shared_pointer securitySession = transport->getSecuritySession();
ChannelSecuritySession::shared_pointer css;
try {
css = securitySession->createChannelSession(channelName);
if (!css)
throw SecurityException("null channelSecuritySession");
} catch (SecurityException& se) {
// TODO use std::make_shared
std::tr1::shared_ptr<ServerChannelRequesterImpl> tp(new ServerChannelRequesterImpl(transport, channelName, cid, css));
ChannelRequester::shared_pointer cr = tp;
Status asStatus(Status::STATUSTYPE_ERROR,
string("Insufficient rights to create a channel: ") + se.what());
cr->channelCreated(asStatus, Channel::shared_pointer());
return;
}
if (channelName == SERVER_CHANNEL_NAME)
{
// TODO singleton!!!
ServerRPCService::shared_pointer serverRPCService(new ServerRPCService(_context));
// TODO use std::make_shared
std::tr1::shared_ptr<ServerChannelRequesterImpl> tp(new ServerChannelRequesterImpl(transport, channelName, cid, css));
std::tr1::shared_ptr<ServerChannelRequesterImpl> tp(new ServerChannelRequesterImpl(transport, channelName, cid));
ChannelRequester::shared_pointer cr = tp;
Channel::shared_pointer serverChannel = createRPCChannel(ChannelProvider::shared_pointer(), channelName, cr, serverRPCService);
cr->channelCreated(Status::Ok, serverChannel);
@ -766,7 +764,7 @@ void ServerCreateChannelHandler::handleResponse(osiSockAddr* responseFrom,
ServerContextImpl::s_channelNameToProvider_t::const_iterator it;
if (_providers.size() == 1)
ServerChannelRequesterImpl::create(_providers[0], transport, channelName, cid, css);
ServerChannelRequesterImpl::create(_providers[0], transport, channelName, cid);
else {
ChannelProvider::shared_pointer prov;
{
@ -775,7 +773,7 @@ void ServerCreateChannelHandler::handleResponse(osiSockAddr* responseFrom,
prov = it->second.lock();
}
if(prov)
ServerChannelRequesterImpl::create(prov, transport, channelName, cid, css);
ServerChannelRequesterImpl::create(prov, transport, channelName, cid);
}
}
}
@ -786,12 +784,11 @@ void ServerCreateChannelHandler::disconnect(Transport::shared_pointer const & tr
}
ServerChannelRequesterImpl::ServerChannelRequesterImpl(const Transport::shared_pointer &transport,
const string channelName, const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css) :
const string channelName, const pvAccessID cid) :
_serverChannel(),
_transport(std::tr1::static_pointer_cast<detail::BlockingServerTCPTransportCodec>(transport)),
_channelName(channelName),
_cid(cid),
_css(css),
_status(),
_mutex()
{
@ -799,10 +796,10 @@ ServerChannelRequesterImpl::ServerChannelRequesterImpl(const Transport::shared_p
ChannelRequester::shared_pointer ServerChannelRequesterImpl::create(
ChannelProvider::shared_pointer const & provider, Transport::shared_pointer const & transport,
const string channelName, const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css)
const string channelName, const pvAccessID cid)
{
// TODO use std::make_shared
std::tr1::shared_ptr<ServerChannelRequesterImpl> tp(new ServerChannelRequesterImpl(transport, channelName, cid, css));
std::tr1::shared_ptr<ServerChannelRequesterImpl> tp(new ServerChannelRequesterImpl(transport, channelName, cid));
ChannelRequester::shared_pointer cr = tp;
// TODO exception guard and report error back
provider->createChannel(channelName, cr, transport->getPriority());
@ -824,7 +821,7 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
pvAccessID sid = transport->preallocateChannelSID();
try
{
serverChannel.reset(new ServerChannel(channel, shared_from_this(), _cid, sid, _css));
serverChannel.reset(new ServerChannel(channel, shared_from_this(), _cid, sid));
// ack allocation and register
transport->registerChannel(sid, serverChannel);
@ -836,11 +833,6 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
throw;
}
}
else
{
if (_css)
_css->close();
}
{
@ -861,9 +853,6 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
}
TransportSender::shared_pointer thisSender = shared_from_this();
transport->enqueueSendRequest(thisSender);
// TODO make sure that serverChannel gets destroyed
if (_css)
_css->close();
}
catch (...)
{
@ -874,9 +863,6 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
}
TransportSender::shared_pointer thisSender = shared_from_this();
transport->enqueueSendRequest(thisSender);
// TODO make sure that serverChannel gets destroyed
if (_css)
_css->close();
}
}
}
@ -909,6 +895,17 @@ void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer cons
}
}
std::tr1::shared_ptr<const PeerInfo> ServerChannelRequesterImpl::getPeerInfo()
{
if(detail::BlockingServerTCPTransportCodec::shared_pointer transport = _transport.lock()) {
epicsGuard<epicsMutex> G(transport->_mutex);
return transport->_peerInfo;
} else {
return std::tr1::shared_ptr<const PeerInfo>();
}
}
string ServerChannelRequesterImpl::getRequesterName()
{
detail::BlockingServerTCPTransportCodec::shared_pointer transport = _transport.lock();

View File

@ -18,14 +18,12 @@ size_t ServerChannel::num_instances;
ServerChannel::ServerChannel(Channel::shared_pointer const & channel,
const ChannelRequester::shared_pointer &requester,
pvAccessID cid, pvAccessID sid,
ChannelSecuritySession::shared_pointer const & css):
pvAccessID cid, pvAccessID sid):
_channel(channel),
_requester(requester),
_cid(cid),
_sid(sid),
_destroyed(false),
_channelSecuritySession(css)
_destroyed(false)
{
REFTRACE_INCREMENT(num_instances);
if (!channel.get())
@ -76,10 +74,6 @@ void ServerChannel::destroy()
// removal via unregisterRequest() during iteration
_requests.swap(reqs);
// close channel security session
// TODO try catch
_channelSecuritySession->close();
// ... and the channel
// TODO try catch
_channel->destroy();

View File

@ -533,12 +533,6 @@ epicsTimeStamp& ServerContextImpl::getStartTime()
return _startTime;
}
const Context::securityPlugins_t& ServerContextImpl::getSecurityPlugins()
{
return SecurityPluginRegistry::instance().getServerSecurityPlugins();
}
ServerContext::shared_pointer startPVAServer(std::string const & providerNames, int timeToRun, bool runInSeparateThread, bool printInfo)
{
ServerContext::shared_pointer ret(ServerContext::create(ServerContext::Config()

View File

@ -396,13 +396,7 @@ public:
void aliveNotification() {}
void authNZMessage(epics::pvData::PVField::shared_pointer const & data) {}
virtual std::tr1::shared_ptr<SecuritySession> getSecuritySession() const
{
return std::tr1::shared_ptr<SecuritySession>();
}
void authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) {}
bool isClosed() {