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"
34 #include "selinux-util.h"
39 #include <selinux/selinux.h>
40 #include <selinux/avc.h>
46 static bool initialized = false;
47 static int audit_fd = -1;
57 static int bus_get_selinux_security_context(
58 DBusConnection *connection,
63 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
65 m = dbus_message_new_method_call(
69 "GetConnectionSELinuxSecurityContext");
71 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
75 if (!dbus_message_append_args(
77 DBUS_TYPE_STRING, &name,
79 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
83 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
87 if (dbus_set_error_from_message(error, reply))
90 if (!dbus_message_get_args(
92 DBUS_TYPE_STRING, scon,
99 static int bus_get_audit_data(
100 DBusConnection *connection,
102 struct auditstruct *audit,
108 pid = bus_get_unix_process_id(connection, name, error);
112 r = audit_loginuid_from_pid(pid, &audit->loginuid);
116 r = get_process_uid(pid, &audit->uid);
120 r = get_process_gid(pid, &audit->gid);
124 r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
132 Any time an access gets denied this callback will be called
133 with the aduit data. We then need to just copy the audit data into the msgbuf.
135 static int audit_callback(
137 security_class_t cls,
141 struct auditstruct *audit = (struct auditstruct *) auditdata;
143 snprintf(msgbuf, msgbufsize,
144 "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
148 (audit->path ? " path=\"" : ""),
149 strempty(audit->path),
150 (audit->path ? "\"" : ""),
151 (audit->cmdline ? " cmdline=\"" : ""),
152 strempty(audit->cmdline),
153 (audit->cmdline ? "\"" : ""));
155 msgbuf[msgbufsize-1] = 0;
161 Any time an access gets denied this callback will be called
162 code copied from dbus. If audit is turned on the messages will go as
163 user_avc's into the /var/log/audit/audit.log, otherwise they will be
166 static int log_callback(int type, const char *fmt, ...) {
175 vsnprintf(buf, sizeof(buf), fmt, ap);
176 audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
182 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
189 Function must be called once to initialize the SELinux AVC environment.
191 If you want to cleanup memory you should need to call selinux_access_finish.
193 static int access_init(void) {
196 if (avc_open(NULL, 0)) {
197 log_error("avc_open() failed: %m");
201 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
202 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
204 if (security_getenforce() >= 0)
213 static int selinux_init(Manager *m, DBusError *error) {
217 audit_fd = m->audit_fd;
225 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
234 static int get_audit_data(
235 DBusConnection *connection,
236 DBusMessage *message,
237 struct auditstruct *audit,
245 sender = dbus_message_get_sender(message);
247 return bus_get_audit_data(connection, sender, audit, error);
249 if (!dbus_connection_get_unix_fd(connection, &fd))
252 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
254 log_error("Failed to determine peer credentials: %m");
258 audit->uid = ucred.uid;
259 audit->gid = ucred.gid;
261 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
265 r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
273 This function returns the security context of the remote end of the dbus
274 connections. Whether it is on the bus or a local connection.
276 static int get_calling_context(
277 DBusConnection *connection,
278 DBusMessage *message,
279 security_context_t *scon,
287 If sender exists then
288 if sender is NULL this indicates a local connection. Grab the fd
289 from dbus and do an getpeercon to peers process context
291 sender = dbus_message_get_sender(message);
293 r = bus_get_selinux_security_context(connection, sender, scon, error);
297 log_debug("bus_get_selinux_security_context failed %m");
300 if (!dbus_connection_get_unix_fd(connection, &fd)) {
301 log_error("bus_connection_get_unix_fd failed %m");
305 r = getpeercon(fd, scon);
307 log_error("getpeercon failed %m");
315 This function communicates with the kernel to check whether or not it should
317 If the machine is in permissive mode it will return ok. Audit messages will
318 still be generated if the access would be denied in enforcing mode.
320 static int selinux_access_check(
322 DBusConnection *connection,
323 DBusMessage *message,
325 const char *permission,
328 security_context_t scon = NULL, fcon = NULL;
330 const char *tclass = NULL;
331 struct auditstruct audit;
339 r = selinux_init(m, error);
346 log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
348 audit.uid = audit.loginuid = (uid_t) -1;
349 audit.gid = (gid_t) -1;
350 audit.cmdline = NULL;
353 r = get_calling_context(connection, message, &scon, error);
355 log_error("Failed to get caller's security context on: %m");
361 /* get the file context of the unit file */
362 r = getfilecon(path, &fcon);
364 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
366 log_error("Failed to get security context on %s: %m",path);
374 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
376 log_error("Failed to get current process context on: %m");
381 (void) get_audit_data(connection, message, &audit, error);
384 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
386 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
388 log_error("SELinux policy denies access.");
391 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);
398 if (r && security_getenforce() != 1) {
399 dbus_error_init(error);
406 int selinux_unit_access_check(
408 DBusConnection *connection,
409 DBusMessage *message,
410 const char *permission,
419 return selinux_access_check(u->manager, connection, message, u->source_path ? u->source_path : u->fragment_path, permission, error);
422 int selinux_manager_access_check(
424 DBusConnection *connection,
425 DBusMessage *message,
426 const char *permission,
435 return selinux_access_check(m, connection, message, NULL, permission, error);
438 void selinux_access_finish(void) {
448 int selinux_unit_access_check(
450 DBusConnection *connection,
451 DBusMessage *message,
452 const char *permission,
458 int selinux_manager_access_check(
460 DBusConnection *connection,
461 DBusMessage *message,
462 const char *permission,
468 void selinux_access_finish(void) {