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/>.
22 #include "selinux-access.h"
30 #include <selinux/selinux.h>
31 #include <selinux/avc.h>
39 #include "bus-errors.h"
40 #include "dbus-common.h"
42 #include "selinux-util.h"
45 static bool initialized = false;
55 static int bus_get_selinux_security_context(
56 DBusConnection *connection,
61 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
63 m = dbus_message_new_method_call(
67 "GetConnectionSELinuxSecurityContext");
69 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
73 if (!dbus_message_append_args(
75 DBUS_TYPE_STRING, &name,
77 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
81 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
85 if (dbus_set_error_from_message(error, reply))
88 if (!dbus_message_get_args(
90 DBUS_TYPE_STRING, scon,
97 static int bus_get_audit_data(
98 DBusConnection *connection,
100 struct auditstruct *audit,
106 pid = bus_get_unix_process_id(connection, name, error);
110 r = audit_loginuid_from_pid(pid, &audit->loginuid);
114 r = get_process_uid(pid, &audit->uid);
118 r = get_process_gid(pid, &audit->gid);
122 r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
130 Any time an access gets denied this callback will be called
131 with the aduit data. We then need to just copy the audit data into the msgbuf.
133 static int audit_callback(
135 security_class_t cls,
139 struct auditstruct *audit = (struct auditstruct *) auditdata;
141 snprintf(msgbuf, msgbufsize,
142 "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
146 (audit->path ? " path=\"" : ""),
147 strempty(audit->path),
148 (audit->path ? "\"" : ""),
149 (audit->cmdline ? " cmdline=\"" : ""),
150 strempty(audit->cmdline),
151 (audit->cmdline ? "\"" : ""));
153 msgbuf[msgbufsize-1] = 0;
159 Any time an access gets denied this callback will be called
160 code copied from dbus. If audit is turned on the messages will go as
161 user_avc's into the /var/log/audit/audit.log, otherwise they will be
164 static int log_callback(int type, const char *fmt, ...) {
170 if (get_audit_fd() >= 0) {
173 vsnprintf(buf, sizeof(buf), fmt, ap);
174 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
180 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
187 Function must be called once to initialize the SELinux AVC environment.
189 If you want to cleanup memory you should need to call selinux_access_finish.
191 static int access_init(void) {
194 if (avc_open(NULL, 0)) {
195 log_error("avc_open() failed: %m");
199 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
200 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
202 if (security_getenforce() >= 0)
211 static int selinux_access_init(DBusError *error) {
220 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
229 void selinux_access_free(void) {
237 static int get_audit_data(
238 DBusConnection *connection,
239 DBusMessage *message,
240 struct auditstruct *audit,
248 sender = dbus_message_get_sender(message);
250 return bus_get_audit_data(connection, sender, audit, error);
252 if (!dbus_connection_get_unix_fd(connection, &fd))
255 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
257 log_error("Failed to determine peer credentials: %m");
261 audit->uid = ucred.uid;
262 audit->gid = ucred.gid;
264 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
268 r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
276 This function returns the security context of the remote end of the dbus
277 connections. Whether it is on the bus or a local connection.
279 static int get_calling_context(
280 DBusConnection *connection,
281 DBusMessage *message,
282 security_context_t *scon,
290 If sender exists then
291 if sender is NULL this indicates a local connection. Grab the fd
292 from dbus and do an getpeercon to peers process context
294 sender = dbus_message_get_sender(message);
296 r = bus_get_selinux_security_context(connection, sender, scon, error);
300 log_debug("bus_get_selinux_security_context failed %m");
303 if (!dbus_connection_get_unix_fd(connection, &fd)) {
304 log_error("bus_connection_get_unix_fd failed %m");
308 r = getpeercon(fd, scon);
310 log_error("getpeercon failed %m");
318 This function communicates with the kernel to check whether or not it should
320 If the machine is in permissive mode it will return ok. Audit messages will
321 still be generated if the access would be denied in enforcing mode.
323 int selinux_access_check(
324 DBusConnection *connection,
325 DBusMessage *message,
327 const char *permission,
330 security_context_t scon = NULL, fcon = NULL;
332 const char *tclass = NULL;
333 struct auditstruct audit;
343 r = selinux_access_init(error);
347 log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
349 audit.uid = audit.loginuid = (uid_t) -1;
350 audit.gid = (gid_t) -1;
351 audit.cmdline = NULL;
354 r = get_calling_context(connection, message, &scon, error);
356 log_error("Failed to get caller's security context on: %m");
362 /* get the file context of the unit file */
363 r = getfilecon(path, &fcon);
365 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
367 log_error("Failed to get security context on %s: %m",path);
375 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
377 log_error("Failed to get current process context on: %m");
382 (void) get_audit_data(connection, message, &audit, error);
385 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
387 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
389 log_error("SELinux policy denies access.");
392 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);
399 if (r && security_getenforce() != 1) {
400 dbus_error_init(error);
409 int selinux_access_check(
410 DBusConnection *connection,
411 DBusMessage *message,
413 const char *permission,
419 void selinux_access_free(void) {