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");
301 dbus_error_free(error);
304 if (!dbus_connection_get_unix_fd(connection, &fd)) {
305 log_error("bus_connection_get_unix_fd failed %m");
309 r = getpeercon(fd, scon);
311 log_error("getpeercon failed %m");
319 This function communicates with the kernel to check whether or not it should
321 If the machine is in permissive mode it will return ok. Audit messages will
322 still be generated if the access would be denied in enforcing mode.
324 int selinux_access_check(
325 DBusConnection *connection,
326 DBusMessage *message,
328 const char *permission,
331 security_context_t scon = NULL, fcon = NULL;
333 const char *tclass = NULL;
334 struct auditstruct audit;
344 r = selinux_access_init(error);
348 log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
350 audit.uid = audit.loginuid = (uid_t) -1;
351 audit.gid = (gid_t) -1;
352 audit.cmdline = NULL;
355 r = get_calling_context(connection, message, &scon, error);
357 log_error("Failed to get caller's security context on: %m");
363 /* get the file context of the unit file */
364 r = getfilecon(path, &fcon);
366 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
368 log_error("Failed to get security context on %s: %m",path);
376 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
378 log_error("Failed to get current process context on: %m");
383 (void) get_audit_data(connection, message, &audit, error);
386 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
388 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
390 log_error("SELinux policy denies access.");
393 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);
400 if (r && security_getenforce() != 1) {
401 dbus_error_init(error);
410 int selinux_access_check(
411 DBusConnection *connection,
412 DBusMessage *message,
414 const char *permission,
420 void selinux_access_free(void) {