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