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);
282 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
288 Function must be called once to initialize the SELinux AVC environment.
290 If you want to cleanup memory you should need to call selinux_access_finish.
292 static int access_init(void) {
296 if (avc_open(NULL, 0)) {
297 log_full(LOG_ERR, "avc_open failed: %m\n");
301 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) &audit_callback);
302 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback);
303 selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) &setenforce_callback);
305 if ((r = security_getenforce()) >= 0) {
306 setenforce_callback(r);
314 static int selinux_init(Manager *m, DBusError *error) {
319 audit_fd = m->audit_fd;
324 if (selinux_enabled < 0)
325 selinux_enabled = is_selinux_enabled() == 1;
327 if (selinux_enabled) {
328 /* if not first time is not set, then initialize access */
331 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to initialize SELinux.");
341 static int get_audit_data(
342 DBusConnection *connection,
343 DBusMessage *message,
344 struct auditstruct *audit,
350 sender = dbus_message_get_sender(message);
352 return bus_get_audit_data(connection, sender, audit, error);
358 if (!dbus_connection_get_unix_fd(connection, &fd))
361 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
363 log_error("Failed to determine peer credentials: %m");
367 audit->uid = ucred.uid;
368 audit->gid = ucred.gid;
370 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
374 r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
383 This function returns the security context of the remote end of the dbus
384 connections. Whether it is on the bus or a local connection.
386 static int get_calling_context(
387 DBusConnection *connection,
388 DBusMessage *message,
389 security_context_t *scon,
396 If sender exists then
397 if sender is NULL this indicates a local connection. Grab the fd
398 from dbus and do an getpeercon to peers process context
400 sender = dbus_message_get_sender(message);
402 r = bus_get_selinux_security_context(connection, sender, scon, error);
407 r = dbus_connection_get_unix_fd(connection, &fd);
411 r = getpeercon(fd, scon);
420 This function returns the SELinux permission to check and whether or not the
421 check requires a unit file.
423 static void selinux_perm_lookup(const char *method, const char **perm, bool *require_unit) {
426 NULSTR_FOREACH_PAIR(m, p, unit_methods)
427 if (streq(method, m)) {
429 *require_unit = true;
433 NULSTR_FOREACH_PAIR(m, p, system_methods)
434 if (streq(method, m)) {
436 *require_unit = false;
440 *require_unit = false;
445 This function communicates with the kernel to check whether or not it should
447 If the machine is in permissive mode it will return ok. Audit messages will
448 still be generated if the access would be denied in enforcing mode.
450 static int selinux_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error, const char *perm, const char *path) {
451 security_context_t scon = NULL;
452 security_context_t fcon = NULL;
454 const char *tclass = NULL;
455 struct auditstruct audit;
457 audit.uid = audit.loginuid = (uid_t) -1;
458 audit.gid = (gid_t) -1;
459 audit.cmdline = NULL;
462 r = get_calling_context(connection, message, &scon, error);
468 /* get the file context of the unit file */
469 r = getfilecon(path, &fcon);
471 log_full(LOG_ERR, "Failed to get security context on: %s %m\n",path);
479 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to get current context, SELinux policy denies access.");
484 (void) get_audit_data(connection, message, &audit, error);
487 r = selinux_check_access(scon, fcon, tclass, perm, &audit);
490 log_error("SELinux Denied \"%s\"", audit.cmdline);
492 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
495 log_debug("SELinux checkaccess scon %s tcon %s tclass %s perm %s path %s: %d", scon, fcon, tclass, perm, path, r);
508 Clean up memory allocated in selinux_avc_init
510 void selinux_access_finish(void) {
516 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
522 r = selinux_init(m, error);
526 if (! selinux_enabled)
529 member = dbus_message_get_member(message);
531 selinux_perm_lookup(member, &perm, &require_unit);
532 log_debug("SELinux dbus-unit Look %s up perm %s require_unit %d", member, perm, require_unit);
534 r = selinux_access_check(connection, message, m, error, perm, path);
535 if (r < 0 && !selinux_enforcing) {
536 dbus_error_init(error);
543 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
550 r = selinux_init(m, error);
554 if (! selinux_enabled)
557 member = dbus_message_get_member(message);
559 selinux_perm_lookup(member, &perm, &require_unit);
560 log_debug("SELinux dbus-manager Lookup %s perm %s require_unit %d", member, perm, require_unit);
566 if (!dbus_message_get_args(
569 DBUS_TYPE_STRING, &name,
570 DBUS_TYPE_INVALID)) {
575 r = manager_load_unit(m, name, NULL, error, &u);
577 dbus_set_error(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
581 path = u->source_path ? u->source_path : u->fragment_path;
583 r = selinux_access_check(connection, message, m, error, perm, path);
586 /* if SELinux is in permissive mode return 0 */
587 if (r && (!selinux_enforcing)) {
588 dbus_error_init(error);
595 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
599 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
603 void selinux_access_finish(void) {