1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Dan Walsh
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.
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.
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/>.
25 #include "selinux-access.h"
30 #include "dbus-unit.h"
31 #include "bus-errors.h"
32 #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 unit_methods[] =
69 "DisableUnitFiles\0" "disable\0"
70 "EnableUnitFiles\0" "enable\0"
71 "GetUnit\0" "status\0"
72 "GetUnitFileState\0" "status\0"
75 "LinkUnitFiles\0" "enable\0"
76 "MaskUnitFiles\0" "disable\0"
77 "PresetUnitFiles\0" "enable\0"
78 "ReenableUnitFiles\0" "enable\0"
79 "ReloadOrRestart\0" "start\0"
80 "ReloadOrRestartUnit\0" "start\0"
81 "ReloadOrTryRestart\0" "start\0"
82 "ReloadOrTryRestartUnit\0" "start\0"
84 "ReloadUnit\0" "reload\0"
85 "ResetFailedUnit\0" "stop\0"
87 "RestartUnit\0" "start\0"
89 "StartUnit\0" "start\0"
90 "StartUnitReplace\0" "start\0"
93 "TryRestart\0" "start\0"
94 "TryRestartUnit\0" "start\0"
95 "UnmaskUnitFiles\0" "enable\0";
97 static const char system_methods[] =
98 "ClearJobs\0" "reboot\0"
99 "CreateSnapshot\0" "status\0"
102 "FlushDevices\0" "halt\0"
104 "GetAll\0" "status\0"
105 "GetJob\0" "status\0"
106 "GetSeat\0" "status\0"
107 "GetSession\0" "status\0"
108 "GetSessionByPID\0" "status\0"
109 "GetUnitByPID\0" "status\0"
110 "GetUser\0" "status\0"
112 "Introspect\0" "status\0"
114 "KillSession\0" "halt\0"
115 "KillUser\0" "halt\0"
116 "LoadUnit\0" "reload\0"
117 "ListJobs\0" "status\0"
118 "ListSeats\0" "status\0"
119 "ListSessions\0" "status\0"
120 "ListUnits\0" "status\0"
121 "ListUnitFiles\0" "status\0"
122 "ListUsers\0" "status\0"
123 "LockSession\0" "halt\0"
124 "PowerOff\0" "halt\0"
125 "Reboot\0" "reboot\0"
126 "Reload\0" "reload\0"
127 "Reexecute\0" "reload\0"
128 "ResetFailed\0" "reload\0"
129 "Subscribe\0" "status\0"
130 "SwithcRoot\0" "reboot\0"
131 "SetEnvironment\0" "status\0"
132 "SetUserLinger\0" "halt\0"
133 "TerminateSeat\0" "halt\0"
134 "TerminateSession\0" "halt\0"
135 "TerminateUser\0" "halt\0"
136 "Unsubscribe\0" "status\0"
137 "UnsetEnvironment\0" "status\0"
138 "UnsetAndSetEnvironment\0" "status\0";
141 If the admin toggles the selinux enforcment mode this callback
142 will get called before the next access check
144 static int setenforce_callback(int enforcing)
146 selinux_enforcing = enforcing;
150 /* This mimics dbus_bus_get_unix_user() */
151 static int bus_get_selinux_security_context(
152 DBusConnection *connection,
157 DBusMessage *m = NULL, *reply = NULL;
160 m = dbus_message_new_method_call(
164 "GetConnectionSELinuxSecurityContext");
167 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
171 r = dbus_message_append_args(
173 DBUS_TYPE_STRING, &name,
177 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
181 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
187 r = dbus_set_error_from_message(error, reply);
193 r = dbus_message_get_args(
195 DBUS_TYPE_STRING, scon,
205 dbus_message_unref(m);
208 dbus_message_unref(reply);
213 /* This mimics dbus_bus_get_unix_user() */
214 static int bus_get_audit_data(
215 DBusConnection *connection,
217 struct auditstruct *audit,
223 pid = bus_get_unix_process_id(connection, name, error);
227 r = audit_loginuid_from_pid(pid, &audit->loginuid);
231 r = get_process_uid(pid, &audit->uid);
235 r = get_process_gid(pid, &audit->gid);
239 r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
247 Any time an access gets denied this callback will be called
248 with the aduit data. We then need to just copy the audit data into the msgbuf.
250 static int audit_callback(void *auditdata, security_class_t cls,
251 char *msgbuf, size_t msgbufsize)
253 struct auditstruct *audit = (struct auditstruct *) auditdata;
254 snprintf(msgbuf, msgbufsize,
255 "name=\"%s\" cmdline=\"%s\" auid=%d uid=%d gid=%d",
256 audit->path, audit->cmdline, audit->loginuid,
257 audit->uid, audit->gid);
262 Any time an access gets denied this callback will be called
263 code copied from dbus. If audit is turned on the messages will go as
264 user_avc's into the /var/log/audit/audit.log, otherwise they will be
267 static int log_callback(int type, const char *fmt, ...)
274 char buf[LINE_MAX*2];
276 vsnprintf(buf, sizeof(buf), fmt, ap);
277 audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC,
278 buf, NULL, NULL, NULL, 0);
283 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
289 Function must be called once to initialize the SELinux AVC environment.
291 If you want to cleanup memory you should need to call selinux_access_finish.
293 static int access_init(void) {
297 if (avc_open(NULL, 0)) {
298 log_full(LOG_ERR, "avc_open failed: %m\n");
302 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) &audit_callback);
303 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback);
304 selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) &setenforce_callback);
306 if ((r = security_getenforce()) >= 0) {
307 setenforce_callback(r);
315 static int selinux_init(Manager *m, DBusError *error) {
320 audit_fd = m->audit_fd;
325 if (selinux_enabled < 0)
326 selinux_enabled = is_selinux_enabled() == 1;
328 if (selinux_enabled) {
329 /* if not first time is not set, then initialize access */
332 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to initialize SELinux.");
342 static int get_audit_data(
343 DBusConnection *connection,
344 DBusMessage *message,
345 struct auditstruct *audit,
351 sender = dbus_message_get_sender(message);
353 return bus_get_audit_data(connection, sender, audit, error);
359 if (!dbus_connection_get_unix_fd(connection, &fd))
362 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
364 log_error("Failed to determine peer credentials: %m");
368 audit->uid = ucred.uid;
369 audit->gid = ucred.gid;
371 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
375 r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
384 This function returns the security context of the remote end of the dbus
385 connections. Whether it is on the bus or a local connection.
387 static int get_calling_context(
388 DBusConnection *connection,
389 DBusMessage *message,
390 security_context_t *scon,
397 If sender exists then
398 if sender is NULL this indicates a local connection. Grab the fd
399 from dbus and do an getpeercon to peers process context
401 sender = dbus_message_get_sender(message);
403 r = bus_get_selinux_security_context(connection, sender, scon, error);
408 r = dbus_connection_get_unix_fd(connection, &fd);
412 r = getpeercon(fd, scon);
421 This function returns the SELinux permission to check and whether or not the
422 check requires a unit file.
424 static void selinux_perm_lookup(const char *method, const char **perm, bool *require_unit) {
427 NULSTR_FOREACH_PAIR(m, p, unit_methods)
428 if (streq(method, m)) {
430 *require_unit = true;
434 NULSTR_FOREACH_PAIR(m, p, system_methods)
435 if (streq(method, m)) {
437 *require_unit = false;
441 *require_unit = false;
446 This function communicates with the kernel to check whether or not it should
448 If the machine is in permissive mode it will return ok. Audit messages will
449 still be generated if the access would be denied in enforcing mode.
451 static int selinux_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error, const char *perm, const char *path) {
452 security_context_t scon = NULL;
453 security_context_t fcon = NULL;
455 const char *tclass = NULL;
456 struct auditstruct audit;
458 audit.uid = audit.loginuid = (uid_t) -1;
459 audit.gid = (gid_t) -1;
460 audit.cmdline = NULL;
463 r = get_calling_context(connection, message, &scon, error);
469 /* get the file context of the unit file */
470 r = getfilecon(path, &fcon);
472 log_full(LOG_ERR, "Failed to get security context on: %s %m\n",path);
480 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to get current context, SELinux policy denies access.");
485 (void) get_audit_data(connection, message, &audit, error);
488 r = selinux_check_access(scon, fcon, tclass, perm, &audit);
491 log_error("SELinux Denied \"%s\"", audit.cmdline);
493 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
496 log_debug("SELinux checkaccess scon %s tcon %s tclass %s perm %s path %s: %d", scon, fcon, tclass, perm, path, r);
509 Clean up memory allocated in selinux_avc_init
511 void selinux_access_finish(void) {
517 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
523 r = selinux_init(m, error);
527 if (! selinux_enabled)
530 member = dbus_message_get_member(message);
532 selinux_perm_lookup(member, &perm, &require_unit);
533 log_debug("SELinux dbus-unit Look %s up perm %s require_unit %d", member, perm, require_unit);
535 r = selinux_access_check(connection, message, m, error, perm, path);
536 if (r < 0 && !selinux_enforcing) {
537 dbus_error_init(error);
544 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
551 r = selinux_init(m, error);
555 if (! selinux_enabled)
558 member = dbus_message_get_member(message);
560 selinux_perm_lookup(member, &perm, &require_unit);
561 log_debug("SELinux dbus-manager Lookup %s perm %s require_unit %d", member, perm, require_unit);
567 if (!dbus_message_get_args(
570 DBUS_TYPE_STRING, &name,
571 DBUS_TYPE_INVALID)) {
576 r = manager_load_unit(m, name, NULL, error, &u);
578 dbus_set_error(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
582 path = u->source_path ? u->source_path : u->fragment_path;
584 r = selinux_access_check(connection, message, m, error, perm, path);
587 /* if SELinux is in permissive mode return 0 */
588 if (r && (!selinux_enforcing)) {
589 dbus_error_init(error);
596 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
600 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
604 void selinux_access_finish(void) {