]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
smack: support smack access change-rule
authorWaLyong Cho <walyong.cho@samsung.com>
Wed, 10 Jun 2015 02:33:00 +0000 (11:33 +0900)
committerWaLyong Cho <walyong.cho@samsung.com>
Mon, 22 Jun 2015 14:44:09 +0000 (23:44 +0900)
Smack is also able to have modification rules of existing rules. In
this case, the rule has additional argument to modify previous
rule. /sys/fs/smackfs/load2 node can only take three arguments:
subject object access. So if modification rules are written to
/sys/fs/smackfs/load2, EINVAL error is happen. Those modification
rules have to be written to /sys/fs/smackfs/change-rule.
To distinguish access with operation of cipso2, split write_rules()
for each operation. And, in write access rules, parse the rule and if
the rule has four argument then write into
/sys/fs/smackfs/change-rule.
https://lwn.net/Articles/532340/

fwrite() or fputs() are fancy functions to write byte stream such like
regular file. But special files on linux such like proc, sysfs are not
stream of bytes. Those special files on linux have to be written with
specific size.
By this reason, in some of many case, fputs() was failed to write
buffer to smack load2 node.
The write operation for the smack nodes should be performed with
write().

src/core/smack-setup.c

index ff2a02004d78307919a1cfc7092384cfd8e6b7f6..ddb02a1580c1f13f8660d382f9af261f0a386bd7 100644 (file)
 #include "fileio.h"
 #include "log.h"
 
-#define SMACK_CONFIG "/etc/smack/accesses.d/"
-#define CIPSO_CONFIG "/etc/smack/cipso.d/"
-
 #ifdef HAVE_SMACK
 
-static int write_rules(const char* dstpath, const char* srcdir) {
-        _cleanup_fclose_ FILE *dst = NULL;
+static int write_access2_rules(const char* srcdir) {
+        _cleanup_close_ int load2_fd = -1, change_fd = -1;
         _cleanup_closedir_ DIR *dir = NULL;
         struct dirent *entry;
         char buf[NAME_MAX];
         int dfd = -1;
         int r = 0;
 
-        dst = fopen(dstpath, "we");
-        if (!dst)  {
+        load2_fd = open("/sys/fs/smackfs/load2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+        if (load2_fd < 0)  {
+                if (errno != ENOENT)
+                        log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/load2': %m");
+                return -errno; /* negative error */
+        }
+
+        change_fd = open("/sys/fs/smackfs/change-rule", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+        if (change_fd < 0)  {
                 if (errno != ENOENT)
-                        log_warning_errno(errno, "Failed to open %s: %m", dstpath);
+                        log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/change-rule': %m");
                 return -errno; /* negative error */
         }
 
-        /* write rules to dst from every file in the directory */
+        /* write rules to load2 or change-rule from every file in the directory */
         dir = opendir(srcdir);
         if (!dir) {
                 if (errno != ENOENT)
-                        log_warning_errno(errno, "Failed to opendir %s: %m", srcdir);
+                        log_warning_errno(errno, "Failed to opendir '%s': %m", srcdir);
                 return errno; /* positive on purpose */
         }
 
@@ -69,11 +73,14 @@ static int write_rules(const char* dstpath, const char* srcdir) {
                 int fd;
                 _cleanup_fclose_ FILE *policy = NULL;
 
+                if (!dirent_is_file(entry))
+                        continue;
+
                 fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
                 if (fd < 0) {
                         if (r == 0)
                                 r = -errno;
-                        log_warning_errno(errno, "Failed to open %s: %m", entry->d_name);
+                        log_warning_errno(errno, "Failed to open '%s': %m", entry->d_name);
                         continue;
                 }
 
@@ -82,30 +89,108 @@ static int write_rules(const char* dstpath, const char* srcdir) {
                         if (r == 0)
                                 r = -errno;
                         safe_close(fd);
-                        log_error_errno(errno, "Failed to open %s: %m", entry->d_name);
+                        log_error_errno(errno, "Failed to open '%s': %m", entry->d_name);
                         continue;
                 }
 
                 /* load2 write rules in the kernel require a line buffered stream */
                 FOREACH_LINE(buf, policy,
-                             log_error_errno(errno, "Failed to read line from %s: %m",
-                                       entry->d_name)) {
-                        if (!fputs(buf, dst)) {
+                             log_error_errno(errno, "Failed to read line from '%s': %m",
+                                             entry->d_name)) {
+
+                        _cleanup_free_ char *sbj = NULL, *obj = NULL, *acc1 = NULL, *acc2 = NULL;
+
+                        if (isempty(truncate_nl(buf)))
+                                continue;
+
+                        /* if 3 args -> load rule   : subject object access1 */
+                        /* if 4 args -> change rule : subject object access1 access2 */
+                        if (sscanf(buf, "%ms %ms %ms %ms", &sbj, &obj, &acc1, &acc2) < 3) {
+                                log_error_errno(errno, "Failed to parse rule '%s' in '%s', ignoring.", buf, entry->d_name);
+                                continue;
+                        }
+
+                        if (write(isempty(acc2) ? load2_fd : change_fd, buf, strlen(buf)) < 0) {
                                 if (r == 0)
-                                        r = -EINVAL;
-                                log_error("Failed to write line to %s", dstpath);
-                                break;
+                                        r = -errno;
+                                log_error_errno(errno, "Failed to write '%s' to '%s' in '%s'",
+                                                buf, isempty(acc2) ? "/sys/fs/smackfs/load2" : "/sys/fs/smackfs/change-rule", entry->d_name);
                         }
-                        if (fflush(dst)) {
+                }
+        }
+
+        return r;
+}
+
+static int write_cipso2_rules(const char* srcdir) {
+        _cleanup_close_ int cipso2_fd = -1;
+        _cleanup_closedir_ DIR *dir = NULL;
+        struct dirent *entry;
+        char buf[NAME_MAX];
+        int dfd = -1;
+        int r = 0;
+
+        cipso2_fd = open("/sys/fs/smackfs/cipso2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+        if (cipso2_fd < 0)  {
+                if (errno != ENOENT)
+                        log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/cipso2': %m");
+                return -errno; /* negative error */
+        }
+
+        /* write rules to cipso2 from every file in the directory */
+        dir = opendir(srcdir);
+        if (!dir) {
+                if (errno != ENOENT)
+                        log_warning_errno(errno, "Failed to opendir '%s': %m", srcdir);
+                return errno; /* positive on purpose */
+        }
+
+        dfd = dirfd(dir);
+        assert(dfd >= 0);
+
+        FOREACH_DIRENT(entry, dir, return 0) {
+                int fd;
+                _cleanup_fclose_ FILE *policy = NULL;
+
+                if (!dirent_is_file(entry))
+                        continue;
+
+                fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
+                if (fd < 0) {
+                        if (r == 0)
+                                r = -errno;
+                        log_error_errno(errno, "Failed to open '%s': %m", entry->d_name);
+                        continue;
+                }
+
+                policy = fdopen(fd, "re");
+                if (!policy) {
+                        if (r == 0)
+                                r = -errno;
+                        safe_close(fd);
+                        log_error_errno(errno, "Failed to open '%s': %m", entry->d_name);
+                        continue;
+                }
+
+                /* cipso2 write rules in the kernel require a line buffered stream */
+                FOREACH_LINE(buf, policy,
+                             log_error_errno(errno, "Failed to read line from '%s': %m",
+                                             entry->d_name)) {
+
+                        if (isempty(truncate_nl(buf)))
+                                continue;
+
+                        if (write(cipso2_fd, buf, strlen(buf)) < 0) {
                                 if (r == 0)
                                         r = -errno;
-                                log_error_errno(errno, "Failed to flush writes to %s: %m", dstpath);
+                                log_error_errno(errno, "Failed to write '%s' to '/sys/fs/smackfs/cipso2' in '%s'",
+                                                buf, entry->d_name);
                                 break;
                         }
                 }
         }
 
-       return r;
+        return r;
 }
 
 #endif
@@ -118,13 +203,13 @@ int mac_smack_setup(bool *loaded_policy) {
 
         assert(loaded_policy);
 
-        r = write_rules("/sys/fs/smackfs/load2", SMACK_CONFIG);
+        r = write_access2_rules("/etc/smack/accesses.d/");
         switch(r) {
         case -ENOENT:
                 log_debug("Smack is not enabled in the kernel.");
                 return 0;
         case ENOENT:
-                log_debug("Smack access rules directory " SMACK_CONFIG " not found");
+                log_debug("Smack access rules directory '/etc/smack/accesses.d/' not found");
                 return 0;
         case 0:
                 log_info("Successfully loaded Smack policies.");
@@ -142,13 +227,13 @@ int mac_smack_setup(bool *loaded_policy) {
                             SMACK_RUN_LABEL, strerror(-r));
 #endif
 
-        r = write_rules("/sys/fs/smackfs/cipso2", CIPSO_CONFIG);
+        r = write_cipso2_rules("/etc/smack/cipso.d/");
         switch(r) {
         case -ENOENT:
                 log_debug("Smack/CIPSO is not enabled in the kernel.");
                 return 0;
         case ENOENT:
-                log_debug("Smack/CIPSO access rules directory " CIPSO_CONFIG " not found");
+                log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found");
                 return 0;
         case 0:
                 log_info("Successfully loaded Smack/CIPSO policies.");