Plumbing to perform SELinux checks in varlink API (#38146)

This PR does minimal changes to introduce varlink support. Ideally, the
code should switch to using `mac_selinux_get_our_label()` and new
`mac_selinux_get_peer_label()`. But I leave it for now to minimize
breakage. `mac_selinux_get_peer_label()` remains unused.

This is a prep step to merge
https://github.com/systemd/systemd/pull/38032
This commit is contained in:
Yu Watanabe
2025-07-11 10:20:36 +09:00
committed by GitHub
5 changed files with 233 additions and 69 deletions
+164 -38
View File
@@ -9,6 +9,7 @@
#include <unistd.h>
#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 */
+10 -3
View File
@@ -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__)
+38 -27
View File
@@ -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
+16
View File
@@ -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);
+5 -1
View File
@@ -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) {