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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "selinux-access.h"
27 #include <selinux/avc.h>
28 #include <selinux/selinux.h>
36 #include "alloc-util.h"
40 #include "path-util.h"
41 #include "selinux-util.h"
42 #include "stdio-util.h"
46 static bool initialized
= false;
55 Any time an access gets denied this callback will be called
56 with the audit data. We then need to just copy the audit data into the msgbuf.
58 static int audit_callback(
64 const struct audit_info
*audit
= auditdata
;
65 uid_t uid
= 0, login_uid
= 0;
67 char login_uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
68 char uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
69 char gid_buf
[DECIMAL_STR_MAX(gid_t
) + 1] = "n/a";
71 if (sd_bus_creds_get_audit_login_uid(audit
->creds
, &login_uid
) >= 0)
72 xsprintf(login_uid_buf
, UID_FMT
, login_uid
);
73 if (sd_bus_creds_get_euid(audit
->creds
, &uid
) >= 0)
74 xsprintf(uid_buf
, UID_FMT
, uid
);
75 if (sd_bus_creds_get_egid(audit
->creds
, &gid
) >= 0)
76 xsprintf(gid_buf
, GID_FMT
, gid
);
78 snprintf(msgbuf
, msgbufsize
,
79 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
80 login_uid_buf
, uid_buf
, gid_buf
,
81 audit
->path
? " path=\"" : "", strempty(audit
->path
), audit
->path
? "\"" : "",
82 audit
->cmdline
? " cmdline=\"" : "", strempty(audit
->cmdline
), audit
->cmdline
? "\"" : "");
87 static int callback_type_to_priority(int type
) {
106 libselinux uses this callback when access gets denied or other
107 events happen. If audit is turned on, messages will be reported
108 using audit netlink, otherwise they will be logged using the usual
111 Code copied from dbus and modified.
113 _printf_(2, 3) static int log_callback(int type
, const char *fmt
, ...) {
122 _cleanup_free_
char *buf
= NULL
;
126 r
= vasprintf(&buf
, fmt
, ap
);
130 audit_log_user_avc_message(fd
, AUDIT_USER_AVC
, buf
, NULL
, NULL
, NULL
, 0);
137 log_internalv(LOG_AUTH
| callback_type_to_priority(type
), 0, __FILE__
, __LINE__
, __FUNCTION__
, fmt
, ap
);
143 static int access_init(sd_bus_error
*error
) {
145 if (!mac_selinux_use())
151 if (avc_open(NULL
, 0) != 0) {
152 int enforce
, saved_errno
= errno
;
154 enforce
= security_getenforce();
155 log_full_errno(enforce
!= 0 ? LOG_ERR
: LOG_WARNING
, saved_errno
, "Failed to open the SELinux AVC: %m");
157 /* If enforcement isn't on, then let's suppress this
158 * error, and just don't do any AVC checks. The
159 * warning we printed is hence all the admin will
164 /* Return an access denied error, if we couldn't load
165 * the AVC but enforcing mode was on, or we couldn't
166 * determine whether it is one. */
167 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to open the SELinux AVC: %s", strerror(saved_errno
));
170 selinux_set_callback(SELINUX_CB_AUDIT
, (union selinux_callback
) audit_callback
);
171 selinux_set_callback(SELINUX_CB_LOG
, (union selinux_callback
) log_callback
);
178 This function communicates with the kernel to check whether or not it should
180 If the machine is in permissive mode it will return ok. Audit messages will
181 still be generated if the access would be denied in enforcing mode.
183 int mac_selinux_generic_access_check(
184 sd_bus_message
*message
,
186 const char *permission
,
187 sd_bus_error
*error
) {
189 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
190 const char *tclass
= NULL
, *scon
= NULL
;
191 struct audit_info audit_info
= {};
192 _cleanup_free_
char *cl
= NULL
;
193 security_context_t fcon
= NULL
;
194 char **cmdline
= NULL
;
201 r
= access_init(error
);
205 r
= sd_bus_query_sender_creds(
207 SD_BUS_CREDS_PID
|SD_BUS_CREDS_EUID
|SD_BUS_CREDS_EGID
|
208 SD_BUS_CREDS_CMDLINE
|SD_BUS_CREDS_AUDIT_LOGIN_UID
|
209 SD_BUS_CREDS_SELINUX_CONTEXT
|
210 SD_BUS_CREDS_AUGMENT
/* get more bits from /proc */,
215 /* The SELinux context is something we really should have
216 * gotten directly from the message or sender, and not be an
217 * augmented field. If it was augmented we cannot use it for
218 * authorization, since this is racy and vulnerable. Let's add
219 * an extra check, just in case, even though this really
220 * shouldn't be possible. */
221 assert_return((sd_bus_creds_get_augmented_mask(creds
) & SD_BUS_CREDS_SELINUX_CONTEXT
) == 0, -EPERM
);
223 r
= sd_bus_creds_get_selinux_context(creds
, &scon
);
228 /* Get the file context of the unit file */
230 r
= getfilecon_raw(path
, &fcon
);
232 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get file context on %s.", path
);
238 r
= getcon_raw(&fcon
);
240 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get current context.");
247 sd_bus_creds_get_cmdline(creds
, &cmdline
);
248 cl
= strv_join(cmdline
, " ");
250 audit_info
.creds
= creds
;
251 audit_info
.path
= path
;
252 audit_info
.cmdline
= cl
;
254 r
= selinux_check_access(scon
, fcon
, tclass
, permission
, &audit_info
);
256 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "SELinux policy denies access.");
258 log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon
, fcon
, tclass
, permission
, path
, cl
, r
);
263 if (r
< 0 && security_getenforce() != 1) {
264 sd_bus_error_free(error
);
273 int mac_selinux_generic_access_check(
274 sd_bus_message
*message
,
276 const char *permission
,
277 sd_bus_error
*error
) {