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 be55e51a016636b30ced7f8d2f947e3d10de984e..9479e08ff4867a2bc71fa4338c46cec05978853a 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 5161dd354528fd2c068db84af68a3d23f3312d59..bc8ff106a43bf07fec979a7b94cc8e740f40f595 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 14442364b356fd04bb5fa12b9571d171cba1cd9b..6c2ca1f2d7c2f1a13a2283ac5346d8f9b7dd3753 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 d0ba8c0e0540f9d9a103168f78950b8b620fd11b..164e76d09afd7d22eaf38761fa48a753b49fc0b9 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 5865fcfdd5c1abeb3d7dd58b0e36c4bc7d3e9b80..6e883447ea2678b3d7d8d4e27adc6bf17404497b 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 a665be54944720308f8e06eeb5f773f762a2f81a..679fc3e3b5efcba5fdd1cbc2d71f35cbac7b2aa5 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 84bc99ba495db00c63d16e5f63b0ce78d5844835..5aeea5cb59e19973bdb6cbd770bcd3383f2fe44c 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 4def7ed2923bdf88cb5a9fd0de54d626acd335c9..b460b76d11d98694d9ac888ffb18b5576e659020 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 224b0e86582d4ba8e5c1322516447d0330c858e7..0f498c18e6c2afa4b6ea177becde809cf170c24a 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 a2fa3188c9bbc0ff7f8b955323fd31d336217076..7a815ed4664228506b01c21cb30fbf48bd54e496 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 e2099a2be63e82a2b68179b6ea9790ba9e3b0789..844e76088435459f004d7e84577c8b2dad31c74d 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 bb915106968735d2663322bddc8fee5f04d65fa3..6d5d51433a21f1d1f2ba18b424d1ae4e211d2b10 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);