1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "selinux-access.h"
8 #include <selinux/avc.h>
9 #include <selinux/selinux.h>
16 #include "alloc-util.h"
19 #include "errno-util.h"
20 #include "format-util.h"
22 #include "path-util.h"
23 #include "selinux-util.h"
24 #include "stdio-util.h"
28 static bool initialized
= false;
37 Any time an access gets denied this callback will be called
38 with the audit data. We then need to just copy the audit data into the msgbuf.
40 static int audit_callback(
46 const struct audit_info
*audit
= auditdata
;
47 uid_t uid
= 0, login_uid
= 0;
49 char login_uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
50 char uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
51 char gid_buf
[DECIMAL_STR_MAX(gid_t
) + 1] = "n/a";
53 if (sd_bus_creds_get_audit_login_uid(audit
->creds
, &login_uid
) >= 0)
54 xsprintf(login_uid_buf
, UID_FMT
, login_uid
);
55 if (sd_bus_creds_get_euid(audit
->creds
, &uid
) >= 0)
56 xsprintf(uid_buf
, UID_FMT
, uid
);
57 if (sd_bus_creds_get_egid(audit
->creds
, &gid
) >= 0)
58 xsprintf(gid_buf
, GID_FMT
, gid
);
60 snprintf(msgbuf
, msgbufsize
,
61 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
62 login_uid_buf
, uid_buf
, gid_buf
,
63 audit
->path
? " path=\"" : "", strempty(audit
->path
), audit
->path
? "\"" : "",
64 audit
->cmdline
? " cmdline=\"" : "", strempty(audit
->cmdline
), audit
->cmdline
? "\"" : "");
69 static int callback_type_to_priority(int type
) {
88 libselinux uses this callback when access gets denied or other
89 events happen. If audit is turned on, messages will be reported
90 using audit netlink, otherwise they will be logged using the usual
93 Code copied from dbus and modified.
95 _printf_(2, 3) static int log_callback(int type
, const char *fmt
, ...) {
105 _cleanup_free_
char *buf
= NULL
;
109 r
= vasprintf(&buf
, fmt
, ap
);
113 if (type
== SELINUX_AVC
)
114 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC
, buf
, NULL
, NULL
, NULL
, 0);
115 else if (type
== SELINUX_ERROR
)
116 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_SELINUX_ERR
, buf
, NULL
, NULL
, NULL
, 0);
123 fmt2
= strjoina("selinux: ", fmt
);
127 DISABLE_WARNING_FORMAT_NONLITERAL
;
128 log_internalv(LOG_AUTH
| callback_type_to_priority(type
),
129 0, PROJECT_FILE
, __LINE__
, __FUNCTION__
,
137 static int access_init(sd_bus_error
*error
) {
139 if (!mac_selinux_use())
145 if (avc_open(NULL
, 0) != 0) {
146 int saved_errno
= errno
;
149 enforce
= security_getenforce() != 0;
150 log_full_errno(enforce
? LOG_ERR
: LOG_WARNING
, saved_errno
, "Failed to open the SELinux AVC: %m");
152 /* If enforcement isn't on, then let's suppress this
153 * error, and just don't do any AVC checks. The
154 * warning we printed is hence all the admin will
159 /* Return an access denied error, if we couldn't load
160 * the AVC but enforcing mode was on, or we couldn't
161 * determine whether it is one. */
162 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to open the SELinux AVC: %s", strerror_safe(saved_errno
));
165 selinux_set_callback(SELINUX_CB_AUDIT
, (union selinux_callback
) audit_callback
);
166 selinux_set_callback(SELINUX_CB_LOG
, (union selinux_callback
) log_callback
);
173 This function communicates with the kernel to check whether or not it should
175 If the machine is in permissive mode it will return ok. Audit messages will
176 still be generated if the access would be denied in enforcing mode.
178 int mac_selinux_generic_access_check(
179 sd_bus_message
*message
,
181 const char *permission
,
182 sd_bus_error
*error
) {
184 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
185 const char *tclass
, *scon
;
186 _cleanup_free_
char *cl
= NULL
;
187 _cleanup_freecon_
char *fcon
= NULL
;
188 char **cmdline
= NULL
;
196 r
= access_init(error
);
200 /* delay call until we checked in `access_init()` if SELinux is actually enabled */
201 enforce
= security_getenforce() != 0;
203 r
= sd_bus_query_sender_creds(
205 SD_BUS_CREDS_PID
|SD_BUS_CREDS_EUID
|SD_BUS_CREDS_EGID
|
206 SD_BUS_CREDS_CMDLINE
|SD_BUS_CREDS_AUDIT_LOGIN_UID
|
207 SD_BUS_CREDS_SELINUX_CONTEXT
|
208 SD_BUS_CREDS_AUGMENT
/* get more bits from /proc */,
213 /* The SELinux context is something we really should have
214 * gotten directly from the message or sender, and not be an
215 * augmented field. If it was augmented we cannot use it for
216 * authorization, since this is racy and vulnerable. Let's add
217 * an extra check, just in case, even though this really
218 * shouldn't be possible. */
219 assert_return((sd_bus_creds_get_augmented_mask(creds
) & SD_BUS_CREDS_SELINUX_CONTEXT
) == 0, -EPERM
);
221 r
= sd_bus_creds_get_selinux_context(creds
, &scon
);
226 /* Get the file context of the unit file */
228 if (getfilecon_raw(path
, &fcon
) < 0) {
231 log_warning_errno(r
, "SELinux getfilecon_raw() on '%s' failed%s (perm=%s): %m",
233 enforce
? "" : ", ignoring",
238 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get file context on %s.", path
);
244 if (getcon_raw(&fcon
) < 0) {
247 log_warning_errno(r
, "SELinux getcon_raw() failed%s (perm=%s): %m",
248 enforce
? "" : ", ignoring",
253 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get current context.");
259 sd_bus_creds_get_cmdline(creds
, &cmdline
);
260 cl
= strv_join(cmdline
, " ");
262 struct audit_info audit_info
= {
268 r
= selinux_check_access(scon
, fcon
, tclass
, permission
, &audit_info
);
270 r
= errno_or_else(EPERM
);
273 sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "SELinux policy denies access.");
276 log_debug_errno(r
, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s path=%s cmdline=%s: %m",
277 scon
, fcon
, tclass
, permission
, enforce
? "enforcing" : "permissive", path
, cl
);
278 return enforce
? r
: 0;
283 int mac_selinux_generic_access_check(
284 sd_bus_message
*message
,
286 const char *permission
,
287 sd_bus_error
*error
) {