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"
40 #include <selinux/selinux.h>
41 #include <selinux/avc.h>
47 static bool initialized = false;
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, ...) {
172 if (get_audit_fd() >= 0) {
175 vsnprintf(buf, sizeof(buf), fmt, ap);
176 audit_log_user_avc_message(get_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(DBusError *error) {
222 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
231 static int get_audit_data(
232 DBusConnection *connection,
233 DBusMessage *message,
234 struct auditstruct *audit,
242 sender = dbus_message_get_sender(message);
244 return bus_get_audit_data(connection, sender, audit, error);
246 if (!dbus_connection_get_unix_fd(connection, &fd))
249 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
251 log_error("Failed to determine peer credentials: %m");
255 audit->uid = ucred.uid;
256 audit->gid = ucred.gid;
258 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
262 r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
270 This function returns the security context of the remote end of the dbus
271 connections. Whether it is on the bus or a local connection.
273 static int get_calling_context(
274 DBusConnection *connection,
275 DBusMessage *message,
276 security_context_t *scon,
284 If sender exists then
285 if sender is NULL this indicates a local connection. Grab the fd
286 from dbus and do an getpeercon to peers process context
288 sender = dbus_message_get_sender(message);
290 r = bus_get_selinux_security_context(connection, sender, scon, error);
294 log_debug("bus_get_selinux_security_context failed %m");
297 if (!dbus_connection_get_unix_fd(connection, &fd)) {
298 log_error("bus_connection_get_unix_fd failed %m");
302 r = getpeercon(fd, scon);
304 log_error("getpeercon failed %m");
312 This function communicates with the kernel to check whether or not it should
314 If the machine is in permissive mode it will return ok. Audit messages will
315 still be generated if the access would be denied in enforcing mode.
317 static int selinux_access_check(
318 DBusConnection *connection,
319 DBusMessage *message,
321 const char *permission,
324 security_context_t scon = NULL, fcon = NULL;
326 const char *tclass = NULL;
327 struct auditstruct audit;
334 r = selinux_init(error);
341 log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
343 audit.uid = audit.loginuid = (uid_t) -1;
344 audit.gid = (gid_t) -1;
345 audit.cmdline = NULL;
348 r = get_calling_context(connection, message, &scon, error);
350 log_error("Failed to get caller's security context on: %m");
356 /* get the file context of the unit file */
357 r = getfilecon(path, &fcon);
359 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
361 log_error("Failed to get security context on %s: %m",path);
369 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
371 log_error("Failed to get current process context on: %m");
376 (void) get_audit_data(connection, message, &audit, error);
379 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
381 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
383 log_error("SELinux policy denies access.");
386 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);
393 if (r && security_getenforce() != 1) {
394 dbus_error_init(error);
401 int selinux_unit_access_check(
403 DBusConnection *connection,
404 DBusMessage *message,
405 const char *permission,
414 return selinux_access_check(connection, message, u->source_path ? u->source_path : u->fragment_path, permission, error);
417 int selinux_manager_access_check(
419 DBusConnection *connection,
420 DBusMessage *message,
421 const char *permission,
430 return selinux_access_check(connection, message, NULL, permission, error);
433 void selinux_access_finish(void) {
443 int selinux_unit_access_check(
445 DBusConnection *connection,
446 DBusMessage *message,
447 const char *permission,
453 int selinux_manager_access_check(
455 DBusConnection *connection,
456 DBusMessage *message,
457 const char *permission,
463 void selinux_access_finish(void) {