Installation/uninstallation of package-specific smack rules 30/21830/5
authorJacek Bukarewicz <j.bukarewicz@samsung.com>
Tue, 27 May 2014 12:46:06 +0000 (14:46 +0200)
committerJacek Bukarewicz <j.bukarewicz@samsung.com>
Wed, 4 Jun 2014 15:04:36 +0000 (17:04 +0200)
On installation rules are generated from predefined template, applied
to the kernel and saved to disk so they are loaded on system boot.
On uninstallation package-specific rules are revoked from kernel and
removed from disk.

Change-Id: Ib3ce4ecf909c4fe3c6bc1f5a77e24737b7acf918

app-rules-template.smack [new file with mode: 0644]
packaging/security-server.spec
src/CMakeLists.txt
src/server/service/installer.cpp
src/server/service/smack-rules.cpp [new file with mode: 0644]
src/server/service/smack-rules.h [new file with mode: 0644]

diff --git a/app-rules-template.smack b/app-rules-template.smack
new file mode 100644 (file)
index 0000000..543e80c
--- /dev/null
@@ -0,0 +1,6 @@
+System ~APP~ rwx
+~APP~ System wx
+~APP~ System::Shared rx
+~APP~ System::Run rwxat
+~APP~ System::Log rwxa
+~APP~ _ l
index a0f15f13244a2c804e91069f252530fc3196308a..cbbc9df21abd9953ab61d1e67c468fe3275f3422 100644 (file)
@@ -103,6 +103,8 @@ cp LICENSE %{buildroot}/usr/share/license/libsecurity-server-client
 cp LICENSE %{buildroot}/usr/share/license/libsecurity-manager-client
 mkdir -p %{buildroot}/etc/security/
 cp security-server-audit.conf %{buildroot}/etc/security/
+mkdir -p %{buildroot}/etc/smack/
+cp app-rules-template.smack %{buildroot}/etc/smack/
 %make_install
 
 mkdir -p %{buildroot}/usr/lib/systemd/system/multi-user.target.wants
@@ -174,7 +176,6 @@ fi
 %attr(-,root,root) /usr/lib/systemd/system/security-server-cookie-check.socket
 %attr(-,root,root) /usr/lib/systemd/system/sockets.target.wants/security-server-app-privilege-by-name.socket
 %attr(-,root,root) /usr/lib/systemd/system/security-server-app-privilege-by-name.socket
-%attr(-,root,root) /etc/security/security-server-audit.conf
 %attr(-,root,root) /usr/lib/systemd/system/sockets.target.wants/security-server-password-check.socket
 %attr(-,root,root) /usr/lib/systemd/system/security-server-password-check.socket
 %attr(-,root,root) /usr/lib/systemd/system/sockets.target.wants/security-server-password-set.socket
@@ -183,7 +184,8 @@ fi
 %attr(-,root,root) /usr/lib/systemd/system/security-server-password-reset.socket
 %attr(-,root,root) /usr/lib/systemd/system/sockets.target.wants/security-manager-installer.socket
 %attr(-,root,root) /usr/lib/systemd/system/security-manager-installer.socket
-
+%attr(-,root,root) /etc/security/security-server-audit.conf
+%attr(-,root,root) /etc/smack/app-rules-template.smack
 %{_datadir}/license/%{name}
 
 %files -n libsecurity-server-client
index fc9c23bbbc20dfcfa94080391e10d877b3619787..02dbcf63df9d79debfc83ca3771caa49e2c37bb3 100644 (file)
@@ -27,6 +27,7 @@ SET(SECURITY_SERVER_SOURCES
     ${SERVER2_PATH}/service/password-manager.cpp
     ${SERVER2_PATH}/service/password-file-buffer.cpp
     ${SERVER2_PATH}/service/smack-common.cpp
+    ${SERVER2_PATH}/service/smack-rules.cpp
     ${SERVER2_PATH}/service/installer.cpp
     )
 
index 16b270cd087c1e0d970859f69ad9c67b83f63136..b02e22d8dacfdde068651ffb5c296e7ac7b8e33d 100644 (file)
@@ -17,7 +17,8 @@
  */
 /*
  * @file        installer.cpp
- * @author      Michal Witanowski (m.witanowski@samsung.com)
+ * @author      Michal Witanowski <m.witanowski@samsung.com>
+ * @author      Jacek Bukarewicz <j.bukarewicz@samsung.com>
  * @brief       Implementation of installer service for libprivilege-control encapsulation.
  */
 
@@ -29,6 +30,7 @@
 #include "protocols.h"
 #include "security-server.h"
 #include "security-manager.h"
+#include "smack-rules.h"
 
 namespace SecurityServer {
 
@@ -237,10 +239,20 @@ bool InstallerService::processAppInstall(MessageBuffer &buffer, MessageBuffer &s
         }
     }
 
+    // TODO: This should be done only for the first application in the package
+    if (!SecurityManager::SmackRules::installPackageRules(req.pkgId)) {
+        LogError("Failed to apply package-specific smack rules");
+        goto error_label;
+    }
+
     // finish database transaction
     result = perm_end();
     LogDebug("perm_end() returned " << result);
     if (PC_OPERATION_SUCCESS != result) {
+        // TODO: Uncomment once proper pkgId -> smack label mapping is implemented (currently all
+        //       applications are mapped to user label and removal of such rules would be harmful)
+        //SecurityManager::SmackRules::uninstallPackageRules(req.pkgId);
+
         // error in libprivilege-control
         Serialization::Serialize(send, SECURITY_SERVER_API_ERROR_SERVER_ERROR);
         return false;
@@ -285,6 +297,18 @@ bool InstallerService::processAppUninstall(MessageBuffer &buffer, MessageBuffer
         return false;
     }
 
+    // TODO: Uncomment once proper pkgId -> smack label mapping is implemented (currently all
+    //       applications are mapped to user label and removal of such rules would be harmful)
+    // TODO: This should be performed only for the last application in the package
+    // TODO: retrieve pkgId as it's not available in the request
+    //if (!SecurityManager::SmackRules::uninstallPackageRules(pkgId)) {
+    //    LogError("Error on uninstallation of package-specific smack rules");
+    //    result = perm_rollback();
+    //    LogDebug("perm_rollback() returned " << result);
+    //    Serialization::Serialize(send, SECURITY_SERVER_API_ERROR_SERVER_ERROR);
+    //    return false;
+    //}
+
     // finish database transaction
     result = perm_end();
     LogDebug("perm_end() returned " << result);
diff --git a/src/server/service/smack-rules.cpp b/src/server/service/smack-rules.cpp
new file mode 100644 (file)
index 0000000..24e9add
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Rafal Krypa <r.krypa@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
+ */
+/**
+ * @file        smack-rules.cpp
+ * @author      Jacek Bukarewicz <j.bukarewicz@samsung.com>
+ * @version     1.0
+ * @brief       Implementation of a class managing smack rules
+ *
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/smack.h>
+#include <fcntl.h>
+#include <fstream>
+
+#include <dpl/log/log.h>
+
+#include "smack-rules.h"
+#include "security-manager-common.h"
+
+namespace SecurityManager {
+
+const char *const SMACK_APP_LABEL_TEMPLATE     = "~APP~";
+const char *const APP_RULES_TEMPLATE_FILE_PATH = "/etc/smack/app-rules-template.smack";
+const char *const APP_RULES_DIRECTORY          = "/etc/smack/accesses.d/";
+
+SmackRules::SmackRules()
+{
+    if (smack_accesses_new(&m_handle) < 0) {
+        LogError("Failed to create smack_accesses handle");
+        throw std::bad_alloc();
+    }
+}
+
+SmackRules::~SmackRules() {
+    smack_accesses_free(m_handle);
+}
+
+bool SmackRules::add(const std::string &subject, const std::string &object,
+        const std::string &permissions)
+{
+    return 0 == smack_accesses_add(m_handle, subject.c_str(), object.c_str(), permissions.c_str());
+}
+
+bool SmackRules::clear() const
+{
+    return 0 == smack_accesses_clear(m_handle);
+}
+
+bool SmackRules::apply() const
+{
+    return 0 == smack_accesses_apply(m_handle);
+}
+
+bool SmackRules::loadFromFile(const std::string &path)
+{
+    int fd;
+    bool ret = true;
+
+    fd = open(path.c_str(), O_RDONLY);
+    if (fd == -1) {
+        LogError("Failed to open file: %s" << path);
+        return false;
+    }
+
+    if (smack_accesses_add_from_file(m_handle, fd)) {
+        LogError("Failed to load smack rules from file: %s" << path);
+        ret = false;
+    }
+
+    close(fd);
+    return ret;
+}
+
+bool SmackRules::saveToFile(const std::string &path) const
+{
+    int fd;
+    bool ret = true;
+
+    fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
+    if (fd == -1) {
+        LogError("Failed to create file: %s" << path);
+        return false;
+    }
+
+    if (smack_accesses_save(m_handle, fd)) {
+        LogError("Failed to save rules to file: %s" << path);
+        unlink(path.c_str());
+        ret = false;
+    }
+
+    close(fd);
+    return ret;
+}
+
+
+bool SmackRules::addFromTemplateFile(const std::string &pkgId)
+{
+    std::vector<std::string> templateRules;
+    std::string line;
+    std::ifstream templateRulesFile(APP_RULES_TEMPLATE_FILE_PATH);
+
+    if (!templateRulesFile.is_open()) {
+        LogError("Cannot open rules template file: " << APP_RULES_TEMPLATE_FILE_PATH);
+        return false;
+    }
+
+    while (std::getline(templateRulesFile, line)) {
+        templateRules.push_back(line);
+    }
+
+    if (templateRulesFile.bad()) {
+        LogError("Error reading template file: " << APP_RULES_TEMPLATE_FILE_PATH);
+        return false;
+    }
+
+    return addFromTemplate(templateRules, pkgId);
+}
+
+bool SmackRules::addFromTemplate(const std::vector<std::string> &templateRules,
+        const std::string &pkgId)
+{
+    std::string tokens[3];
+    std::string &subject = tokens[0];
+    std::string &object = tokens[1];
+    std::string &permissions = tokens[2];
+
+    for (auto rule = templateRules.begin(); rule != templateRules.end(); ++rule) {
+        if (rule->length() == 0)
+            continue;
+
+        if (!tokenizeRule(*rule, tokens, sizeof(tokens) / sizeof(*tokens))) {
+            return false;
+        }
+
+        bool subjectIsTemplate = (subject == SMACK_APP_LABEL_TEMPLATE);
+        bool objectIsTemplate = (object == SMACK_APP_LABEL_TEMPLATE);
+
+        if (objectIsTemplate == subjectIsTemplate) {
+            LogError("Invalid rule template. Exactly one app label template expected: " << *rule);
+            return false;
+        }
+
+        if (subjectIsTemplate) {
+            if (!SecurityManager::generateAppLabel(pkgId, subject)) {
+                LogError("Failed to generate app label from pkgid: " << pkgId);
+                return false;
+            }
+        }
+
+        if (objectIsTemplate) {
+            if (!SecurityManager::generateAppLabel(pkgId, object)) {
+                LogError("Failed to generate app label from pkgid: " << pkgId);
+                return false;
+            }
+        }
+
+        if (!add(subject, object, permissions)) {
+            LogError("Failed to add rule: " << subject << " " << object << " " << permissions);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+bool SmackRules::tokenizeRule(const std::string &rule, std::string tokens[], int size)
+{
+    size_t startPos;
+    size_t endPos = 0;
+    const char delimiters[] = " \t\n\r";
+
+    for (int i = 0; i < size; i++) {
+        startPos = rule.find_first_not_of(delimiters, endPos);
+        if (startPos == std::string::npos) {
+            LogError("Unexpected end of rule: " << rule);
+            return false;
+        }
+
+        endPos = rule.find_first_of(delimiters, startPos);
+        tokens[i] = rule.substr(startPos, endPos - startPos);
+    }
+
+    if (endPos != std::string::npos &&
+        rule.find_first_not_of(delimiters, endPos) != std::string::npos) {
+        LogError("Too many tokens found in rule: " << rule);
+        return false;
+    }
+
+    return true;
+}
+
+std::string SmackRules::getPackageRulesFilePath(const std::string &pkgId)
+{
+    std::string path(APP_RULES_DIRECTORY);
+    path.append(pkgId);
+    path.append(".smack");
+    return path;
+}
+
+bool SmackRules::installPackageRules(const std::string &pkgId) {
+    try {
+         SmackRules smackRules;
+         std::string path = getPackageRulesFilePath(pkgId);
+
+         if (!smackRules.addFromTemplateFile(pkgId)) {
+             LogError("Failed to load smack rules for pkgId " << pkgId);
+             return false;
+         }
+
+         if (!smackRules.apply()) {
+             LogError("Failed to apply application rules to kernel");
+             return false;
+         }
+
+         if (!smackRules.saveToFile(path)) {
+             smackRules.clear();
+             return false;
+         }
+
+         return true;
+     } catch (const std::bad_alloc &e) {
+         LogError("Out of memory while trying to install smack rules for pkgId " << pkgId);
+         return false;
+     }
+}
+
+bool SmackRules::uninstallPackageRules(const std::string &pkgId) {
+    std::string path = getPackageRulesFilePath(pkgId);
+    if (access(path.c_str(), F_OK) == -1) {
+        if (errno == ENOENT) {
+            LogWarning("Smack rules were not installed for pkgId: " << pkgId);
+            return true;
+        }
+
+        LogWarning("Cannot access smack rules path: " << path);
+        return false;
+    }
+
+    try {
+        SmackRules rules;
+        if (rules.loadFromFile(path)) {
+            if (!rules.clear()) {
+                LogWarning("Failed to clear smack kernel rules for pkgId: " << pkgId);
+                // don't stop uninstallation
+            }
+        } else {
+            LogWarning("Failed to load rules from file: " << path);
+            // don't stop uninstallation
+        }
+
+        if (unlink(path.c_str()) == -1) {
+            LogError("Failed to remove smack rules file: " << path);
+            return false;
+        }
+
+        return true;
+    } catch (const std::bad_alloc &e) {
+        LogError("Out of memory while trying to uninstall smack rules for pkgId: " << pkgId);
+        return false;
+    }
+}
+
+} // namespace SecurityManager
+
diff --git a/src/server/service/smack-rules.h b/src/server/service/smack-rules.h
new file mode 100644 (file)
index 0000000..bf50a4d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Rafal Krypa <r.krypa@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
+ */
+/**
+ * @file        smack-rules.h
+ * @author      Jacek Bukarewicz <j.bukarewicz@samsung.com>
+ * @version     1.0
+ * @brief       Header file of a class managing smack rules
+ *
+ */
+#ifndef _SMACK_RULES_H_
+#define _SMACK_RULES_H_
+
+#include <vector>
+#include <string>
+
+struct smack_accesses;
+
+namespace SecurityManager {
+
+class SmackRules
+{
+public:
+    SmackRules();
+    virtual ~SmackRules();
+
+    bool add(const std::string &subject, const std::string &object,
+            const std::string &permissions);
+    bool loadFromFile(const std::string &path);
+    bool addFromTemplate(const std::vector<std::string> &templateRules, const std::string &pkgId);
+    bool addFromTemplateFile(const std::string &pkgId);
+
+    bool apply() const;
+    bool clear() const;
+    bool saveToFile(const std::string &path) const;
+
+    /**
+     * Install package-specific smack rules.
+     *
+     * Function creates smack rules using predefined template. Rules are applied
+     * to the kernel and saved on persistent storage so they are loaded on system boot.
+     *
+     * @param[in] pkgId - package identifier
+     * @return true on success, false on error
+     */
+    static bool installPackageRules(const std::string &pkgId);
+    /**
+     * Uninstall package-specific smack rules.
+     *
+     * Function loads package-specific smack rules, revokes them from the kernel
+     * and removes from persistent storage.
+     *
+     * @param[in] pkgId - package identifier
+     * @return true if smack rule file has been uninstalled or didn't exist
+     *         false otherwise
+     */
+    static bool uninstallPackageRules(const std::string &pkgId);
+private:
+    static bool tokenizeRule(const std::string &rule, std::string tokens[], int size);
+    static std::string getPackageRulesFilePath(const std::string &pkgId);
+
+    smack_accesses *m_handle;
+};
+
+} // namespace SecurityManager
+
+#endif /* _SMACK_RULES_H_ */