1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "selinux-access.h"
7 #include <selinux/avc.h>
8 #include <selinux/selinux.h>
13 #include "alloc-util.h"
15 #include "errno-util.h"
16 #include "format-util.h"
17 #include "libaudit-util.h"
19 #include "selinux-util.h"
20 #include "stdio-util.h"
21 #include "string-util.h"
25 static bool initialized
= false;
35 Any time an access gets denied this callback will be called
36 with the audit data. We then need to just copy the audit data into the msgbuf.
38 static int audit_callback(
44 const struct audit_info
*audit
= auditdata
;
45 uid_t uid
= 0, login_uid
= 0;
47 char login_uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
48 char uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
49 char gid_buf
[DECIMAL_STR_MAX(gid_t
) + 1] = "n/a";
51 if (sd_bus_creds_get_audit_login_uid(audit
->creds
, &login_uid
) >= 0)
52 xsprintf(login_uid_buf
, UID_FMT
, login_uid
);
53 if (sd_bus_creds_get_euid(audit
->creds
, &uid
) >= 0)
54 xsprintf(uid_buf
, UID_FMT
, uid
);
55 if (sd_bus_creds_get_egid(audit
->creds
, &gid
) >= 0)
56 xsprintf(gid_buf
, GID_FMT
, gid
);
58 (void) snprintf(msgbuf
, msgbufsize
,
59 "auid=%s uid=%s gid=%s%s%s%s%s%s%s%s%s%s",
60 login_uid_buf
, uid_buf
, gid_buf
,
61 audit
->path
? " path=\"" : "", strempty(audit
->path
), audit
->path
? "\"" : "",
62 audit
->cmdline
? " cmdline=\"" : "", strempty(audit
->cmdline
), audit
->cmdline
? "\"" : "",
63 audit
->function
? " function=\"" : "", strempty(audit
->function
), audit
->function
? "\"" : "");
68 static int callback_type_to_priority(int type
) {
87 libselinux uses this callback when access gets denied or other
88 events happen. If audit is turned on, messages will be reported
89 using audit netlink, otherwise they will be logged using the usual
92 Code copied from dbus and modified.
94 _printf_(2, 3) static int log_callback(int type
, const char *fmt
, ...) {
99 int fd
= get_core_audit_fd();
102 _cleanup_free_
char *buf
= NULL
;
106 r
= vasprintf(&buf
, fmt
, ap
);
110 if (type
== SELINUX_AVC
)
111 audit_log_user_avc_message(fd
, AUDIT_USER_AVC
, buf
, NULL
, NULL
, NULL
, getuid());
112 else if (type
== SELINUX_ERROR
)
113 audit_log_user_avc_message(fd
, AUDIT_USER_SELINUX_ERR
, buf
, NULL
, NULL
, NULL
, getuid());
120 fmt2
= strjoina("selinux: ", fmt
);
124 DISABLE_WARNING_FORMAT_NONLITERAL
;
125 log_internalv(LOG_AUTH
| callback_type_to_priority(type
),
126 0, PROJECT_FILE
, __LINE__
, __func__
,
134 static int access_init(sd_bus_error
*error
) {
137 if (!mac_selinux_use())
143 if (avc_open(NULL
, 0) != 0) {
144 r
= -errno
; /* Save original errno for later */
146 bool enforce
= security_getenforce() != 0;
147 log_full_errno(enforce
? LOG_ERR
: LOG_WARNING
, r
, "Failed to open the SELinux AVC: %m");
149 /* If enforcement isn't on, then let's suppress this error, and just don't do any AVC checks.
150 * The warning we printed is hence all the admin will see. */
154 /* Return an access denied error based on the original errno, if we couldn't load the AVC but
155 * enforcing mode was on, or we couldn't determine whether it is one. */
157 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to open the SELinux AVC: %m");
160 selinux_set_callback(SELINUX_CB_AUDIT
, (union selinux_callback
) { .func_audit
= audit_callback
});
161 selinux_set_callback(SELINUX_CB_LOG
, (union selinux_callback
) { .func_log
= log_callback
});
168 This function communicates with the kernel to check whether or not it should
170 If the machine is in permissive mode it will return ok. Audit messages will
171 still be generated if the access would be denied in enforcing mode.
173 int mac_selinux_access_check_internal(
174 sd_bus_message
*message
,
176 const char *permission
,
177 const char *function
,
178 sd_bus_error
*error
) {
180 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
181 const char *tclass
, *scon
, *acon
;
182 _cleanup_free_
char *cl
= NULL
;
183 _cleanup_freecon_
char *fcon
= NULL
;
184 char **cmdline
= NULL
;
192 r
= access_init(error
);
196 /* delay call until we checked in `access_init()` if SELinux is actually enabled */
197 enforce
= mac_selinux_enforcing();
199 r
= sd_bus_query_sender_creds(
201 SD_BUS_CREDS_PID
|SD_BUS_CREDS_EUID
|SD_BUS_CREDS_EGID
|
202 SD_BUS_CREDS_CMDLINE
|SD_BUS_CREDS_AUDIT_LOGIN_UID
|
203 SD_BUS_CREDS_SELINUX_CONTEXT
|
204 SD_BUS_CREDS_AUGMENT
/* get more bits from /proc */,
209 /* The SELinux context is something we really should have gotten directly from the message or sender,
210 * and not be an augmented field. If it was augmented we cannot use it for authorization, since this
211 * is racy and vulnerable. Let's add an extra check, just in case, even though this really shouldn't
213 assert_return((sd_bus_creds_get_augmented_mask(creds
) & SD_BUS_CREDS_SELINUX_CONTEXT
) == 0, -EPERM
);
215 r
= sd_bus_creds_get_selinux_context(creds
, &scon
);
219 if (unit
&& unit
->access_selinux_context
) {
220 /* Nice! The unit comes with a SELinux context read from the unit file */
221 acon
= unit
->access_selinux_context
;
224 /* If no unit context is known, use our own */
225 if (getcon_raw(&fcon
) < 0) {
226 log_warning_errno(errno
, "SELinux getcon_raw() failed%s (perm=%s): %m",
227 enforce
? "" : ", ignoring",
232 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get current context: %m");
238 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "We appear not to have any SELinux context: %m");
245 (void) sd_bus_creds_get_cmdline(creds
, &cmdline
);
246 cl
= strv_join(cmdline
, " ");
248 struct audit_info audit_info
= {
250 .path
= unit
? unit
->fragment_path
: NULL
,
252 .function
= function
,
255 r
= selinux_check_access(scon
, acon
, tclass
, permission
, &audit_info
);
257 errno
= -(r
= errno_or_else(EPERM
));
260 sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "SELinux policy denies access: %m");
263 log_full_errno_zerook(LOG_DEBUG
, r
,
264 "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m",
265 scon
, acon
, tclass
, permission
, enforce
? "enforcing" : "permissive", function
, strna(unit
? unit
->fragment_path
: NULL
), empty_to_na(cl
));
266 return enforce
? r
: 0;
269 #else /* HAVE_SELINUX */
271 int mac_selinux_access_check_internal(
272 sd_bus_message
*message
,
274 const char *permission
,
275 const char *function
,
276 sd_bus_error
*error
) {
281 #endif /* HAVE_SELINUX */