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;
62 DBusMessageIter iter, sub;
67 m = dbus_message_new_method_call(
71 "GetConnectionSELinuxSecurityContext");
73 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
77 if (!dbus_message_append_args(
79 DBUS_TYPE_STRING, &name,
81 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
85 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
89 if (dbus_set_error_from_message(error, reply))
92 if (!dbus_message_iter_init(reply, &iter))
95 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
98 dbus_message_iter_recurse(&iter, &sub);
99 dbus_message_iter_get_fixed_array(&sub, &bytes, &nbytes);
101 b = strndup(bytes, nbytes);
110 static int bus_get_audit_data(
111 DBusConnection *connection,
113 struct auditstruct *audit,
119 pid = bus_get_unix_process_id(connection, name, error);
123 r = audit_loginuid_from_pid(pid, &audit->loginuid);
127 r = get_process_uid(pid, &audit->uid);
131 r = get_process_gid(pid, &audit->gid);
135 r = get_process_cmdline(pid, 0, true, &audit->cmdline);
143 Any time an access gets denied this callback will be called
144 with the aduit data. We then need to just copy the audit data into the msgbuf.
146 static int audit_callback(
148 security_class_t cls,
152 struct auditstruct *audit = (struct auditstruct *) auditdata;
154 snprintf(msgbuf, msgbufsize,
155 "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
159 (audit->path ? " path=\"" : ""),
160 strempty(audit->path),
161 (audit->path ? "\"" : ""),
162 (audit->cmdline ? " cmdline=\"" : ""),
163 strempty(audit->cmdline),
164 (audit->cmdline ? "\"" : ""));
166 msgbuf[msgbufsize-1] = 0;
172 Any time an access gets denied this callback will be called
173 code copied from dbus. If audit is turned on the messages will go as
174 user_avc's into the /var/log/audit/audit.log, otherwise they will be
177 static int log_callback(int type, const char *fmt, ...) {
183 if (get_audit_fd() >= 0) {
186 vsnprintf(buf, sizeof(buf), fmt, ap);
187 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
193 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
200 Function must be called once to initialize the SELinux AVC environment.
202 If you want to cleanup memory you should need to call selinux_access_finish.
204 static int access_init(void) {
207 if (avc_open(NULL, 0)) {
208 log_error("avc_open() failed: %m");
212 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
213 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
215 if (security_getenforce() >= 0)
224 static int selinux_access_init(DBusError *error) {
233 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
242 void selinux_access_free(void) {
250 static int get_audit_data(
251 DBusConnection *connection,
252 DBusMessage *message,
253 struct auditstruct *audit,
261 sender = dbus_message_get_sender(message);
263 return bus_get_audit_data(connection, sender, audit, error);
265 if (!dbus_connection_get_unix_fd(connection, &fd))
268 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
270 log_error("Failed to determine peer credentials: %m");
274 audit->uid = ucred.uid;
275 audit->gid = ucred.gid;
277 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
281 r = get_process_cmdline(ucred.pid, 0, true, &audit->cmdline);
289 This function returns the security context of the remote end of the dbus
290 connections. Whether it is on the bus or a local connection.
292 static int get_calling_context(
293 DBusConnection *connection,
294 DBusMessage *message,
295 security_context_t *scon,
303 If sender exists then
304 if sender is NULL this indicates a local connection. Grab the fd
305 from dbus and do an getpeercon to peers process context
307 sender = dbus_message_get_sender(message);
309 r = bus_get_selinux_security_context(connection, sender, scon, error);
313 log_error("bus_get_selinux_security_context failed: %m");
317 if (!dbus_connection_get_unix_fd(connection, &fd)) {
318 log_error("bus_connection_get_unix_fd failed %m");
322 r = getpeercon(fd, scon);
324 log_error("getpeercon failed %m");
332 This function communicates with the kernel to check whether or not it should
334 If the machine is in permissive mode it will return ok. Audit messages will
335 still be generated if the access would be denied in enforcing mode.
337 int selinux_access_check(
338 DBusConnection *connection,
339 DBusMessage *message,
341 const char *permission,
344 security_context_t scon = NULL, fcon = NULL;
346 const char *tclass = NULL;
347 struct auditstruct audit;
357 r = selinux_access_init(error);
361 audit.uid = audit.loginuid = (uid_t) -1;
362 audit.gid = (gid_t) -1;
363 audit.cmdline = NULL;
366 r = get_calling_context(connection, message, &scon, error);
368 log_error("Failed to get caller's security context on: %m");
374 /* get the file context of the unit file */
375 r = getfilecon(path, &fcon);
377 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
379 log_error("Failed to get security context on %s: %m",path);
387 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
389 log_error("Failed to get current process context on: %m");
394 (void) get_audit_data(connection, message, &audit, error);
397 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
399 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
401 log_error("SELinux policy denies access.");
404 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);
411 if (r && security_getenforce() != 1) {
412 dbus_error_init(error);
421 int selinux_access_check(
422 DBusConnection *connection,
423 DBusMessage *message,
425 const char *permission,
431 void selinux_access_free(void) {