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