]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
core/selinux-access: use empty_to_na where appropriate
[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 196
6344f3e2
LP
197 r = access_init(error);
198 if (r <= 0)
ffc227c9
LP
199 return r;
200
194fe322 201 /* delay call until we checked in `access_init()` if SELinux is actually enabled */
fd5e402f 202 enforce = mac_selinux_enforcing();
194fe322 203
5b12334d
LP
204 r = sd_bus_query_sender_creds(
205 message,
05bae4a6 206 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
5b12334d 207 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
8fd00193
LP
208 SD_BUS_CREDS_SELINUX_CONTEXT|
209 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
5b12334d
LP
210 &creds);
211 if (r < 0)
cff789b7 212 return r;
e2417e41 213
23e9a7dd
LP
214 /* The SELinux context is something we really should have gotten directly from the message or sender,
215 * and not be an augmented field. If it was augmented we cannot use it for authorization, since this
216 * is racy and vulnerable. Let's add an extra check, just in case, even though this really shouldn't
217 * be possible. */
0f514420
LP
218 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
219
5b12334d 220 r = sd_bus_creds_get_selinux_context(creds, &scon);
718db961 221 if (r < 0)
cff789b7 222 return r;
cad45ba1 223
23e9a7dd
LP
224 if (unit_context) {
225 /* Nice! The unit comes with a SELinux context read from the unit file */
226 acon = unit_context;
718db961 227 tclass = "service";
e2417e41 228 } else {
23e9a7dd 229 /* If no unit context is known, use our own */
cff789b7 230 if (getcon_raw(&fcon) < 0) {
af614e45 231 log_warning_errno(errno, "SELinux getcon_raw() failed%s (perm=%s): %m",
cff789b7
ZJS
232 enforce ? "" : ", ignoring",
233 permission);
234 if (!enforce)
235 return 0;
236
62f174cf 237 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m");
e2417e41 238 }
af614e45
LP
239 if (!fcon) {
240 if (!enforce)
241 return 0;
242
243 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "We appear not to have any SELinux context: %m");
244 }
718db961 245
23e9a7dd 246 acon = fcon;
718db961 247 tclass = "system";
e2417e41
DW
248 }
249
4ac08d8a 250 (void) sd_bus_creds_get_cmdline(creds, &cmdline);
5b12334d
LP
251 cl = strv_join(cmdline, " ");
252
cff789b7
ZJS
253 struct audit_info audit_info = {
254 .creds = creds,
23e9a7dd 255 .path = unit_path,
cff789b7 256 .cmdline = cl,
f0804759 257 .function = function,
cff789b7 258 };
e2417e41 259
23e9a7dd 260 r = selinux_check_access(scon, acon, tclass, permission, &audit_info);
cff789b7 261 if (r < 0) {
62f174cf 262 errno = -(r = errno_or_else(EPERM));
e2417e41 263
cff789b7 264 if (enforce)
62f174cf 265 sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access: %m");
e2417e41
DW
266 }
267
2669c666 268 log_full_errno_zerook(LOG_DEBUG, r,
f0804759 269 "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m",
8bd2f941 270 scon, acon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(unit_path), empty_to_na(cl));
cff789b7 271 return enforce ? r : 0;
6344f3e2
LP
272}
273
fd5e402f 274#else /* HAVE_SELINUX */
6344f3e2 275
963438a0 276int mac_selinux_access_check_internal(
6344f3e2 277 sd_bus_message *message,
23e9a7dd
LP
278 const char *unit_path,
279 const char *unit_label,
6344f3e2 280 const char *permission,
f0804759 281 const char *function,
6344f3e2
LP
282 sd_bus_error *error) {
283
8a188de9 284 return 0;
e2417e41 285}
6344f3e2 286
fd5e402f 287#endif /* HAVE_SELINUX */