]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
man: also mention /run/log/journal in systemd-jouranld.service(8)
[thirdparty/systemd.git] / src / core / selinux-access.c
CommitLineData
e2417e41
DW
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Dan Walsh
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
e2417e41
DW
22#include "selinux-access.h"
23
24#ifdef HAVE_SELINUX
e2417e41
DW
25
26#include <stdio.h>
27#include <string.h>
28#include <errno.h>
ffc227c9 29#include <limits.h>
e2417e41
DW
30#include <selinux/selinux.h>
31#include <selinux/avc.h>
32#ifdef HAVE_AUDIT
33#include <libaudit.h>
34#endif
ffc227c9
LP
35#include <dbus.h>
36
37#include "util.h"
38#include "log.h"
39#include "bus-errors.h"
40#include "dbus-common.h"
41#include "audit.h"
42#include "selinux-util.h"
43#include "audit-fd.h"
e2417e41 44
cad45ba1 45static bool initialized = false;
e2417e41
DW
46
47struct auditstruct {
48 const char *path;
49 char *cmdline;
50 uid_t loginuid;
51 uid_t uid;
52 gid_t gid;
53};
54
e2417e41
DW
55static int bus_get_selinux_security_context(
56 DBusConnection *connection,
57 const char *name,
58 char **scon,
59 DBusError *error) {
60
cad45ba1 61 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
a33c48d8
DW
62 DBusMessageIter iter, sub;
63 const char *bytes;
64 char *b;
65 int nbytes;
e2417e41
DW
66
67 m = dbus_message_new_method_call(
68 DBUS_SERVICE_DBUS,
69 DBUS_PATH_DBUS,
70 DBUS_INTERFACE_DBUS,
71 "GetConnectionSELinuxSecurityContext");
72 if (!m) {
e2417e41 73 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
cad45ba1 74 return -ENOMEM;
e2417e41
DW
75 }
76
cad45ba1
LP
77 if (!dbus_message_append_args(
78 m,
79 DBUS_TYPE_STRING, &name,
80 DBUS_TYPE_INVALID)) {
e2417e41 81 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
cad45ba1 82 return -ENOMEM;
e2417e41
DW
83 }
84
85 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
cad45ba1
LP
86 if (!reply)
87 return -EIO;
e2417e41 88
cad45ba1
LP
89 if (dbus_set_error_from_message(error, reply))
90 return -EIO;
e2417e41 91
a33c48d8 92 if (!dbus_message_iter_init(reply, &iter))
cad45ba1 93 return -EIO;
e2417e41 94
a33c48d8
DW
95 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
96 return -EIO;
97
98 dbus_message_iter_recurse(&iter, &sub);
99 dbus_message_iter_get_fixed_array(&sub, &bytes, &nbytes);
100
101 b = strndup(bytes, nbytes);
102 if (!b)
103 return -ENOMEM;
104
105 *scon = b;
106
cad45ba1 107 return 0;
e2417e41
DW
108}
109
e2417e41
DW
110static int bus_get_audit_data(
111 DBusConnection *connection,
112 const char *name,
113 struct auditstruct *audit,
114 DBusError *error) {
115
116 pid_t pid;
c3090674 117 int r;
e2417e41 118
c3090674
LP
119 pid = bus_get_unix_process_id(connection, name, error);
120 if (pid <= 0)
cad45ba1 121 return -EIO;
e2417e41 122
c3090674
LP
123 r = audit_loginuid_from_pid(pid, &audit->loginuid);
124 if (r < 0)
125 return r;
e2417e41 126
c3090674
LP
127 r = get_process_uid(pid, &audit->uid);
128 if (r < 0)
129 return r;
e2417e41 130
c3090674
LP
131 r = get_process_gid(pid, &audit->gid);
132 if (r < 0)
133 return r;
e2417e41 134
9bdbc2e2 135 r = get_process_cmdline(pid, 0, true, &audit->cmdline);
c3090674
LP
136 if (r < 0)
137 return r;
e2417e41 138
c3090674 139 return 0;
e2417e41
DW
140}
141
142/*
143 Any time an access gets denied this callback will be called
144 with the aduit data. We then need to just copy the audit data into the msgbuf.
145*/
cad45ba1
LP
146static int audit_callback(
147 void *auditdata,
148 security_class_t cls,
149 char *msgbuf,
150 size_t msgbufsize) {
151
e2417e41 152 struct auditstruct *audit = (struct auditstruct *) auditdata;
cad45ba1 153
e2417e41 154 snprintf(msgbuf, msgbufsize,
cad45ba1 155 "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
d67227c8 156 audit->loginuid,
cad45ba1
LP
157 audit->uid,
158 audit->gid,
159 (audit->path ? " path=\"" : ""),
160 strempty(audit->path),
161 (audit->path ? "\"" : ""),
162 (audit->cmdline ? " cmdline=\"" : ""),
163 strempty(audit->cmdline),
164 (audit->cmdline ? "\"" : ""));
d67227c8 165
cad45ba1 166 msgbuf[msgbufsize-1] = 0;
d67227c8 167
e2417e41
DW
168 return 0;
169}
170
171/*
172 Any time an access gets denied this callback will be called
173 code copied from dbus. If audit is turned on the messages will go as
174 user_avc's into the /var/log/audit/audit.log, otherwise they will be
175 sent to syslog.
176*/
44a6b1b6 177_printf_attr_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41
DW
178 va_list ap;
179
180 va_start(ap, fmt);
cad45ba1 181
e2417e41 182#ifdef HAVE_AUDIT
c1165f82 183 if (get_audit_fd() >= 0) {
ace188cf
LP
184 _cleanup_free_ char *buf = NULL;
185 int r;
e2417e41 186
ace188cf 187 r = vasprintf(&buf, fmt, ap);
7f1736f7 188 va_end(ap);
cad45ba1 189
ace188cf
LP
190 if (r >= 0) {
191 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
192 return 0;
193 }
194
195 va_start(ap, fmt);
e2417e41
DW
196 }
197#endif
198 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
199 va_end(ap);
cad45ba1 200
e2417e41
DW
201 return 0;
202}
203
204/*
205 Function must be called once to initialize the SELinux AVC environment.
206 Sets up callbacks.
207 If you want to cleanup memory you should need to call selinux_access_finish.
208*/
209static int access_init(void) {
cad45ba1 210 int r;
e2417e41
DW
211
212 if (avc_open(NULL, 0)) {
cad45ba1 213 log_error("avc_open() failed: %m");
e2417e41
DW
214 return -errno;
215 }
216
cad45ba1
LP
217 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
218 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
e2417e41 219
cad45ba1 220 if (security_getenforce() >= 0)
e2417e41 221 return 0;
cad45ba1 222
e2417e41
DW
223 r = -errno;
224 avc_destroy();
cad45ba1 225
e2417e41
DW
226 return r;
227}
228
ffc227c9 229static int selinux_access_init(DBusError *error) {
e2417e41
DW
230 int r;
231
cad45ba1 232 if (initialized)
e2417e41
DW
233 return 0;
234
cad45ba1 235 if (use_selinux()) {
e2417e41
DW
236 r = access_init();
237 if (r < 0) {
d67227c8 238 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
e2417e41
DW
239 return r;
240 }
e2417e41
DW
241 }
242
cad45ba1 243 initialized = true;
e2417e41
DW
244 return 0;
245}
246
ffc227c9
LP
247void selinux_access_free(void) {
248 if (!initialized)
249 return;
250
251 avc_destroy();
252 initialized = false;
253}
254
e2417e41 255static int get_audit_data(
cad45ba1
LP
256 DBusConnection *connection,
257 DBusMessage *message,
258 struct auditstruct *audit,
259 DBusError *error) {
e2417e41
DW
260
261 const char *sender;
cad45ba1
LP
262 int r, fd;
263 struct ucred ucred;
0b9cc004 264 socklen_t len = sizeof(ucred);
e2417e41
DW
265
266 sender = dbus_message_get_sender(message);
c3090674
LP
267 if (sender)
268 return bus_get_audit_data(connection, sender, audit, error);
c3090674 269
cad45ba1
LP
270 if (!dbus_connection_get_unix_fd(connection, &fd))
271 return -EINVAL;
e2417e41 272
cad45ba1
LP
273 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
274 if (r < 0) {
275 log_error("Failed to determine peer credentials: %m");
276 return -errno;
277 }
c3090674 278
cad45ba1
LP
279 audit->uid = ucred.uid;
280 audit->gid = ucred.gid;
e2417e41 281
cad45ba1
LP
282 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
283 if (r < 0)
284 return r;
e2417e41 285
9bdbc2e2 286 r = get_process_cmdline(ucred.pid, 0, true, &audit->cmdline);
cad45ba1
LP
287 if (r < 0)
288 return r;
e2417e41 289
cad45ba1 290 return 0;
e2417e41
DW
291}
292
293/*
294 This function returns the security context of the remote end of the dbus
295 connections. Whether it is on the bus or a local connection.
296*/
297static int get_calling_context(
cad45ba1
LP
298 DBusConnection *connection,
299 DBusMessage *message,
300 security_context_t *scon,
301 DBusError *error) {
e2417e41
DW
302
303 const char *sender;
304 int r;
d67227c8 305 int fd;
e2417e41
DW
306
307 /*
308 If sender exists then
309 if sender is NULL this indicates a local connection. Grab the fd
310 from dbus and do an getpeercon to peers process context
311 */
312 sender = dbus_message_get_sender(message);
313 if (sender) {
314 r = bus_get_selinux_security_context(connection, sender, scon, error);
cad45ba1
LP
315 if (r >= 0)
316 return r;
e2417e41 317
23635a85 318 log_error("bus_get_selinux_security_context failed: %m");
a33c48d8 319 return r;
d67227c8
DW
320 }
321
cad45ba1 322 if (!dbus_connection_get_unix_fd(connection, &fd)) {
d67227c8
DW
323 log_error("bus_connection_get_unix_fd failed %m");
324 return -EINVAL;
325 }
326
327 r = getpeercon(fd, scon);
328 if (r < 0) {
329 log_error("getpeercon failed %m");
330 return -errno;
e2417e41
DW
331 }
332
333 return 0;
334}
335
e2417e41
DW
336/*
337 This function communicates with the kernel to check whether or not it should
338 allow the access.
339 If the machine is in permissive mode it will return ok. Audit messages will
340 still be generated if the access would be denied in enforcing mode.
341*/
ffc227c9 342int selinux_access_check(
cad45ba1
LP
343 DBusConnection *connection,
344 DBusMessage *message,
345 const char *path,
346 const char *permission,
347 DBusError *error) {
348
349 security_context_t scon = NULL, fcon = NULL;
e2417e41
DW
350 int r = 0;
351 const char *tclass = NULL;
352 struct auditstruct audit;
c3090674 353
cad45ba1
LP
354 assert(connection);
355 assert(message);
356 assert(permission);
357 assert(error);
358
cad45ba1
LP
359 if (!use_selinux())
360 return 0;
361
ffc227c9
LP
362 r = selinux_access_init(error);
363 if (r < 0)
364 return r;
365
c3090674
LP
366 audit.uid = audit.loginuid = (uid_t) -1;
367 audit.gid = (gid_t) -1;
e2417e41
DW
368 audit.cmdline = NULL;
369 audit.path = path;
370
371 r = get_calling_context(connection, message, &scon, error);
cad45ba1 372 if (r < 0) {
d67227c8 373 log_error("Failed to get caller's security context on: %m");
e2417e41 374 goto finish;
d67227c8 375 }
cad45ba1 376
e2417e41
DW
377 if (path) {
378 tclass = "service";
379 /* get the file context of the unit file */
380 r = getfilecon(path, &fcon);
381 if (r < 0) {
d67227c8
DW
382 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
383 r = -errno;
cad45ba1 384 log_error("Failed to get security context on %s: %m",path);
e2417e41
DW
385 goto finish;
386 }
387
388 } else {
389 tclass = "system";
390 r = getcon(&fcon);
391 if (r < 0) {
d67227c8
DW
392 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
393 r = -errno;
394 log_error("Failed to get current process context on: %m");
e2417e41
DW
395 goto finish;
396 }
397 }
398
399 (void) get_audit_data(connection, message, &audit, error);
400
cad45ba1
LP
401 errno = 0;
402 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
c3090674 403 if (r < 0) {
cad45ba1 404 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41 405 r = -errno;
d67227c8 406 log_error("SELinux policy denies access.");
e2417e41
DW
407 }
408
cad45ba1
LP
409 log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, audit.cmdline, r);
410
e2417e41 411finish:
e2417e41
DW
412 free(audit.cmdline);
413 freecon(scon);
414 freecon(fcon);
415
cad45ba1 416 if (r && security_getenforce() != 1) {
e2417e41
DW
417 dbus_error_init(error);
418 r = 0;
419 }
420
421 return r;
422}
423
e2417e41 424#else
cad45ba1 425
ffc227c9 426int selinux_access_check(
cad45ba1
LP
427 DBusConnection *connection,
428 DBusMessage *message,
ffc227c9 429 const char *path,
cad45ba1
LP
430 const char *permission,
431 DBusError *error) {
432
e2417e41
DW
433 return 0;
434}
435
ffc227c9 436void selinux_access_free(void) {
c3090674 437}
cad45ba1 438
e2417e41 439#endif