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