]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
ci: enable arm64 runner for build/unit jobs
[thirdparty/systemd.git] / src / core / selinux-access.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
e2417e41 2
e2417e41
DW
3#include "selinux-access.h"
4
349cc4a5 5#if HAVE_SELINUX
e2417e41 6
e2417e41 7#include <selinux/avc.h>
cf0fbc49 8#include <selinux/selinux.h>
4f18ff2e 9#include <unistd.h>
ffc227c9 10
718db961 11#include "sd-bus.h"
15a5e950 12
b5efdb8a 13#include "alloc-util.h"
15a5e950 14#include "audit-fd.h"
4bbccb02 15#include "errno-util.h"
ca78ad1d 16#include "format-util.h"
cdd5fac0 17#include "libaudit-util.h"
ffc227c9 18#include "log.h"
ffc227c9 19#include "selinux-util.h"
15a5e950 20#include "stdio-util.h"
836e4e7e 21#include "string-util.h"
5b12334d 22#include "strv.h"
836e4e7e 23#include "unit.h"
e2417e41 24
cad45ba1 25static bool initialized = false;
e2417e41 26
5b12334d
LP
27struct audit_info {
28 sd_bus_creds *creds;
e2417e41 29 const char *path;
5b12334d 30 const char *cmdline;
f0804759 31 const char *function;
e2417e41
DW
32};
33
e2417e41
DW
34/*
35 Any time an access gets denied this callback will be called
dec23413 36 with the audit data. We then need to just copy the audit data into the msgbuf.
e2417e41 37*/
cad45ba1
LP
38static int audit_callback(
39 void *auditdata,
40 security_class_t cls,
41 char *msgbuf,
42 size_t msgbufsize) {
43
5b12334d
LP
44 const struct audit_info *audit = auditdata;
45 uid_t uid = 0, login_uid = 0;
46 gid_t gid = 0;
5ffa8c81
ZJS
47 char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
48 char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
49 char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a";
5b12334d 50
dec23413 51 if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
5ffa8c81 52 xsprintf(login_uid_buf, UID_FMT, login_uid);
05bae4a6 53 if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
5ffa8c81 54 xsprintf(uid_buf, UID_FMT, uid);
05bae4a6 55 if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
5ffa8c81 56 xsprintf(gid_buf, GID_FMT, gid);
cad45ba1 57
121ed16c 58 (void) snprintf(msgbuf, msgbufsize,
f0804759 59 "auid=%s uid=%s gid=%s%s%s%s%s%s%s%s%s%s",
121ed16c
LB
60 login_uid_buf, uid_buf, gid_buf,
61 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
f0804759
CG
62 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "",
63 audit->function ? " function=\"" : "", strempty(audit->function), audit->function ? "\"" : "");
d67227c8 64
e2417e41
DW
65 return 0;
66}
67
17af49f2 68static int callback_type_to_priority(int type) {
79893116 69 switch (type) {
c04754bc
LP
70
71 case SELINUX_ERROR:
72 return LOG_ERR;
73
74 case SELINUX_WARNING:
75 return LOG_WARNING;
76
77 case SELINUX_INFO:
78 return LOG_INFO;
79
17af49f2 80 case SELINUX_AVC:
c04754bc
LP
81 default:
82 return LOG_NOTICE;
17af49f2
ZJS
83 }
84}
85
e2417e41 86/*
17af49f2
ZJS
87 libselinux uses this callback when access gets denied or other
88 events happen. If audit is turned on, messages will be reported
89 using audit netlink, otherwise they will be logged using the usual
90 channels.
91
92 Code copied from dbus and modified.
e2417e41 93*/
44b601bc 94_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41 95 va_list ap;
c8a806f2 96 const char *fmt2;
e2417e41 97
349cc4a5 98#if HAVE_AUDIT
0dba7b5c 99 int fd = get_core_audit_fd();
17af49f2
ZJS
100
101 if (fd >= 0) {
ace188cf
LP
102 _cleanup_free_ char *buf = NULL;
103 int r;
e2417e41 104
5b12334d 105 va_start(ap, fmt);
ace188cf 106 r = vasprintf(&buf, fmt, ap);
7f1736f7 107 va_end(ap);
cad45ba1 108
ace188cf 109 if (r >= 0) {
6227fc14 110 if (type == SELINUX_AVC)
0dba7b5c 111 audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, getuid());
6227fc14 112 else if (type == SELINUX_ERROR)
0dba7b5c 113 audit_log_user_avc_message(fd, AUDIT_USER_SELINUX_ERR, buf, NULL, NULL, NULL, getuid());
6227fc14 114
ace188cf
LP
115 return 0;
116 }
e2417e41
DW
117 }
118#endif
5b12334d 119
c8a806f2
ZJS
120 fmt2 = strjoina("selinux: ", fmt);
121
5b12334d 122 va_start(ap, fmt);
56e577c6
LP
123
124 DISABLE_WARNING_FORMAT_NONLITERAL;
032b7541 125 log_internalv(LOG_AUTH | callback_type_to_priority(type),
7fbae5b7 126 0, PROJECT_FILE, __LINE__, __func__,
032b7541 127 fmt2, ap);
56e577c6 128 REENABLE_WARNING;
e2417e41 129 va_end(ap);
cad45ba1 130
e2417e41
DW
131 return 0;
132}
133
6344f3e2 134static int access_init(sd_bus_error *error) {
38553034 135 int r;
e2417e41 136
6344f3e2
LP
137 if (!mac_selinux_use())
138 return 0;
e2417e41 139
6344f3e2
LP
140 if (initialized)
141 return 1;
cad45ba1 142
6344f3e2 143 if (avc_open(NULL, 0) != 0) {
38553034 144 r = -errno; /* Save original errno for later */
e2417e41 145
38553034
ZJS
146 bool enforce = security_getenforce() != 0;
147 log_full_errno(enforce ? LOG_ERR : LOG_WARNING, r, "Failed to open the SELinux AVC: %m");
e2417e41 148
38553034
ZJS
149 /* If enforcement isn't on, then let's suppress this error, and just don't do any AVC checks.
150 * The warning we printed is hence all the admin will see. */
257188f8 151 if (!enforce)
6344f3e2 152 return 0;
e2417e41 153
38553034
ZJS
154 /* Return an access denied error based on the original errno, if we couldn't load the AVC but
155 * enforcing mode was on, or we couldn't determine whether it is one. */
156 errno = -r;
157 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %m");
6344f3e2 158 }
718db961 159
57e70396
CG
160 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) { .func_audit = audit_callback });
161 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) { .func_log = log_callback });
e2417e41 162
cad45ba1 163 initialized = true;
6344f3e2 164 return 1;
e2417e41
DW
165}
166
e2417e41
DW
167/*
168 This function communicates with the kernel to check whether or not it should
169 allow the access.
170 If the machine is in permissive mode it will return ok. Audit messages will
171 still be generated if the access would be denied in enforcing mode.
172*/
963438a0 173int mac_selinux_access_check_internal(
718db961 174 sd_bus_message *message,
836e4e7e 175 const Unit *unit,
cad45ba1 176 const char *permission,
f0804759 177 const char *function,
718db961 178 sd_bus_error *error) {
cad45ba1 179
4afd3348 180 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
23e9a7dd 181 const char *tclass, *scon, *acon;
5b12334d 182 _cleanup_free_ char *cl = NULL;
cff789b7 183 _cleanup_freecon_ char *fcon = NULL;
5b12334d 184 char **cmdline = NULL;
194fe322 185 bool enforce;
718db961 186 int r = 0;
c3090674 187
cad45ba1
LP
188 assert(message);
189 assert(permission);
f0804759 190 assert(function);
cad45ba1 191
6344f3e2
LP
192 r = access_init(error);
193 if (r <= 0)
ffc227c9
LP
194 return r;
195
194fe322 196 /* delay call until we checked in `access_init()` if SELinux is actually enabled */
fd5e402f 197 enforce = mac_selinux_enforcing();
194fe322 198
5b12334d
LP
199 r = sd_bus_query_sender_creds(
200 message,
05bae4a6 201 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
5b12334d 202 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
8fd00193
LP
203 SD_BUS_CREDS_SELINUX_CONTEXT|
204 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
5b12334d
LP
205 &creds);
206 if (r < 0)
cff789b7 207 return r;
e2417e41 208
23e9a7dd
LP
209 /* The SELinux context is something we really should have gotten directly from the message or sender,
210 * and not be an augmented field. If it was augmented we cannot use it for authorization, since this
211 * is racy and vulnerable. Let's add an extra check, just in case, even though this really shouldn't
212 * be possible. */
0f514420
LP
213 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
214
5b12334d 215 r = sd_bus_creds_get_selinux_context(creds, &scon);
718db961 216 if (r < 0)
cff789b7 217 return r;
cad45ba1 218
836e4e7e 219 if (unit && unit->access_selinux_context) {
23e9a7dd 220 /* Nice! The unit comes with a SELinux context read from the unit file */
836e4e7e 221 acon = unit->access_selinux_context;
718db961 222 tclass = "service";
e2417e41 223 } else {
23e9a7dd 224 /* If no unit context is known, use our own */
cff789b7 225 if (getcon_raw(&fcon) < 0) {
af614e45 226 log_warning_errno(errno, "SELinux getcon_raw() failed%s (perm=%s): %m",
cff789b7
ZJS
227 enforce ? "" : ", ignoring",
228 permission);
229 if (!enforce)
230 return 0;
231
62f174cf 232 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m");
e2417e41 233 }
af614e45
LP
234 if (!fcon) {
235 if (!enforce)
236 return 0;
237
238 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "We appear not to have any SELinux context: %m");
239 }
718db961 240
23e9a7dd 241 acon = fcon;
718db961 242 tclass = "system";
e2417e41
DW
243 }
244
4ac08d8a 245 (void) sd_bus_creds_get_cmdline(creds, &cmdline);
5b12334d
LP
246 cl = strv_join(cmdline, " ");
247
cff789b7
ZJS
248 struct audit_info audit_info = {
249 .creds = creds,
836e4e7e 250 .path = unit ? unit->fragment_path : NULL,
cff789b7 251 .cmdline = cl,
f0804759 252 .function = function,
cff789b7 253 };
e2417e41 254
23e9a7dd 255 r = selinux_check_access(scon, acon, tclass, permission, &audit_info);
cff789b7 256 if (r < 0) {
62f174cf 257 errno = -(r = errno_or_else(EPERM));
e2417e41 258
cff789b7 259 if (enforce)
62f174cf 260 sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access: %m");
e2417e41
DW
261 }
262
2669c666 263 log_full_errno_zerook(LOG_DEBUG, r,
f0804759 264 "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m",
836e4e7e 265 scon, acon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(unit ? unit->fragment_path : NULL), empty_to_na(cl));
cff789b7 266 return enforce ? r : 0;
6344f3e2
LP
267}
268
fd5e402f 269#else /* HAVE_SELINUX */
6344f3e2 270
963438a0 271int mac_selinux_access_check_internal(
6344f3e2 272 sd_bus_message *message,
836e4e7e 273 const Unit *unit,
6344f3e2 274 const char *permission,
f0804759 275 const char *function,
6344f3e2
LP
276 sd_bus_error *error) {
277
8a188de9 278 return 0;
e2417e41 279}
6344f3e2 280
fd5e402f 281#endif /* HAVE_SELINUX */