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)
--- /dev/null
+#
+# 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
#
#
-# 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
[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
#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
{
~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);
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
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();
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);
{
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
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);
}
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;
%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
using namespace std;
-#ifdef HAVE_CYNARA
+// #ifdef HAVE_CYNARA
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()
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);
}
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()
LOGE("cynara_async_configuration_set_cache_size failed: " << cynaraErrorCodeToString(ret));
}
- LOGD("new cache size: " << newCacheSize);
+ LOGD("New cache size: " << newCacheSize);
}
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
if(netherConfig.daemonMode)
{
- LOGD("FORKING TO BACKGROUND");
+ LOGD("Running in background, fork()");
if(!runAsDaemon())
{
LOGE("Failed to run as daemon: " << strerror(errno));
}
else
{
- LOGD("RUNNING IF FOREGROUND");
+ LOGD("Running in foreground");
manager.process();
}
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";
}
}
-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
{
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);