diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index d56f9456f6..0181d88c72 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -9,6 +9,7 @@ #include #include "sd-bus.h" +#include "sd-varlink.h" #include "alloc-util.h" #include "audit-fd.h" @@ -26,6 +27,7 @@ static bool initialized = false; struct audit_info { sd_bus_creds *creds; + sd_varlink *link; const char *path; const char *cmdline; const char *function; @@ -48,12 +50,23 @@ static int audit_callback( char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a"; char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a"; - if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0) - xsprintf(login_uid_buf, UID_FMT, login_uid); - if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0) - xsprintf(uid_buf, UID_FMT, uid); - if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0) - xsprintf(gid_buf, GID_FMT, gid); + if (audit->creds) { + /* DBus case */ + if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0) + xsprintf(login_uid_buf, UID_FMT, login_uid); + if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0) + xsprintf(uid_buf, UID_FMT, uid); + if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0) + xsprintf(gid_buf, GID_FMT, gid); + } + + if (audit->link) { + /* varlink */ + if (sd_varlink_get_peer_uid(audit->link, &uid) >= 0) + xsprintf(uid_buf, UID_FMT, uid); + if (sd_varlink_get_peer_gid(audit->link, &gid) >= 0) + xsprintf(gid_buf, GID_FMT, gid); + } (void) snprintf(msgbuf, msgbufsize, "auid=%s uid=%s gid=%s%s%s%s%s%s%s%s%s%s", @@ -164,13 +177,84 @@ static int access_init(sd_bus_error *error) { return 1; } +static int get_our_contexts(const Unit *unit, const char **ret_acon, const char **ret_tclass, char **ret_fcon) { + _cleanup_freecon_ char *fcon = NULL; + + assert(ret_acon); + assert(ret_tclass); + assert(ret_fcon); + + if (unit && unit->access_selinux_context) { + /* Nice! The unit comes with a SELinux context read from the unit file */ + *ret_acon = unit->access_selinux_context; + *ret_tclass = "service"; + *ret_fcon = NULL; + return 0; + } + + /* If no unit context is known, use our own */ + + /* Ideally, we should call mac_selinux_get_our_label() here because it + * does exactly the same - call getcon_raw(). However, it involves + * selinux_init() which opens label DB. It was not part of the + * original code. I don't want to change it for now. */ + if (getcon_raw(&fcon) < 0) + return log_debug_errno(errno, "SELinux getcon_raw() failed: %m"); + + if (!fcon) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "SELinux returned no context of the current process"); + + *ret_acon = fcon; + *ret_tclass = "system"; + *ret_fcon = TAKE_PTR(fcon); + return 0; +} + +static int check_access( + const char *scon, + const char *tcon, + const char *tclass, + const char *permission, + struct audit_info *audit_info, + sd_bus_error *error) { + bool enforce = mac_selinux_enforcing(); + int r; + + assert(scon); + assert(tcon); + assert(tclass); + assert(permission); + assert(audit_info); + assert(audit_info->function); + + r = selinux_check_access(scon, tcon, tclass, permission, &audit_info); + if (r < 0) { + errno = -(r = errno_or_else(EPERM)); + + if (enforce) + sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access: %m"); + } + + return log_selinux_enforcing_errno( + r, + "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m", + scon, + tcon, + tclass, + permission, + enforce ? "enforcing" : "permissive", + audit_info->function, + empty_to_na(audit_info->path), + empty_to_na(audit_info->cmdline)); +} + /* This function communicates with the kernel to check whether or not it should allow the access. If the machine is in permissive mode it will return ok. Audit messages will still be generated if the access would be denied in enforcing mode. */ -int mac_selinux_access_check_internal( +int mac_selinux_access_check_bus_internal( sd_bus_message *message, const Unit *unit, const char *permission, @@ -216,30 +300,21 @@ int mac_selinux_access_check_internal( if (r < 0) return r; - if (unit && unit->access_selinux_context) { - /* Nice! The unit comes with a SELinux context read from the unit file */ - acon = unit->access_selinux_context; - tclass = "service"; - } else { - /* If no unit context is known, use our own */ - if (getcon_raw(&fcon) < 0) { - log_warning_errno(errno, "SELinux getcon_raw() failed%s (perm=%s): %m", - enforce ? "" : ", ignoring", - permission); - if (!enforce) - return 0; + r = get_our_contexts(unit, &acon, &tclass, &fcon); + if (r < 0) { + log_selinux_enforcing_errno( + r, + "Failed to retrieves SELinux context of current process (perm=%s)%s: %m", + permission, + enforce ? "" : ", ignoring"); - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m"); - } - if (!fcon) { - if (!enforce) - return 0; + if (!enforce) + return 0; + if (r == -EOPNOTSUPP) return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "We appear not to have any SELinux context: %m"); - } - acon = fcon; - tclass = "system"; + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m"); } (void) sd_bus_creds_get_cmdline(creds, &cmdline); @@ -252,23 +327,66 @@ int mac_selinux_access_check_internal( .function = function, }; - r = selinux_check_access(scon, acon, tclass, permission, &audit_info); - if (r < 0) { - errno = -(r = errno_or_else(EPERM)); + return check_access(scon, acon, tclass, permission, &audit_info, error); +} - if (enforce) - sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access: %m"); - } +int mac_selinux_access_check_varlink_internal( + sd_varlink *link, + const Unit *unit, + const char *permission, + const char *function) { + _cleanup_freecon_ char *fcon = NULL, *scon = NULL; + const char *tclass, *acon; + int r; - log_full_errno_zerook(LOG_DEBUG, r, - "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m", - scon, acon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(unit ? unit->fragment_path : NULL), empty_to_na(cl)); - return enforce ? r : 0; + assert(link); + assert(permission); + assert(function); + + r = access_init(/* error= */ NULL); + if (r <= 0) + return log_debug_errno(r, "Failed to init SELinux: %m"); + + /* delay call until we checked in `access_init()` if SELinux is actually enabled */ + bool enforce = mac_selinux_enforcing(); + + int fd = sd_varlink_get_fd(link); + if (fd < 0) + return log_debug_errno(fd, "Failed to get varlink peer fd: %m"); + + /* We should call mac_selinux_get_peer_label() here similarly to get_our_contexts(). + * See the explanation there why not. */ + if (getpeercon_raw(fd, &scon) < 0) + return log_selinux_enforcing_errno( + errno, + "Failed to get peer SELinux context%s: %m", + enforce ? "" : ", ignoring"); + + if (!scon) + return log_selinux_enforcing_errno( + SYNTHETIC_ERRNO(EOPNOTSUPP), + "Peer does not have SELinux context"); + + r = get_our_contexts(unit, &acon, &tclass, &fcon); + if (r < 0) + return log_selinux_enforcing_errno( + r, + "Failed to retrieves SELinux context of current process (perm=%s)%s: %m", + permission, + enforce ? "" : ", ignoring"); + + struct audit_info audit_info = { + .link = link, + .path = unit ? unit->fragment_path : NULL, + .function = function, + }; + + return check_access(scon, acon, tclass, permission, &audit_info, /* error= */ NULL); } #else /* HAVE_SELINUX */ -int mac_selinux_access_check_internal( +int mac_selinux_access_check_bus_internal( sd_bus_message *message, const Unit *unit, const char *permission, @@ -278,4 +396,12 @@ int mac_selinux_access_check_internal( return 0; } +int mac_selinux_access_check_varlink_internal( + sd_varlink *link, + const Unit *unit, + const char *permission, + const char *function) { + return 0; +} + #endif /* HAVE_SELINUX */ diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h index 9c319f20ac..722122841b 100644 --- a/src/core/selinux-access.h +++ b/src/core/selinux-access.h @@ -3,10 +3,17 @@ #include "core-forward.h" -int mac_selinux_access_check_internal(sd_bus_message *message, const Unit *unit, const char *permission, const char *function, sd_bus_error *error); +int mac_selinux_access_check_bus_internal(sd_bus_message *message, const Unit *unit, const char *permission, const char *function, sd_bus_error *error); +int mac_selinux_access_check_varlink_internal(sd_varlink *link, const Unit *unit, const char *permission, const char *function); #define mac_selinux_access_check(message, permission, error) \ - mac_selinux_access_check_internal((message), NULL, (permission), __func__, (error)) + mac_selinux_access_check_bus_internal((message), NULL, (permission), __func__, (error)) #define mac_selinux_unit_access_check(unit, message, permission, error) \ - mac_selinux_access_check_internal((message), (unit), (permission), __func__, (error)) + mac_selinux_access_check_bus_internal((message), (unit), (permission), __func__, (error)) + +#define mac_selinux_access_check_varlink(link, permission) \ + mac_selinux_access_check_varlink_internal((link), NULL, (permission), __func__) + +#define mac_selinux_unit_access_check_varlink(unit, link, permission) \ + mac_selinux_access_check_bus_internal((link), (unit), (permission), __func__) diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c index baf5dc5c4a..f7c4c7d2f8 100644 --- a/src/shared/selinux-util.c +++ b/src/shared/selinux-util.c @@ -41,21 +41,6 @@ static int last_policyload = 0; static struct selabel_handle *label_hnd = NULL; static bool have_status_page = false; -#define log_enforcing(...) \ - log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__) - -#define log_enforcing_errno(error, ...) \ - ({ \ - bool _enforcing = mac_selinux_enforcing(); \ - int _level = _enforcing ? LOG_ERR : LOG_WARNING; \ - int _e = (error); \ - \ - int _r = (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ - : -ERRNO_VALUE(_e); \ - _enforcing ? _r : 0; \ - }) - static int mac_selinux_label_pre(int dir_fd, const char *path, mode_t mode) { return mac_selinux_create_file_prepare_at(dir_fd, path, mode); } @@ -115,7 +100,7 @@ static int open_label_db(void) { hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); if (!hnd) - return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m"); + return log_selinux_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m"); if (DEBUG_LOGGING) { after_timestamp = now(CLOCK_MONOTONIC); @@ -160,7 +145,7 @@ static int selinux_init(bool force) { r = selinux_status_open(/* netlink fallback= */ 1); if (r < 0) { if (!ERRNO_IS_PRIVILEGE(errno)) - return log_enforcing_errno(errno, "Failed to open SELinux status page: %m"); + return log_selinux_enforcing_errno(errno, "Failed to open SELinux status page: %m"); log_warning_errno(errno, "selinux_status_open() with netlink fallback failed, not checking for policy reloads: %m"); } else if (r == 1) log_warning("selinux_status_open() failed to open the status page, using the netlink fallback."); @@ -294,7 +279,7 @@ static int selinux_fix_fd( if (errno == ENOENT) return 0; - return log_enforcing_errno(errno, "Unable to lookup intended SELinux security context of %s: %m", label_path); + return log_selinux_enforcing_errno(errno, "Unable to lookup intended SELinux security context of %s: %m", label_path); } if (setfilecon_raw(FORMAT_PROC_FD_PATH(fd), fcon) < 0) { @@ -314,7 +299,7 @@ static int selinux_fix_fd( if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq_ptr(fcon, oldcon)) return 0; - return log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path); + return log_selinux_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path); } return 0; @@ -387,7 +372,7 @@ int mac_selinux_apply(const char *path, const char *label) { assert(label); if (setfilecon(path, label) < 0) - return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path); + return log_selinux_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path); #endif return 0; } @@ -406,7 +391,7 @@ int mac_selinux_apply_fd(int fd, const char *path, const char *label) { assert(label); if (setfilecon(FORMAT_PROC_FD_PATH(fd), label) < 0) - return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path)); + return log_selinux_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path)); #endif return 0; } @@ -471,6 +456,32 @@ int mac_selinux_get_our_label(char **ret_label) { #endif } +int mac_selinux_get_peer_label(int socket_fd, char **ret_label) { + assert(socket_fd >= 0); + assert(ret_label); + +#if HAVE_SELINUX + int r; + + r = selinux_init(/* force= */ false); + if (r < 0) + return r; + if (r == 0) + return -EOPNOTSUPP; + + _cleanup_freecon_ char *con = NULL; + if (getpeercon_raw(socket_fd, &con) < 0) + return -errno; + if (!con) + return -EOPNOTSUPP; + + *ret_label = TAKE_PTR(con); + return 0; +#else + return -EOPNOTSUPP; +#endif +} + int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **ret_label) { #if HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL; @@ -558,11 +569,11 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) if (errno == ENOENT) return 0; - return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath); + return log_selinux_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath); } if (setfscreatecon_raw(filecon) < 0) - return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath); + return log_selinux_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath); return 0; } @@ -615,7 +626,7 @@ int mac_selinux_create_file_prepare_label(const char *path, const char *label) { return r; if (setfscreatecon_raw(label) < 0) - return log_enforcing_errno(errno, "Failed to set specified SELinux security context '%s' for '%s': %m", label, strna(path)); + return log_selinux_enforcing_errno(errno, "Failed to set specified SELinux security context '%s' for '%s': %m", label, strna(path)); #endif return 0; } @@ -644,7 +655,7 @@ int mac_selinux_create_socket_prepare(const char *label) { return r; if (setsockcreatecon(label) < 0) - return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label); + return log_selinux_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label); #endif return 0; @@ -724,12 +735,12 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { if (errno == ENOENT) goto skipped; - r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path); + r = log_selinux_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path); if (r < 0) return r; } else { if (setfscreatecon_raw(fcon) < 0) { - r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path); + r = log_selinux_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path); if (r < 0) return r; } else diff --git a/src/shared/selinux-util.h b/src/shared/selinux-util.h index a9180c4c99..d8df89d79e 100644 --- a/src/shared/selinux-util.h +++ b/src/shared/selinux-util.h @@ -17,6 +17,21 @@ static inline void freeconp(char **p) { #define _cleanup_freecon_ _cleanup_(freeconp) +#define log_selinux_enforcing(...) \ + log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__) + +#define log_selinux_enforcing_errno(error, ...) \ + ({ \ + bool _enforcing = mac_selinux_enforcing(); \ + int _level = _enforcing ? LOG_ERR : LOG_WARNING; \ + int _e = (error); \ + \ + int _r = (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ + : -ERRNO_VALUE(_e); \ + _enforcing ? _r : 0; \ + }) + bool mac_selinux_use(void); void mac_selinux_retest(void); bool mac_selinux_enforcing(void); @@ -35,6 +50,7 @@ int mac_selinux_apply_fd(int fd, const char *path, const char *label); int mac_selinux_get_create_label_from_exe(const char *exe, char **ret_label); int mac_selinux_get_our_label(char **ret_label); +int mac_selinux_get_peer_label(int socket_fd, char **ret_label); int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **ret_label); int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode); diff --git a/src/test/test-selinux.c b/src/test/test-selinux.c index e51b3a283b..93e30896b7 100644 --- a/src/test/test-selinux.c +++ b/src/test/test-selinux.c @@ -53,7 +53,7 @@ static void test_cleanup(void) { } static void test_misc(const char* fname) { - _cleanup_freecon_ char *label = NULL, *label2 = NULL, *label3 = NULL; + _cleanup_freecon_ char *label = NULL, *label2 = NULL, *label3 = NULL, *label4 = NULL; int r; _cleanup_close_ int fd = -EBADF; @@ -73,6 +73,10 @@ static void test_misc(const char* fname) { r = mac_selinux_get_child_mls_label(fd, fname, label2, &label3); log_info_errno(r, "mac_selinux_get_child_mls_label → %d, \"%s\" (%m)", r, strnull(label3)); + + r = mac_selinux_get_peer_label(fd, &label4); + log_info_errno(r, "mac_selinux_get_peer_label → %d, \"%s\" (%m)", + r, strnull(label4)); } static void test_create_file_prepare(const char* fname) {