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);
107 log_debug("GetConnectionSELinuxSecurityContext %s (pid %ld)", *scon, (long) bus_get_unix_process_id(connection, name, error));
112 static int bus_get_audit_data(
113 DBusConnection *connection,
115 struct auditstruct *audit,
121 pid = bus_get_unix_process_id(connection, name, error);
125 r = audit_loginuid_from_pid(pid, &audit->loginuid);
129 r = get_process_uid(pid, &audit->uid);
133 r = get_process_gid(pid, &audit->gid);
137 r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
145 Any time an access gets denied this callback will be called
146 with the aduit data. We then need to just copy the audit data into the msgbuf.
148 static int audit_callback(
150 security_class_t cls,
154 struct auditstruct *audit = (struct auditstruct *) auditdata;
156 snprintf(msgbuf, msgbufsize,
157 "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
161 (audit->path ? " path=\"" : ""),
162 strempty(audit->path),
163 (audit->path ? "\"" : ""),
164 (audit->cmdline ? " cmdline=\"" : ""),
165 strempty(audit->cmdline),
166 (audit->cmdline ? "\"" : ""));
168 msgbuf[msgbufsize-1] = 0;
174 Any time an access gets denied this callback will be called
175 code copied from dbus. If audit is turned on the messages will go as
176 user_avc's into the /var/log/audit/audit.log, otherwise they will be
179 static int log_callback(int type, const char *fmt, ...) {
185 if (get_audit_fd() >= 0) {
188 vsnprintf(buf, sizeof(buf), fmt, ap);
189 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
195 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
202 Function must be called once to initialize the SELinux AVC environment.
204 If you want to cleanup memory you should need to call selinux_access_finish.
206 static int access_init(void) {
209 if (avc_open(NULL, 0)) {
210 log_error("avc_open() failed: %m");
214 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
215 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
217 if (security_getenforce() >= 0)
226 static int selinux_access_init(DBusError *error) {
235 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
244 void selinux_access_free(void) {
252 static int get_audit_data(
253 DBusConnection *connection,
254 DBusMessage *message,
255 struct auditstruct *audit,
263 sender = dbus_message_get_sender(message);
265 return bus_get_audit_data(connection, sender, audit, error);
267 if (!dbus_connection_get_unix_fd(connection, &fd))
270 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
272 log_error("Failed to determine peer credentials: %m");
276 audit->uid = ucred.uid;
277 audit->gid = ucred.gid;
279 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
283 r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
291 This function returns the security context of the remote end of the dbus
292 connections. Whether it is on the bus or a local connection.
294 static int get_calling_context(
295 DBusConnection *connection,
296 DBusMessage *message,
297 security_context_t *scon,
305 If sender exists then
306 if sender is NULL this indicates a local connection. Grab the fd
307 from dbus and do an getpeercon to peers process context
309 sender = dbus_message_get_sender(message);
311 log_error("SELinux Got Sender %s", sender);
313 r = bus_get_selinux_security_context(connection, sender, scon, error);
317 log_error("bus_get_selinux_security_context failed: %m");
321 log_debug("SELinux No Sender");
322 if (!dbus_connection_get_unix_fd(connection, &fd)) {
323 log_error("bus_connection_get_unix_fd failed %m");
327 r = getpeercon(fd, scon);
329 log_error("getpeercon failed %m");
337 This function communicates with the kernel to check whether or not it should
339 If the machine is in permissive mode it will return ok. Audit messages will
340 still be generated if the access would be denied in enforcing mode.
342 int selinux_access_check(
343 DBusConnection *connection,
344 DBusMessage *message,
346 const char *permission,
349 security_context_t scon = NULL, fcon = NULL;
351 const char *tclass = NULL;
352 struct auditstruct audit;
362 r = selinux_access_init(error);
366 log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
368 audit.uid = audit.loginuid = (uid_t) -1;
369 audit.gid = (gid_t) -1;
370 audit.cmdline = NULL;
373 r = get_calling_context(connection, message, &scon, error);
375 log_error("Failed to get caller's security context on: %m");
381 /* get the file context of the unit file */
382 r = getfilecon(path, &fcon);
384 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
386 log_error("Failed to get security context on %s: %m",path);
394 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
396 log_error("Failed to get current process context on: %m");
401 (void) get_audit_data(connection, message, &audit, error);
404 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
406 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
408 log_error("SELinux policy denies access.");
411 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);
418 if (r && security_getenforce() != 1) {
419 dbus_error_init(error);
428 int selinux_access_check(
429 DBusConnection *connection,
430 DBusMessage *message,
432 const char *permission,
438 void selinux_access_free(void) {