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:
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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() {
|
||||
|
Reference in New Issue
Block a user