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 _printf_attr_(2, 3) static int log_callback(int type
, const char *fmt
, ...) {
183 if (get_audit_fd() >= 0) {
184 _cleanup_free_
char *buf
= NULL
;
187 r
= vasprintf(&buf
, fmt
, ap
);
191 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC
, buf
, NULL
, NULL
, NULL
, 0);
198 log_metav(LOG_USER
| LOG_INFO
, __FILE__
, __LINE__
, __FUNCTION__
, fmt
, ap
);
205 Function must be called once to initialize the SELinux AVC environment.
207 If you want to cleanup memory you should need to call selinux_access_finish.
209 static int access_init(void) {
212 if (avc_open(NULL
, 0)) {
213 log_error("avc_open() failed: %m");
217 selinux_set_callback(SELINUX_CB_AUDIT
, (union selinux_callback
) audit_callback
);
218 selinux_set_callback(SELINUX_CB_LOG
, (union selinux_callback
) log_callback
);
220 if (security_getenforce() >= 0)
229 static int selinux_access_init(DBusError
*error
) {
238 dbus_set_error(error
, DBUS_ERROR_ACCESS_DENIED
, "Failed to initialize SELinux.");
247 void selinux_access_free(void) {
255 static int get_audit_data(
256 DBusConnection
*connection
,
257 DBusMessage
*message
,
258 struct auditstruct
*audit
,
264 socklen_t len
= sizeof(ucred
);
266 sender
= dbus_message_get_sender(message
);
268 return bus_get_audit_data(connection
, sender
, audit
, error
);
270 if (!dbus_connection_get_unix_fd(connection
, &fd
))
273 r
= getsockopt(fd
, SOL_SOCKET
, SO_PEERCRED
, &ucred
, &len
);
275 log_error("Failed to determine peer credentials: %m");
279 audit
->uid
= ucred
.uid
;
280 audit
->gid
= ucred
.gid
;
282 r
= audit_loginuid_from_pid(ucred
.pid
, &audit
->loginuid
);
286 r
= get_process_cmdline(ucred
.pid
, 0, true, &audit
->cmdline
);
294 This function returns the security context of the remote end of the dbus
295 connections. Whether it is on the bus or a local connection.
297 static int get_calling_context(
298 DBusConnection
*connection
,
299 DBusMessage
*message
,
300 security_context_t
*scon
,
308 If sender exists then
309 if sender is NULL this indicates a local connection. Grab the fd
310 from dbus and do an getpeercon to peers process context
312 sender
= dbus_message_get_sender(message
);
314 r
= bus_get_selinux_security_context(connection
, sender
, scon
, error
);
318 log_error("bus_get_selinux_security_context failed: %m");
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 audit
.uid
= audit
.loginuid
= (uid_t
) -1;
367 audit
.gid
= (gid_t
) -1;
368 audit
.cmdline
= NULL
;
371 r
= get_calling_context(connection
, message
, &scon
, error
);
373 log_error("Failed to get caller's security context on: %m");
379 /* get the file context of the unit file */
380 r
= getfilecon(path
, &fcon
);
382 dbus_set_error(error
, DBUS_ERROR_ACCESS_DENIED
, "Failed to get file context on %s.", path
);
384 log_error("Failed to get security context on %s: %m",path
);
392 dbus_set_error(error
, DBUS_ERROR_ACCESS_DENIED
, "Failed to get current context.");
394 log_error("Failed to get current process context on: %m");
399 (void) get_audit_data(connection
, message
, &audit
, error
);
402 r
= selinux_check_access(scon
, fcon
, tclass
, permission
, &audit
);
404 dbus_set_error(error
, DBUS_ERROR_ACCESS_DENIED
, "SELinux policy denies access.");
406 log_error("SELinux policy denies access.");
409 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
);
416 if (r
&& security_getenforce() != 1) {
417 dbus_error_init(error
);
426 int selinux_access_check(
427 DBusConnection
*connection
,
428 DBusMessage
*message
,
430 const char *permission
,
436 void selinux_access_free(void) {