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