]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
Merge pull request #13905 from poettering/cpuset-fixes
[thirdparty/systemd.git] / src / core / selinux-access.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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
TA
9#include <selinux/selinux.h>
10#include <stdio.h>
349cc4a5 11#if HAVE_AUDIT
e2417e41
DW
12#include <libaudit.h>
13#endif
ffc227c9 14
718db961 15#include "sd-bus.h"
15a5e950 16
b5efdb8a 17#include "alloc-util.h"
15a5e950 18#include "audit-fd.h"
718db961 19#include "bus-util.h"
4bbccb02 20#include "errno-util.h"
ca78ad1d 21#include "format-util.h"
ffc227c9 22#include "log.h"
15a5e950 23#include "path-util.h"
ffc227c9 24#include "selinux-util.h"
15a5e950 25#include "stdio-util.h"
5b12334d 26#include "strv.h"
15a5e950 27#include "util.h"
e2417e41 28
cad45ba1 29static bool initialized = false;
e2417e41 30
5b12334d
LP
31struct audit_info {
32 sd_bus_creds *creds;
e2417e41 33 const char *path;
5b12334d 34 const char *cmdline;
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
e2417e41 61 snprintf(msgbuf, msgbufsize,
dec23413
ZJS
62 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
63 login_uid_buf, uid_buf, gid_buf,
5b12334d
LP
64 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
65 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
d67227c8 66
e2417e41
DW
67 return 0;
68}
69
17af49f2
ZJS
70static int callback_type_to_priority(int type) {
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
MS
114 if (type == SELINUX_AVC)
115 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
116 else if (type == SELINUX_ERROR)
117 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_SELINUX_ERR, buf, NULL, NULL, NULL, 0);
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);
032b7541
ZJS
127#pragma GCC diagnostic push
128#pragma GCC diagnostic ignored "-Wformat-nonliteral"
129 log_internalv(LOG_AUTH | callback_type_to_priority(type),
62c6bbbc 130 0, PROJECT_FILE, __LINE__, __FUNCTION__,
032b7541
ZJS
131 fmt2, ap);
132#pragma GCC diagnostic pop
e2417e41 133 va_end(ap);
cad45ba1 134
e2417e41
DW
135 return 0;
136}
137
6344f3e2 138static int access_init(sd_bus_error *error) {
e2417e41 139
6344f3e2
LP
140 if (!mac_selinux_use())
141 return 0;
e2417e41 142
6344f3e2
LP
143 if (initialized)
144 return 1;
cad45ba1 145
6344f3e2
LP
146 if (avc_open(NULL, 0) != 0) {
147 int enforce, saved_errno = errno;
e2417e41 148
6344f3e2
LP
149 enforce = security_getenforce();
150 log_full_errno(enforce != 0 ? 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. */
156 if (enforce == 0)
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;
5b12334d
LP
185 const char *tclass = NULL, *scon = NULL;
186 struct audit_info audit_info = {};
187 _cleanup_free_ char *cl = NULL;
2ed96880 188 char *fcon = NULL;
5b12334d 189 char **cmdline = NULL;
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
5b12334d
LP
200 r = sd_bus_query_sender_creds(
201 message,
05bae4a6 202 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
5b12334d 203 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
8fd00193
LP
204 SD_BUS_CREDS_SELINUX_CONTEXT|
205 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
5b12334d
LP
206 &creds);
207 if (r < 0)
208 goto finish;
e2417e41 209
0f514420
LP
210 /* The SELinux context is something we really should have
211 * gotten directly from the message or sender, and not be an
212 * augmented field. If it was augmented we cannot use it for
213 * authorization, since this is racy and vulnerable. Let's add
214 * an extra check, just in case, even though this really
215 * shouldn't be possible. */
216 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
217
5b12334d 218 r = sd_bus_creds_get_selinux_context(creds, &scon);
718db961 219 if (r < 0)
e2417e41 220 goto finish;
cad45ba1 221
e2417e41 222 if (path) {
718db961
LP
223 /* Get the file context of the unit file */
224
24154879 225 r = getfilecon_raw(path, &fcon);
e2417e41 226 if (r < 0) {
718db961 227 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
e2417e41
DW
228 goto finish;
229 }
230
718db961 231 tclass = "service";
e2417e41 232 } else {
24154879 233 r = getcon_raw(&fcon);
e2417e41 234 if (r < 0) {
718db961 235 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
e2417e41
DW
236 goto finish;
237 }
718db961
LP
238
239 tclass = "system";
e2417e41
DW
240 }
241
5b12334d
LP
242 sd_bus_creds_get_cmdline(creds, &cmdline);
243 cl = strv_join(cmdline, " ");
244
245 audit_info.creds = creds;
246 audit_info.path = path;
247 audit_info.cmdline = cl;
e2417e41 248
236f83af 249 r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
718db961
LP
250 if (r < 0)
251 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41 252
5b12334d 253 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 254
e2417e41 255finish:
e2417e41
DW
256 freecon(fcon);
257
5b12334d 258 if (r < 0 && security_getenforce() != 1) {
718db961 259 sd_bus_error_free(error);
e2417e41
DW
260 r = 0;
261 }
262
263 return r;
6344f3e2
LP
264}
265
8a188de9 266#else
6344f3e2
LP
267
268int mac_selinux_generic_access_check(
269 sd_bus_message *message,
270 const char *path,
271 const char *permission,
272 sd_bus_error *error) {
273
8a188de9 274 return 0;
e2417e41 275}
6344f3e2
LP
276
277#endif