access security

This commit is contained in:
Matej Sekoranja
2014-09-09 14:28:43 +02:00
parent b14333f720
commit 8a2abf09b2
18 changed files with 863 additions and 73 deletions

View File

@@ -235,3 +235,5 @@ testApp/utils/testAtomicBoolean.cpp
testApp/utils/testHexDump.cpp
testApp/utils/testInetAddressUtils.cpp
testApp/utils/transportRegistryTest.cpp
src/remote/security.h
src/remote/security.cpp

View File

@@ -41,7 +41,7 @@ namespace pvAccess {
const epics::pvData::int16 PVA_MESSAGE_HEADER_SIZE = 8;
/** All messages must be aligned to 8-bytes (64-bit). */
const epics::pvData::int32 PVA_ALIGNMENT = 1;
const epics::pvData::int32 PVA_ALIGNMENT = 1; //8;
/**
* UDP maximum send message size.

View File

@@ -3,6 +3,7 @@
SRC_DIRS += $(PVACCESS_SRC)/remote
INC += remote.h
INC += security.h
INC += blockingUDP.h
INC += beaconHandler.h
INC += blockingTCP.h
@@ -22,3 +23,4 @@ LIBSRCS += blockingTCPAcceptor.cpp
LIBSRCS += transportRegistry.cpp
LIBSRCS += serializationHelper.cpp
LIBSRCS += codec.cpp
LIBSRCS += security.cpp

View File

@@ -166,8 +166,6 @@ namespace epics {
THROW_BASE_EXCEPTION(temp.str().c_str());
}
// TODO send security token
LOG(logLevelDebug, "Connected to PVA server: %s.", ipAddrStr);
_namedLocker.releaseSynchronizationObject(&address);

View File

@@ -121,6 +121,18 @@ namespace epics {
// noop
}
virtual void authNZInitialize(void*) {
// noop
}
virtual void authNZMessage(epics::pvData::PVField::shared_pointer const & data) {
// noop
}
virtual std::tr1::shared_ptr<SecuritySession> getSecuritySession() const {
return std::tr1::shared_ptr<SecuritySession>();
}
// NOTE: this is not yet used for UDP
virtual void setByteOrder(int byteOrder) {
// called from receive thread... or before processing

View File

@@ -15,6 +15,9 @@
#include <sstream>
#include <stdexcept>
#include <limits>
#include <vector>
#include <string>
#include <map>
#define epicsExportSharedSymbols
#include <pv/blockingTCP.h>
@@ -23,7 +26,9 @@
#include <pv/hexDump.h>
#include <pv/logger.h>
#include <pv/codec.h>
#include <pv/serializationHelper.h>
using namespace std;
using namespace epics::pvData;
using namespace epics::pvAccess;
@@ -1433,6 +1438,11 @@ namespace epics {
void BlockingTCPTransportCodec::internalClose(bool force) {
BlockingSocketAbstractCodec::internalClose(force);
// TODO sync
if (_securitySession)
_securitySession->close();
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug,
@@ -1461,9 +1471,47 @@ namespace epics {
_verifiedEvent.signal();
}
void BlockingTCPTransportCodec::authNZMessage(epics::pvData::PVField::shared_pointer const & data) {
// TODO sync
if (_securitySession)
_securitySession->messageReceived(data);
else
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelWarn, "authNZ message received from '%s' but no security plug-in session active.", ipAddrStr);
}
}
class SecurityPluginMessageTransportSender : public TransportSender {
public:
POINTER_DEFINITIONS(SecurityPluginMessageTransportSender);
SecurityPluginMessageTransportSender(PVField::shared_pointer const & data) :
m_data(data)
{
}
void send(ByteBuffer* buffer, TransportSendControl* control) {
control->startMessage((int8)5, 0);
SerializationHelper::serializeFull(buffer, control, m_data);
// send immediately
control->flush(true);
}
void lock() {}
void unlock() {}
private:
PVField::shared_pointer m_data;
};
void BlockingTCPTransportCodec::sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data) {
// TODO not optimal since it allocates a new object every time
SecurityPluginMessageTransportSender::shared_pointer spmts(new SecurityPluginMessageTransportSender(data));
enqueueSendRequest(spmts);
}
@@ -1477,7 +1525,7 @@ namespace epics {
int32_t receiveBufferSize) :
BlockingTCPTransportCodec(true, context, channel, responseHandler,
sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY),
_lastChannelSID(0), _verifyOrVerified(false)
_lastChannelSID(0), _verifyOrVerified(false), _securityRequired(false)
{
// NOTE: priority not yet known, default priority is used to
@@ -1574,8 +1622,29 @@ namespace epics {
buffer->putShort(0x7FFF);
// list of authNZ plugin names
// TODO
buffer->putByte(0);
map<string, SecurityPlugin::shared_pointer> securityPlugins;
vector<string> validSPNames;
validSPNames.reserve(securityPlugins.size());
for (map<string, SecurityPlugin::shared_pointer>::const_iterator iter =
securityPlugins.begin();
iter != securityPlugins.end(); iter++)
{
SecurityPlugin::shared_pointer securityPlugin = iter->second;
if (securityPlugin->isValidFor(_socketAddress))
validSPNames.push_back(securityPlugin->getId());
}
size_t validSPCount = validSPNames.size();
SerializeHelper::writeSize(validSPCount, buffer, this);
for (vector<string>::const_iterator iter =
validSPNames.begin();
iter != validSPNames.end(); iter++)
SerializeHelper::serializeString(*iter, buffer, this);
// TODO sync
_securityRequired = (validSPCount > 0);
// send immediately
control->flush(true);
@@ -1625,7 +1694,82 @@ namespace epics {
destroyAllChannels();
}
void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status)
{
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], ipAddrStr);
}
if (!isVerified()) // TODO sync
verified(status);
else if (!status.isSuccess())
{
string errorMessage = "Re-authentication failed: " + status.getMessage();
if (!status.getStackDump().empty())
errorMessage += "\n" + status.getStackDump();
LOG(logLevelInfo, errorMessage.c_str());
close();
}
}
epics::pvData::Status BlockingServerTCPTransportCodec::invalidSecurityPluginNameStatus(Status::STATUSTYPE_ERROR, "invalid security plug-in name");
void BlockingServerTCPTransportCodec::authNZInitialize(void *arg)
{
struct InitData {
std::string securityPluginName;
PVField::shared_pointer data;
};
InitData* initData = static_cast<InitData*>(arg);
// check if plug-in name is valid
SecurityPlugin::shared_pointer securityPlugin;
map<string, SecurityPlugin::shared_pointer>::iterator spIter =
_context->getSecurityPlugins().find(initData->securityPluginName);
if (spIter != _context->getSecurityPlugins().end())
securityPlugin = spIter->second;
if (!securityPlugin)
{
if (_securityRequired)
{
verified(invalidSecurityPluginNameStatus);
return;
}
else
{
securityPlugin = NoSecurityPlugin::INSTANCE;
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "No security plug-in installed, selecting default plug-in '%s' for PVA client: %s.", securityPlugin->getId().c_str(), ipAddrStr);
}
}
}
if (!securityPlugin->isValidFor(_socketAddress))
verified(invalidSecurityPluginNameStatus);
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Accepted security plug-in '%s' for PVA client: %s.", initData->securityPluginName.c_str(), ipAddrStr);
}
// create session
SecurityPluginControl::shared_pointer spc = std::tr1::dynamic_pointer_cast<SecurityPluginControl>(shared_from_this());
// TODO sync
_securitySession = securityPlugin->createSession(_socketAddress, spc, initData->data);
}
@@ -1858,9 +2002,23 @@ namespace epics {
// QoS (aka connection priority)
buffer->putShort(getPriority());
// authNZ plugin name
// TODO
SerializeHelper::serializeString("", buffer, control);
// TODO sync
if (_securitySession)
{
// selected authNZ plug-in name
SerializeHelper::serializeString(_securitySession->getSecurityPlugin()->getId(), buffer, control);
// optional authNZ plug-in initialization data
SerializationHelper::serializeFull(buffer, control, _securitySession->initializationData());
}
else
{
// emptry authNZ plug-in name
SerializeHelper::serializeString("", buffer, control);
// no authNZ plug-in initialization data
SerializationHelper::serializeNullField(buffer, control);
}
// send immediately
control->flush(true);
@@ -1872,8 +2030,44 @@ namespace epics {
}
}
void BlockingClientTCPTransportCodec::authNZInitialize(void *arg)
{
vector<string>* offeredSecurityPlugins = static_cast< vector<string>* >(arg);
if (!offeredSecurityPlugins->empty())
{
map<string, SecurityPlugin::shared_pointer>& availableSecurityPlugins =
_context->getSecurityPlugins();
for (vector<string>::const_iterator offeredSP = offeredSecurityPlugins->begin();
offeredSP != offeredSecurityPlugins->end(); offeredSP++)
{
map<string, SecurityPlugin::shared_pointer>::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());
// TODO sync
_securitySession = securityPlugin->createSession(_socketAddress, spc, PVField::shared_pointer());
}
}
}
}
TransportSender::shared_pointer transportSender = std::tr1::dynamic_pointer_cast<TransportSender>(shared_from_this());
enqueueSendRequest(transportSender);
}
void BlockingClientTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status)
{
// noop for client side (server will send ConnectionValidation message)
}
}
}
}

View File

@@ -36,6 +36,7 @@
#include <pv/pvaConstants.h>
#include <pv/remote.h>
#include <pv/security.h>
#include <pv/transportRegistry.h>
#include <pv/introspectionRegistry.h>
#include <pv/namedLockPattern.h>
@@ -415,7 +416,8 @@ namespace epics {
class BlockingTCPTransportCodec :
public BlockingSocketAbstractCodec
public BlockingSocketAbstractCodec,
public SecurityPluginControl
{
@@ -522,6 +524,17 @@ namespace epics {
void verified(epics::pvData::Status const & status);
bool isVerified() const { return _verified; } // TODO sync
std::tr1::shared_ptr<SecuritySession> getSecuritySession() const {
// TODO sync
return _securitySession;
}
void authNZMessage(epics::pvData::PVField::shared_pointer const & data);
void sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data);
protected:
BlockingTCPTransportCodec(
@@ -548,6 +561,8 @@ namespace epics {
IntrospectionRegistry _incomingIR;
IntrospectionRegistry _outgoingIR;
SecuritySession::shared_pointer _securitySession;
private:
std::auto_ptr<ResponseHandler> _responseHandler;
@@ -558,6 +573,7 @@ namespace epics {
bool _verified;
epics::pvData::Mutex _verifiedMutex;
epics::pvData::Event _verifiedEvent;
};
@@ -619,10 +635,6 @@ namespace epics {
int getChannelCount();
epics::pvData::PVField::shared_pointer getSecurityToken() {
return epics::pvData::PVField::shared_pointer();
}
void lock() {
// noop
}
@@ -655,6 +667,10 @@ namespace epics {
// noop on server-side
}
void authNZInitialize(void *);
void authenticationCompleted(epics::pvData::Status const & status);
void send(epics::pvData::ByteBuffer* buffer,
TransportSendControl* control);
@@ -684,6 +700,10 @@ namespace epics {
bool _verifyOrVerified;
bool _securityRequired;
static epics::pvData::Status invalidSecurityPluginNameStatus;
};
class epicsShareClass BlockingClientTCPTransportCodec :
@@ -759,7 +779,11 @@ namespace epics {
void send(epics::pvData::ByteBuffer* buffer,
TransportSendControl* control);
void authNZInitialize(void *);
void authenticationCompleted(epics::pvData::Status const & status);
protected:
virtual void internalClose(bool force);

View File

@@ -12,6 +12,9 @@
# undef epicsExportSharedSymbols
#endif
#include <map>
#include <string>
#include <osiSock.h>
#include <osdSock.h>
@@ -157,6 +160,7 @@ namespace epics {
};
class TransportClient;
class SecuritySession;
/**
* Interface defining transport (connection).
@@ -288,9 +292,24 @@ namespace epics {
* @return <code>true</code> if connected.
*/
virtual bool isClosed() = 0;
/**
* Used to initialize authNZ (select security plug-in).
* @param data
*/
virtual void authNZInitialize(void*) = 0;
/**
* 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;
};
class Channel;
class SecurityPlugin;
/**
* Not public IF, used by Transports, etc.
@@ -311,6 +330,11 @@ namespace epics {
virtual Configuration::shared_pointer getConfiguration() = 0;
/**
* Get map of available security plug-ins.
* @return the map of available security plug-ins
*/
virtual std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> >& getSecurityPlugins() = 0;
///
@@ -465,12 +489,6 @@ namespace epics {
virtual ~ChannelHostingTransport() {}
/**
* Get security token.
* @return security token, can be <code>null</code>.
*/
virtual epics::pvData::PVField::shared_pointer getSecurityToken() = 0;
/**
* Preallocate new channel SID.
* @return new channel server id (SID).

13
src/remote/security.cpp Normal file
View File

@@ -0,0 +1,13 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#define epicsExportSharedSymbols
#include <pv/security.h>
using namespace epics::pvAccess;
NoSecurityPlugin::shared_pointer NoSecurityPlugin::INSTANCE(new NoSecurityPlugin());

458
src/remote/security.h Normal file
View File

@@ -0,0 +1,458 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#ifndef SECURITY_H
#define SECURITY_H
#ifdef epicsExportSharedSymbols
# define securityEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#include <string>
#include <osiSock.h>
#include <osdSock.h>
#include <pv/status.h>
#include <pv/pvData.h>
#include <pv/sharedPtr.h>
#ifdef securityEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef securityEpicsExportSharedSymbols
#endif
#include <pv/remote.h>
#include <pv/serializationHelper.h>
#include <shareLib.h>
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 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 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 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 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 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 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 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 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 epicsShareClass SecurityException: public std::runtime_error {
public:
explicit SecurityException(std::string const & what): std::runtime_error(what) {}
};
class epicsShareClass SecuritySession {
public:
POINTER_DEFINITIONS(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)
throw (SecurityException) = 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
*/
// 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) throw (SecurityException) = 0;
};
class epicsShareClass NoSecurityPlugin :
public SecurityPlugin,
public SecuritySession,
public ChannelSecuritySession,
public std::tr1::enable_shared_from_this<ChannelSecuritySession> {
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 std::tr1::dynamic_pointer_cast<SecurityPlugin>(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*/)
throw (SecurityException)
{
return shared_from_this();
}
/**
* Short, unique name for the plug-in, used to identify the plugin.
* @return the ID.
*/
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*/) throw (SecurityException) {
control->authenticationCompleted(epics::pvData::Status::Ok);
return std::tr1::dynamic_pointer_cast<SecuritySession>(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 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 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 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 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 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 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 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;
}
};
class epicsShareClass AuthNZHandler :
public AbstractResponseHandler,
private epics::pvData::NoDefaultMethods
{
public:
AuthNZHandler(Context* context) :
AbstractResponseHandler(context, "authNZ message")
{
}
virtual ~AuthNZHandler() {}
virtual void handleResponse(osiSockAddr* responseFrom,
Transport::shared_pointer const & transport,
epics::pvData::int8 version,
epics::pvData::int8 command,
size_t payloadSize,
epics::pvData::ByteBuffer* payloadBuffer)
{
AbstractResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer);
epics::pvData::PVField::shared_pointer data =
SerializationHelper::deserializeFull(payloadBuffer, transport.get());
transport->authNZMessage(data);
}
};
class epicsShareClass SecurityPluginRegistry :
private epics::pvData::NoDefaultMethods
{
public:
static SecurityPluginRegistry& instance()
{
static SecurityPluginRegistry thisInstance;
return thisInstance;
}
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> >& getClientSecurityPlugins()
{
return m_clientSecurityPlugins;
}
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> >& getServerSecurityPlugins()
{
return m_serverSecurityPlugins;
}
void installClientSecurityPlugin(std::tr1::shared_ptr<SecurityPlugin> plugin)
{
m_clientSecurityPlugins[plugin->getId()] = plugin;
}
void installServerSecurityPlugin(std::tr1::shared_ptr<SecurityPlugin> plugin)
{
m_serverSecurityPlugins[plugin->getId()] = plugin;
}
private:
SecurityPluginRegistry() {}
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> > m_clientSecurityPlugins;
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> > m_serverSecurityPlugins;
};
}
}
#endif // SECURITY_H

View File

@@ -40,14 +40,19 @@ PVStructure::shared_pointer SerializationHelper::deserializeStructureAndCreatePV
PVStructure::shared_pointer SerializationHelper::deserializeStructureFull(ByteBuffer* buffer, DeserializableControl* control)
{
PVStructure::shared_pointer pvStructure;
FieldConstPtr structureField = control->cachedDeserialize(buffer);
if (structureField.get() != 0)
return std::tr1::static_pointer_cast<PVStructure>(deserializeFull(buffer, control));
}
PVField::shared_pointer SerializationHelper::deserializeFull(ByteBuffer* buffer, DeserializableControl* control)
{
PVField::shared_pointer pvField;
FieldConstPtr field = control->cachedDeserialize(buffer);
if (field.get() != 0)
{
pvStructure = _pvDataCreate->createPVStructure(std::tr1::static_pointer_cast<const Structure>(structureField));
pvStructure->deserialize(buffer, control);
pvField = _pvDataCreate->createPVField(field);
pvField->deserialize(buffer, control);
}
return pvStructure;
return pvField;
}
void SerializationHelper::serializeNullField(ByteBuffer* buffer, SerializableControl* control)
@@ -64,15 +69,20 @@ void SerializationHelper::serializePVRequest(ByteBuffer* buffer, SerializableCon
void SerializationHelper::serializeStructureFull(ByteBuffer* buffer, SerializableControl* control, PVStructure::shared_pointer const & pvStructure)
{
if (pvStructure.get() == 0)
{
serializeNullField(buffer, control);
}
else
{
control->cachedSerialize(pvStructure->getField(), buffer);
pvStructure->serialize(buffer, control);
}
serializeFull(buffer, control, pvStructure);
}
void SerializationHelper::serializeFull(ByteBuffer* buffer, SerializableControl* control, PVField::shared_pointer const & pvField)
{
if (pvField.get() == 0)
{
serializeNullField(buffer, control);
}
else
{
control->cachedSerialize(pvField->getField(), buffer);
pvField->serialize(buffer, control);
}
}
}}

View File

@@ -67,7 +67,19 @@ namespace epics {
*/
static epics::pvData::PVStructure::shared_pointer deserializeStructureFull(epics::pvData::ByteBuffer* payloadBuffer, epics::pvData::DeserializableControl* control);
static void serializeNullField(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control);
/**
* Deserialize optional PVField.
* @param payloadBuffer data buffer.
* @return deserialized PVField, can be <code>null</code>.
*/
static epics::pvData::PVField::shared_pointer deserializeFull(epics::pvData::ByteBuffer* payloadBuffer, epics::pvData::DeserializableControl* control);
/**
* Serialize <code>null</code> PVField.
* @param buffer
* @param control
*/
static void serializeNullField(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control);
/**
* Serialize PVRequest.
@@ -81,6 +93,11 @@ namespace epics {
*/
static void serializeStructureFull(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control, epics::pvData::PVStructure::shared_pointer const & pvStructure);
/**
* 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);
};
}

View File

@@ -32,6 +32,7 @@
#include <pv/configuration.h>
#include <pv/beaconHandler.h>
#include <pv/logger.h>
#include <pv/security.h>
#include <pv/pvAccessMB.h>
@@ -47,9 +48,9 @@ namespace epics {
namespace pvAccess {
string ClientContextImpl::PROVIDER_NAME = "pva";
Status ChannelImpl::channelDestroyed = Status(
Status ChannelImpl::channelDestroyed(
Status::STATUSTYPE_WARNING, "channel destroyed");
Status ChannelImpl::channelDisconnected = Status(
Status ChannelImpl::channelDisconnected(
Status::STATUSTYPE_WARNING, "channel disconnected");
string emptyString;
ConvertPtr convert = getConvert();
@@ -389,15 +390,15 @@ namespace epics {
PVDataCreatePtr BaseRequestImpl::pvDataCreate = getPVDataCreate();
Status BaseRequestImpl::notInitializedStatus = Status(Status::STATUSTYPE_ERROR, "request not initialized");
Status BaseRequestImpl::destroyedStatus = Status(Status::STATUSTYPE_ERROR, "request destroyed");
Status BaseRequestImpl::channelNotConnected = Status(Status::STATUSTYPE_ERROR, "channel not connected");
Status BaseRequestImpl::channelDestroyed = Status(Status::STATUSTYPE_ERROR, "channel destroyed");
Status BaseRequestImpl::otherRequestPendingStatus = Status(Status::STATUSTYPE_ERROR, "other request pending");
Status BaseRequestImpl::invalidPutStructureStatus = Status(Status::STATUSTYPE_ERROR, "incompatible put structure");
Status BaseRequestImpl::invalidPutArrayStatus = Status(Status::STATUSTYPE_ERROR, "incompatible put array");
Status BaseRequestImpl::invalidBitSetLengthStatus = Status(Status::STATUSTYPE_ERROR, "invalid bit-set length");
Status BaseRequestImpl::pvRequestNull = Status(Status::STATUSTYPE_ERROR, "pvRequest == 0");
Status BaseRequestImpl::notInitializedStatus(Status::STATUSTYPE_ERROR, "request not initialized");
Status BaseRequestImpl::destroyedStatus(Status::STATUSTYPE_ERROR, "request destroyed");
Status BaseRequestImpl::channelNotConnected(Status::STATUSTYPE_ERROR, "channel not connected");
Status BaseRequestImpl::channelDestroyed(Status::STATUSTYPE_ERROR, "channel destroyed");
Status BaseRequestImpl::otherRequestPendingStatus(Status::STATUSTYPE_ERROR, "other request pending");
Status BaseRequestImpl::invalidPutStructureStatus(Status::STATUSTYPE_ERROR, "incompatible put structure");
Status BaseRequestImpl::invalidPutArrayStatus(Status::STATUSTYPE_ERROR, "incompatible put array");
Status BaseRequestImpl::invalidBitSetLengthStatus(Status::STATUSTYPE_ERROR, "invalid bit-set length");
Status BaseRequestImpl::pvRequestNull(Status::STATUSTYPE_ERROR, "pvRequest == 0");
PVStructure::shared_pointer BaseRequestImpl::nullPVStructure;
Structure::const_shared_pointer BaseRequestImpl::nullStructure;
@@ -2821,14 +2822,17 @@ namespace epics {
// TODO
// TODO serverIntrospectionRegistryMaxSize
/*int serverIntrospectionRegistryMaxSize = */ payloadBuffer->getShort();
// TODO authNZ
size_t size = SerializeHelper::readSize(payloadBuffer, transport.get());
for (size_t i = 0; i < size; i++)
SerializeHelper::deserializeString(payloadBuffer, transport.get());
TransportSender::shared_pointer sender = dynamic_pointer_cast<TransportSender>(transport);
if (sender.get())
transport->enqueueSendRequest(sender);
// authNZ
size_t size = SerializeHelper::readSize(payloadBuffer, transport.get());
vector<string> offeredSecurityPlugins;
offeredSecurityPlugins.reserve(size);
for (size_t i = 0; i < size; i++)
offeredSecurityPlugins.push_back(
SerializeHelper::deserializeString(payloadBuffer, transport.get())
);
transport->authNZInitialize(&offeredSecurityPlugins);
}
};
@@ -2965,8 +2969,8 @@ namespace epics {
m_handlerTable[CMD_ECHO].reset(new NoopResponse(context, "Echo")); /* 2 */
m_handlerTable[CMD_SEARCH].reset(new NoopResponse(context, "Search")); /* 3 */
m_handlerTable[CMD_SEARCH_RESPONSE].reset(new SearchResponseHandler(context)); /* 4 */
m_handlerTable[CMD_AUTHNZ].reset(new NoopResponse(context, "Introspection search")); /* 5 */
m_handlerTable[CMD_ACL_CHANGE] = dataResponse; /* 6 */
m_handlerTable[CMD_AUTHNZ].reset(new AuthNZHandler(context.get())); /* 5 */
m_handlerTable[CMD_ACL_CHANGE].reset(new NoopResponse(context, "Access rights change")); /* 6 */
m_handlerTable[CMD_CREATE_CHANNEL].reset(new CreateChannelHandler(context)); /* 7 */
m_handlerTable[CMD_DESTROY_CHANNEL].reset(new NoopResponse(context, "Destroy channel")); /* 8 */ // TODO it might be useful to implement this...
m_handlerTable[CMD_CONNECTION_VALIDATED].reset(new ClientConnectionValidatedHandler(context)); /* 9 */
@@ -4631,6 +4635,11 @@ TODO
// TODO
}
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> >& getSecurityPlugins()
{
return SecurityPluginRegistry::instance().getClientSecurityPlugins();
}
/**
* Get channel search manager.
* @return channel search manager.

View File

@@ -13,13 +13,13 @@ namespace epics {
namespace pvAccess {
const Status BaseChannelRequester::okStatus = Status();
const Status BaseChannelRequester::badCIDStatus = Status(Status::STATUSTYPE_ERROR, "bad channel id");
const Status BaseChannelRequester::badIOIDStatus = Status(Status::STATUSTYPE_ERROR, "bad request id");
const Status BaseChannelRequester::noReadACLStatus = Status(Status::STATUSTYPE_ERROR, "no read access");
const Status BaseChannelRequester::noWriteACLStatus = Status(Status::STATUSTYPE_ERROR, "no write access");
const Status BaseChannelRequester::noProcessACLStatus = Status(Status::STATUSTYPE_ERROR, "no process access");
const Status BaseChannelRequester::otherRequestPendingStatus = Status(Status::STATUSTYPE_ERROR, "other request pending");
const Status BaseChannelRequester::notAChannelRequestStatus = Status(Status::STATUSTYPE_ERROR, "not a channel request");
const Status BaseChannelRequester::badCIDStatus(Status::STATUSTYPE_ERROR, "bad channel id");
const Status BaseChannelRequester::badIOIDStatus(Status::STATUSTYPE_ERROR, "bad request id");
const Status BaseChannelRequester::noReadACLStatus(Status::STATUSTYPE_ERROR, "no read access");
const Status BaseChannelRequester::noWriteACLStatus(Status::STATUSTYPE_ERROR, "no write access");
const Status BaseChannelRequester::noProcessACLStatus(Status::STATUSTYPE_ERROR, "no process access");
const Status BaseChannelRequester::otherRequestPendingStatus(Status::STATUSTYPE_ERROR, "other request pending");
const Status BaseChannelRequester::notAChannelRequestStatus(Status::STATUSTYPE_ERROR, "not a channel request");
const int32 BaseChannelRequester::NULL_REQUEST = -1;

View File

@@ -19,6 +19,7 @@
#include <pv/pvAccessMB.h>
#include <pv/rpcServer.h>
#include <pv/security.h>
using std::string;
using std::ostringstream;
@@ -92,8 +93,8 @@ ServerResponseHandler::ServerResponseHandler(ServerContextImpl::shared_pointer c
m_handlerTable[CMD_ECHO].reset(new ServerEchoHandler(context)); /* 2 */
m_handlerTable[CMD_SEARCH].reset(new ServerSearchHandler(context)); /* 3 */
m_handlerTable[CMD_SEARCH_RESPONSE] = badResponse;
m_handlerTable[CMD_AUTHNZ] = badResponse; /* 5 */
m_handlerTable[CMD_ACL_CHANGE] = badResponse; /* 6 - introspection search */
m_handlerTable[CMD_AUTHNZ].reset(new AuthNZHandler(context.get())); /* 5 */
m_handlerTable[CMD_ACL_CHANGE] = badResponse; /* 6 - access right change */
m_handlerTable[CMD_CREATE_CHANNEL].reset(new ServerCreateChannelHandler(context)); /* 7 */
m_handlerTable[CMD_DESTROY_CHANNEL].reset(new ServerDestroyChannelHandler(context)); /* 8 */
m_handlerTable[CMD_CONNECTION_VALIDATED] = badResponse; /* 9 */
@@ -152,13 +153,28 @@ void ServerConnectionValidationHandler::handleResponse(
/* int clientIntrospectionRegistryMaxSize = */ payloadBuffer->getShort();
// TODO connectionQoS
/* int16 connectionQoS = */ payloadBuffer->getShort();
// TODO authNZ
/*std::string authNZ = */ SerializeHelper::deserializeString(payloadBuffer, transport.get());
// TODO call this after authNZ has done their work
transport->verified(Status::Ok);
// authNZ
std::string securityPluginName = SerializeHelper::deserializeString(payloadBuffer, transport.get());
// optional authNZ plug-in initialization data
PVField::shared_pointer data;
if (payloadBuffer->getRemaining())
SerializationHelper::deserializeFull(payloadBuffer, transport.get());
struct {
std::string securityPluginName;
PVField::shared_pointer data;
} initData = { securityPluginName, data };
transport->authNZInitialize(&initData);
}
void ServerEchoHandler::handleResponse(osiSockAddr* responseFrom,
Transport::shared_pointer const & transport, int8 version, int8 command,
size_t payloadSize, ByteBuffer* payloadBuffer)
@@ -630,7 +646,7 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
pvAccessID sid = casTransport->preallocateChannelSID();
try
{
epics::pvData::PVField::shared_pointer securityToken = casTransport->getSecurityToken();
epics::pvData::PVField::shared_pointer securityToken; // TODO replace with security session
serverChannel.reset(new ServerChannelImpl(channel, _cid, sid, securityToken));
// ack allocation and register

View File

@@ -10,6 +10,7 @@
#include <pv/responseHandlers.h>
#include <pv/logger.h>
#include <pv/serverContext.h>
#include <pv/security.h>
using namespace std;
using namespace epics::pvData;
@@ -673,6 +674,11 @@ void ServerContextImpl::newServerDetected()
// not used
}
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> >& ServerContextImpl::getSecurityPlugins()
{
return SecurityPluginRegistry::instance().getServerSecurityPlugins();
}
struct ThreadRunnerParam {

View File

@@ -138,6 +138,7 @@ public:
Transport::shared_pointer getSearchTransport();
Configuration::shared_pointer getConfiguration();
TransportRegistry::shared_pointer getTransportRegistry();
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> >& getSecurityPlugins();
std::auto_ptr<ResponseHandler> createResponseHandler();
virtual void newServerDetected();

View File

@@ -357,6 +357,16 @@ namespace epics {
void aliveNotification() {}
void authNZMessage(epics::pvData::PVField::shared_pointer const & data) {}
void authNZInitialize(void*) {}
virtual std::tr1::shared_ptr<SecuritySession> getSecuritySession() const
{
return std::tr1::shared_ptr<SecuritySession>();
}
bool isClosed() { return false; }