Merge branch 'auth'

* auth:
  fix osdGetRoles
  oops
  Add showauth executable
  getgrouplist() on OSX has different sig.
  auth fix mingw static
  fix search PeerInfo
  PeerInfo in pva/server.h and pva/sharedstate.h
  make PeerInfo available during search phase
  auth status void accidental success.
  minor doc
  always need to link with netapi32 now
  authorize with local group lookup
  Add osdGetRoles()
  authorization framework
  pvasr show PeerInfo
  redo security (aka. access control)
  remove asCheck

# Conflicts:
#	src/server/pv/responseHandlers.h
#	src/server/responseHandlers.cpp
This commit is contained in:
Michael Davidsaver
2019-05-12 14:40:29 -07:00
38 changed files with 1139 additions and 911 deletions

View File

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

View File

@ -3,6 +3,7 @@ TOP=..
include $(TOP)/configure/CONFIG
PROD_LIBS += pvAccessCA ca pvAccess pvData Com
PROD_SYS_LIBS_WIN32 += netapi32 ws2_32
TESTPROD_HOST += getme
getme_SRCS = getme.cpp

View File

@ -30,7 +30,7 @@ pvlist_SRCS += pvlist.cpp
PROD_LIBS += pvAccessCA pvAccess pvData ca Com
PROD_SYS_LIBS_WIN32 += ws2_32
PROD_SYS_LIBS_WIN32 += netapi32 ws2_32
include $(TOP)/configure/RULES
#----------------------------------------

View File

@ -32,7 +32,7 @@ LIB_LIBS += Com
SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_PVA_MAINTENANCE_VERSION)
# needed for Windows
LIB_SYS_LIBS_WIN32 += ws2_32
LIB_SYS_LIBS_WIN32 += netapi32 ws2_32
include $(TOP)/configure/RULES

View File

@ -7,7 +7,7 @@ pvAccessCA_LIBS += pvAccess pvData ca Com
SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_PVA_MAINTENANCE_VERSION)
# needed for Windows
LIB_SYS_LIBS_WIN32 += ws2_32
LIB_SYS_LIBS_WIN32 += netapi32 ws2_32
INC += pv/caProvider.h

View File

@ -393,6 +393,8 @@ public:
static ChannelFind::shared_pointer buildDummy(const std::tr1::shared_ptr<ChannelProvider>& provider);
};
struct PeerInfo; // see pv/security.h
/**
*
*/
@ -410,6 +412,30 @@ public:
const epics::pvData::Status& status,
ChannelFind::shared_pointer const & channelFind,
bool wasFound) = 0;
/**
* @brief Return information on requesting 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.
*
* The returned PeerInfo is only required to have valid values for: peer, transport, and transportVersion.
* PeerInfo::authority may be empty if authentication has not yet occured (UDP search).
*
* 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()
{ return std::tr1::shared_ptr<const PeerInfo>(); }
};
/**
@ -819,7 +845,6 @@ public:
};
class ChannelRequester;
/**
@ -1133,6 +1158,26 @@ public:
* @param connectionState The new connection state.
*/
virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) = 0;
/**
* @brief Return information on connected peer if applicable.
*
* A server-type ChannelProvider will use this method to discover if a remote client
* has provided credentials which may be used in access control decisions.
*
* Default implementation returns NULL.
*
* isConnected()==true and getPeerInfo()==NULL when the ChannelProvider does not provide
* information about the peer. This should be treated as an unauthenticated, anonymous,
* peer.
*
* The returned instance must not change, and a different instance should be returned
* if/when peer information changes (eg. after reconnect).
*
* May return !NULL when !isConnected(). getPeerInfo() must be called _before_
* testing isConnected() in situations where connection state is being polled.
*/
virtual std::tr1::shared_ptr<const PeerInfo> getPeerInfo();
};
//! Used when ChannelProvider::createChannel() is passed a NULL ChannelRequester

View File

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

View File

@ -27,7 +27,7 @@ size_t ResponseHandler::num_instances;
ResponseHandler::ResponseHandler(Context* context, const std::string& description)
:_description(description)
,_debugLevel(context->getConfiguration()->getPropertyAsInteger(PVACCESS_DEBUG, 0))
,_debugLevel(context->getConfiguration()->getPropertyAsInteger(PVACCESS_DEBUG, 0)) // actually $EPICS_PVA_DEBUG
{
REFTRACE_INCREMENT(num_instances);
}

View File

@ -40,6 +40,8 @@ using namespace std;
using namespace epics::pvData;
using namespace epics::pvAccess;
typedef epicsGuard<epicsMutex> Guard;
namespace {
struct BreakTransport : TransportSender
{
@ -1087,15 +1089,11 @@ void BlockingTCPTransportCodec::internalClose()
Transport::shared_pointer thisSharedPtr = this->shared_from_this();
_context->getTransportRegistry()->remove(thisSharedPtr);
// TODO sync
if (_securitySession)
_securitySession->close();
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug,
"TCP socket to %s is to be closed.",
inetAddressToString(_socketAddress).c_str());
_socketName.c_str());
}
}
@ -1244,7 +1242,6 @@ BlockingTCPTransportCodec::BlockingTCPTransportCodec(bool serverFlag, const Cont
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
_socketName = ipAddrStr;
}
}
@ -1361,24 +1358,28 @@ bool BlockingTCPTransportCodec::verify(epics::pvData::int32 timeoutMs) {
}
void BlockingTCPTransportCodec::verified(epics::pvData::Status const & status) {
epics::pvData::Lock lock(_verifiedMutex);
epics::pvData::Lock lock(_mutex);
if (IS_LOGGABLE(logLevelDebug) && !status.isOK())
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Failed to verify connection to %s: %s.", ipAddrStr, status.getMessage().c_str());
// TODO stack dump
LOG(logLevelDebug, "Failed to verify connection to %s: %s.", _socketName.c_str(), status.getMessage().c_str());
}
{
Guard G(_mutex);
_verified = status.isSuccess();
}
_verifiedEvent.signal();
}
void BlockingTCPTransportCodec::authNZMessage(epics::pvData::PVField::shared_pointer const & data) {
// TODO sync
if (_securitySession)
_securitySession->messageReceived(data);
void BlockingTCPTransportCodec::authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) {
AuthenticationSession::shared_pointer sess;
{
Guard G(_mutex);
sess = _authSession;
}
if (sess)
sess->messageReceived(data);
else
{
char ipAddrStr[48];
@ -1392,7 +1393,7 @@ class SecurityPluginMessageTransportSender : public TransportSender {
public:
POINTER_DEFINITIONS(SecurityPluginMessageTransportSender);
SecurityPluginMessageTransportSender(PVField::shared_pointer const & data) :
SecurityPluginMessageTransportSender(PVStructure::const_shared_pointer const & data) :
_data(data)
{
}
@ -1405,11 +1406,10 @@ public:
}
private:
PVField::shared_pointer _data;
PVStructure::const_shared_pointer _data;
};
void BlockingTCPTransportCodec::sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data) {
// TODO not optimal since it allocates a new object every time
void BlockingTCPTransportCodec::sendSecurityPluginMessage(epics::pvData::PVStructure::const_shared_pointer const & data) {
SecurityPluginMessageTransportSender::shared_pointer spmts(new SecurityPluginMessageTransportSender(data));
enqueueSendRequest(spmts);
}
@ -1423,10 +1423,12 @@ BlockingServerTCPTransportCodec::BlockingServerTCPTransportCodec(
SOCKET channel,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize) :
BlockingTCPTransportCodec(true, context, channel, responseHandler,
sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY),
_lastChannelSID(0), _verifyOrVerified(false), _securityRequired(false)
int32_t receiveBufferSize)
:BlockingTCPTransportCodec(true, context, channel, responseHandler,
sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY)
,_lastChannelSID(0)
,_verificationStatus(pvData::Status::fatal("Uninitialized error"))
,_verifyOrVerified(false)
{
// NOTE: priority not yet known, default priority is used to
//register/unregister
@ -1530,29 +1532,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 +1575,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 +1613,28 @@ 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
if(peer)
AuthorizationRegistry::plugins().run(peer);
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 +1647,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 +1897,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 +1937,68 @@ void BlockingClientTCPTransportCodec::send(ByteBuffer* buffer,
void BlockingClientTCPTransportCodec::authNZInitialize(const std::vector<std::string>& offeredSecurityPlugins)
{
if (!offeredSecurityPlugins.empty())
{
const Context::securityPlugins_t& availableSecurityPlugins(_context->getSecurityPlugins());
AuthenticationRegistry& plugins = AuthenticationRegistry::clients();
std::string selectedName;
AuthenticationPlugin::shared_pointer plugin;
for (vector<string>::const_iterator offeredSP = offeredSecurityPlugins.begin();
offeredSP != offeredSecurityPlugins.end(); offeredSP++)
{
Context::securityPlugins_t::const_iterator spi(availableSecurityPlugins.find(*offeredSP));
if (spi != availableSecurityPlugins.end())
{
SecurityPlugin::shared_pointer securityPlugin = spi->second;
if (securityPlugin->isValidFor(_socketAddress))
{
// create session
SecurityPluginControl::shared_pointer spc = std::tr1::dynamic_pointer_cast<SecurityPluginControl>(shared_from_this());
// because of a missing break; the original SecurityPlugin effectively treated the offered list as being
// in order of increasing preference (last is preferred).
// we continue with this because, hey isn't compatibility fun...
// TODO sync
_securitySession = securityPlugin->createSession(_socketAddress, spc, PVField::shared_pointer());
for(std::vector<std::string>::const_reverse_iterator it(offeredSecurityPlugins.rbegin()), end(offeredSecurityPlugins.rend());
it!=end; ++it)
{
plugin = plugins.lookup(*it);
if(plugin) {
selectedName = *it;
break;
}
}
if(!plugin) {
// mis-match and legacy. some early servers (java?) don't advertise any plugins.
// treat this as anonymous
selectedName = "anonymous";
plugin = plugins.lookup(selectedName);
assert(plugin); // fallback required
}
{
PeerInfo::shared_pointer info(new PeerInfo);
info->peer = _socketName; // this is the server name
info->transport = "pva";
info->transportVersion = getRevision();
info->authority = selectedName;
AuthenticationSession::shared_pointer sess(plugin->createSession(info, shared_from_this(), pvData::PVStructure::shared_pointer()));
Guard G(_mutex);
_authSessionName = selectedName;
_authSession = sess;
}
TransportSender::shared_pointer transportSender = std::tr1::dynamic_pointer_cast<TransportSender>(shared_from_this());
enqueueSendRequest(transportSender);
}
void BlockingClientTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status)
void BlockingClientTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status,
const std::tr1::shared_ptr<PeerInfo>& peer)
{
// noop for client side (server will send ConnectionValidation message)
}
void BlockingClientTCPTransportCodec::verified(epics::pvData::Status const & status)
{
AuthenticationSession::shared_pointer sess;
{
Guard G(_mutex);
sess = _authSession;
}
if(sess)
sess->authenticationComplete(status);
this->BlockingTCPTransportCodec::verified(status);
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -7,6 +7,72 @@
#ifndef SECURITY_H
#define SECURITY_H
/** @page pva_security PVA Security
*
* Summary of PVA auth. process.
*
@msc
arcgradient = 8;
CP [label="Client Plugin"], CS [label="Client Session"], C [label="Client"], S [label="Server"], SS [label="Server Session"], SP [label="Server Plugin"];
C:>S [label="Opens TCP connection"];
S box SP [label="Server lists plugins"];
S=>SP [label="isValidFor()"];
C<:S [label="CONNECTION_VALIDATION"];
CP box C [label="Client lists plugins"];
CP box C [label="Client select plugin"];
CP<=C [label="createSession()"];
CP>>C [label="Returns Session"];
CS<=C [label="initializationData()"];
CS>>C [label="Initial payload"];
C:>S [label="CONNECTION_VALIDATION"];
S=>SP [label="createSession()"];
S<<SP [label="Returns Session"];
--- [label="Optional (Repeatable)"];
S<=SS [label="sendSecurityPluginMessage()"];
C<:S [label="AUTHZ"];
CS<=C [label="messageReceived()"];
...;
CS=>C [label="sendSecurityPluginMessage()"];
C:>S [label="AUTHZ"];
S=>SS [label="messageReceived()"];
...;
--- [label="Completion"];
S<=SS [label="authenticationCompleted()"];
C<:S [label="CONNECTION_VALIDATED"];
CS<=C [label="authenticationComplete()"];
@endmsc
*
* Ownership
*
@dot
digraph authplugin {
External;
AuthenticationRegistry [shape=box];
AuthenticationPlugin [shape=box];
AuthenticationPluginControl [shape=box];
AuthenticationSession [shape=box];
External -> AuthenticationRegistry;
AuthenticationRegistry -> AuthenticationPlugin;
External -> AuthenticationSession;
AuthenticationSession -> AuthenticationPluginControl [color=green, style=dashed];
External -> AuthenticationPluginControl;
AuthenticationPluginControl -> AuthenticationSession;
AuthenticationPlugin -> AuthenticationSession [style=dashed];
}
@enddot
*
* Locking
*
* All methods of AuthenticationSession are called from a single thread.
* Methods of AuthenticationPlugin and AuthenticationPluginControl can be called
* from any threads.
*
* AuthenticationPluginControl is an Operation, AuthenticationSession is a Requester.
* @see provider_roles_requester_locking
*/
#ifdef epicsExportSharedSymbols
# define securityEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
@ -14,6 +80,7 @@
#include <string>
#include <osiSock.h>
#include <epicsMutex.h>
#include <pv/status.h>
#include <pv/pvData.h>
@ -34,428 +101,211 @@
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*/)
{
return shared_from_this();
}
/**
* Short, unique name for the plug-in, used to identify the plugin.
* @return the ID.
/** A particular authentication exchange. See AuthenticationPlugin::createSession()
*
* @note Must not hold a strong reference to AuthenticationPluginControl
*/
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;
}
};
class epicsShareClass CAClientSecurityPlugin :
public NoSecurityPlugin {
protected:
epics::pvData::PVStructure::shared_pointer m_userAndHost;
CAClientSecurityPlugin();
public:
POINTER_DEFINITIONS(CAClientSecurityPlugin);
static CAClientSecurityPlugin::shared_pointer INSTANCE;
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";
}
};
class epicsShareClass SecurityPluginRegistry
class epicsShareClass AuthenticationSession
{
EPICS_NOT_COPYABLE(SecurityPluginRegistry)
public:
POINTER_DEFINITIONS(AuthenticationSession);
static SecurityPluginRegistry& instance()
{
static SecurityPluginRegistry thisInstance;
return thisInstance;
}
virtual ~AuthenticationSession();
typedef std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> > securityPlugins_t;
//! 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(); }
securityPlugins_t& getClientSecurityPlugins()
{
return m_clientSecurityPlugins;
}
//! 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) {}
securityPlugins_t& getServerSecurityPlugins()
{
return m_serverSecurityPlugins;
}
/** For client plugins only. Notification that server has declared the exchange complete.
* @param status Check Status::isSuccess()
* @param peer Final information about pe
*/
virtual void authenticationComplete(const epics::pvData::Status& status) {}
};
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());
}
//! Callbacks for use by AuthenticationSession
class epicsShareClass AuthenticationPluginControl
{
public:
POINTER_DEFINITIONS(AuthenticationPluginControl);
virtual ~AuthenticationPluginControl();
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());
}
//! 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;
/** 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;
};
//! Actor through which authentication exchanges are initiated.
class epicsShareClass AuthenticationPlugin
{
public:
POINTER_DEFINITIONS(AuthenticationPlugin);
virtual ~AuthenticationPlugin();
/** 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
{
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;
};
//! I modify PeerInfo after authentication is complete.
//! Usually to update PeerInfo::roles
class epicsShareClass AuthorizationPlugin
{
public:
POINTER_DEFINITIONS(AuthorizationPlugin);
virtual ~AuthorizationPlugin();
//! Hook to modify PeerInfo
virtual void authorize(const std::tr1::shared_ptr<PeerInfo>& peer) =0;
};
class epicsShareClass AuthorizationRegistry
{
EPICS_NOT_COPYABLE(AuthorizationRegistry)
public:
POINTER_DEFINITIONS(AuthenticationRegistry);
static AuthorizationRegistry &plugins();
AuthorizationRegistry();
~AuthorizationRegistry();
private:
typedef std::map<int, AuthorizationPlugin::shared_pointer> map_t;
map_t map;
void *busy;
mutable epicsMutex mutex;
public:
void add(int prio, const AuthorizationPlugin::shared_pointer& plugin);
bool remove(const AuthorizationPlugin::shared_pointer& plugin);
void run(const std::tr1::shared_ptr<PeerInfo>& peer);
};
/** @brief Query OS specific DB for role/group names assocated with a user account.
* @param account User name
* @param roles Role names are added to this set. Existing names are not removed.
*/
epicsShareFunc
void osdGetRoles(const std::string &account, PeerInfo::roles_t& roles);
}
}

View File

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

View File

@ -6,51 +6,300 @@
#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);
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;
}
};
//
// host name
//
struct CAPlugin : public pva::AuthenticationPlugin
{
const bool server;
// fully const after ctor
const pvd::PVStructurePtr user;
std::string hostName;
if (gethostname(buffer, sizeof(buffer)) == 0)
hostName = buffer;
// TODO more error handling
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");
m_userAndHost->getSubFieldT<PVString>("host")->put(buffer);
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;
}
};
struct GroupsPlugin : public pva::AuthorizationPlugin
{
virtual ~GroupsPlugin() {}
void authorize(const std::tr1::shared_ptr<pva::PeerInfo>& peer)
{
if(!peer->identified)
return; // no groups for anonymous
pva::osdGetRoles(peer->account, peer->roles);
}
};
} // 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() {}
AuthorizationPlugin::~AuthorizationPlugin() {}
AuthorizationRegistry::~AuthorizationRegistry() {}
namespace {
struct authGbl_t {
mutable epicsMutex mutex;
AuthenticationRegistry servers, clients;
AuthorizationRegistry authorizers;
} *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);
}
{
GroupsPlugin::shared_pointer plugin(new GroupsPlugin);
authGbl->authorizers.add(0, 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();
}
AuthorizationRegistry::AuthorizationRegistry()
:busy(0)
{}
AuthorizationRegistry& AuthorizationRegistry::plugins()
{
epicsThreadOnce(&authGblOnce, &authGblInit, 0);
assert(authGbl);
return authGbl->authorizers;
}
void AuthorizationRegistry::add(int prio, const AuthorizationPlugin::shared_pointer& plugin)
{
Guard G(mutex);
// we don't expect changes after server start
if(busy)
throw std::runtime_error("AuthorizationRegistry busy");
if(map.find(prio)!=map.end())
THROW_EXCEPTION2(std::logic_error, "Authorization plugin already registered with this priority");
map[prio] = plugin;
}
bool AuthorizationRegistry::remove(const AuthorizationPlugin::shared_pointer& plugin)
{
Guard G(mutex);
if(busy)
throw std::runtime_error("AuthorizationRegistry busy");
for(map_t::iterator it(map.begin()), end(map.end()); it!=end; ++it) {
if(it->second==plugin) {
map.erase(it);
return true;
}
}
return false;
}
void AuthorizationRegistry::run(const std::tr1::shared_ptr<PeerInfo>& peer)
{
int marker;
{
Guard G(mutex);
if(busy)
throw std::runtime_error("AuthorizationRegistry busy");
busy = &marker;
}
for(map_t::iterator it(map.begin()), end(map.end()); it!=end; ++it)
{
(it->second)->authorize(peer);
}
{
Guard G(mutex);
assert(busy==&marker);
busy = 0;
}
}
void AuthNZHandler::handleResponse(osiSockAddr* responseFrom,
Transport::shared_pointer const & transport,
@ -61,15 +310,17 @@ void AuthNZHandler::handleResponse(osiSockAddr* responseFrom,
{
ResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer);
epics::pvData::PVField::shared_pointer data =
SerializationHelper::deserializeFull(payloadBuffer, transport.get());
pvd::PVStructure::shared_pointer data;
{
pvd::PVField::shared_pointer raw(SerializationHelper::deserializeFull(payloadBuffer, transport.get()));
if(raw->getField()->getType()==pvd::structure) {
data = std::tr1::static_pointer_cast<pvd::PVStructure>(raw);
} else {
// was originally possible, but never used
}
}
transport->authNZMessage(data);
}
SecurityPluginRegistry::SecurityPluginRegistry() {
// install CA client security plugin by default
installClientSecurityPlugin(CAClientSecurityPlugin::INSTANCE);
}
}} // namespace epics::pvAccess

View File

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

View File

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

View File

@ -135,13 +135,16 @@ class ServerChannelFindRequesterImpl:
public std::tr1::enable_shared_from_this<ServerChannelFindRequesterImpl>
{
public:
ServerChannelFindRequesterImpl(ServerContextImpl::shared_pointer const & context, epics::pvData::int32 expectedResponseCount);
ServerChannelFindRequesterImpl(ServerContextImpl::shared_pointer const & context,
const PeerInfo::const_shared_pointer& peer,
epics::pvData::int32 expectedResponseCount);
virtual ~ServerChannelFindRequesterImpl() {}
void clear();
ServerChannelFindRequesterImpl* set(std::string _name, epics::pvData::int32 searchSequenceId,
epics::pvData::int32 cid, osiSockAddr const & sendTo, bool responseRequired, bool serverSearch);
virtual void channelFindResult(const epics::pvData::Status& status, ChannelFind::shared_pointer const & channelFind, bool wasFound) OVERRIDE FINAL;
virtual std::tr1::shared_ptr<const PeerInfo> getPeerInfo() OVERRIDE FINAL;
virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) OVERRIDE FINAL;
virtual void callback() OVERRIDE FINAL;
@ -155,9 +158,10 @@ private:
osiSockAddr _sendTo;
bool _responseRequired;
bool _wasFound;
ServerContextImpl::shared_pointer _context;
epics::pvData::Mutex _mutex;
epics::pvData::int32 _expectedResponseCount;
const ServerContextImpl::shared_pointer _context;
const PeerInfo::const_shared_pointer _peer;
mutable epics::pvData::Mutex _mutex;
const epics::pvData::int32 _expectedResponseCount;
epics::pvData::int32 _responseCount;
bool _serverSearch;
};
@ -201,14 +205,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 +222,6 @@ private:
std::tr1::weak_ptr<detail::BlockingServerTCPTransportCodec> _transport;
const std::string _channelName;
const pvAccessID _cid;
ChannelSecuritySession::shared_pointer const & _css;
bool _created;
epics::pvData::Status _status;
epics::pvData::Mutex _mutex;

View File

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

View File

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

View File

@ -17,6 +17,7 @@ namespace epics{namespace pvAccess{
class ChannelProvider;
class Channel;
class ChannelRequester;
struct PeerInfo; // see pv/security.h
}} // epics::pvAccess
//! See @ref pvas API
@ -168,7 +169,10 @@ public:
friend struct Impl;
bool isclaimed;
std::string cname;
Search(const std::string& name) :isclaimed(false),cname(name) {}
const ::epics::pvAccess::PeerInfo* peerinfo;
Search(const std::string& name, const ::epics::pvAccess::PeerInfo* peer)
:isclaimed(false),cname(name),peerinfo(peer)
{}
public:
//! The name being queried
const std::string& name() const { return cname; }
@ -176,6 +180,10 @@ public:
bool claimed() const { return isclaimed; }
//! Has been claimed()
void claim() { isclaimed = true; }
//! Information about peer making search request.
//! May be NULL if not information is available.
//! @since >7.1.0
const ::epics::pvAccess::PeerInfo* peer() const { return peerinfo; }
};
typedef std::vector<Search> search_type;

View File

@ -242,6 +242,10 @@ public:
//! The name of the channel through which this request was made (eg. for logging purposes).
std::string channelName() const;
//! Information about peer transport and authentication.
//! @returns May be NULL if no information is available
const epics::pvAccess::PeerInfo* peer() const;
void complete(); //!< shorthand for successful completion w/o data (Put or RPC with void return)
//! Complete with success or error w/o data.
void complete(const epics::pvData::Status& sts);

View File

@ -194,15 +194,30 @@ void ServerConnectionValidationHandler::handleResponse(
std::string securityPluginName = SerializeHelper::deserializeString(payloadBuffer, transport.get());
// optional authNZ plug-in initialization data
PVField::shared_pointer data;
if (payloadBuffer->getRemaining())
data = SerializationHelper::deserializeFull(payloadBuffer, transport.get());
PVStructure::shared_pointer data;
if (payloadBuffer->getRemaining()) {
PVField::shared_pointer raw(SerializationHelper::deserializeFull(payloadBuffer, transport.get()));
if(raw && raw->getField()->getType()==structure) {
data = std::tr1::static_pointer_cast<PVStructure>(raw);
} else {
// was originally allowed, but never used
}
}
detail::BlockingServerTCPTransportCodec* casTransport(static_cast<detail::BlockingServerTCPTransportCodec*>(transport.get()));
//TODO: simplify byzantine class heirarchy...
assert(casTransport);
try {
casTransport->authNZInitialize(securityPluginName, data);
}catch(std::exception& e){
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", securityPluginName.c_str(), casTransport->getRemoteName().c_str());
}
casTransport->verified(pvData::Status::error(e.what()));
throw;
}
}
@ -320,6 +335,14 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
}
}
PeerInfo::shared_pointer info;
if(allowed) {
info.reset(new PeerInfo);
info->transport = "pva";
info->peer = inetAddressToString(*responseFrom);
info->transportVersion = transport->getRevision();
}
if (count > 0)
{
// regular name search
@ -335,7 +358,7 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
const std::vector<ChannelProvider::shared_pointer>& _providers = _context->getChannelProviders();
int providerCount = _providers.size();
std::tr1::shared_ptr<ServerChannelFindRequesterImpl> tp(new ServerChannelFindRequesterImpl(_context, providerCount));
std::tr1::shared_ptr<ServerChannelFindRequesterImpl> tp(new ServerChannelFindRequesterImpl(_context, info, providerCount));
tp->set(name, searchSequenceId, cid, responseAddress, responseRequired, false);
for (int i = 0; i < providerCount; i++)
@ -353,7 +376,7 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
double delay = double(rand())/RAND_MAX; // [0, 1]
delay = delay*0.1 + 0.05;
std::tr1::shared_ptr<ServerChannelFindRequesterImpl> tp(new ServerChannelFindRequesterImpl(_context, 1));
std::tr1::shared_ptr<ServerChannelFindRequesterImpl> tp(new ServerChannelFindRequesterImpl(_context, info, 1));
tp->set("", searchSequenceId, 0, responseAddress, true, true);
TimerCallback::shared_pointer tc = tp;
@ -362,12 +385,13 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
}
}
ServerChannelFindRequesterImpl::ServerChannelFindRequesterImpl(ServerContextImpl::shared_pointer const & context,
ServerChannelFindRequesterImpl::ServerChannelFindRequesterImpl(ServerContextImpl::shared_pointer const & context, const PeerInfo::const_shared_pointer &peer,
int32 expectedResponseCount) :
_guid(context->getGUID()),
_sendTo(),
_wasFound(false),
_context(context),
_peer(peer),
_expectedResponseCount(expectedResponseCount),
_responseCount(0),
_serverSearch(false)
@ -443,6 +467,11 @@ void ServerChannelFindRequesterImpl::channelFindResult(const Status& /*status*/,
}
}
std::tr1::shared_ptr<const PeerInfo> ServerChannelFindRequesterImpl::getPeerInfo()
{
return _peer;
}
void ServerChannelFindRequesterImpl::send(ByteBuffer* buffer, TransportSendControl* control)
{
control->startMessage(CMD_SEARCH_RESPONSE, 12+4+16+2);
@ -727,30 +756,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);
@ -761,7 +773,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;
{
@ -770,7 +782,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);
}
}
}
@ -781,22 +793,21 @@ 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),
_created(false)
{
}
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());
@ -820,7 +831,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);
@ -832,11 +843,6 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
throw;
}
}
else
{
if (_css)
_css->close();
}
{
@ -858,9 +864,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 (...)
{
@ -871,9 +874,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();
}
}
}
@ -907,6 +907,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();
@ -1029,14 +1040,6 @@ void ServerGetHandler::handleResponse(osiSockAddr* responseFrom,
// pvRequest
PVStructure::shared_pointer pvRequest(SerializationHelper::deserializePVRequest(payloadBuffer, transport.get()));
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeCreateChannelGet(ioid, pvRequest);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_GET, transport, ioid, qosCode, asStatus);
return;
}
// create...
ServerChannelGetRequesterImpl::create(_context, channel, ioid, transport, pvRequest);
}
@ -1057,16 +1060,6 @@ void ServerGetHandler::handleResponse(osiSockAddr* responseFrom,
return;
}
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGet(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_GET, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
ChannelGet::shared_pointer channelGet = request->getChannelGet();
if (lastRequest)
channelGet->lastRequest();
@ -1185,9 +1178,6 @@ void ServerChannelGetRequesterImpl::destroy()
Lock guard(_mutex);
_channel->unregisterRequest(_ioid);
// asCheck
_channel->getChannelSecuritySession()->release(_ioid);
if (_channelGet)
{
_channelGet->destroy();
@ -1285,14 +1275,6 @@ void ServerPutHandler::handleResponse(osiSockAddr* responseFrom,
// pvRequest
PVStructure::shared_pointer pvRequest(SerializationHelper::deserializePVRequest(payloadBuffer, transport.get()));
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeCreateChannelPut(ioid, pvRequest);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PUT, transport, ioid, qosCode, asStatus);
return;
}
// create...
ServerChannelPutRequesterImpl::create(_context, channel, ioid, transport, pvRequest);
}
@ -1321,16 +1303,6 @@ void ServerPutHandler::handleResponse(osiSockAddr* responseFrom,
if (get)
{
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGet(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PUT, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
channelPut->get();
}
else
@ -1349,16 +1321,6 @@ void ServerPutHandler::handleResponse(osiSockAddr* responseFrom,
lock.unlock();
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizePut(ioid, putPVStructure, putBitSet);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PUT, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
channelPut->put(putPVStructure, putBitSet);
}
}
@ -1451,9 +1413,6 @@ void ServerChannelPutRequesterImpl::destroy()
Lock guard(_mutex);
_channel->unregisterRequest(_ioid);
// asCheck
_channel->getChannelSecuritySession()->release(_ioid);
if (_channelPut)
{
_channelPut->destroy();
@ -1554,14 +1513,6 @@ void ServerPutGetHandler::handleResponse(osiSockAddr* responseFrom,
// pvRequest
PVStructure::shared_pointer pvRequest(SerializationHelper::deserializePVRequest(payloadBuffer, transport.get()));
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeCreateChannelPutGet(ioid, pvRequest);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PUT_GET, transport, ioid, qosCode, asStatus);
return;
}
// create...
ServerChannelPutGetRequesterImpl::create(_context, channel, ioid, transport, pvRequest);
}
@ -1590,30 +1541,10 @@ void ServerPutGetHandler::handleResponse(osiSockAddr* responseFrom,
if (getGet)
{
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGet(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PUT_GET, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
channelPutGet->getGet();
}
else if(getPut)
{
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGet(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PUT_GET, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
channelPutGet->getPut();
}
else
@ -1631,16 +1562,6 @@ void ServerPutGetHandler::handleResponse(osiSockAddr* responseFrom,
lock.unlock();
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizePutGet(ioid, putPVStructure, putBitSet);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PUT_GET, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
channelPutGet->putGet(putPVStructure, putBitSet);
}
}
@ -1760,9 +1681,6 @@ void ServerChannelPutGetRequesterImpl::destroy()
Lock guard(_mutex);
_channel->unregisterRequest(_ioid);
// asCheck
_channel->getChannelSecuritySession()->release(_ioid);
if (_channelPutGet)
{
_channelPutGet->destroy();
@ -1877,14 +1795,6 @@ void ServerMonitorHandler::handleResponse(osiSockAddr* responseFrom,
// pvRequest
PVStructure::shared_pointer pvRequest(SerializationHelper::deserializePVRequest(payloadBuffer, transport.get()));
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeCreateMonitor(ioid, pvRequest);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_MONITOR, transport, ioid, qosCode, asStatus);
return;
}
// create...
ServerMonitorRequesterImpl::shared_pointer request(ServerMonitorRequesterImpl::create(_context, channel, ioid, transport, pvRequest));
@ -1930,17 +1840,6 @@ void ServerMonitorHandler::handleResponse(osiSockAddr* responseFrom,
}
*/
// TODO for now we do a get check
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGet(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_MONITOR, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
if (process)
{
if (get)
@ -2045,9 +1944,6 @@ void ServerMonitorRequesterImpl::destroy()
Lock guard(_mutex);
_channel->unregisterRequest(_ioid);
// asCheck
_channel->getChannelSecuritySession()->release(_ioid);
window.swap(_window_closed);
monitor.swap(_channelMonitor);
@ -2243,14 +2139,6 @@ void ServerArrayHandler::handleResponse(osiSockAddr* responseFrom,
// pvRequest
PVStructure::shared_pointer pvRequest(SerializationHelper::deserializePVRequest(payloadBuffer, transport.get()));
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeCreateChannelArray(ioid, pvRequest);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_ARRAY, transport, ioid, qosCode, asStatus);
return;
}
// create...
ServerChannelArrayRequesterImpl::create(_context, channel, ioid, transport, pvRequest);
}
@ -2284,46 +2172,16 @@ void ServerArrayHandler::handleResponse(osiSockAddr* responseFrom,
size_t count = SerializeHelper::readSize(payloadBuffer, transport.get());
size_t stride = SerializeHelper::readSize(payloadBuffer, transport.get());
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGet(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_ARRAY, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
request->getChannelArray()->getArray(offset, count, stride);
}
else if (setLength)
{
size_t length = SerializeHelper::readSize(payloadBuffer, transport.get());
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeSetLength(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_ARRAY, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
request->getChannelArray()->setLength(length);
}
else if (getLength)
{
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGet(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_ARRAY, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
request->getChannelArray()->getLength();
}
else
@ -2342,16 +2200,6 @@ void ServerArrayHandler::handleResponse(osiSockAddr* responseFrom,
);
}
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizePut(ioid, array);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_ARRAY, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
channelArray->putArray(array, offset, array->getLength(), stride);
}
}
@ -2473,9 +2321,6 @@ void ServerChannelArrayRequesterImpl::destroy()
Lock guard(_mutex);
_channel->unregisterRequest(_ioid);
// asCheck
_channel->getChannelSecuritySession()->release(_ioid);
if (_channelArray)
{
_channelArray->destroy();
@ -2657,14 +2502,6 @@ void ServerProcessHandler::handleResponse(osiSockAddr* responseFrom,
// pvRequest
PVStructure::shared_pointer pvRequest(SerializationHelper::deserializePVRequest(payloadBuffer, transport.get()));
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeCreateChannelProcess(ioid, pvRequest);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PROCESS, transport, ioid, qosCode, asStatus);
return;
}
// create...
ServerChannelProcessRequesterImpl::create(_context, channel, ioid, transport, pvRequest);
}
@ -2688,16 +2525,6 @@ void ServerProcessHandler::handleResponse(osiSockAddr* responseFrom,
if (lastRequest)
request->getChannelProcess()->lastRequest();
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeProcess(ioid);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_PROCESS, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
request->getChannelProcess()->process();
}
}
@ -2766,9 +2593,6 @@ void ServerChannelProcessRequesterImpl::destroy()
Lock guard(_mutex);
_channel->unregisterRequest(_ioid);
// asCheck
_channel->getChannelSecuritySession()->release(_ioid);
if (_channelProcess.get())
{
_channelProcess->destroy();
@ -2836,14 +2660,6 @@ void ServerGetFieldHandler::handleResponse(osiSockAddr* responseFrom,
req = tp;
}
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeGetField(ioid, subField);
if (!asStatus.isSuccess())
{
req->getDone(asStatus, FieldConstPtr());
return;
}
channel->installGetField(req);
// TODO exception check
@ -2926,14 +2742,6 @@ void ServerRPCHandler::handleResponse(osiSockAddr* responseFrom,
// pvRequest
PVStructure::shared_pointer pvRequest(SerializationHelper::deserializePVRequest(payloadBuffer, transport.get()));
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeCreateChannelRPC(ioid, pvRequest);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_RPC, transport, ioid, qosCode, asStatus);
return;
}
// create...
ServerChannelRPCRequesterImpl::create(_context, channel, ioid, transport, pvRequest);
}
@ -2966,16 +2774,6 @@ void ServerRPCHandler::handleResponse(osiSockAddr* responseFrom,
if (lastRequest)
channelRPC->lastRequest();
// asCheck
Status asStatus = channel->getChannelSecuritySession()->authorizeRPC(ioid, pvArgument);
if (!asStatus.isSuccess())
{
BaseChannelRequester::sendFailureMessage((int8)CMD_RPC, transport, ioid, qosCode, asStatus);
if (lastRequest)
request->destroy();
return;
}
channelRPC->request(pvArgument);
}
}
@ -3047,9 +2845,6 @@ void ServerChannelRPCRequesterImpl::destroy()
Lock guard(_mutex);
_channel->unregisterRequest(_ioid);
// asCheck
_channel->getChannelSecuritySession()->release(_ioid);
if (_channelRPC.get())
{
_channelRPC->destroy();

View File

@ -17,6 +17,7 @@
#define epicsExportSharedSymbols
#include "pva/server.h"
#include "pv/pvAccess.h"
#include "pv/security.h"
#include "pv/reftrack.h"
namespace pvd = epics::pvData;
@ -216,8 +217,9 @@ struct DynamicProvider::Impl : public pva::ChannelProvider
{
bool found = false;
{
pva::PeerInfo::const_shared_pointer info(requester->getPeerInfo());
search_type search;
search.push_back(DynamicProvider::Search(name));
search.push_back(DynamicProvider::Search(name, info ? info.get() : 0));
handler->hasChannels(search);

View File

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

View File

@ -115,7 +115,7 @@ void ServerContextImpl::loadConfiguration()
Configuration::const_shared_pointer config = configuration;
// TODO for now just a simple switch
int32 debugLevel = config->getPropertyAsInteger(PVACCESS_DEBUG, 0);
int32 debugLevel = config->getPropertyAsInteger(PVACCESS_DEBUG, 0); // actually $EPICS_PVA_DEBUG
if (debugLevel > 0)
SET_LOG_LEVEL(logLevelDebug);
@ -398,7 +398,7 @@ void ServerContextImpl::printInfo(ostream& str, int lvl)
{
const Transport::shared_pointer& transport(*it);
str<<"client "<<transport->getType()<<"://"<<transport->getRemoteName()
str<<" "<<transport->getType()<<"://"<<transport->getRemoteName()
<<" ver="<<unsigned(transport->getRevision())
<<" "<<(transport->isClosed()?"closed!":"");
@ -406,6 +406,31 @@ void ServerContextImpl::printInfo(ostream& str, int lvl)
if(casTransport) {
str<<" "<<(casTransport ? casTransport->getChannelCount() : size_t(-1))<<" channels";
PeerInfo::const_shared_pointer peer;
{
epicsGuard<epicsMutex> G(casTransport->_mutex);
peer = casTransport->_peerInfo;
}
if(peer) {
str<<" user: "<<peer->authority<<"/"<<peer->account;
if(!peer->realm.empty())
str<<"@"<<peer->realm;
if(lvl>=2 && !peer->roles.empty()) {
str<<" groups:";
int n=0;
for(PeerInfo::roles_t::const_iterator it(peer->roles.begin()), end(peer->roles.end()); it!=end; ++it, ++n) {
if(n)
str<<',';
str<<(*it);
}
}
if(lvl>=3 && peer->aux) {
str<<" aux. auth.:\n";
format::indent_scope I(str);
str<<(*peer->aux);
}
}
}
str<<"\n";
@ -533,12 +558,6 @@ epicsTimeStamp& ServerContextImpl::getStartTime()
return _startTime;
}
const Context::securityPlugins_t& ServerContextImpl::getSecurityPlugins()
{
return SecurityPluginRegistry::instance().getServerSecurityPlugins();
}
ServerContext::shared_pointer startPVAServer(std::string const & providerNames, int timeToRun, bool runInSeparateThread, bool printInfo)
{
ServerContext::shared_pointer ret(ServerContext::create(ServerContext::Config()

View File

@ -300,6 +300,11 @@ std::string Operation::channelName() const
return ret;
}
const pva::PeerInfo* Operation::peer() const
{
return impl->info ? impl->info.get() : 0;
}
void Operation::complete()
{
impl->complete(pvd::Status(), 0);

View File

@ -34,7 +34,11 @@ struct PutOP : public pvas::Operation::Impl
const pvd::BitSet& changed)
:Impl(pvRequest, value, changed)
,op(op)
{}
{
pva::ChannelRequester::shared_pointer req(op->channel->getChannelRequester());
if(req)
info = req->getPeerInfo();
}
virtual ~PutOP() {}
virtual pva::Channel::shared_pointer getChannel() OVERRIDE FINAL

View File

@ -32,7 +32,11 @@ struct RPCOP : public pvas::Operation::Impl
const pvd::PVStructure::const_shared_pointer& value)
:Impl(pvRequest, value, pvd::BitSet().set(0))
,op(op)
{}
{
pva::ChannelRequester::shared_pointer req(op->channel->getChannelRequester());
if(req)
info = req->getPeerInfo();
}
virtual ~RPCOP() {}
virtual pva::Channel::shared_pointer getChannel() OVERRIDE FINAL

View File

@ -9,6 +9,7 @@
#include "pva/sharedstate.h"
#include <pv/pvAccess.h>
#include <pv/security.h>
#include <pv/reftrack.h>
#define FOR_EACH(TYPE, IT, END, OBJ) for(TYPE IT((OBJ).begin()), END((OBJ).end()); IT != END; ++IT)
@ -135,6 +136,8 @@ struct Operation::Impl
const pvd::PVStructure::const_shared_pointer pvRequest, value;
const pvd::BitSet changed;
//! const after sub-class ctor
pva::PeerInfo::const_shared_pointer info;
bool done;
int debugLvl;

View File

@ -14,6 +14,7 @@ INC += pv/fairQueue.h
INC += pv/requester.h
INC += pv/destroyable.h
pvAccess_SRCS += getgroups.cpp
pvAccess_SRCS += hexDump.cpp
pvAccess_SRCS += inetAddressUtil.cpp
pvAccess_SRCS += logger.cpp

182
src/utils/getgroups.cpp Normal file
View File

@ -0,0 +1,182 @@
/**
* 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.
*/
#include <set>
#if defined(_WIN32)
# define USE_LANMAN
#elif !defined(__rtems__) && !defined(vxWorks)
# define USE_UNIX_GROUPS
#endif
/* conditionally include any system headers */
#if defined(USE_UNIX_GROUPS)
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
// getgrouplist() has a slightly different prototype on OSX.
# ifdef __APPLE__
// OSX has gid_t, which isn't "int", but doesn't use it here.
// int getgrouplist(const char *name, int basegid, int *groups, int *ngroups);
typedef int osi_gid_t;
# else
// int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups);
typedef gid_t osi_gid_t;
# endif
#elif defined(USE_LANMAN)
#include <stdlib.h>
#include <windows.h>
#include <lm.h>
#endif
#define epicsExportSharedSymbols
#include <pv/security.h>
namespace epics {
namespace pvAccess {
#if defined(USE_UNIX_GROUPS)
void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles)
{
passwd *user = getpwnam(account.c_str());
if(!user)
return; // don't know who this is
typedef std::set<gid_t> gids_t;
gids_t gids;
gids.insert(user->pw_gid); // include primary group
/* List supplementary groups.
*
* Rant...
* getgrouplist() differs subtly when the *count is too short.
* Some libc (Mac) don't update the count
* Some libc (glibc) don't write a truncated list.
*
* We might also use getgrent(), but this isn't reentrant, and
* would anyway require visiting all groups.
* The GNU alternative getgrent_r() would require us to allocate
* enough space to hold the list of all members of the largest
* group. This may be hundreds.
*
* So we iterate with getgrouplist() as the lesser evil...
*/
{
// start with a guess
std::vector<osi_gid_t> gtemp(16);
while(true) {
int gcount = int(gtemp.size());
int ret = getgrouplist(user->pw_name, user->pw_gid, &gtemp[0], &gcount);
if(ret!=-1 && gcount>=0 && gcount <= int(gtemp.size())) {
// success
gtemp.resize(gcount);
break;
} else if(ret!=-1) {
// success, but invalid count? give up
gtemp.clear();
break;
} else if(gcount == int(gtemp.size())) {
// too small, but gcount not updated. (Mac)
// arbitrary increase to size and retry
gtemp.resize(gtemp.size()*2u);
} else if(gcount > int(gtemp.size())) {
// too small, gcount holds actual size. retry
gtemp.resize(gcount);
} else {
// too small, but gcount got smaller? give up
gtemp.clear();
break;
}
}
for(size_t i=0, N=gtemp.size(); i<N; i++)
gids.insert(gtemp[i]);
}
// map GIDs to names
for(gids_t::iterator it(gids.begin()), end(gids.end()); it!=end; it++) {
assert((*it)!=0); // 0 indicates a logic error above
group* gr = getgrgid(*it);
if(!gr)
continue;
roles.insert(gr->gr_name);
}
}
#elif defined(USE_LANMAN)
void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles)
{
NET_API_STATUS sts;
LPLOCALGROUP_USERS_INFO_0 pinfo = NULL;
DWORD ninfo = 0, nmaxinfo = 0;
std::vector<wchar_t> wbuf;
{
size_t N = mbstowcs(NULL, account.c_str(), 0);
if(N==size_t(-1))
return; // username has invalid MB char
wbuf.resize(N+1);
N = mbstowcs(&wbuf[0], account.c_str(), account.size());
assert(N+1==wbuf.size());
wbuf[N] = 0; // paranoia
}
// this call may involve network I/O
sts = NetUserGetLocalGroups(NULL, &wbuf[0], 0,
LG_INCLUDE_INDIRECT,
(LPBYTE*)&pinfo,
MAX_PREFERRED_LENGTH,
&ninfo, &nmaxinfo);
if(sts!=NERR_Success)
return; // silently do nothing.
try {
std::vector<char> buf;
for(DWORD i=0; i<ninfo; i++) {
size_t N = wcstombs(NULL, pinfo[i].lgrui0_name, 0);
if(N==size_t(-1))
continue; // has invalid MB char
buf.resize(N+1);
N = wcstombs(&buf[0], pinfo[i].lgrui0_name, buf.size());
buf[N] = 0; // paranoia
roles.insert(&buf[0]);
}
NetApiBufferFree(pinfo);
}catch(...){
NetApiBufferFree(pinfo);
throw;
}
}
#else
void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles)
{}
#endif
}} // namespace epics::pvAccess

View File

@ -11,6 +11,7 @@ USR_CPPFLAGS += -I$(TOP)/src/remoteClient
PVACCESS_TEST = $(TOP)/testApp
PROD_LIBS += pvAccess pvData Com
PROD_SYS_LIBS_WIN32 += netapi32 ws2_32
include $(PVACCESS_TEST)/utils/Makefile
include $(PVACCESS_TEST)/remote/Makefile

View File

@ -10,7 +10,6 @@ TESTS += testChannelAccess
TESTPROD_HOST += testCodec
testCodec_SRCS = testCodec
testHarness_SRCS += testCodec.cpp
testCodec_SYS_LIBS_WIN32 += ws2_32
TESTS += testCodec
TESTPROD_HOST += testRPC

View File

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

View File

@ -17,14 +17,11 @@ testInetAddressUtils = testInetAddressUtils.cpp
# Avoid errors from inlined htonl() etc. used as template argument
# TODO this is gcc only
testInetAddressUtils_CXXFLAGS = -O0
# needed for 64-bit Windows
testInetAddressUtils_SYS_LIBS_WIN32 += ws2_32
testHarness_SRCS += testInetAddressUtils.cpp
TESTS += testInetAddressUtils
TESTPROD_HOST += configurationTest
configurationTest_SRCS += configurationTest.cpp
configurationTest_SYS_LIBS_WIN32 += ws2_32
#testHarness_SRCS += configurationTest.cpp
TESTS += configurationTest
@ -36,3 +33,6 @@ TESTPROD_HOST += testWildcard
testWildcard = testWildcard.cpp
testHarness_SRCS += testWildcard.cpp
TESTS += testWildcard
TESTPROD_HOST += showauth
showauth_SRCS += showauth.cpp

View File

@ -0,0 +1,42 @@
/**
* 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.
*/
#include <vector>
#include <stdexcept>
#include <iostream>
#include <osiProcess.h>
#include <pv/security.h>
namespace pva = epics::pvAccess;
int main(int argc, char *argv[])
{
int ret = 0;
try {
std::vector<char> name(256u);
if(osiGetUserName(&name[0], name.size())!=osiGetUserNameSuccess)
throw std::runtime_error("Unable to determine username");
name[name.size()-1] = '\0';
std::cout<<"User: "<<(&name[0])<<"\n";
pva::PeerInfo::roles_t roles;
pva::osdGetRoles(&name[0], roles);
std::cout<<"Groups: \n";
for(pva::PeerInfo::roles_t::const_iterator it(roles.begin()), end(roles.end());
it!=end; ++it)
{
std::cout<<" "<<*it<<"\n";
}
} catch(std::exception& e) {
std::cerr<<"Error: "<<e.what()<<"\n";
ret = 2;
}
return ret;
}

View File

@ -7,6 +7,7 @@ include $(TOP)/configure/CONFIG
USR_CPPFLAGS += -I$(TOP)/src/ca
PROD_LIBS += pvAccess pvAccessCA pvData $(EPICS_BASE_IOC_LIBS)
PROD_SYS_LIBS_WIN32 += netapi32 ws2_32
TESTPROD_HOST += testCaProvider
testCaProvider_SRCS += testCaProvider.cpp