2 This file is part of systemd.
4 Copyright 2012 Dan Walsh
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include "selinux-access.h"
25 #include <selinux/avc.h>
26 #include <selinux/selinux.h>
34 #include "alloc-util.h"
38 #include "path-util.h"
39 #include "selinux-util.h"
40 #include "stdio-util.h"
44 static bool initialized
= false;
53 Any time an access gets denied this callback will be called
54 with the audit data. We then need to just copy the audit data into the msgbuf.
56 static int audit_callback(
62 const struct audit_info
*audit
= auditdata
;
63 uid_t uid
= 0, login_uid
= 0;
65 char login_uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
66 char uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
67 char gid_buf
[DECIMAL_STR_MAX(gid_t
) + 1] = "n/a";
69 if (sd_bus_creds_get_audit_login_uid(audit
->creds
, &login_uid
) >= 0)
70 xsprintf(login_uid_buf
, UID_FMT
, login_uid
);
71 if (sd_bus_creds_get_euid(audit
->creds
, &uid
) >= 0)
72 xsprintf(uid_buf
, UID_FMT
, uid
);
73 if (sd_bus_creds_get_egid(audit
->creds
, &gid
) >= 0)
74 xsprintf(gid_buf
, GID_FMT
, gid
);
76 snprintf(msgbuf
, msgbufsize
,
77 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
78 login_uid_buf
, uid_buf
, gid_buf
,
79 audit
->path
? " path=\"" : "", strempty(audit
->path
), audit
->path
? "\"" : "",
80 audit
->cmdline
? " cmdline=\"" : "", strempty(audit
->cmdline
), audit
->cmdline
? "\"" : "");
85 static int callback_type_to_priority(int type
) {
104 libselinux uses this callback when access gets denied or other
105 events happen. If audit is turned on, messages will be reported
106 using audit netlink, otherwise they will be logged using the usual
109 Code copied from dbus and modified.
111 _printf_(2, 3) static int log_callback(int type
, const char *fmt
, ...) {
120 _cleanup_free_
char *buf
= NULL
;
124 r
= vasprintf(&buf
, fmt
, ap
);
128 audit_log_user_avc_message(fd
, AUDIT_USER_AVC
, buf
, NULL
, NULL
, NULL
, 0);
135 log_internalv(LOG_AUTH
| callback_type_to_priority(type
), 0, __FILE__
, __LINE__
, __FUNCTION__
, fmt
, ap
);
141 static int access_init(sd_bus_error
*error
) {
143 if (!mac_selinux_use())
149 if (avc_open(NULL
, 0) != 0) {
150 int enforce
, saved_errno
= errno
;
152 enforce
= security_getenforce();
153 log_full_errno(enforce
!= 0 ? LOG_ERR
: LOG_WARNING
, saved_errno
, "Failed to open the SELinux AVC: %m");
155 /* If enforcement isn't on, then let's suppress this
156 * error, and just don't do any AVC checks. The
157 * warning we printed is hence all the admin will
162 /* Return an access denied error, if we couldn't load
163 * the AVC but enforcing mode was on, or we couldn't
164 * determine whether it is one. */
165 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to open the SELinux AVC: %s", strerror(saved_errno
));
168 selinux_set_callback(SELINUX_CB_AUDIT
, (union selinux_callback
) audit_callback
);
169 selinux_set_callback(SELINUX_CB_LOG
, (union selinux_callback
) log_callback
);
176 This function communicates with the kernel to check whether or not it should
178 If the machine is in permissive mode it will return ok. Audit messages will
179 still be generated if the access would be denied in enforcing mode.
181 int mac_selinux_generic_access_check(
182 sd_bus_message
*message
,
184 const char *permission
,
185 sd_bus_error
*error
) {
187 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
188 const char *tclass
= NULL
, *scon
= NULL
;
189 struct audit_info audit_info
= {};
190 _cleanup_free_
char *cl
= NULL
;
191 security_context_t fcon
= NULL
;
192 char **cmdline
= NULL
;
199 r
= access_init(error
);
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 r
= getfilecon_raw(path
, &fcon
);
230 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get file context on %s.", path
);
236 r
= getcon_raw(&fcon
);
238 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get current context.");
245 sd_bus_creds_get_cmdline(creds
, &cmdline
);
246 cl
= strv_join(cmdline
, " ");
248 audit_info
.creds
= creds
;
249 audit_info
.path
= path
;
250 audit_info
.cmdline
= cl
;
252 r
= selinux_check_access(scon
, fcon
, tclass
, permission
, &audit_info
);
254 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "SELinux policy denies access.");
256 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
);
261 if (r
< 0 && security_getenforce() != 1) {
262 sd_bus_error_free(error
);
271 int mac_selinux_generic_access_check(
272 sd_bus_message
*message
,
274 const char *permission
,
275 sd_bus_error
*error
) {