added dbuspolicy-finder utility 42/205142/29
authorBaumann <a.baumann@samsung.com>
Mon, 8 Apr 2019 17:19:37 +0000 (19:19 +0200)
committerHyotaek Shim <hyotaek.shim@samsung.com>
Wed, 26 Jun 2019 05:14:39 +0000 (05:14 +0000)
It shows filters policies and possible privilege combinations.

Change-Id: Ia4078eb76a7fde3fc457280a08189208089464d2

Makefile.am
packaging/libdbuspolicy.spec
src/dbuspolicy_finder.cpp [new file with mode: 0644]

index 4e1f706..bbf8203 100644 (file)
@@ -112,7 +112,17 @@ dbuspolicy_printer_LDADD = src/libinternal.la \
 
 EXTRA_dbuspolicy_printer_DEPENDENCIES = ${top_srcdir}/src/libdbuspolicy1.sym
 
-bin_PROGRAMS = dbuspolicy-serializer dbuspolicy-printer
+dbuspolicy_finder_SOURCES =\
+                         src/dbuspolicy_finder.cpp
+
+dbuspolicy_finder_CFLAGS="-Isrc/internal/include $(AM_CFLAGS)"
+
+dbuspolicy_finder_LDADD = src/libinternal.la \
+       $(CYNARA_LIBS) \
+       $(DLOG_LIBS) \
+       -lexpat
+
+bin_PROGRAMS = dbuspolicy-serializer dbuspolicy-printer dbuspolicy-finder
 dbuspolicy_printerdir = /bin/
 
 dist_bin_SCRIPTS = src/dbuspolicy-verifier
index 3b1ce0a..43d32b5 100644 (file)
@@ -142,6 +142,7 @@ mv %{_unitdir}/.%{name}.backup.default.target %{_unitdir}/default.target
 %defattr(-,root,root)
 %{_bindir}/dbuspolicy-serializer
 %{_bindir}/dbuspolicy-printer
+%{_bindir}/dbuspolicy-finder
 
 %files assert-data-valid
 %manifest %{name}.manifest
diff --git a/src/dbuspolicy_finder.cpp b/src/dbuspolicy_finder.cpp
new file mode 100644 (file)
index 0000000..17e1516
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+#include "internal/policy_containers.hpp"
+#include "internal/print_content.hpp"
+#include "internal/storage_backend_xml.hpp"
+#include "internal/uid_gid_helpers.hpp"
+#include "primary-conf-files.c"
+#include <cassert>
+#include <getopt.h>
+#include <iostream>
+#include <string>
+#include <vector>
+
+using namespace ldp_xml_parser;
+using namespace ldp_xml;
+
+class Print_once {
+       bool printing;
+       const std::string message;
+
+       public:
+       Print_once(const std::string & _message)
+               : printing{true},
+               message{_message} {
+       }
+
+       void print() {
+               if (printing)
+                       std::cout << "\t" << message << std::endl;
+               printing = false;
+       }
+};
+
+static const std::map<std::string, MessageType> types {
+       {  "method_call", MessageType::METHOD_CALL  },
+       {"method_return", MessageType::METHOD_RETURN},
+       {       "signal", MessageType::SIGNAL       },
+       {        "error", MessageType::ERROR        }
+};
+
+MessageType stringToMessageType(std::string tmp) {
+       std::transform(tmp.begin(), tmp.end(), tmp.begin(),
+               [](unsigned char c) { return std::tolower(c); } );
+
+       const auto iter = types.find(tmp);
+
+       if (iter == types.end()) {
+               std::cout << "There is no such MessageType" << std::endl;
+               return MessageType::ANY;
+       } else {
+               return iter->second;
+       }
+}
+
+void printDecision(const Decision & di, const std::string & token, const std::string & extraText, Print_once & printer) {
+       if (di == Decision::ANY)
+               return;
+       printer.print();
+
+       if (di == Decision::ALLOW)
+               std::cout << "allow";
+       else if (di == Decision::DENY)
+               std::cout << "deny";
+       else if (di == Decision::CHECK)
+               std::cout << "check";
+       std::cout << extraText << "=\"" << token << "\"" << std::endl;
+}
+
+void checkIfMatches(const std::shared_ptr<TreeNode> & iter, const MatchItemOwn & mio, const std::string & currentToken, bool noFilter, Print_once & printer) {
+       const auto & pdi = iter->getOwnPrefixDecisionItem().getDecision();
+       const auto & sdi = iter->getOwnDecisionItem      ().getDecision();
+       static const std::string name("own");
+       static const std::string prefix("own_prefix");
+
+       if (noFilter) {
+               printDecision(pdi, currentToken, name, printer);
+               if (iter->getChildren().empty())
+                       printDecision(sdi, currentToken, name, printer);
+       } else if (mio.getName() == currentToken) {
+               printDecision(sdi, currentToken, name, printer);
+               printDecision(pdi, "", prefix, printer);
+       } else if (mio.getName().substr(0, currentToken.length()) == currentToken) {
+               printDecision(pdi, currentToken, prefix, printer);
+       } else if (mio.getName().empty()) {
+               if (iter->getChildren().empty())
+                       printDecision(sdi, currentToken, name, printer);
+               printDecision(pdi, "", prefix, printer);
+       } else {
+               return;
+       }
+
+       for (const auto & child : iter->getChildren()) {
+               const auto & childPointer = child.second;
+               checkIfMatches(childPointer, mio, currentToken + "." + childPointer->getToken(), noFilter, printer);
+       }
+}
+
+template <typename T> void matchPolicy(const T & policy, const typename T::item_type::match_type & mi, bool noFilter, const std::string & message) {
+       Print_once printer(message);
+
+       for (const auto & iter : policy.getItems()) {
+               if (noFilter || mi.match(iter.getType(), iter.getInterface(), iter.getPath(), iter.getMember(), iter.getName(), iter.isNamePrefix(), Decision::ANY)) {
+                       printer.print();
+                       std::cout << iter << std::endl;
+               }
+       }
+}
+
+template <> void matchPolicy<PolicyOwn>(const PolicyOwn & policy, const MatchItemOwn & mi, bool noFilter, const std::string & message) {
+       Print_once printer(message);
+
+       for (const auto & child : policy.getTree().getRoot()->getChildren()) {
+               const auto & childPointer = child.second;
+               checkIfMatches(childPointer, mi, childPointer->getToken(), noFilter, printer);
+       }
+}
+
+template <> void matchPolicy<PolicyAccess>(const PolicyAccess & policy, const MatchItemAccess & mi, bool noFilter, const std::string & message) {
+       Print_once printer(message);
+
+       for (const auto & iter : policy.getItems()) {
+               if (noFilter || mi.match(iter.getType(), iter.getUid(), iter.getGid())) {
+                       printer.print();
+                       std::cout << iter << std::endl;
+               }
+       }
+}
+
+template <typename T> void pickPolicy(const StorageBackendXML & storage, const typename T::item_type::match_type & mi, uid_t numberUser, gid_t numberGroup, bool noFilter) {
+       matchPolicy<T>(storage.getPolicyContextDefault<T>(), mi, noFilter, "Context Default:");
+
+       const auto & groups = storage.getPoliciesGroup<T>();
+       const auto & users = storage.getPoliciesUser<T>();
+
+       if (numberGroup == ((gid_t) -1) && numberUser == ((uid_t) -1)) {
+               for (const auto & gid : groups) {
+                       matchPolicy<T>(gid.second, mi, noFilter, std::string("Group:") + std::to_string(gid.first));
+               }
+               for (const auto & uid : users) {
+                       matchPolicy<T>(uid.second, mi, noFilter, std::string("User:") + std::to_string(uid.first));
+               }
+       } else {
+               if (numberGroup != (gid_t) -1) {
+                       const auto it = groups.find(numberGroup);
+                       if (it == groups.end())
+                               std::cout << "No rules for group in policy!" << std::endl;
+                       else
+                               matchPolicy<T>(it->second, mi, noFilter, std::string("Group:") + std::to_string(numberGroup));
+               }
+               if (numberUser != (uid_t) -1) {
+                       const auto it = users.find(numberUser);
+                       if (it == users.end())
+                               std::cout << "No rules for user in policy!" << std::endl;
+                       else
+                               matchPolicy<T>(it->second, mi, noFilter, std::string("User:") + std::to_string(numberUser));
+               }
+       }
+       matchPolicy<T>(storage.getPolicyContextMandatory<T>(), mi, noFilter, "Context Mandatory:");
+}
+
+void pickPolicy(const StorageBackendXML & storage, const MatchItemAccess & mi, bool noFilter) {
+       matchPolicy<PolicyAccess>(storage.getPolicyContextDefault<PolicyAccess>(),   mi, noFilter, "Context Default:");
+       matchPolicy<PolicyAccess>(storage.getPolicyContextMandatory<PolicyAccess>(), mi, noFilter, "Context Mandatory:");
+}
+
+template <typename T> void handlePolicy(const std::string & interface, const std::string & member, const std::string & path, MessageType type,
+               const std::string & serviceName, const StorageBackendXML & storage, uid_t numberUser, gid_t numberGroup) {
+       KdbusBusNames names;
+       typename T::item_type::match_type mi(interface.c_str(), member.c_str(), path.c_str(), type, names.addSpaceSeparatedNames(serviceName.c_str()));
+       pickPolicy<T>(storage, mi, numberUser, numberGroup, interface.empty() && member.empty() && path.empty() && (type == MessageType::ANY) && serviceName.empty());
+}
+
+int showHelp() {
+       std::cout << "\nUsage:\n"
+               "dbuspolicy-filter {send|receive|access|own} [options]\n"
+               "\n"
+               "  -c, --configuration {file name} | --session | --system\n"
+               "  -h, --help         \thelp\n"
+               "\n"
+               "Options for 'send':\n"
+               "  -t, --type=TYPE    \ttype {method_call|method_return|signal|error}\n"
+               "  -g, --group=GROUP  \tgroup {group_name|gid}\n"
+               "  -u, --user=USER    \tuser {username|uid}\n"
+               "  -n, --name         \tdestination\n"
+               "  -i, --interface    \tinterface\n"
+               "  -p, --path         \tpath\n"
+               "  -m, --member       \tmember\n"
+               "Options for 'receive':\n"
+               "  -t, --type=TYPE    \ttype {method_call|method_return|signal|error}\n"
+               "  -g, --group=GROUP  \tgroup {group_name|gid}\n"
+               "  -u, --user=USER    \tuser {username|uid}\n"
+               "  -n, --name         \tsender\n"
+               "  -i, --interface    \tinterface\n"
+               "  -p, --path         \tpath\n"
+               "  -m, --member       \tmember\n"
+               "Options for 'access':\n"
+               "  -g, --group=GROUP  \tgroup {group_name|gid}\n"
+               "  -u, --user=USER    \tuser {username|uid}\n"
+               "Options for 'own':\n"
+               "  -g, --group=GROUP  \tgroup {group_name|gid}\n"
+               "  -u, --user=USER    \tuser {username|uid}\n"
+               "  -n, --name         \ta well-known bus name" << std::endl;
+       return EXIT_SUCCESS;
+}
+
+int bailOut(const std::string & error) {
+       std::cout << error << std::endl;
+       showHelp();
+       return EXIT_FAILURE;
+}
+
+int main(int argc, char *argv[]) {
+       tslog::init();
+
+       int opt;
+       gid_t numberGroup = (gid_t) -1;
+       uid_t numberUser = (uid_t) -1;
+       std::string serviceName = "";
+       std::string interface = "";
+       std::string path = "";
+       std::string member = "";
+       MessageType type = MessageType::ANY;
+       const char * file_name = nullptr;
+       constexpr int SESSION = 1;
+       constexpr int SYSTEM = 2;
+
+       const char * const short_opts = ":t:g:u:n:i:p:m:h:c:";
+       const option long_opts[] = {
+               {"type"         , required_argument, nullptr,     't'},
+               {"group"        , required_argument, nullptr,     'g'},
+               {"user"         , required_argument, nullptr,     'u'},
+               {"name"         , required_argument, nullptr,     'n'},
+               {"interface"    , required_argument, nullptr,     'i'},
+               {"path"         , required_argument, nullptr,     'p'},
+               {"member"       , required_argument, nullptr,     'm'},
+               {"help"         ,       no_argument, nullptr,     'h'},
+               {"configuration", required_argument, nullptr,     'c'},
+               {"session"      ,       no_argument, nullptr, SESSION},
+               {"system"       ,       no_argument, nullptr,  SYSTEM},
+               {nullptr        ,                 0, nullptr,    '\0'}
+       };
+
+       while ((opt = getopt_long(argc, argv, short_opts, long_opts, nullptr)) != -1) {
+               switch (opt) {
+               case 't':
+                       type = stringToMessageType(optarg);
+                       break;
+               case 'g':
+                       try {
+                               numberGroup = std::stoi(optarg, nullptr, 10);
+                       } catch (std::invalid_argument & e) {
+                               numberGroup = convertToGid(optarg);
+                       }
+                       break;
+               case 'u':
+                       try {
+                               numberUser = std::stoi(optarg, nullptr, 10);
+                       } catch (std::invalid_argument & e) {
+                               numberUser = convertToUid(optarg);
+                       }
+                       break;
+               case 'n':
+                       serviceName = optarg;
+                       break;
+               case 'i':
+                       interface = optarg;
+                       break;
+               case 'p':
+                       path = optarg;
+                       break;
+               case 'm':
+                       member = optarg;
+                       break;
+               case 'h':
+                       return showHelp();
+               case 'c':
+                       if (file_name != nullptr)
+                               return bailOut("Choose only one configuration file!");
+                       file_name = optarg;
+                       break;
+               case SESSION:
+                       if (file_name != nullptr)
+                               return bailOut("Choose only one configuration file!");
+                       file_name = session_bus_conf_file_primary();
+                       break;
+               case SYSTEM:
+                       if (file_name != nullptr)
+                               return bailOut("Choose only one configuration file!");
+                       file_name = system_bus_conf_file_primary();
+                       break;
+               case '?':
+                       return bailOut("Unknown parameter!");
+               }
+       }
+
+       std::string mandatory = "";
+
+       if (optind < argc) {
+               static const std::array<const std::string, 4> options {{"send", "receive", "own", "access"}};
+               std::string tmp(argv[optind]);
+               std::transform(tmp.begin(), tmp.end(), tmp.begin(),
+                               [](unsigned char c) { return std::tolower(c);});
+
+               const auto iter = std::find(options.begin(), options.end(), tmp);
+
+               if (iter != options.end())
+                       mandatory = *iter;
+       }
+       if (mandatory.empty())
+               return bailOut("Wrong format - mandatory argument is missing, type either 'send', 'receive', 'access' or 'own'!");
+
+       if (file_name == nullptr)
+               return bailOut("Please provide configuration file!");
+
+       StorageBackendXML storage;
+       storage.init(file_name);
+
+       if (mandatory == "send") {
+               handlePolicy<PolicySend>(interface, member, path, type, serviceName, storage, numberUser, numberGroup);
+       } else if (mandatory == "receive") {
+               handlePolicy<PolicyReceive>(interface, member, path, type, serviceName, storage, numberUser, numberGroup);
+       } else if (mandatory == "access") {
+               if (!((type == MessageType::ANY) && (serviceName == "") && (interface == "") && (path == "") && (member == ""))) {
+                       return bailOut("Invalid set of arguments");
+               }
+               std::vector<gid_t> gids(1, numberGroup);
+               MatchItemAccess mia(numberUser, gids);
+               bool noFilter = (numberUser == ((uid_t) -1)) && gids.empty();
+               pickPolicy(storage, mia, noFilter);
+       } else if (mandatory == "own") {
+               if (!((type == MessageType::ANY) && (interface == "") && (path == "") && (member == ""))) {
+                       return bailOut("Invalid set of arguments");
+               }
+               MatchItemOwn mio(serviceName.c_str());
+               bool noFilter = serviceName.empty();
+               pickPolicy<PolicyOwn>(storage, mio, numberUser, numberGroup, noFilter);
+       } else {
+               assert(false);
+       }
+       return EXIT_SUCCESS;
+}