From 13cac5f4cc6d98c63ee025349f1f58f90ca7529f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jan 2019 13:06:45 -0800 Subject: [PATCH 01/17] remove asCheck This interface will be removed. --- src/server/responseHandlers.cpp | 216 -------------------------------- 1 file changed, 216 deletions(-) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 81412be..a17b75d 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -1031,14 +1031,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); } @@ -1059,16 +1051,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(); @@ -1187,9 +1169,6 @@ void ServerChannelGetRequesterImpl::destroy() Lock guard(_mutex); _channel->unregisterRequest(_ioid); - // asCheck - _channel->getChannelSecuritySession()->release(_ioid); - if (_channelGet) { _channelGet->destroy(); @@ -1287,14 +1266,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); } @@ -1323,16 +1294,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 @@ -1351,16 +1312,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); } } @@ -1453,9 +1404,6 @@ void ServerChannelPutRequesterImpl::destroy() Lock guard(_mutex); _channel->unregisterRequest(_ioid); - // asCheck - _channel->getChannelSecuritySession()->release(_ioid); - if (_channelPut) { _channelPut->destroy(); @@ -1556,14 +1504,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); } @@ -1592,30 +1532,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 @@ -1633,16 +1553,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); } } @@ -1762,9 +1672,6 @@ void ServerChannelPutGetRequesterImpl::destroy() Lock guard(_mutex); _channel->unregisterRequest(_ioid); - // asCheck - _channel->getChannelSecuritySession()->release(_ioid); - if (_channelPutGet) { _channelPutGet->destroy(); @@ -1879,14 +1786,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)); @@ -1932,17 +1831,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) @@ -2047,9 +1935,6 @@ void ServerMonitorRequesterImpl::destroy() Lock guard(_mutex); _channel->unregisterRequest(_ioid); - // asCheck - _channel->getChannelSecuritySession()->release(_ioid); - window.swap(_window_closed); monitor.swap(_channelMonitor); @@ -2245,14 +2130,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); } @@ -2286,46 +2163,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 @@ -2344,16 +2191,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); } } @@ -2475,9 +2312,6 @@ void ServerChannelArrayRequesterImpl::destroy() Lock guard(_mutex); _channel->unregisterRequest(_ioid); - // asCheck - _channel->getChannelSecuritySession()->release(_ioid); - if (_channelArray) { _channelArray->destroy(); @@ -2659,14 +2493,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); } @@ -2690,16 +2516,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(); } } @@ -2768,9 +2584,6 @@ void ServerChannelProcessRequesterImpl::destroy() Lock guard(_mutex); _channel->unregisterRequest(_ioid); - // asCheck - _channel->getChannelSecuritySession()->release(_ioid); - if (_channelProcess.get()) { _channelProcess->destroy(); @@ -2838,14 +2651,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 @@ -2928,14 +2733,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); } @@ -2968,16 +2765,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); } } @@ -3049,9 +2836,6 @@ void ServerChannelRPCRequesterImpl::destroy() Lock guard(_mutex); _channel->unregisterRequest(_ioid); - // asCheck - _channel->getChannelSecuritySession()->release(_ioid); - if (_channelRPC.get()) { _channelRPC->destroy(); From fa484a883a03bfc7074ec5e22c998015ec04c154 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 12 Jan 2019 11:48:52 -0800 Subject: [PATCH 02/17] redo security (aka. access control) Break up monolithic SecurityPlugin API into three parts. 1. Authentication. 2. Authorization. 3. Access Control decision. Left to ChannelProviders --- documentation/Doxyfile | 1 + src/client/pv/pvAccess.h | 21 + src/client/pvAccess.cpp | 7 + src/remote/codec.cpp | 256 ++++++----- src/remote/pv/blockingUDP.h | 6 +- src/remote/pv/codec.h | 50 +- src/remote/pv/remote.h | 13 +- src/remote/pv/security.h | 607 +++++++++---------------- src/remote/pv/serializationHelper.h | 2 +- src/remote/security.cpp | 246 ++++++++-- src/remote/serializationHelper.cpp | 2 +- src/remoteClient/clientContextImpl.cpp | 8 - src/server/pv/responseHandlers.h | 6 +- src/server/pv/serverChannelImpl.h | 8 +- src/server/pv/serverContextImpl.h | 1 - src/server/responseHandlers.cpp | 77 ++-- src/server/serverChannelImpl.cpp | 10 +- src/server/serverContext.cpp | 6 - testApp/remote/testCodec.cpp | 8 +- 19 files changed, 665 insertions(+), 670 deletions(-) diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 85bb1ca..fb515da 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -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 diff --git a/src/client/pv/pvAccess.h b/src/client/pv/pvAccess.h index 74e6d3f..66edf75 100644 --- a/src/client/pv/pvAccess.h +++ b/src/client/pv/pvAccess.h @@ -819,6 +819,7 @@ public: }; +struct PeerInfo; // see pv/security.h class ChannelRequester; @@ -1133,6 +1134,26 @@ public: * @param connectionState The new connection state. */ virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) = 0; + + /** + * @brief Return information on connected peer if applicable. + * + * A server-type ChannelProvider will use this method to discover if a remote client + * has provided credentials which may be used in access control decisions. + * + * Default implementation returns NULL. + * + * isConnected()==true and getPeerInfo()==NULL when the ChannelProvider does not provide + * information about the peer. This should be treated as an unauthenticated, anonymous, + * peer. + * + * The returned instance must not change, and a different instance should be returned + * if/when peer information changes (eg. after reconnect). + * + * May return !NULL when !isConnected(). getPeerInfo() must be called _before_ + * testing isConnected() in situations where connection state is being polled. + */ + virtual std::tr1::shared_ptr getPeerInfo(); }; //! Used when ChannelProvider::createChannel() is passed a NULL ChannelRequester diff --git a/src/client/pvAccess.cpp b/src/client/pvAccess.cpp index 7975f74..5d2bef3 100644 --- a/src/client/pvAccess.cpp +++ b/src/client/pvAccess.cpp @@ -8,9 +8,11 @@ #include #include #include +#include #define epicsExportSharedSymbols #include +#include 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) diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 012b029..110fcc7 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -40,6 +40,8 @@ using namespace std; using namespace epics::pvData; using namespace epics::pvAccess; +typedef epicsGuard 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()); } - _verified = status.isSuccess(); + { + Guard G(_mutex); + _verified = status.isSuccess(); + } _verifiedEvent.signal(); } -void BlockingTCPTransportCodec::authNZMessage(epics::pvData::PVField::shared_pointer const & data) { - // TODO sync - if (_securitySession) - _securitySession->messageReceived(data); +void BlockingTCPTransportCodec::authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) { + AuthenticationSession::shared_pointer sess; + { + Guard G(_mutex); + sess = _authSession; + } + if (sess) + sess->messageReceived(data); else { char ipAddrStr[48]; @@ -1392,7 +1393,7 @@ class SecurityPluginMessageTransportSender : public TransportSender { public: POINTER_DEFINITIONS(SecurityPluginMessageTransportSender); - SecurityPluginMessageTransportSender(PVField::shared_pointer const & data) : + SecurityPluginMessageTransportSender(PVStructure::const_shared_pointer const & data) : _data(data) { } @@ -1405,11 +1406,10 @@ public: } private: - PVField::shared_pointer _data; + PVStructure::const_shared_pointer _data; }; -void BlockingTCPTransportCodec::sendSecurityPluginMessage(epics::pvData::PVField::shared_pointer const & data) { - // TODO not optimal since it allocates a new object every time +void BlockingTCPTransportCodec::sendSecurityPluginMessage(epics::pvData::PVStructure::const_shared_pointer const & data) { SecurityPluginMessageTransportSender::shared_pointer spmts(new SecurityPluginMessageTransportSender(data)); enqueueSendRequest(spmts); } @@ -1426,7 +1426,7 @@ BlockingServerTCPTransportCodec::BlockingServerTCPTransportCodec( int32_t receiveBufferSize) : BlockingTCPTransportCodec(true, context, channel, responseHandler, sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY), - _lastChannelSID(0), _verifyOrVerified(false), _securityRequired(false) + _lastChannelSID(0), _verifyOrVerified(false) { // NOTE: priority not yet known, default priority is used to //register/unregister @@ -1530,29 +1530,38 @@ void BlockingServerTCPTransportCodec::send(ByteBuffer* buffer, // TODO buffer->putShort(0x7FFF); - // list of authNZ plugin names - const Context::securityPlugins_t& securityPlugins = _context->getSecurityPlugins(); - vector 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 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::const_iterator iter = - validSPNames.begin(); - iter != validSPNames.end(); iter++) + SerializeHelper::writeSize(validSPNames.size(), buffer, this); + for (vector::const_iterator iter(validSPNames.begin()), end(validSPNames.end()); + iter != end; iter++) + { SerializeHelper::serializeString(*iter, buffer, this); + } - // TODO sync - _securityRequired = (validSPCount > 0); + { + Guard G(_mutex); + advertisedAuthPlugins.swap(validSPNames); + } // send immediately control->flush(true); @@ -1564,10 +1573,12 @@ void BlockingServerTCPTransportCodec::send(ByteBuffer* buffer, // control->startMessage(CMD_CONNECTION_VALIDATED, 0); + pvData::Status sts; { - Lock lock(_verificationStatusMutex); - _verificationStatus.serialize(buffer, control); + Lock lock(_mutex); + sts = _verificationStatus; } + sts.serialize(buffer, control); // send immediately control->flush(true); @@ -1600,14 +1611,25 @@ void BlockingServerTCPTransportCodec::internalClose() { destroyAllChannels(); } -void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status) +void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Status const & status, + const std::tr1::shared_ptr& peer) { if (IS_LOGGABLE(logLevelDebug)) { LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], _socketName.c_str()); } - if (!isVerified()) // TODO sync + bool isVerified; + { + Guard G(_mutex); + isVerified = _verified; + if(status.isSuccess()) + _peerInfo = peer; + else + _peerInfo.reset(); + } + + if (!isVerified) verified(status); else if (!status.isSuccess()) { @@ -1620,59 +1642,35 @@ void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Sta } } -epics::pvData::Status BlockingServerTCPTransportCodec::invalidSecurityPluginNameStatus(Status::STATUSTYPE_ERROR, "invalid security plug-in name"); - void BlockingServerTCPTransportCodec::authNZInitialize(const std::string& securityPluginName, - const epics::pvData::PVField::shared_pointer& data) + const epics::pvData::PVStructure::shared_pointer& data) { - // check if plug-in name is valid - SecurityPlugin::shared_pointer securityPlugin; + AuthenticationPlugin::shared_pointer plugin(AuthenticationRegistry::servers().lookup(securityPluginName)); + // attempting the force use of an un-advertised/non-existant plugin is treated as a protocol error. + // We cheat here by assuming the the registry doesn't often change after server start, + // and don't test if securityPluginName is in advertisedAuthPlugins + if(!plugin) + throw std::runtime_error(_socketName+" failing attempt to select non-existant auth. plugin "+securityPluginName); - Context::securityPlugins_t::const_iterator spIter(_context->getSecurityPlugins().find(securityPluginName)); - if (spIter != _context->getSecurityPlugins().end()) - securityPlugin = spIter->second; - if (!securityPlugin) - { - if (_securityRequired) - { - verified(invalidSecurityPluginNameStatus); - return; - } - else - { - securityPlugin = NoSecurityPlugin::INSTANCE; + PeerInfo::shared_pointer info(new PeerInfo); + info->peer = _socketName; + info->transport = "pva"; + info->transportVersion = getRevision(); + info->authority = securityPluginName; - if (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()); - } - } - } - - if (!securityPlugin->isValidFor(_socketAddress)) - verified(invalidSecurityPluginNameStatus); + if (!plugin->isValidFor(*info)) + verified(pvData::Status::error("invalid security plug-in name")); 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); + LOG(logLevelDebug, "Accepted security plug-in '%s' for PVA client: %s.", securityPluginName.c_str(), _socketName.c_str()); } - try - { - // create session - SecurityPluginControl::shared_pointer spc = std::tr1::dynamic_pointer_cast(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); - } + AuthenticationSession::shared_pointer sess(plugin->createSession(info, shared_from_this(), data)); + + Guard G(_mutex); + _authSessionName = securityPluginName; + _authSession.swap(sess); } @@ -1894,17 +1892,25 @@ void BlockingClientTCPTransportCodec::send(ByteBuffer* buffer, // QoS (aka connection priority) buffer->putShort(getPriority()); - // TODO sync - if (_securitySession) + std::string pluginName; + AuthenticationSession::shared_pointer session; + { + Guard G(_mutex); + pluginName = _authSessionName; + session = _authSession; + } + + if (session) { // selected authNZ plug-in name - SerializeHelper::serializeString(_securitySession->getSecurityPlugin()->getId(), buffer, control); + SerializeHelper::serializeString(_authSessionName, buffer, control); // optional authNZ plug-in initialization data - SerializationHelper::serializeFull(buffer, control, _securitySession->initializationData()); + SerializationHelper::serializeFull(buffer, control, session->initializationData()); } else { + //TODO: allowed? // emptry authNZ plug-in name SerializeHelper::serializeString("", buffer, control); @@ -1926,38 +1932,68 @@ void BlockingClientTCPTransportCodec::send(ByteBuffer* buffer, void BlockingClientTCPTransportCodec::authNZInitialize(const std::vector& offeredSecurityPlugins) { - if (!offeredSecurityPlugins.empty()) + AuthenticationRegistry& plugins = AuthenticationRegistry::clients(); + std::string selectedName; + AuthenticationPlugin::shared_pointer plugin; + + // 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... + + for(std::vector::const_reverse_iterator it(offeredSecurityPlugins.rbegin()), end(offeredSecurityPlugins.rend()); + it!=end; ++it) { - const Context::securityPlugins_t& availableSecurityPlugins(_context->getSecurityPlugins()); - - for (vector::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(shared_from_this()); - - // TODO sync - _securitySession = securityPlugin->createSession(_socketAddress, spc, PVField::shared_pointer()); - } - } + 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(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& 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); +} + } } } diff --git a/src/remote/pv/blockingUDP.h b/src/remote/pv/blockingUDP.h index 9f55558..d84b3c4 100644 --- a/src/remote/pv/blockingUDP.h +++ b/src/remote/pv/blockingUDP.h @@ -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 getSecuritySession() const OVERRIDE FINAL { - return std::tr1::shared_ptr(); - } - // NOTE: this is not yet used for UDP virtual void setByteOrder(int byteOrder) OVERRIDE FINAL { // called from receive thread... or before processing diff --git a/src/remote/pv/codec.h b/src/remote/pv/codec.h index e792bdf..2b2f910 100644 --- a/src/remote/pv/codec.h +++ b/src/remote/pv/codec.h @@ -296,7 +296,7 @@ private: class BlockingTCPTransportCodec: public AbstractCodec, - public SecurityPluginControl, + public AuthenticationPluginControl, public std::tr1::enable_shared_from_this { @@ -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 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(); - _verificationStatus = status; - _verificationStatusMutex.unlock(); + { + epicsGuard G(_mutex); + _verificationStatus = status; + } 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& 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 advertisedAuthPlugins; }; @@ -673,8 +671,10 @@ public: void authNZInitialize(const std::vector& offeredSecurityPlugins); - virtual void authenticationCompleted(epics::pvData::Status const & status) OVERRIDE FINAL; + virtual void authenticationCompleted(epics::pvData::Status const & status, + const std::tr1::shared_ptr& 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; }; } diff --git a/src/remote/pv/remote.h b/src/remote/pv/remote.h index dcfc378..d41b39b 100644 --- a/src/remote/pv/remote.h +++ b/src/remote/pv/remote.h @@ -291,13 +291,12 @@ public: * Pass data to the active security plug-in session. * @param data the data (any data), can be null. */ - virtual void authNZMessage(epics::pvData::PVField::shared_pointer const & data) = 0; - - virtual std::tr1::shared_ptr 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 > 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 /// diff --git a/src/remote/pv/security.h b/src/remote/pv/security.h index fb247c0..1af1d7b 100644 --- a/src/remote/pv/security.h +++ b/src/remote/pv/security.h @@ -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<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 #include +#include #include #include @@ -34,426 +101,166 @@ namespace epics { namespace pvAccess { -// notify client only on demand, configurable via pvRequest -// add the following method to ChannelRequest: -// void credentialsChanged(std::vector credentials); +/** @brief Information provded by a client to a server-type ChannelProvider. + * + * 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. + */ +struct epicsShareClass PeerInfo { + POINTER_DEFINITIONS(PeerInfo); + static size_t num_instances; -// pvAccess message: channel client id, ioid (if invalid then it's for channel) and array of bitSets -// or leave to the plugin? + 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; -// when clients gets initial credentialsChanged call before create is called -// and then on each change + typedef std::set roles_t; + //! Set of strings which may be used to modify access control decisions. + roles_t roles; -class epicsShareClass ChannelSecuritySession { -public: - POINTER_DEFINITIONS(ChannelSecuritySession); + unsigned transportVersion; //!< If applicable, the protocol minor version number - virtual ~ChannelSecuritySession() {} + // attributes for programatic consumption + bool local; //!< Short-hand for transport=="local" + bool identified; //!< Short-hand for authority!="anonymous" - /// 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; + PeerInfo(); + virtual ~PeerInfo(); }; -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 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 true 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 - * - * @warning a Ref. loop is created if the SecuritySession stores a pointer to 'control' - */ - // 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; -}; - - - -class epicsShareClass NoSecurityPlugin : - public SecurityPlugin, - public SecuritySession, - public ChannelSecuritySession, - public std::tr1::enable_shared_from_this { -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 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. - */ - 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 true 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 +/** A particular authentication exchange. See AuthenticationPlugin::createSession() + * + * @note Must not hold a strong reference to AuthenticationPluginControl + */ +class epicsShareClass AuthenticationSession { - EPICS_NOT_COPYABLE(SecurityPluginRegistry) public: + POINTER_DEFINITIONS(AuthenticationSession); - static SecurityPluginRegistry& instance() - { - static SecurityPluginRegistry thisInstance; - return thisInstance; - } + virtual ~AuthenticationSession(); - typedef std::map > 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 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 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& 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 createSession( + const std::tr1::shared_ptr& peer, + std::tr1::shared_ptr 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 > map_t; + map_t map; + mutable epicsMutex mutex; +public: + typedef std::vector 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; }; } diff --git a/src/remote/pv/serializationHelper.h b/src/remote/pv/serializationHelper.h index 4894c28..dd0b9d0 100644 --- a/src/remote/pv/serializationHelper.h +++ b/src/remote/pv/serializationHelper.h @@ -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); }; diff --git a/src/remote/security.cpp b/src/remote/security.cpp index d0104a8..abb268d 100644 --- a/src/remote/security.cpp +++ b/src/remote/security.cpp @@ -6,51 +6,223 @@ #include +#include +#include +#include +#include + #define epicsExportSharedSymbols #include -using namespace epics::pvData; -using namespace epics::pvAccess; +typedef epicsGuard 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("user")->put(userName); + virtual std::tr1::shared_ptr createSession( + const std::tr1::shared_ptr& peer, + std::tr1::shared_ptr const & control, + epics::pvData::PVStructure::shared_pointer const & data) OVERRIDE FINAL + { + std::tr1::shared_ptr 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 buffer(256u); + if(osiGetUserName(&buffer[0], buffer.size()) != osiGetUserNameSuccess) + throw std::runtime_error("Unable to determine user account name"); - m_userAndHost->getSubFieldT("host")->put(buffer); + buffer[buffer.size()-1] = '\0'; + user->getSubFieldT("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("host")->put(&buffer[0]); + } + virtual ~CAPlugin() {} + + virtual std::tr1::shared_ptr createSession( + const std::tr1::shared_ptr& peer, + std::tr1::shared_ptr const & control, + epics::pvData::PVStructure::shared_pointer const & data) OVERRIDE FINAL + { + std::tr1::shared_ptr sess(new SimpleSession(user)); // no init data + if(server) { + peer->identified = true; + peer->account = data->getSubFieldT("user")->get(); + peer->aux = pvd::getPVDataCreate()->createPVStructure(data); // clone to ensure it won't be modified + control->authenticationCompleted(pvd::Status::Ok, peer); + } + return sess; + } +}; + +} // namespace + +namespace epics { +namespace pvAccess { + +size_t PeerInfo::num_instances; + +PeerInfo::PeerInfo() + :transportVersion(0u) + ,local(false) + ,identified(false) +{ + REFTRACE_INCREMENT(num_instances); } +PeerInfo::~PeerInfo() +{ + REFTRACE_DECREMENT(num_instances); +} + +AuthenticationSession::~AuthenticationSession() {} + +AuthenticationPluginControl::~AuthenticationPluginControl() {} + +AuthenticationPlugin::~AuthenticationPlugin() {} + +AuthenticationRegistry::~AuthenticationRegistry() {} + +namespace { +struct authGbl_t { + mutable epicsMutex mutex; + AuthenticationRegistry servers, clients; +} *authGbl; + +void authGblInit(void *) +{ + authGbl = new authGbl_t; + + { + AnonPlugin::shared_pointer plugin(new AnonPlugin(true)); + authGbl->servers.add(-1024, "anonymous", plugin); + } + { + AnonPlugin::shared_pointer plugin(new AnonPlugin(false)); + authGbl->clients.add(-1024, "anonymous", plugin); + } + + { + CAPlugin::shared_pointer plugin(new CAPlugin(true)); + authGbl->servers.add(0, "ca", plugin); + } + { + CAPlugin::shared_pointer plugin(new CAPlugin(false)); + authGbl->clients.add(0, "ca", plugin); + } + + epics::registerRefCounter("PeerInfo", &PeerInfo::num_instances); +} + +epicsThreadOnceId authGblOnce = EPICS_THREAD_ONCE_INIT; +} // namespace + +AuthenticationRegistry& AuthenticationRegistry::clients() +{ + epicsThreadOnce(&authGblOnce, &authGblInit, 0); + assert(authGbl); + return authGbl->clients; +} + +AuthenticationRegistry& AuthenticationRegistry::servers() +{ + epicsThreadOnce(&authGblOnce, &authGblInit, 0); + assert(authGbl); + return authGbl->servers; +} + +void AuthenticationRegistry::snapshot(list_t &plugmap) const +{ + plugmap.clear(); + Guard G(mutex); + plugmap.reserve(map.size()); + for(map_t::const_iterator it(map.begin()), end(map.end()); it!=end; ++it) { + plugmap.push_back(it->second); + } +} + +void AuthenticationRegistry::add(int prio, const std::string& name, + const AuthenticationPlugin::shared_pointer& plugin) +{ + Guard G(mutex); + if(map.find(prio)!=map.end()) + THROW_EXCEPTION2(std::logic_error, "Authentication plugin already registered with this priority"); + map[prio] = std::make_pair(name, plugin); +} + +bool AuthenticationRegistry::remove(const AuthenticationPlugin::shared_pointer& plugin) +{ + Guard G(mutex); + for(map_t::iterator it(map.begin()), end(map.end()); it!=end; ++it) { + if(it->second.second==plugin) { + map.erase(it); + return true; + } + } + return false; +} + +AuthenticationPlugin::shared_pointer AuthenticationRegistry::lookup(const std::string& name) const +{ + Guard G(mutex); + // assuming the number of plugins is small, we don't index by name. + for(map_t::const_iterator it(map.begin()), end(map.end()); it!=end; ++it) { + if(it->second.first==name) + return it->second.second; + } + return AuthenticationPlugin::shared_pointer(); +} + + void AuthNZHandler::handleResponse(osiSockAddr* responseFrom, Transport::shared_pointer const & transport, @@ -61,15 +233,17 @@ void AuthNZHandler::handleResponse(osiSockAddr* responseFrom, { ResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer); - epics::pvData::PVField::shared_pointer data = - SerializationHelper::deserializeFull(payloadBuffer, transport.get()); + pvd::PVStructure::shared_pointer data; + { + pvd::PVField::shared_pointer raw(SerializationHelper::deserializeFull(payloadBuffer, transport.get())); + if(raw->getField()->getType()==pvd::structure) { + data = std::tr1::static_pointer_cast(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 diff --git a/src/remote/serializationHelper.cpp b/src/remote/serializationHelper.cpp index e1bad43..63a9e13 100644 --- a/src/remote/serializationHelper.cpp +++ b/src/remote/serializationHelper.cpp @@ -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) { diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index 3a4cc70..25e4e5b 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -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. diff --git a/src/server/pv/responseHandlers.h b/src/server/pv/responseHandlers.h index b8f62b0..5e6ee74 100644 --- a/src/server/pv/responseHandlers.h +++ b/src/server/pv/responseHandlers.h @@ -201,14 +201,15 @@ public: protected: ServerChannelRequesterImpl(Transport::shared_pointer const & transport, const std::string channelName, - const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css); + const pvAccessID cid); public: virtual ~ServerChannelRequesterImpl() {} static ChannelRequester::shared_pointer create(ChannelProvider::shared_pointer const & provider, Transport::shared_pointer const & transport, const std::string channelName, - const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css); + const pvAccessID cid); virtual void channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel) OVERRIDE FINAL; virtual void channelStateChange(Channel::shared_pointer const & c, const Channel::ConnectionState isConnected) OVERRIDE FINAL; + virtual std::tr1::shared_ptr getPeerInfo() OVERRIDE FINAL; virtual std::string getRequesterName() OVERRIDE FINAL; virtual void message(std::string const & message, epics::pvData::MessageType messageType) OVERRIDE FINAL; virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) OVERRIDE FINAL; @@ -217,7 +218,6 @@ private: std::tr1::weak_ptr _transport; const std::string _channelName; const pvAccessID _cid; - ChannelSecuritySession::shared_pointer const & _css; epics::pvData::Status _status; epics::pvData::Mutex _mutex; }; diff --git a/src/server/pv/serverChannelImpl.h b/src/server/pv/serverChannelImpl.h index f74c4b5..87d03c7 100644 --- a/src/server/pv/serverChannelImpl.h +++ b/src/server/pv/serverChannelImpl.h @@ -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& request); void unregisterRequest(pvAccessID id); @@ -77,8 +73,6 @@ private: bool _destroyed; mutable epics::pvData::Mutex _mutex; - - const ChannelSecuritySession::shared_pointer _channelSecuritySession; }; } diff --git a/src/server/pv/serverContextImpl.h b/src/server/pv/serverContextImpl.h index 40a2536..ff07e93 100644 --- a/src/server/pv/serverContextImpl.h +++ b/src/server/pv/serverContextImpl.h @@ -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; diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index a17b75d..a5fc1d4 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -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(raw); + } else { + // was originally allowed, but never used + } + } detail::BlockingServerTCPTransportCodec* casTransport(static_cast(transport.get())); //TODO: simplify byzantine class heirarchy... assert(casTransport); - casTransport->authNZInitialize(securityPluginName, data); + try { + casTransport->authNZInitialize(securityPluginName, data); + }catch(std::exception& e){ + if (IS_LOGGABLE(logLevelDebug)) + { + LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", securityPluginName.c_str(), casTransport->getRemoteName().c_str()); + } + casTransport->verified(pvData::Status::error(e.what())); + throw; + } } @@ -732,30 +747,13 @@ void ServerCreateChannelHandler::handleResponse(osiSockAddr* responseFrom, return; } - SecuritySession::shared_pointer securitySession = transport->getSecuritySession(); - ChannelSecuritySession::shared_pointer css; - try { - css = securitySession->createChannelSession(channelName); - if (!css) - throw SecurityException("null channelSecuritySession"); - } catch (SecurityException& se) { - // TODO use std::make_shared - std::tr1::shared_ptr 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 tp(new ServerChannelRequesterImpl(transport, channelName, cid, css)); + std::tr1::shared_ptr tp(new ServerChannelRequesterImpl(transport, channelName, cid)); ChannelRequester::shared_pointer cr = tp; Channel::shared_pointer serverChannel = createRPCChannel(ChannelProvider::shared_pointer(), channelName, cr, serverRPCService); cr->channelCreated(Status::Ok, serverChannel); @@ -766,7 +764,7 @@ void ServerCreateChannelHandler::handleResponse(osiSockAddr* responseFrom, ServerContextImpl::s_channelNameToProvider_t::const_iterator it; if (_providers.size() == 1) - ServerChannelRequesterImpl::create(_providers[0], transport, channelName, cid, css); + ServerChannelRequesterImpl::create(_providers[0], transport, channelName, cid); else { ChannelProvider::shared_pointer prov; { @@ -775,7 +773,7 @@ void ServerCreateChannelHandler::handleResponse(osiSockAddr* responseFrom, prov = it->second.lock(); } if(prov) - ServerChannelRequesterImpl::create(prov, transport, channelName, cid, css); + ServerChannelRequesterImpl::create(prov, transport, channelName, cid); } } } @@ -786,12 +784,11 @@ void ServerCreateChannelHandler::disconnect(Transport::shared_pointer const & tr } ServerChannelRequesterImpl::ServerChannelRequesterImpl(const Transport::shared_pointer &transport, - const string channelName, const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css) : + const string channelName, const pvAccessID cid) : _serverChannel(), _transport(std::tr1::static_pointer_cast(transport)), _channelName(channelName), _cid(cid), - _css(css), _status(), _mutex() { @@ -799,10 +796,10 @@ ServerChannelRequesterImpl::ServerChannelRequesterImpl(const Transport::shared_p ChannelRequester::shared_pointer ServerChannelRequesterImpl::create( ChannelProvider::shared_pointer const & provider, Transport::shared_pointer const & transport, - const string channelName, const pvAccessID cid, ChannelSecuritySession::shared_pointer const & css) + const string channelName, const pvAccessID cid) { // TODO use std::make_shared - std::tr1::shared_ptr tp(new ServerChannelRequesterImpl(transport, channelName, cid, css)); + std::tr1::shared_ptr tp(new ServerChannelRequesterImpl(transport, channelName, cid)); ChannelRequester::shared_pointer cr = tp; // TODO exception guard and report error back provider->createChannel(channelName, cr, transport->getPriority()); @@ -824,7 +821,7 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s pvAccessID sid = transport->preallocateChannelSID(); try { - serverChannel.reset(new ServerChannel(channel, shared_from_this(), _cid, sid, _css)); + serverChannel.reset(new ServerChannel(channel, shared_from_this(), _cid, sid)); // ack allocation and register transport->registerChannel(sid, serverChannel); @@ -836,11 +833,6 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s throw; } } - else - { - if (_css) - _css->close(); - } { @@ -861,9 +853,6 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s } TransportSender::shared_pointer thisSender = shared_from_this(); transport->enqueueSendRequest(thisSender); - // TODO make sure that serverChannel gets destroyed - if (_css) - _css->close(); } catch (...) { @@ -874,9 +863,6 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s } TransportSender::shared_pointer thisSender = shared_from_this(); transport->enqueueSendRequest(thisSender); - // TODO make sure that serverChannel gets destroyed - if (_css) - _css->close(); } } } @@ -909,6 +895,17 @@ void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer cons } } +std::tr1::shared_ptr ServerChannelRequesterImpl::getPeerInfo() +{ + if(detail::BlockingServerTCPTransportCodec::shared_pointer transport = _transport.lock()) { + epicsGuard G(transport->_mutex); + return transport->_peerInfo; + + } else { + return std::tr1::shared_ptr(); + } +} + string ServerChannelRequesterImpl::getRequesterName() { detail::BlockingServerTCPTransportCodec::shared_pointer transport = _transport.lock(); diff --git a/src/server/serverChannelImpl.cpp b/src/server/serverChannelImpl.cpp index f98b77c..1402989 100644 --- a/src/server/serverChannelImpl.cpp +++ b/src/server/serverChannelImpl.cpp @@ -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(); diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index f10c338..447dc1c 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -533,12 +533,6 @@ epicsTimeStamp& ServerContextImpl::getStartTime() return _startTime; } - -const Context::securityPlugins_t& ServerContextImpl::getSecurityPlugins() -{ - return SecurityPluginRegistry::instance().getServerSecurityPlugins(); -} - ServerContext::shared_pointer startPVAServer(std::string const & providerNames, int timeToRun, bool runInSeparateThread, bool printInfo) { ServerContext::shared_pointer ret(ServerContext::create(ServerContext::Config() diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 0ad7959..229d7c6 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -396,13 +396,7 @@ public: void aliveNotification() {} - void authNZMessage(epics::pvData::PVField::shared_pointer const & data) {} - - virtual std::tr1::shared_ptr getSecuritySession() const - { - return std::tr1::shared_ptr(); - } - + void authNZMessage(epics::pvData::PVStructure::shared_pointer const & data) {} bool isClosed() { From 9babc06ab38d417a0482234404d25ae207c90309 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 12 Jan 2019 15:13:53 -0800 Subject: [PATCH 03/17] pvasr show PeerInfo --- src/server/serverContext.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 447dc1c..25c3b8e 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -398,7 +398,7 @@ void ServerContextImpl::printInfo(ostream& str, int lvl) { const Transport::shared_pointer& transport(*it); - str<<"client "<getType()<<"://"<getRemoteName() + str<<" "<getType()<<"://"<getRemoteName() <<" ver="<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 G(casTransport->_mutex); + peer = casTransport->_peerInfo; + } + if(peer) { + str<<" user: "<authority<<"/"<account; + if(!peer->realm.empty()) + str<<"@"<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"; From 3209899172c03bb7c2610eeb17d748f9f6d1b54d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jan 2019 19:21:47 -0800 Subject: [PATCH 04/17] authorization framework --- src/remote/codec.cpp | 3 ++ src/remote/pv/security.h | 36 ++++++++++++++++++++++++ src/remote/security.cpp | 60 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 110fcc7..33755b8 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -1619,6 +1619,9 @@ void BlockingServerTCPTransportCodec::authenticationCompleted(epics::pvData::Sta LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], _socketName.c_str()); } + if(peer) + AuthorizationRegistry::plugins().run(peer); + bool isVerified; { Guard G(_mutex); diff --git a/src/remote/pv/security.h b/src/remote/pv/security.h index 1af1d7b..68f2e5b 100644 --- a/src/remote/pv/security.h +++ b/src/remote/pv/security.h @@ -263,6 +263,42 @@ public: 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& peer) =0; +}; + +class epicsShareClass AuthorizationRegistry +{ + EPICS_NOT_COPYABLE(AuthorizationRegistry) +public: + POINTER_DEFINITIONS(AuthenticationRegistry); + + static AuthorizationRegistry &plugins(); + + AuthorizationRegistry(); + ~AuthorizationRegistry(); + +private: + typedef std::map 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& peer); +}; + } } diff --git a/src/remote/security.cpp b/src/remote/security.cpp index abb268d..45c0201 100644 --- a/src/remote/security.cpp +++ b/src/remote/security.cpp @@ -132,10 +132,15 @@ AuthenticationPlugin::~AuthenticationPlugin() {} AuthenticationRegistry::~AuthenticationRegistry() {} +AuthorizationPlugin::~AuthorizationPlugin() {} + +AuthorizationRegistry::~AuthorizationRegistry() {} + namespace { struct authGbl_t { mutable epicsMutex mutex; AuthenticationRegistry servers, clients; + AuthorizationRegistry authorizers; } *authGbl; void authGblInit(void *) @@ -223,6 +228,61 @@ AuthenticationPlugin::shared_pointer AuthenticationRegistry::lookup(const std::s } +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& peer) +{ + int marker; + { + Guard G(mutex); + if(busy) + throw std::runtime_error("AuthorizationRegistry busy"); + busy = ▮ + } + 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, From b168c51aacbb32a3b562b4542b30c912b3193759 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jan 2019 20:37:10 -0800 Subject: [PATCH 05/17] Add osdGetRoles() --- src/Makefile | 2 +- src/remote/pv/security.h | 7 +++ src/utils/Makefile | 1 + src/utils/getgroups.cpp | 132 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 src/utils/getgroups.cpp diff --git a/src/Makefile b/src/Makefile index a97e52d..34d816a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/remote/pv/security.h b/src/remote/pv/security.h index 68f2e5b..87cab62 100644 --- a/src/remote/pv/security.h +++ b/src/remote/pv/security.h @@ -299,6 +299,13 @@ public: void run(const std::tr1::shared_ptr& 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); + } } diff --git a/src/utils/Makefile b/src/utils/Makefile index 0cbe92a..fd35b02 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -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 diff --git a/src/utils/getgroups.cpp b/src/utils/getgroups.cpp new file mode 100644 index 0000000..e3580b5 --- /dev/null +++ b/src/utils/getgroups.cpp @@ -0,0 +1,132 @@ + +#include + +#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 +#include +#include +#include +#include + +#elif defined(USE_LANMAN) + +#include +#include +#include + +#endif + +#define epicsExportSharedSymbols +#include + +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 gids_t; + gids_t gids; + + gids.insert(user->pw_gid); // include primary group + + // include supplementary groups + { + std::vector gtemp(16); + int gcount = int(gtemp.size()); + + if(getgrouplist(user->pw_name, user->pw_gid, >emp[0], &gcount)==-1 && gcount>=0 && gcount<=NGROUPS_MAX) { + gtemp.resize(gcount); + // try again. This time if we fail, then there is some other error + getgrouplist(user->pw_name, user->pw_gid, >emp[0], &gcount); + } + gtemp.resize(std::min(gcount, NGROUPS_MAX)); + + for(size_t i=0, N=gtemp.size(); igr_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 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 buf; + + for(DWORD i=0; i Date: Fri, 11 Jan 2019 20:52:42 -0800 Subject: [PATCH 06/17] authorize with local group lookup --- src/remote/security.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/remote/security.cpp b/src/remote/security.cpp index 45c0201..c19c8e2 100644 --- a/src/remote/security.cpp +++ b/src/remote/security.cpp @@ -104,6 +104,19 @@ struct CAPlugin : public pva::AuthenticationPlugin } }; +struct GroupsPlugin : public pva::AuthorizationPlugin +{ + virtual ~GroupsPlugin() {} + + void authorize(const std::tr1::shared_ptr& peer) + { + if(!peer->identified) + return; // no groups for anonymous + + pva::osdGetRoles(peer->account, peer->roles); + } +}; + } // namespace namespace epics { @@ -164,6 +177,10 @@ void authGblInit(void *) 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); } From bfff9275bdadc89ec5c493fa8dbf24ba59722132 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 12 Jan 2019 17:07:12 -0800 Subject: [PATCH 07/17] always need to link with netapi32 now --- pvtoolsSrc/Makefile | 2 +- src/ca/Makefile | 2 +- testApp/remote/Makefile | 2 +- testApp/utils/Makefile | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pvtoolsSrc/Makefile b/pvtoolsSrc/Makefile index 39a1b17..1b3a3c1 100644 --- a/pvtoolsSrc/Makefile +++ b/pvtoolsSrc/Makefile @@ -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 #---------------------------------------- diff --git a/src/ca/Makefile b/src/ca/Makefile index 6bed7c3..bf0e4ef 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -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 diff --git a/testApp/remote/Makefile b/testApp/remote/Makefile index 5d9e805..96eb216 100644 --- a/testApp/remote/Makefile +++ b/testApp/remote/Makefile @@ -10,7 +10,7 @@ TESTS += testChannelAccess TESTPROD_HOST += testCodec testCodec_SRCS = testCodec testHarness_SRCS += testCodec.cpp -testCodec_SYS_LIBS_WIN32 += ws2_32 +testCodec_SYS_LIBS_WIN32 += netapi32 ws2_32 TESTS += testCodec TESTPROD_HOST += testRPC diff --git a/testApp/utils/Makefile b/testApp/utils/Makefile index 2d46e26..79f6f47 100644 --- a/testApp/utils/Makefile +++ b/testApp/utils/Makefile @@ -18,13 +18,13 @@ testInetAddressUtils = testInetAddressUtils.cpp # TODO this is gcc only testInetAddressUtils_CXXFLAGS = -O0 # needed for 64-bit Windows -testInetAddressUtils_SYS_LIBS_WIN32 += ws2_32 +testInetAddressUtils_SYS_LIBS_WIN32 += netapi32 ws2_32 testHarness_SRCS += testInetAddressUtils.cpp TESTS += testInetAddressUtils TESTPROD_HOST += configurationTest configurationTest_SRCS += configurationTest.cpp -configurationTest_SYS_LIBS_WIN32 += ws2_32 +configurationTest_SYS_LIBS_WIN32 += netapi32 ws2_32 #testHarness_SRCS += configurationTest.cpp TESTS += configurationTest From b46e0972ea77ae1b00e443d63d2169670a065858 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 13 Jan 2019 19:00:24 -0800 Subject: [PATCH 08/17] minor doc --- src/remote/abstractResponseHandler.cpp | 2 +- src/server/serverContext.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote/abstractResponseHandler.cpp b/src/remote/abstractResponseHandler.cpp index 6dad2ef..71790e4 100644 --- a/src/remote/abstractResponseHandler.cpp +++ b/src/remote/abstractResponseHandler.cpp @@ -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); } diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 25c3b8e..dde73e3 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -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); From bdd27303159217641576d30d71fe752e18806011 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 13 Jan 2019 19:01:15 -0800 Subject: [PATCH 09/17] auth status void accidental success. The default value of _verificationStatus is reported on auth timeout. Make sure it isn't success. --- src/remote/codec.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/remote/codec.cpp b/src/remote/codec.cpp index 33755b8..f3454a0 100644 --- a/src/remote/codec.cpp +++ b/src/remote/codec.cpp @@ -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) + 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 From d8f9ef2557557189b63e6b267748883a74ca2973 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 Jan 2019 09:06:30 -0800 Subject: [PATCH 10/17] make PeerInfo available during search phase --- src/client/pv/pvAccess.h | 28 ++++++++++++++++++++++++++-- src/server/pv/responseHandlers.h | 11 +++++++---- src/server/responseHandlers.cpp | 15 ++++++++++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/client/pv/pvAccess.h b/src/client/pv/pvAccess.h index 66edf75..888a4e5 100644 --- a/src/client/pv/pvAccess.h +++ b/src/client/pv/pvAccess.h @@ -393,6 +393,8 @@ public: static ChannelFind::shared_pointer buildDummy(const std::tr1::shared_ptr& 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 getPeerInfo() + { return std::tr1::shared_ptr(); } }; /** @@ -819,8 +845,6 @@ public: }; -struct PeerInfo; // see pv/security.h - class ChannelRequester; /** diff --git a/src/server/pv/responseHandlers.h b/src/server/pv/responseHandlers.h index 5e6ee74..aec3d07 100644 --- a/src/server/pv/responseHandlers.h +++ b/src/server/pv/responseHandlers.h @@ -135,7 +135,9 @@ class ServerChannelFindRequesterImpl: public std::tr1::enable_shared_from_this { 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, @@ -155,9 +157,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; }; diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index a5fc1d4..7d3f455 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -335,6 +335,14 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, } } + PeerInfo::shared_pointer info; + if(allowed) { + info.reset(new PeerInfo); + info->transport = "pva"; + info->peer = transport->getRemoteName(); + info->transportVersion = transport->getRevision(); + } + if (count > 0) { // regular name search @@ -350,7 +358,7 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, const std::vector& _providers = _context->getChannelProviders(); int providerCount = _providers.size(); - std::tr1::shared_ptr tp(new ServerChannelFindRequesterImpl(_context, providerCount)); + std::tr1::shared_ptr tp(new ServerChannelFindRequesterImpl(_context, info, providerCount)); tp->set(name, searchSequenceId, cid, responseAddress, responseRequired, false); for (int i = 0; i < providerCount; i++) @@ -368,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 tp(new ServerChannelFindRequesterImpl(_context, 1)); + std::tr1::shared_ptr tp(new ServerChannelFindRequesterImpl(_context, info, 1)); tp->set("", searchSequenceId, 0, responseAddress, true, true); TimerCallback::shared_pointer tc = tp; @@ -377,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) From 34f53c123a9fd3d40c0ecb58f29eccb7afc3a358 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 27 Jan 2019 19:27:40 -0800 Subject: [PATCH 11/17] PeerInfo in pva/server.h and pva/sharedstate.h --- src/server/pva/server.h | 10 +++++++++- src/server/pva/sharedstate.h | 4 ++++ src/server/server.cpp | 4 +++- src/server/sharedstate_channel.cpp | 5 +++++ src/server/sharedstate_put.cpp | 6 +++++- src/server/sharedstate_rpc.cpp | 6 +++++- src/server/sharedstateimpl.h | 3 +++ 7 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/server/pva/server.h b/src/server/pva/server.h index 3bd2196..0ac6724 100644 --- a/src/server/pva/server.h +++ b/src/server/pva/server.h @@ -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_type; diff --git a/src/server/pva/sharedstate.h b/src/server/pva/sharedstate.h index aadc245..c6be107 100644 --- a/src/server/pva/sharedstate.h +++ b/src/server/pva/sharedstate.h @@ -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); diff --git a/src/server/server.cpp b/src/server/server.cpp index 7e64650..f9ff685 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -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); diff --git a/src/server/sharedstate_channel.cpp b/src/server/sharedstate_channel.cpp index a972064..c76ba32 100644 --- a/src/server/sharedstate_channel.cpp +++ b/src/server/sharedstate_channel.cpp @@ -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); diff --git a/src/server/sharedstate_put.cpp b/src/server/sharedstate_put.cpp index 04d0cbc..b4117a0 100644 --- a/src/server/sharedstate_put.cpp +++ b/src/server/sharedstate_put.cpp @@ -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 diff --git a/src/server/sharedstate_rpc.cpp b/src/server/sharedstate_rpc.cpp index 4a8eca5..0d584d0 100644 --- a/src/server/sharedstate_rpc.cpp +++ b/src/server/sharedstate_rpc.cpp @@ -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 diff --git a/src/server/sharedstateimpl.h b/src/server/sharedstateimpl.h index ed67724..853c8d9 100644 --- a/src/server/sharedstateimpl.h +++ b/src/server/sharedstateimpl.h @@ -9,6 +9,7 @@ #include "pva/sharedstate.h" #include +#include #include #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; From c5fe16eff2ac1f71e6e5b711cfdcdabfd12b2771 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Feb 2019 21:27:50 -0800 Subject: [PATCH 12/17] fix search PeerInfo --- src/server/pv/responseHandlers.h | 3 ++- src/server/responseHandlers.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server/pv/responseHandlers.h b/src/server/pv/responseHandlers.h index aec3d07..03dc30b 100644 --- a/src/server/pv/responseHandlers.h +++ b/src/server/pv/responseHandlers.h @@ -144,6 +144,7 @@ public: 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 getPeerInfo() OVERRIDE FINAL; virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) OVERRIDE FINAL; virtual void callback() OVERRIDE FINAL; @@ -158,7 +159,7 @@ private: bool _responseRequired; bool _wasFound; const ServerContextImpl::shared_pointer _context; - const PeerInfo::const_shared_pointer& _peer; + const PeerInfo::const_shared_pointer _peer; mutable epics::pvData::Mutex _mutex; const epics::pvData::int32 _expectedResponseCount; epics::pvData::int32 _responseCount; diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 7d3f455..a5afe34 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -339,7 +339,7 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, if(allowed) { info.reset(new PeerInfo); info->transport = "pva"; - info->peer = transport->getRemoteName(); + info->peer = inetAddressToString(*responseFrom); info->transportVersion = transport->getRevision(); } @@ -467,6 +467,11 @@ void ServerChannelFindRequesterImpl::channelFindResult(const Status& /*status*/, } } +std::tr1::shared_ptr ServerChannelFindRequesterImpl::getPeerInfo() +{ + return _peer; +} + void ServerChannelFindRequesterImpl::send(ByteBuffer* buffer, TransportSendControl* control) { control->startMessage(CMD_SEARCH_RESPONSE, 12+4+16+2); From cddbabf8bc2b33cf987c15288c5432213f343077 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 4 Mar 2019 18:17:12 -0800 Subject: [PATCH 13/17] auth fix mingw static --- examples/Makefile | 1 + testApp/Makefile | 1 + testApp/remote/Makefile | 1 - testApp/utils/Makefile | 3 --- testCa/Makefile | 1 + 5 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 213d459..3aef798 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -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 diff --git a/testApp/Makefile b/testApp/Makefile index 4d59b61..36f3675 100644 --- a/testApp/Makefile +++ b/testApp/Makefile @@ -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 diff --git a/testApp/remote/Makefile b/testApp/remote/Makefile index 96eb216..0047a82 100644 --- a/testApp/remote/Makefile +++ b/testApp/remote/Makefile @@ -10,7 +10,6 @@ TESTS += testChannelAccess TESTPROD_HOST += testCodec testCodec_SRCS = testCodec testHarness_SRCS += testCodec.cpp -testCodec_SYS_LIBS_WIN32 += netapi32 ws2_32 TESTS += testCodec TESTPROD_HOST += testRPC diff --git a/testApp/utils/Makefile b/testApp/utils/Makefile index 79f6f47..ab5f17c 100644 --- a/testApp/utils/Makefile +++ b/testApp/utils/Makefile @@ -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 += netapi32 ws2_32 testHarness_SRCS += testInetAddressUtils.cpp TESTS += testInetAddressUtils TESTPROD_HOST += configurationTest configurationTest_SRCS += configurationTest.cpp -configurationTest_SYS_LIBS_WIN32 += netapi32 ws2_32 #testHarness_SRCS += configurationTest.cpp TESTS += configurationTest diff --git a/testCa/Makefile b/testCa/Makefile index 47092ac..1b1ded0 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -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 From 4ea6831800dfaef57418bfc2c20ce7b6e71d3cd3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 10 Apr 2019 11:27:16 -0700 Subject: [PATCH 14/17] getgrouplist() on OSX has different sig. --- examples/Makefile | 3 +++ src/utils/getgroups.cpp | 14 +++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 3aef798..44c6f39 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -5,6 +5,9 @@ include $(TOP)/configure/CONFIG PROD_LIBS += pvAccessCA ca pvAccess pvData Com PROD_SYS_LIBS_WIN32 += netapi32 ws2_32 +#TESTPROD_HOST += heinz +#heinz_SRCS += heinz.cpp + TESTPROD_HOST += getme getme_SRCS = getme.cpp diff --git a/src/utils/getgroups.cpp b/src/utils/getgroups.cpp index e3580b5..2bcc0c0 100644 --- a/src/utils/getgroups.cpp +++ b/src/utils/getgroups.cpp @@ -16,6 +16,16 @@ #include #include +// 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 @@ -45,7 +55,7 @@ void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles) // include supplementary groups { - std::vector gtemp(16); + std::vector gtemp(16); int gcount = int(gtemp.size()); if(getgrouplist(user->pw_name, user->pw_gid, >emp[0], &gcount)==-1 && gcount>=0 && gcount<=NGROUPS_MAX) { @@ -103,14 +113,12 @@ void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles) std::vector buf; for(DWORD i=0; i Date: Wed, 10 Apr 2019 11:57:23 -0700 Subject: [PATCH 15/17] Add showauth executable --- testApp/utils/Makefile | 3 +++ testApp/utils/showauth.cpp | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 testApp/utils/showauth.cpp diff --git a/testApp/utils/Makefile b/testApp/utils/Makefile index ab5f17c..e36b00b 100644 --- a/testApp/utils/Makefile +++ b/testApp/utils/Makefile @@ -33,3 +33,6 @@ TESTPROD_HOST += testWildcard testWildcard = testWildcard.cpp testHarness_SRCS += testWildcard.cpp TESTS += testWildcard + +TESTPROD_HOST += showauth +showauth_SRCS += showauth.cpp diff --git a/testApp/utils/showauth.cpp b/testApp/utils/showauth.cpp new file mode 100644 index 0000000..e82d5f8 --- /dev/null +++ b/testApp/utils/showauth.cpp @@ -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 +#include +#include + +#include +#include + +namespace pva = epics::pvAccess; + +int main(int argc, char *argv[]) +{ + int ret = 0; + try { + std::vector 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: "< Date: Wed, 17 Apr 2019 09:07:56 -0700 Subject: [PATCH 16/17] oops --- examples/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 44c6f39..3aef798 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -5,9 +5,6 @@ include $(TOP)/configure/CONFIG PROD_LIBS += pvAccessCA ca pvAccess pvData Com PROD_SYS_LIBS_WIN32 += netapi32 ws2_32 -#TESTPROD_HOST += heinz -#heinz_SRCS += heinz.cpp - TESTPROD_HOST += getme getme_SRCS = getme.cpp From 523cb46e21717e564a6dc9e7792faf1c1cb4f728 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 28 Apr 2019 20:09:44 -0700 Subject: [PATCH 17/17] fix osdGetRoles Ignore NGROUPS_MAX. On Darwin NGROUPS_MAX==16. The actual limit is higher. getgrouplist() is weird... --- src/utils/getgroups.cpp | 56 +++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/src/utils/getgroups.cpp b/src/utils/getgroups.cpp index 2bcc0c0..7159d94 100644 --- a/src/utils/getgroups.cpp +++ b/src/utils/getgroups.cpp @@ -1,3 +1,8 @@ +/** +* 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 @@ -53,17 +58,54 @@ void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles) gids.insert(user->pw_gid); // include primary group - // include supplementary groups + /* 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 gtemp(16); - int gcount = int(gtemp.size()); - if(getgrouplist(user->pw_name, user->pw_gid, >emp[0], &gcount)==-1 && gcount>=0 && gcount<=NGROUPS_MAX) { - gtemp.resize(gcount); - // try again. This time if we fail, then there is some other error - getgrouplist(user->pw_name, user->pw_gid, >emp[0], &gcount); + while(true) { + int gcount = int(gtemp.size()); + int ret = getgrouplist(user->pw_name, user->pw_gid, >emp[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; + } } - gtemp.resize(std::min(gcount, NGROUPS_MAX)); for(size_t i=0, N=gtemp.size(); i