From 6656aefb42385b468dd96867118d049f945cbf81 Mon Sep 17 00:00:00 2001 From: WaLyong Cho Date: Wed, 10 Jun 2015 11:33:00 +0900 Subject: [PATCH 1/2] smack: support smack access change-rule 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 | 137 +++++++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c index ff2a02004d..ddb02a1580 100644 --- a/src/core/smack-setup.c +++ b/src/core/smack-setup.c @@ -34,31 +34,35 @@ #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 %s: %m", dstpath); + log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/load2': %m"); return -errno; /* negative error */ } - /* write rules to dst from every file in the directory */ + 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 '/sys/fs/smackfs/change-rule': %m"); + return -errno; /* negative error */ + } + + /* 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)) { - if (r == 0) - r = -EINVAL; - log_error("Failed to write line to %s", dstpath); - break; + 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 (fflush(dst)) { + + if (write(isempty(acc2) ? load2_fd : change_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 '%s' in '%s'", + buf, isempty(acc2) ? "/sys/fs/smackfs/load2" : "/sys/fs/smackfs/change-rule", entry->d_name); + } + } + } + + 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 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."); From e174dce27173396ed8034c9cfda87eb210365126 Mon Sep 17 00:00:00 2001 From: WaLyong Cho Date: Mon, 8 Jun 2015 19:41:01 +0900 Subject: [PATCH 2/2] smack: add default smack process label config Similar to SmackProcessLabel=, if this configuration is set, systemd executes processes with given SMACK label. If unit has SmackProcessLabel=, this config is overwritten. But, do NOT be confused with SMACK64EXEC of execute file. This default execute process label(and also label which is set by SmackProcessLabel=) is set fork-ed process SMACK subject label and used to access the execute file. If the execution file has also SMACK64EXEC, finally executed process has SMACK64EXEC subject. While if the execution file has no SMACK64EXEC, the executed process has label of this config(or label which is set by SmackProcessLabel=). Because if execution file has no SMACK64EXEC then excuted process inherits label from caller process(in this case, the caller is systemd). --- configure.ac | 10 ++++++++-- src/core/execute.c | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8b1e275d27..88b52c45fe 100644 --- a/configure.ac +++ b/configure.ac @@ -673,8 +673,14 @@ fi AC_ARG_WITH(smack-run-label, AS_HELP_STRING([--with-smack-run-label=STRING], - [run systemd --system with a specific SMACK label]), - [AC_DEFINE_UNQUOTED(SMACK_RUN_LABEL, ["$withval"], [Run with a smack label])], + [run systemd --system itself with a specific SMACK label]), + [AC_DEFINE_UNQUOTED(SMACK_RUN_LABEL, ["$withval"], [Run systemd itself with SMACK label])], + []) + +AC_ARG_WITH(smack-default-process-label, +AS_HELP_STRING([--with-smack-default-process-label=STRING], + [default SMACK label for executed processes]), + [AC_DEFINE_UNQUOTED(SMACK_DEFAULT_PROCESS_LABEL, ["$withval"], [Default SMACK label for executed processes])], []) if test "x${have_smack}" = xyes ; then diff --git a/src/core/execute.c b/src/core/execute.c index 94cc101738..c92db51330 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1717,6 +1717,15 @@ static int exec_child( return r; } } +#ifdef SMACK_DEFAULT_PROCESS_LABEL + else { + r = mac_smack_apply_pid(0, SMACK_DEFAULT_PROCESS_LABEL); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + } +#endif #endif if (context->user) {