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