]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[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;
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
e2417e41 60 snprintf(msgbuf, msgbufsize,
dec23413
ZJS
61 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
62 login_uid_buf, uid_buf, gid_buf,
5b12334d
LP
63 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
64 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
d67227c8 65
e2417e41
DW
66 return 0;
67}
68
17af49f2
ZJS
69static int callback_type_to_priority(int type) {
70 switch(type) {
c04754bc
LP
71
72 case SELINUX_ERROR:
73 return LOG_ERR;
74
75 case SELINUX_WARNING:
76 return LOG_WARNING;
77
78 case SELINUX_INFO:
79 return LOG_INFO;
80
17af49f2 81 case SELINUX_AVC:
c04754bc
LP
82 default:
83 return LOG_NOTICE;
17af49f2
ZJS
84 }
85}
86
e2417e41 87/*
17af49f2
ZJS
88 libselinux uses this callback when access gets denied or other
89 events happen. If audit is turned on, messages will be reported
90 using audit netlink, otherwise they will be logged using the usual
91 channels.
92
93 Code copied from dbus and modified.
e2417e41 94*/
44b601bc 95_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41 96 va_list ap;
c8a806f2 97 const char *fmt2;
e2417e41 98
349cc4a5 99#if HAVE_AUDIT
17af49f2
ZJS
100 int fd;
101
102 fd = get_audit_fd();
103
104 if (fd >= 0) {
ace188cf
LP
105 _cleanup_free_ char *buf = NULL;
106 int r;
e2417e41 107
5b12334d 108 va_start(ap, fmt);
ace188cf 109 r = vasprintf(&buf, fmt, ap);
7f1736f7 110 va_end(ap);
cad45ba1 111
ace188cf 112 if (r >= 0) {
6227fc14
MS
113 if (type == SELINUX_AVC)
114 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
115 else if (type == SELINUX_ERROR)
116 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_SELINUX_ERR, buf, NULL, NULL, NULL, 0);
117
ace188cf
LP
118 return 0;
119 }
e2417e41
DW
120 }
121#endif
5b12334d 122
c8a806f2
ZJS
123 fmt2 = strjoina("selinux: ", fmt);
124
5b12334d 125 va_start(ap, fmt);
56e577c6
LP
126
127 DISABLE_WARNING_FORMAT_NONLITERAL;
032b7541 128 log_internalv(LOG_AUTH | callback_type_to_priority(type),
62c6bbbc 129 0, PROJECT_FILE, __LINE__, __FUNCTION__,
032b7541 130 fmt2, ap);
56e577c6 131 REENABLE_WARNING;
e2417e41 132 va_end(ap);
cad45ba1 133
e2417e41
DW
134 return 0;
135}
136
6344f3e2 137static int access_init(sd_bus_error *error) {
e2417e41 138
6344f3e2
LP
139 if (!mac_selinux_use())
140 return 0;
e2417e41 141
6344f3e2
LP
142 if (initialized)
143 return 1;
cad45ba1 144
6344f3e2 145 if (avc_open(NULL, 0) != 0) {
257188f8 146 int saved_errno = errno;
3cd44590 147 bool enforce;
e2417e41 148
3cd44590 149 enforce = security_getenforce() != 0;
257188f8 150 log_full_errno(enforce ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
e2417e41 151
6344f3e2
LP
152 /* If enforcement isn't on, then let's suppress this
153 * error, and just don't do any AVC checks. The
154 * warning we printed is hence all the admin will
155 * see. */
257188f8 156 if (!enforce)
6344f3e2 157 return 0;
e2417e41 158
6344f3e2
LP
159 /* Return an access denied error, if we couldn't load
160 * the AVC but enforcing mode was on, or we couldn't
161 * determine whether it is one. */
4bbccb02 162 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror_safe(saved_errno));
6344f3e2 163 }
718db961 164
6344f3e2
LP
165 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
166 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) 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*/
8a188de9 178int mac_selinux_generic_access_check(
718db961 179 sd_bus_message *message,
cad45ba1
LP
180 const char *path,
181 const char *permission,
718db961 182 sd_bus_error *error) {
cad45ba1 183
4afd3348 184 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
22cd7aab 185 const char *tclass, *scon;
5b12334d 186 _cleanup_free_ char *cl = NULL;
cff789b7 187 _cleanup_freecon_ char *fcon = NULL;
5b12334d 188 char **cmdline = NULL;
194fe322 189 bool enforce;
718db961 190 int r = 0;
c3090674 191
cad45ba1
LP
192 assert(message);
193 assert(permission);
194 assert(error);
195
6344f3e2
LP
196 r = access_init(error);
197 if (r <= 0)
ffc227c9
LP
198 return r;
199
194fe322 200 /* delay call until we checked in `access_init()` if SELinux is actually enabled */
fd5e402f 201 enforce = mac_selinux_enforcing();
194fe322 202
5b12334d
LP
203 r = sd_bus_query_sender_creds(
204 message,
05bae4a6 205 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
5b12334d 206 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
8fd00193
LP
207 SD_BUS_CREDS_SELINUX_CONTEXT|
208 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
5b12334d
LP
209 &creds);
210 if (r < 0)
cff789b7 211 return r;
e2417e41 212
0f514420
LP
213 /* The SELinux context is something we really should have
214 * gotten directly from the message or sender, and not be an
215 * augmented field. If it was augmented we cannot use it for
216 * authorization, since this is racy and vulnerable. Let's add
217 * an extra check, just in case, even though this really
218 * shouldn't be possible. */
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
e2417e41 225 if (path) {
718db961
LP
226 /* Get the file context of the unit file */
227
cff789b7
ZJS
228 if (getfilecon_raw(path, &fcon) < 0) {
229 r = -errno;
cff789b7 230
9bf4984a 231 log_warning_errno(r, "SELinux getfilecon_raw() on '%s' failed%s (perm=%s): %m",
cff789b7
ZJS
232 path,
233 enforce ? "" : ", ignoring",
234 permission);
235 if (!enforce)
236 return 0;
237
238 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
e2417e41
DW
239 }
240
718db961 241 tclass = "service";
cff789b7 242
e2417e41 243 } else {
cff789b7
ZJS
244 if (getcon_raw(&fcon) < 0) {
245 r = -errno;
cff789b7 246
9bf4984a 247 log_warning_errno(r, "SELinux getcon_raw() failed%s (perm=%s): %m",
cff789b7
ZJS
248 enforce ? "" : ", ignoring",
249 permission);
250 if (!enforce)
251 return 0;
252
253 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
e2417e41 254 }
718db961
LP
255
256 tclass = "system";
e2417e41
DW
257 }
258
5b12334d
LP
259 sd_bus_creds_get_cmdline(creds, &cmdline);
260 cl = strv_join(cmdline, " ");
261
cff789b7
ZJS
262 struct audit_info audit_info = {
263 .creds = creds,
264 .path = path,
265 .cmdline = cl,
266 };
e2417e41 267
236f83af 268 r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
cff789b7
ZJS
269 if (r < 0) {
270 r = errno_or_else(EPERM);
e2417e41 271
cff789b7
ZJS
272 if (enforce)
273 sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41
DW
274 }
275
1b7d1cae
CG
276 log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s path=%s cmdline=%s: %m",
277 scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", path, cl);
cff789b7 278 return enforce ? r : 0;
6344f3e2
LP
279}
280
fd5e402f 281#else /* HAVE_SELINUX */
6344f3e2
LP
282
283int mac_selinux_generic_access_check(
284 sd_bus_message *message,
285 const char *path,
286 const char *permission,
287 sd_bus_error *error) {
288
8a188de9 289 return 0;
e2417e41 290}
6344f3e2 291
fd5e402f 292#endif /* HAVE_SELINUX */