/** * Copyright - See the COPYRIGHT that is included with this distribution. * pvAccessCPP is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ #ifndef SECURITY_H #define SECURITY_H #ifdef epicsExportSharedSymbols # define securityEpicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include #include #include #include #include #include #ifdef securityEpicsExportSharedSymbols # define epicsExportSharedSymbols # undef securityEpicsExportSharedSymbols #endif #include #include #include #include namespace epics { namespace pvAccess { // notify client only on demand, configurable via pvRequest // add the following method to ChannelRequest: // void credentialsChanged(std::vector credentials); // pvAccess message: channel client id, ioid (if invalid then it's for channel) and array of bitSets // or leave to the plugin? // when clients gets initial credentialsChanged call before create is called // and then on each change class epicsShareClass ChannelSecuritySession { public: POINTER_DEFINITIONS(ChannelSecuritySession); virtual ~ChannelSecuritySession() {} /// closes this session virtual void close() = 0; // for every authroizeCreate... a release() must be called virtual void release(pvAccessID ioid) = 0; // bitSet w/ one bit virtual epics::pvData::Status authorizeCreateChannelProcess( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0; virtual epics::pvData::Status authorizeProcess(pvAccessID ioid) = 0; // bitSet w/ one bit (allowed, not allowed) and rest of the bit per field virtual epics::pvData::Status authorizeCreateChannelGet( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0; virtual epics::pvData::Status authorizeGet(pvAccessID ioid) = 0; // read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field // write: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field virtual epics::pvData::Status authorizeCreateChannelPut( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0; virtual epics::pvData::Status authorizePut( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & dataToPut, epics::pvData::BitSet::shared_pointer const & fieldsToPut) = 0; // read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field // write: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field // process: bitSet w/ one bit (allowed, not allowed) virtual epics::pvData::Status authorizeCreateChannelPutGet( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0; virtual epics::pvData::Status authorizePutGet( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & dataToPut, epics::pvData::BitSet::shared_pointer const & fieldsToPut) = 0; // bitSet w/ one bit virtual epics::pvData::Status authorizeCreateChannelRPC( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0; // one could authorize per operation basis virtual epics::pvData::Status authorizeRPC( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & arguments) = 0; // read: bitSet w/ one bit (allowed, not allowed) and rest of the bit per field virtual epics::pvData::Status authorizeCreateMonitor( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0; virtual epics::pvData::Status authorizeMonitor(pvAccessID ioid) = 0; // read: bitSet w/ one bit (allowed, not allowed) and rest put/get/set length virtual epics::pvData::Status authorizeCreateChannelArray( pvAccessID ioid, epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0; // use authorizeGet virtual epics::pvData::Status authorizePut(pvAccessID ioid, epics::pvData::PVArray::shared_pointer const & dataToPut) = 0; virtual epics::pvData::Status authorizeSetLength(pvAccessID ioid) = 0; // introspection authorization virtual epics::pvData::Status authorizeGetField(pvAccessID ioid, std::string const & subField) = 0; }; class SecurityPlugin; class epicsShareClass 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 */ // 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 AuthNZHandler : public AbstractResponseHandler, private epics::pvData::NoDefaultMethods { public: AuthNZHandler(Context* context) : AbstractResponseHandler(context, "authNZ message") { } virtual ~AuthNZHandler() {} virtual void handleResponse(osiSockAddr* responseFrom, Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command, size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer); }; class epicsShareClass SecurityPluginRegistry : private epics::pvData::NoDefaultMethods { public: static SecurityPluginRegistry& instance() { static SecurityPluginRegistry thisInstance; return thisInstance; } std::map >& getClientSecurityPlugins() { return m_clientSecurityPlugins; } std::map >& getServerSecurityPlugins() { return m_serverSecurityPlugins; } 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()); } 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()); } private: SecurityPluginRegistry(); std::map > m_clientSecurityPlugins; std::map > m_serverSecurityPlugins; }; } } #endif // SECURITY_H