]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
tree-wide: drop license boilerplate
[thirdparty/systemd.git] / src / core / selinux-access.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e2417e41
DW
2/***
3 This file is part of systemd.
4
5 Copyright 2012 Dan Walsh
e2417e41
DW
6***/
7
e2417e41
DW
8#include "selinux-access.h"
9
349cc4a5 10#if HAVE_SELINUX
e2417e41 11
e2417e41 12#include <errno.h>
e2417e41 13#include <selinux/avc.h>
cf0fbc49
TA
14#include <selinux/selinux.h>
15#include <stdio.h>
349cc4a5 16#if HAVE_AUDIT
e2417e41
DW
17#include <libaudit.h>
18#endif
ffc227c9 19
718db961 20#include "sd-bus.h"
15a5e950 21
b5efdb8a 22#include "alloc-util.h"
15a5e950 23#include "audit-fd.h"
718db961 24#include "bus-util.h"
ffc227c9 25#include "log.h"
15a5e950 26#include "path-util.h"
ffc227c9 27#include "selinux-util.h"
15a5e950 28#include "stdio-util.h"
5b12334d 29#include "strv.h"
15a5e950 30#include "util.h"
e2417e41 31
cad45ba1 32static bool initialized = false;
e2417e41 33
5b12334d
LP
34struct audit_info {
35 sd_bus_creds *creds;
e2417e41 36 const char *path;
5b12334d 37 const char *cmdline;
e2417e41
DW
38};
39
e2417e41
DW
40/*
41 Any time an access gets denied this callback will be called
dec23413 42 with the audit data. We then need to just copy the audit data into the msgbuf.
e2417e41 43*/
cad45ba1
LP
44static int audit_callback(
45 void *auditdata,
46 security_class_t cls,
47 char *msgbuf,
48 size_t msgbufsize) {
49
5b12334d
LP
50 const struct audit_info *audit = auditdata;
51 uid_t uid = 0, login_uid = 0;
52 gid_t gid = 0;
5ffa8c81
ZJS
53 char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
54 char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
55 char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a";
5b12334d 56
dec23413 57 if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
5ffa8c81 58 xsprintf(login_uid_buf, UID_FMT, login_uid);
05bae4a6 59 if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
5ffa8c81 60 xsprintf(uid_buf, UID_FMT, uid);
05bae4a6 61 if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
5ffa8c81 62 xsprintf(gid_buf, GID_FMT, gid);
cad45ba1 63
e2417e41 64 snprintf(msgbuf, msgbufsize,
dec23413
ZJS
65 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
66 login_uid_buf, uid_buf, gid_buf,
5b12334d
LP
67 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
68 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
d67227c8 69
e2417e41
DW
70 return 0;
71}
72
17af49f2
ZJS
73static int callback_type_to_priority(int type) {
74 switch(type) {
c04754bc
LP
75
76 case SELINUX_ERROR:
77 return LOG_ERR;
78
79 case SELINUX_WARNING:
80 return LOG_WARNING;
81
82 case SELINUX_INFO:
83 return LOG_INFO;
84
17af49f2 85 case SELINUX_AVC:
c04754bc
LP
86 default:
87 return LOG_NOTICE;
17af49f2
ZJS
88 }
89}
90
e2417e41 91/*
17af49f2
ZJS
92 libselinux uses this callback when access gets denied or other
93 events happen. If audit is turned on, messages will be reported
94 using audit netlink, otherwise they will be logged using the usual
95 channels.
96
97 Code copied from dbus and modified.
e2417e41 98*/
44b601bc 99_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41 100 va_list ap;
c8a806f2 101 const char *fmt2;
e2417e41 102
349cc4a5 103#if HAVE_AUDIT
17af49f2
ZJS
104 int fd;
105
106 fd = get_audit_fd();
107
108 if (fd >= 0) {
ace188cf
LP
109 _cleanup_free_ char *buf = NULL;
110 int r;
e2417e41 111
5b12334d 112 va_start(ap, fmt);
ace188cf 113 r = vasprintf(&buf, fmt, ap);
7f1736f7 114 va_end(ap);
cad45ba1 115
ace188cf 116 if (r >= 0) {
17af49f2 117 audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
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);
032b7541
ZJS
126#pragma GCC diagnostic push
127#pragma GCC diagnostic ignored "-Wformat-nonliteral"
128 log_internalv(LOG_AUTH | callback_type_to_priority(type),
129 0, __FILE__, __LINE__, __FUNCTION__,
130 fmt2, ap);
131#pragma GCC diagnostic pop
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
LP
145 if (avc_open(NULL, 0) != 0) {
146 int enforce, saved_errno = errno;
e2417e41 147
6344f3e2
LP
148 enforce = security_getenforce();
149 log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
e2417e41 150
6344f3e2
LP
151 /* If enforcement isn't on, then let's suppress this
152 * error, and just don't do any AVC checks. The
153 * warning we printed is hence all the admin will
154 * see. */
155 if (enforce == 0)
156 return 0;
e2417e41 157
6344f3e2
LP
158 /* Return an access denied error, if we couldn't load
159 * the AVC but enforcing mode was on, or we couldn't
160 * determine whether it is one. */
161 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror(saved_errno));
162 }
718db961 163
6344f3e2
LP
164 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
165 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) 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*/
8a188de9 177int mac_selinux_generic_access_check(
718db961 178 sd_bus_message *message,
cad45ba1
LP
179 const char *path,
180 const char *permission,
718db961 181 sd_bus_error *error) {
cad45ba1 182
4afd3348 183 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
5b12334d
LP
184 const char *tclass = NULL, *scon = NULL;
185 struct audit_info audit_info = {};
186 _cleanup_free_ char *cl = NULL;
2ed96880 187 char *fcon = NULL;
5b12334d 188 char **cmdline = NULL;
718db961 189 int r = 0;
c3090674 190
cad45ba1
LP
191 assert(message);
192 assert(permission);
193 assert(error);
194
6344f3e2
LP
195 r = access_init(error);
196 if (r <= 0)
ffc227c9
LP
197 return r;
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)
207 goto finish;
e2417e41 208
0f514420
LP
209 /* The SELinux context is something we really should have
210 * gotten directly from the message or sender, and not be an
211 * augmented field. If it was augmented we cannot use it for
212 * authorization, since this is racy and vulnerable. Let's add
213 * an extra check, just in case, even though this really
214 * shouldn't be possible. */
215 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
216
5b12334d 217 r = sd_bus_creds_get_selinux_context(creds, &scon);
718db961 218 if (r < 0)
e2417e41 219 goto finish;
cad45ba1 220
e2417e41 221 if (path) {
718db961
LP
222 /* Get the file context of the unit file */
223
24154879 224 r = getfilecon_raw(path, &fcon);
e2417e41 225 if (r < 0) {
718db961 226 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
e2417e41
DW
227 goto finish;
228 }
229
718db961 230 tclass = "service";
e2417e41 231 } else {
24154879 232 r = getcon_raw(&fcon);
e2417e41 233 if (r < 0) {
718db961 234 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
e2417e41
DW
235 goto finish;
236 }
718db961
LP
237
238 tclass = "system";
e2417e41
DW
239 }
240
5b12334d
LP
241 sd_bus_creds_get_cmdline(creds, &cmdline);
242 cl = strv_join(cmdline, " ");
243
244 audit_info.creds = creds;
245 audit_info.path = path;
246 audit_info.cmdline = cl;
e2417e41 247
236f83af 248 r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
718db961
LP
249 if (r < 0)
250 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41 251
5b12334d 252 log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r);
cad45ba1 253
e2417e41 254finish:
e2417e41
DW
255 freecon(fcon);
256
5b12334d 257 if (r < 0 && security_getenforce() != 1) {
718db961 258 sd_bus_error_free(error);
e2417e41
DW
259 r = 0;
260 }
261
262 return r;
6344f3e2
LP
263}
264
8a188de9 265#else
6344f3e2
LP
266
267int mac_selinux_generic_access_check(
268 sd_bus_message *message,
269 const char *path,
270 const char *permission,
271 sd_bus_error *error) {
272
8a188de9 273 return 0;
e2417e41 274}
6344f3e2
LP
275
276#endif