First draft of simple nether logic. 73/63573/2
authorr.kubiak <r.kubiak@samsung.com>
Wed, 23 Mar 2016 16:58:56 +0000 (17:58 +0100)
committerZbigniew Jasinski <z.jasinski@samsung.com>
Wed, 6 Apr 2016 09:18:45 +0000 (11:18 +0200)
This allows to specify exclusion rules in the
cynara backend, so that certain privileges
can be marked with different packet marks
and thanks to iptables those packets can
hit other chains (not the default ones)
so they can pass through or get redirected
if needed.

Change-Id: I61092196c727bddf975d404171468a251db55ea4
Signed-off-by: Zbigniew Jasinski <z.jasinski@samsung.com>
13 files changed:
conf/CMakeLists.txt
conf/cynara.policy [new file with mode: 0644]
conf/nether.policy
conf/systemd/nether.service
include/nether_CynaraBackend.h
include/nether_Manager.h
include/nether_Netlink.h
include/nether_Types.h
packaging/nether.spec
src/nether_CynaraBackend.cpp
src/nether_Main.cpp
src/nether_Manager.cpp
src/nether_Netlink.cpp

index be55e51..9479e08 100644 (file)
@@ -21,6 +21,7 @@ MESSAGE(STATUS "Installing config files")
 CONFIGURE_FILE(systemd/nether.service.in systemd/nether.service)
 
 INSTALL(FILES nether.policy DESTINATION ${SYSCONF_INSTALL_DIR}/nether)
+INSTALL(FILES cynara.policy DESTINATION ${SYSCONF_INSTALL_DIR}/nether)
 INSTALL(FILES nether.rules DESTINATION ${SYSCONF_INSTALL_DIR}/nether)
 INSTALL(FILES systemd/nether.service DESTINATION ${SYSTEMD_UNIT_DIR})
 INSTALL(FILES systemd/nether.service DESTINATION ${SYSTEMD_UNIT_DIR}/multi-user.target.wants)
diff --git a/conf/cynara.policy b/conf/cynara.policy
new file mode 100644 (file)
index 0000000..9652d41
--- /dev/null
@@ -0,0 +1,39 @@
+#
+#  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  Contact: Roman Kubiak (r.kubiak@samsung.com)
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License
+#
+
+# this is the normal, global INTERNET
+# privilege, connections that are granted
+# this privilege do net get marked, and
+# are passed through with no additional
+# activity
+#
+# if this privilege is granted no additional
+# privileges are checked
+http://tizen.org/privilege/internet
+
+# this is an additional privilege
+# if the normal (first) privielege is not
+# granted, all additional ones are checked
+# these get a special packet mark
+# so that connections matching only this
+# specific privilege are passed to a
+# specific chain (0xfe)
+http://tizen.org/privilege/debug|0x0a
+
+# another additional privilege
+http://tizen.org/privilege/joey|0x0b
index 5161dd3..bc8ff10 100644 (file)
@@ -17,7 +17,7 @@
 #
 
 #
-# Nether policy
+# Nether file policy backend configuration
 # $UID:$GID:$SECCTX ALLOW|DENY|ALLOW_LOG
 # If no match is found for a pcket
 # the default verdict is used (can be set via
index 1444236..6c2ca1f 100644 (file)
@@ -21,7 +21,7 @@ Description=nether service
 
 [Service]
 Type=simple
-ExecStart=/usr/local/bin/nether -l JOURNAL -B /etc/nether/nether.policy -r /etc/nether/nether.rules
+ExecStart=/usr/local/bin/nether -d -l JOURNAL -B /etc/nether/nether.policy -r /etc/nether/nether.rules
 Restart=on-failure
 ExecReload=/bin/kill -HUP $MAINPID
 
index d0ba8c0..164e76d 100644 (file)
 #ifndef NETHER_CYNARA_BACKEND_H
 #define NETHER_CYNARA_BACKEND_H
 
-#ifdef HAVE_CYNARA
+// #ifdef HAVE_CYNARA
 
 #include <cynara-client-async.h>
 #include "nether_PolicyBackend.h"
 #include <vector>
+#include <fstream>
+#include <map>
 
+#ifndef NETHER_CYNARA_INTERNET_PRIVILEGE
 #define NETHER_CYNARA_INTERNET_PRIVILEGE "http://tizen.org/privilege/internet"
+#endif // NETHER_CYNARA_INTERNET_PRIVILEGE
+
+class NetherManager;
 
 const std::string cynaraErrorCodeToString(int cynaraErrorCode);
+typedef std::pair<std::string, u_int32_t> PrivilegePair;
 
-class NetherManager;
+struct NetherCynaraCheckInfo
+{
+       NetherCynaraCheckInfo() {}
+       NetherCynaraCheckInfo(NetherPacket _packet, u_int32_t _privilegeId)
+               : packet(_packet),
+                 privilegeId(_privilegeId){}
+
+       NetherCynaraCheckInfo& operator=(const NetherCynaraCheckInfo &other)
+       {
+               packet = other.packet;
+               privilegeId = other.privilegeId;
+
+               return *this;
+       }
+
+       NetherPacket packet;
+       u_int32_t privilegeId = -1;
+};
 
 class NetherCynaraBackend : public NetherPolicyBackend
 {
@@ -44,8 +68,11 @@ class NetherCynaraBackend : public NetherPolicyBackend
                ~NetherCynaraBackend();
                bool initialize();
                bool enqueueVerdict(const NetherPacket &packet);
+               bool reEnqueVerdict(cynara_check_id checkId);
+               bool cynaraCheck(NetherCynaraCheckInfo checkInfo);
                bool processEvents();
                int getDescriptor();
+               bool parseInternalPolicy(const std::string &policyFile);
                NetherDescriptorStatus getDescriptorStatus();
                void setCynaraDescriptor(const int _currentCynaraDescriptor, const NetherDescriptorStatus _currentCynaraDescriptorStatus);
                void setCynaraVerdict(cynara_check_id checkId, int cynaraResult);
@@ -60,8 +87,10 @@ class NetherCynaraBackend : public NetherPolicyBackend
                int currentCynaraDescriptor;
                int cynaraLastResult;
                cynara_async_configuration *cynaraConfig;
-               std::vector<u_int32_t> responseQueue;
+               std::map<cynara_check_id, NetherCynaraCheckInfo> responseQueue;
+               std::vector<PrivilegePair> privilegeChain;
+               u_int32_t allPrivilegesToCheck;
 };
 
-#endif // HAVE_CYNARA
+// #endif // HAVE_CYNARA
 #endif // NETHER_CYNARA_BACKEND_H
index 5865fcf..6e88344 100644 (file)
@@ -39,7 +39,7 @@ class NetherManager : public NetherVerdictListener, public NetherProcessedPacket
                bool process();
                NetherConfig &getConfig();
                static NetherPolicyBackend *getPolicyBackend(const NetherConfig &netherConfig, const bool primary = true);
-               bool verdictCast(const u_int32_t packetId, const NetherVerdict verdict);
+               bool verdictCast(const u_int32_t packetId, const NetherVerdict verdict, int mark);
                void packetReceived(const NetherPacket &packet);
                bool restoreRules();
 
index a665be5..679fc3e 100644 (file)
@@ -39,7 +39,7 @@ class NetherNetlink : public NetherPacketProcessor
                bool reload();
                static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data);
                bool processPacket(char *packetBuffer, const int packetReadSize);
-               void setVerdict(const u_int32_t packetId, const NetherVerdict verdict);
+               void setVerdict(const u_int32_t packetId, const NetherVerdict verdict, int mark = -1);
                int getDescriptor();
                const NetherConfig &getNetherConfig();
                void getInterfaceInfo(struct nfq_data *nfa, NetherPacket &netherPacket);
index 84bc99b..5aeea5c 100644 (file)
@@ -198,7 +198,7 @@ class NetherVerdictListener
 {
        public:
                virtual ~NetherVerdictListener() = default;
-               virtual bool verdictCast(const u_int32_t packetId, const NetherVerdict verdict) = 0;
+               virtual bool verdictCast(const u_int32_t packetId, const NetherVerdict verdict, int mark) = 0;
 };
 
 class NetherVerdictCaster
@@ -212,17 +212,17 @@ class NetherVerdictCaster
                        verdictListener = listenerToSet;
                }
 
-               bool castVerdict(const NetherPacket &packet, const NetherVerdict verdict)
+               bool castVerdict(const NetherPacket &packet, const NetherVerdict verdict, const int mark = -1)
                {
                        if(verdictListener)
-                               return (verdictListener->verdictCast(packet.id, verdict));
+                               return (verdictListener->verdictCast(packet.id, verdict, mark));
                        return (false);
                }
 
-               bool castVerdict(const u_int32_t packetId, const NetherVerdict verdict)
+               bool castVerdict(const u_int32_t packetId, const NetherVerdict verdict, const int mark = -1)
                {
                        if(verdictListener)
-                               return (verdictListener->verdictCast(packetId, verdict));
+                               return (verdictListener->verdictCast(packetId, verdict, mark));
                        return (false);
                }
 
@@ -257,7 +257,7 @@ class NetherPacketProcessor
                        if(packetListener) packetListener->packetReceived(packetInfoToWrite);
                }
 
-               virtual void setVerdict(const u_int32_t packetId, const NetherVerdict verdict) = 0;
+               virtual void setVerdict(const u_int32_t packetId, const NetherVerdict verdict, int mark = -1) = 0;
 
        protected:
                NetherProcessedPacketListener *packetListener;
index 4def7ed..b460b76 100644 (file)
@@ -19,6 +19,7 @@ This is a network privilege enforcing service.
 %dir %{_sysconfdir}/nether
 %config %{_sysconfdir}/nether/nether.policy
 %config %{_sysconfdir}/nether/nether.rules
+%config %{_sysconfdir}/nether/cynara.policy
 %{_unitdir}/nether.service
 %{_unitdir}/multi-user.target.wants/nether.service
 %prep
index 224b0e8..0f498c1 100644 (file)
@@ -27,7 +27,7 @@
 
 using namespace std;
 
-#ifdef HAVE_CYNARA
+// #ifdef HAVE_CYNARA
 
 const std::string cynaraErrorCodeToString(int cynaraErrorCode)
 {
@@ -42,13 +42,17 @@ const std::string cynaraErrorCodeToString(int cynaraErrorCode)
 
 NetherCynaraBackend::NetherCynaraBackend(const NetherConfig &netherConfig)
        :   NetherPolicyBackend(netherConfig), currentCynaraDescriptor(0),
-               cynaraLastResult(CYNARA_API_UNKNOWN_ERROR), cynaraConfig(nullptr)
+               cynaraLastResult(CYNARA_API_UNKNOWN_ERROR), cynaraConfig(nullptr),
+               allPrivilegesToCheck(1) /* if there is no additional policy, only one check is done */
 {
-       responseQueue.reserve(1024);
        if (netherConfig.primaryBackendArgs.length() != 0)
        {
                parseBackendArgs();
        }
+       else
+       {
+               privilegeChain.push_back (PrivilegePair (NETHER_CYNARA_INTERNET_PRIVILEGE, 0));
+       }
 }
 
 NetherCynaraBackend::~NetherCynaraBackend()
@@ -92,41 +96,45 @@ void NetherCynaraBackend::checkCallback(cynara_check_id check_id,
                LOGI("unknown reason for call cause="<< cause <<" response="<< response);
 }
 
-bool NetherCynaraBackend::enqueueVerdict(const NetherPacket &packet)
+bool NetherCynaraBackend::cynaraCheck(NetherCynaraCheckInfo checkInfo)
 {
        cynara_check_id checkId;
 
        cynaraLastResult = cynara_async_check_cache(cynaraContext,
-                                                                                               packet.securityContext.c_str(),
+                                                                                               checkInfo.packet.securityContext.c_str(),
                                                                                                "",
-                                                                                               std::to_string(packet.uid).c_str(),
-                                                                                               NETHER_CYNARA_INTERNET_PRIVILEGE);
+                                                                                               std::to_string(checkInfo.packet.uid).c_str(),
+                                                                                               privilegeChain[checkInfo.privilegeId].first.c_str());
 
-       LOGD("cynara_async_check_cache ctx=" << packet.securityContext.c_str() << " user=" << std::to_string(packet.uid).c_str() << " privilege=" << NETHER_CYNARA_INTERNET_PRIVILEGE);
+       LOGD("cynara_async_check_cache ctx=" << checkInfo.packet.securityContext.c_str()
+                                                                                << " user="
+                                                                                << std::to_string(checkInfo.packet.uid).c_str()
+                                                                                << " privilege="
+                                                                                << privilegeChain[checkInfo.privilegeId].first
+                                                                                << " mark="
+                                                                                << privilegeChain[checkInfo.privilegeId].second);
 
        switch(cynaraLastResult)
        {
                case CYNARA_API_ACCESS_ALLOWED:
                        LOGD(cynaraErrorCodeToString(cynaraLastResult).c_str());
-                       return (castVerdict(packet, NetherVerdict::allow));
+                       return (castVerdict(checkInfo.packet, NetherVerdict::allow, privilegeChain[checkInfo.privilegeId].second));
 
                case CYNARA_API_ACCESS_DENIED:
-                       LOGD(cynaraErrorCodeToString(cynaraLastResult).c_str());
-                       return (castVerdict(packet, NetherVerdict::deny));
-
                case CYNARA_API_CACHE_MISS:
+                       LOGD(cynaraErrorCodeToString(cynaraLastResult).c_str());
                        cynaraLastResult = cynara_async_create_request(cynaraContext,
-                                                          packet.securityContext.c_str(),
+                                                          checkInfo.packet.securityContext.c_str(),
                                                           "",
-                                                          std::to_string(packet.uid).c_str(),
-                                                          NETHER_CYNARA_INTERNET_PRIVILEGE,
+                                                          std::to_string(checkInfo.packet.uid).c_str(),
+                                                          privilegeChain[checkInfo.privilegeId].first.c_str(),
                                                           &checkId,
                                                           &checkCallback,
                                                           this);
 
                        if(cynaraLastResult == CYNARA_API_SUCCESS)
                        {
-                               responseQueue[checkId] = packet.id;
+                               responseQueue[checkId] = checkInfo;
 
                                return (true);
                        }
@@ -150,16 +158,47 @@ bool NetherCynaraBackend::enqueueVerdict(const NetherPacket &packet)
        return (true);
 }
 
+bool NetherCynaraBackend::enqueueVerdict(const NetherPacket &packet)
+{
+       return (cynaraCheck ( NetherCynaraCheckInfo(packet, 0) ));
+}
+
+bool NetherCynaraBackend::reEnqueVerdict(cynara_check_id checkId)
+{
+       NetherCynaraCheckInfo checkInfo = responseQueue[checkId];
+
+       /* We goa deny from cynara, we need to check
+               if our internal (BAD, SATANIC, EVIL) policy
+               has other entries and try them too */
+       if (++checkInfo.privilegeId < allPrivilegesToCheck)
+       {
+               return (cynaraCheck(checkInfo));
+       }
+       else
+       {
+               castVerdict(checkInfo.packet.id, NetherVerdict::deny);
+       }
+
+       return (true);
+}
+
 void NetherCynaraBackend::setCynaraVerdict(cynara_check_id checkId, int cynaraResult)
 {
-       u_int32_t packetId = responseQueue[checkId];
+       NetherCynaraCheckInfo checkInfo = responseQueue[checkId];
 
        if(cynaraResult == CYNARA_API_ACCESS_ALLOWED)
-               castVerdict(packetId, NetherVerdict::allow);
+       {
+               castVerdict(checkInfo.packet.id,
+                                       NetherVerdict::allow,
+                                       privilegeChain[checkInfo.privilegeId].second);
+       }
        else
-               castVerdict(packetId, NetherVerdict::deny);
-
-       return;
+       {
+               if (!reEnqueVerdict(checkId))
+               {
+                       LOGE("reEnqueueVerdict failed");
+               }
+       }
 }
 
 int NetherCynaraBackend::getDescriptor()
@@ -203,7 +242,7 @@ void NetherCynaraBackend::setCacheSize(const size_t newCacheSize)
                LOGE("cynara_async_configuration_set_cache_size failed: " << cynaraErrorCodeToString(ret));
        }
 
-       LOGD("new cache size: " << newCacheSize);
+       LOGD("New cache size: " << newCacheSize);
 }
 
 void NetherCynaraBackend::parseBackendArgs()
@@ -219,6 +258,58 @@ void NetherCynaraBackend::parseBackendArgs()
                        std::string::size_type sz;
                        setCacheSize (stoi (valueNamePair[1], &sz, 10));
                }
+
+               if (valueNamePair[0] == "policy")
+               {
+                       parseInternalPolicy (valueNamePair[1]);
+               }
+       }
+}
+
+bool NetherCynaraBackend::parseInternalPolicy(const std::string &policyFile)
+{
+       privilegeChain.clear();
+       allPrivilegesToCheck = 0;
+
+       std::ifstream policyStream (policyFile);
+
+       if (!policyStream.good()) {
+               LOGE("Cynara policy file: " << policyFile << " failed to open");
+               return false;
        }
+
+       std::string s, privname, mark;
+       while (std::getline (policyStream,s))
+       {
+               std::string::size_type begin = s.find_first_not_of( " \f\t\v" );
+
+               // Skip blank lines
+               if (begin == std::string::npos) continue;
+
+               // Skip commentary
+               if (std::string( "#;" ).find( s[ begin ] ) != std::string::npos) continue;
+
+               // Extract the key value
+               std::string::size_type end = s.find( '|', begin );
+               privname = s.substr( begin, end - begin );
+
+               // (No leading or trailing whitespace allowed)
+               privname.erase( privname.find_last_not_of( " \f\t\v" ) + 1 );
+
+               // No blank keys allowed
+               if (privname.empty()) continue;
+
+               // Extract the value (no leading or trailing whitespace allowed)
+               begin = s.find_first_not_of( " \f\n\r\t\v", end + 1 );
+               end   = s.find_last_not_of(  " \f\n\r\t\v" ) + 1;
+               mark = s.substr( begin, end - begin );
+
+               // Insert the properly extracted (key, value) pair into the map
+               std::cout << mark;
+               privilegeChain.push_back(PrivilegePair(privname, std::stoi(mark, 0, 16)));
+       }
+
+       allPrivilegesToCheck = privilegeChain.size();
+       return (true);
 }
-#endif
+// #endif
index a2fa318..7a815ed 100644 (file)
@@ -222,7 +222,7 @@ int main(int argc, char *argv[])
 
        if(netherConfig.daemonMode)
        {
-               LOGD("FORKING TO BACKGROUND");
+               LOGD("Running in background, fork()");
                if(!runAsDaemon())
                {
                        LOGE("Failed to run as daemon: " << strerror(errno));
@@ -235,7 +235,7 @@ int main(int argc, char *argv[])
        }
        else
        {
-               LOGD("RUNNING IF FOREGROUND");
+               LOGD("Running in foreground");
                manager.process();
        }
 
@@ -249,7 +249,7 @@ void showHelp(char *arg)
        cout<< "  -x,--no-rules\t\t\t\tDon't load iptables rules on start (default:no)\n";
        cout<< "  -c,--copy-packets\t\t\tCopy entire packets, needed to read TCP/IP information (default:no)\n";
        cout<< "  -I,--interface-info\t\t\tGet interface info for every packet (default:no)\n";
-       cout<< "  -R,--relaxed\t\t\t\tRun in relaxed mode, instrad of deny do ACCEPT_LOG(default:no)\n";
+       cout<< "  -R,--relaxed\t\t\t\tRun in relaxed mode, instead of deny do ACCEPT_LOG(default:no)\n";
        cout<< "  -l,--log=<backend>\t\t\tSet logging backend STDERR,SYSLOG";
 #if defined(HAVE_SYSTEMD_JOURNAL)
        cout << ",JOURNAL\n";
index e2099a2..844e760 100644 (file)
@@ -281,11 +281,11 @@ NetherPolicyBackend *NetherManager::getPolicyBackend(const NetherConfig &netherC
        }
 }
 
-bool NetherManager::verdictCast(const u_int32_t packetId, const NetherVerdict verdict)
+bool NetherManager::verdictCast(const u_int32_t packetId, const NetherVerdict verdict, int mark)
 {
        if(netherNetlink)
        {
-               netherNetlink->setVerdict(packetId, verdict);
+               netherNetlink->setVerdict(packetId, verdict, mark);
        }
        else
        {
index bb91510..6d5d514 100644 (file)
@@ -177,22 +177,44 @@ int NetherNetlink::callback(struct nfq_q_handle *, struct nfgenmsg *, struct nfq
        return (0);
 }
 
-void NetherNetlink::setVerdict(const u_int32_t packetId, const NetherVerdict verdict)
+void NetherNetlink::setVerdict(const u_int32_t packetId, const NetherVerdict verdict, int mark)
 {
        int ret = 0;
-       LOGD("id=" << packetId << " verdict=" << verdictToString(verdict));
+       LOGD("id=" << packetId << " verdict=" << verdictToString(verdict) << " mark=" << mark);
 
        if(verdict == NetherVerdict::allow)
-               ret = nfq_set_verdict(queueHandle, packetId, NF_ACCEPT, 0, NULL);
+       {
+               if (mark >= 0)
+               {
+                       ret = nfq_set_verdict2(queueHandle,
+                                                               packetId,
+                                                               NF_ACCEPT,
+                                                               mark,
+                                                               0,
+                                                               NULL);
+               }
+               else
+               {
+                       ret = nfq_set_verdict(queueHandle,
+                                                               packetId,
+                                                               NF_ACCEPT,
+                                                               0,
+                                                               NULL);
+               }
+       }
 
        if(verdict == NetherVerdict::deny)
+       {
                ret = nfq_set_verdict2(queueHandle,
                                                                packetId,
                                                                NF_ACCEPT,
                                                                /* if we're relaxed, let's not stress out */
-                                                               netherConfig.relaxed ? netherConfig.markAllowAndLog : netherConfig.markDeny,
+                                                               /* if we get a mark from the verdict caster */
+                                                               /* let's use it, maybe it knows better */
+                                                               mark > 0 ? mark : (netherConfig.relaxed ? netherConfig.markAllowAndLog : netherConfig.markDeny),
                                                                0,
                                                                NULL);
+       }
 
        if(verdict == NetherVerdict::allowAndLog)
                ret = nfq_set_verdict2(queueHandle, packetId, NF_ACCEPT, netherConfig.markAllowAndLog, 0, NULL);