2 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5 This file is part of systemd.
7 Copyright 2012 Dan Walsh
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "selinux-access.h"
31 #include "dbus-unit.h"
32 #include "bus-errors.h"
33 #include "dbus-common.h"
38 #include <selinux/selinux.h>
39 #include <selinux/avc.h>
45 /* FD to send audit messages to */
46 static int audit_fd = -1;
47 static int selinux_enabled = -1;
48 static int first_time = 1;
49 static int selinux_enforcing = 0;
60 Define a mapping between the systemd method calls and the SELinux access to check.
61 We define two tables, one for access checks on unit files, and one for
62 access checks for the system in general.
64 If we do not find a match in either table, then the "undefined" system
68 static const char * const unit_methods[][2] = {{ "DisableUnitFiles", "disable" },
69 { "EnableUnitFiles", "enable" },
70 { "GetUnit", "status" },
71 { "GetUnitFileState", "status" },
73 { "KillUnit", "stop" },
74 { "LinkUnitFiles", "enable" },
75 { "MaskUnitFiles", "disable" },
76 { "PresetUnitFiles", "enable" },
77 { "ReenableUnitFiles", "enable" },
78 { "Reexecute", "start" },
79 { "ReloadOrRestart", "start" },
80 { "ReloadOrRestartUnit", "start" },
81 { "ReloadOrTryRestart", "start" },
82 { "ReloadOrTryRestartUnit", "start" },
83 { "ReloadUnit", "reload" },
84 { "ResetFailedUnit", "stop" },
85 { "Restart", "start" },
86 { "RestartUnit", "start" },
88 { "StartUnit", "start" },
89 { "StartUnitReplace", "start" },
91 { "StopUnit", "stop" },
92 { "TryRestart", "start" },
93 { "TryRestartUnit", "start" },
94 { "UnmaskUnitFiles", "enable" },
98 static const char * const system_methods[][2] = { { "ClearJobs", "reboot" },
99 { "CreateSnapshot", "status" },
100 { "Dump", "status" },
102 { "FlushDevices", "halt" },
104 { "GetAll", "status" },
105 { "GetJob", "status" },
106 { "GetSeat", "status" },
107 { "GetSession", "status" },
108 { "GetSessionByPID", "status" },
109 { "GetUnitByPID", "status" },
110 { "GetUser", "status" },
112 { "Introspect", "status" },
113 { "KExec", "reboot" },
114 { "KillSession", "halt" },
115 { "KillUser", "halt" },
116 { "LoadUnit", "reload" },
117 { "ListJobs", "status" },
118 { "ListSeats", "status" },
119 { "ListSessions", "status" },
120 { "ListUnits", "status" },
121 { "ListUnitFiles", "status" },
122 { "ListUsers", "status" },
123 { "LockSession", "halt" },
124 { "PowerOff", "halt" },
125 { "Reboot", "reboot" },
126 { "Reload", "reload" },
127 { "Reexecute", "reload" },
128 { "ResetFailed", "reload" },
129 { "Subscribe", "status" },
130 { "SwithcRoot", "reboot" },
131 { "SetEnvironment", "status" },
132 { "SetUserLinger", "halt" },
133 { "TerminateSeat", "halt" },
134 { "TerminateSession", "halt" },
135 { "TerminateUser", "halt" },
136 { "Unsubscribe", "status" },
137 { "UnsetEnvironment", "status" },
138 { "UnsetAndSetEnvironment", "status" },
143 If the admin toggles the selinux enforcment mode this callback
144 will get called before the next access check
146 static int setenforce_callback(int enforcing)
148 selinux_enforcing = enforcing;
152 /* This mimics dbus_bus_get_unix_user() */
153 static int bus_get_selinux_security_context(
154 DBusConnection *connection,
159 DBusMessage *m = NULL, *reply = NULL;
162 m = dbus_message_new_method_call(
166 "GetConnectionSELinuxSecurityContext");
169 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
173 r = dbus_message_append_args(
175 DBUS_TYPE_STRING, &name,
179 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
183 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
189 r = dbus_set_error_from_message(error, reply);
195 r = dbus_message_get_args(
197 DBUS_TYPE_STRING, scon,
207 dbus_message_unref(m);
210 dbus_message_unref(reply);
215 static int get_cmdline(pid_t pid, char **cmdline) {
221 snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid);
222 f = fopen(buf, "re");
226 count = fread(buf, 1, sizeof(buf), f);
231 for (n = 0; n < count - 1; n++)
236 (*cmdline) = strdup(buf);
243 static int get_pid_id(pid_t pid, const char *file, uid_t *id) {
247 snprintf(buf, sizeof(buf), "/proc/%lu/%s", (unsigned long) pid, file);
248 f = fopen(buf, "re");
258 /* This mimics dbus_bus_get_unix_user() */
259 static int bus_get_audit_data(
260 DBusConnection *connection,
262 struct auditstruct *audit,
266 DBusMessage *m = NULL, *reply = NULL;
269 m = dbus_message_new_method_call(
273 "GetConnectionUnixProcessID");
276 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
280 r = dbus_message_append_args(
282 DBUS_TYPE_STRING, &name,
286 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
290 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
296 r = dbus_set_error_from_message(error, reply);
302 r = dbus_message_get_args(
304 DBUS_TYPE_UINT32, &pid,
311 r = get_pid_id(pid, "loginuid", &(audit->loginuid));
315 r = get_pid_id(pid, "uid", &(audit->uid));
319 r = get_pid_id(pid, "gid", &(audit->gid));
323 r = get_cmdline(pid, &(audit->cmdline));
330 dbus_message_unref(m);
332 dbus_message_unref(reply);
337 Any time an access gets denied this callback will be called
338 with the aduit data. We then need to just copy the audit data into the msgbuf.
340 static int audit_callback(void *auditdata, security_class_t cls,
341 char *msgbuf, size_t msgbufsize)
343 struct auditstruct *audit = (struct auditstruct *) auditdata;
344 snprintf(msgbuf, msgbufsize,
345 "name=\"%s\" cmdline=\"%s\" auid=%d uid=%d gid=%d",
346 audit->path, audit->cmdline, audit->loginuid,
347 audit->uid, audit->gid);
352 Any time an access gets denied this callback will be called
353 code copied from dbus. If audit is turned on the messages will go as
354 user_avc's into the /var/log/audit/audit.log, otherwise they will be
357 static int log_callback(int type, const char *fmt, ...)
364 char buf[LINE_MAX*2];
366 vsnprintf(buf, sizeof(buf), fmt, ap);
367 audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC,
368 buf, NULL, NULL, NULL, 0);
372 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
378 Function must be called once to initialize the SELinux AVC environment.
380 If you want to cleanup memory you should need to call selinux_access_finish.
382 static int access_init(void) {
386 if (avc_open(NULL, 0)) {
387 log_full(LOG_ERR, "avc_open failed: %m\n");
391 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) &audit_callback);
392 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback);
393 selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) &setenforce_callback);
395 if ((r = security_getenforce()) >= 0) {
396 setenforce_callback(r);
404 static int selinux_init(Manager *m, DBusError *error) {
409 audit_fd = m->audit_fd;
414 if (selinux_enabled < 0)
415 selinux_enabled = is_selinux_enabled() == 1;
417 if (selinux_enabled) {
418 /* if not first time is not set, then initialize access */
421 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to initialize SELinux.");
431 static int get_audit_data(
432 DBusConnection *connection,
433 DBusMessage *message,
434 struct auditstruct *audit,
440 sender = dbus_message_get_sender(message);
442 r = bus_get_audit_data(
453 r = dbus_connection_get_unix_fd(connection, &fd);
459 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
462 log_error("Failed to determine peer credentials: %m");
465 audit->uid = ucred.uid;
466 audit->gid = ucred.gid;
468 r = get_pid_id(ucred.pid, "loginuid", &(audit->loginuid));
472 r = get_cmdline(ucred.pid, &(audit->cmdline));
484 This function returns the security context of the remote end of the dbus
485 connections. Whether it is on the bus or a local connection.
487 static int get_calling_context(
488 DBusConnection *connection,
489 DBusMessage *message,
490 security_context_t *scon,
497 If sender exists then
498 if sender is NULL this indicates a local connection. Grab the fd
499 from dbus and do an getpeercon to peers process context
501 sender = dbus_message_get_sender(message);
503 r = bus_get_selinux_security_context(connection, sender, scon, error);
508 r = dbus_connection_get_unix_fd(connection, &fd);
512 r = getpeercon(fd, scon);
521 This function returns the SELinux permission to check and whether or not the
522 check requires a unit file.
524 static void selinux_perm_lookup(const char *method, const char **perm, int *require_unit)
529 for (i = 0; unit_methods[i][0]; i++) {
530 if (streq(method, unit_methods[i][0])) {
531 *perm = unit_methods[i][1];
537 if (*require_unit < 0) {
538 for (i = 0; system_methods[i][0]; i++) {
539 if (streq(method, system_methods[i][0])) {
540 *perm = system_methods[i][1];
546 if (*require_unit < 0) {
553 This function communicates with the kernel to check whether or not it should
555 If the machine is in permissive mode it will return ok. Audit messages will
556 still be generated if the access would be denied in enforcing mode.
558 static int selinux_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error, const char *perm, const char *path) {
559 security_context_t scon = NULL;
560 security_context_t fcon = NULL;
562 const char *tclass = NULL;
563 struct auditstruct audit;
564 audit.uid = audit.loginuid = audit.gid = -1;
565 audit.cmdline = NULL;
568 r = get_calling_context(connection, message, &scon, error);
574 /* get the file context of the unit file */
575 r = getfilecon(path, &fcon);
577 log_full(LOG_ERR, "Failed to get security context on: %s %m\n",path);
585 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to get current context, SELinux policy denies access.");
590 (void) get_audit_data(connection, message, &audit, error);
593 r = selinux_check_access(scon, fcon, tclass, perm, &audit);
596 log_error("SELinux Denied \"%s\"", audit.cmdline);
598 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
601 log_debug("SELinux checkaccess scon %s tcon %s tclass %s perm %s path %s: %d", scon, fcon, tclass, perm, path, r);
614 Clean up memory allocated in selinux_avc_init
616 void selinux_access_finish(void) {
622 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
625 const char *member = dbus_message_get_member(message);
628 r = selinux_init(m, error);
632 if (! selinux_enabled)
635 selinux_perm_lookup(member, &perm, &require_unit);
636 log_debug("SELinux dbus-unit Look %s up perm %s require_unit %d", member, perm, require_unit);
638 r = selinux_access_check(connection, message, m, error, perm, path);
639 if ((r < 0) && (!selinux_enforcing)) {
640 dbus_error_init(error);
647 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
654 r = selinux_init(m, error);
658 if (! selinux_enabled)
661 member = dbus_message_get_member(message);
663 selinux_perm_lookup(member, &perm, &require_unit);
664 log_debug("SELinux dbus-manager Lookup %s perm %s require_unit %d", member, perm, require_unit);
670 r = dbus_message_get_args(
673 DBUS_TYPE_STRING, &name,
678 u = manager_get_unit(m, name);
680 if ((r = manager_load_unit(m, name, NULL, error, &u)) < 0) {
682 dbus_set_error(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
687 path = (u->fragment_path ? u->fragment_path: u->source_path);
689 r = selinux_access_check(connection, message, m, error, perm, path);
692 /* if SELinux is in permissive mode return 0 */
693 if (r && (!selinux_enforcing)) {
694 dbus_error_init(error);
701 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
705 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
709 void selinux_access_finish(void) {}