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 * 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 /* This mimics dbus_bus_get_unix_user() */
216 static int bus_get_audit_data(
217 DBusConnection *connection,
219 struct auditstruct *audit,
225 pid = bus_get_unix_process_id(connection, name, error);
229 r = audit_loginuid_from_pid(pid, &audit->loginuid);
233 r = get_process_uid(pid, &audit->uid);
237 r = get_process_gid(pid, &audit->gid);
241 r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
249 Any time an access gets denied this callback will be called
250 with the aduit data. We then need to just copy the audit data into the msgbuf.
252 static int audit_callback(void *auditdata, security_class_t cls,
253 char *msgbuf, size_t msgbufsize)
255 struct auditstruct *audit = (struct auditstruct *) auditdata;
256 snprintf(msgbuf, msgbufsize,
257 "name=\"%s\" cmdline=\"%s\" auid=%d uid=%d gid=%d",
258 audit->path, audit->cmdline, audit->loginuid,
259 audit->uid, audit->gid);
264 Any time an access gets denied this callback will be called
265 code copied from dbus. If audit is turned on the messages will go as
266 user_avc's into the /var/log/audit/audit.log, otherwise they will be
269 static int log_callback(int type, const char *fmt, ...)
276 char buf[LINE_MAX*2];
278 vsnprintf(buf, sizeof(buf), fmt, ap);
279 audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC,
280 buf, NULL, NULL, NULL, 0);
284 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
290 Function must be called once to initialize the SELinux AVC environment.
292 If you want to cleanup memory you should need to call selinux_access_finish.
294 static int access_init(void) {
298 if (avc_open(NULL, 0)) {
299 log_full(LOG_ERR, "avc_open failed: %m\n");
303 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) &audit_callback);
304 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback);
305 selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) &setenforce_callback);
307 if ((r = security_getenforce()) >= 0) {
308 setenforce_callback(r);
316 static int selinux_init(Manager *m, DBusError *error) {
321 audit_fd = m->audit_fd;
326 if (selinux_enabled < 0)
327 selinux_enabled = is_selinux_enabled() == 1;
329 if (selinux_enabled) {
330 /* if not first time is not set, then initialize access */
333 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to initialize SELinux.");
343 static int get_audit_data(
344 DBusConnection *connection,
345 DBusMessage *message,
346 struct auditstruct *audit,
352 sender = dbus_message_get_sender(message);
354 return bus_get_audit_data(connection, sender, audit, error);
360 if (!dbus_connection_get_unix_fd(connection, &fd))
363 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
365 log_error("Failed to determine peer credentials: %m");
369 audit->uid = ucred.uid;
370 audit->gid = ucred.gid;
372 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
376 r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
385 This function returns the security context of the remote end of the dbus
386 connections. Whether it is on the bus or a local connection.
388 static int get_calling_context(
389 DBusConnection *connection,
390 DBusMessage *message,
391 security_context_t *scon,
398 If sender exists then
399 if sender is NULL this indicates a local connection. Grab the fd
400 from dbus and do an getpeercon to peers process context
402 sender = dbus_message_get_sender(message);
404 r = bus_get_selinux_security_context(connection, sender, scon, error);
409 r = dbus_connection_get_unix_fd(connection, &fd);
413 r = getpeercon(fd, scon);
422 This function returns the SELinux permission to check and whether or not the
423 check requires a unit file.
425 static void selinux_perm_lookup(const char *method, const char **perm, int *require_unit)
430 for (i = 0; unit_methods[i][0]; i++) {
431 if (streq(method, unit_methods[i][0])) {
432 *perm = unit_methods[i][1];
438 if (*require_unit < 0) {
439 for (i = 0; system_methods[i][0]; i++) {
440 if (streq(method, system_methods[i][0])) {
441 *perm = system_methods[i][1];
447 if (*require_unit < 0) {
454 This function communicates with the kernel to check whether or not it should
456 If the machine is in permissive mode it will return ok. Audit messages will
457 still be generated if the access would be denied in enforcing mode.
459 static int selinux_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error, const char *perm, const char *path) {
460 security_context_t scon = NULL;
461 security_context_t fcon = NULL;
463 const char *tclass = NULL;
464 struct auditstruct audit;
466 audit.uid = audit.loginuid = (uid_t) -1;
467 audit.gid = (gid_t) -1;
468 audit.cmdline = NULL;
471 r = get_calling_context(connection, message, &scon, error);
477 /* get the file context of the unit file */
478 r = getfilecon(path, &fcon);
480 log_full(LOG_ERR, "Failed to get security context on: %s %m\n",path);
488 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "Unable to get current context, SELinux policy denies access.");
493 (void) get_audit_data(connection, message, &audit, error);
496 r = selinux_check_access(scon, fcon, tclass, perm, &audit);
499 log_error("SELinux Denied \"%s\"", audit.cmdline);
501 dbus_set_error(error, BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
504 log_debug("SELinux checkaccess scon %s tcon %s tclass %s perm %s path %s: %d", scon, fcon, tclass, perm, path, r);
517 Clean up memory allocated in selinux_avc_init
519 void selinux_access_finish(void) {
525 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
531 r = selinux_init(m, error);
535 if (! selinux_enabled)
538 member = dbus_message_get_member(message);
540 selinux_perm_lookup(member, &perm, &require_unit);
541 log_debug("SELinux dbus-unit Look %s up perm %s require_unit %d", member, perm, require_unit);
543 r = selinux_access_check(connection, message, m, error, perm, path);
544 if (r < 0 && !selinux_enforcing) {
545 dbus_error_init(error);
552 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
559 r = selinux_init(m, error);
563 if (! selinux_enabled)
566 member = dbus_message_get_member(message);
568 selinux_perm_lookup(member, &perm, &require_unit);
569 log_debug("SELinux dbus-manager Lookup %s perm %s require_unit %d", member, perm, require_unit);
575 if (!dbus_message_get_args(
578 DBUS_TYPE_STRING, &name,
579 DBUS_TYPE_INVALID)) {
584 r = manager_load_unit(m, name, NULL, error, &u);
586 dbus_set_error(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
590 path = u->source_path ? u->source_path : u->fragment_path;
592 r = selinux_access_check(connection, message, m, error, perm, path);
595 /* if SELinux is in permissive mode return 0 */
596 if (r && (!selinux_enforcing)) {
597 dbus_error_init(error);
604 int selinux_unit_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, const char *path, DBusError *error) {
608 int selinux_manager_access_check(DBusConnection *connection, DBusMessage *message, Manager *m, DBusError *error) {
612 void selinux_access_finish(void) {