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"
28 #include <selinux/selinux.h>
29 #include <selinux/avc.h>
38 #include "selinux-util.h"
42 static bool initialized
= false;
51 Any time an access gets denied this callback will be called
52 with the audit data. We then need to just copy the audit data into the msgbuf.
54 static int audit_callback(
60 const struct audit_info
*audit
= auditdata
;
61 uid_t uid
= 0, login_uid
= 0;
63 char login_uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
64 char uid_buf
[DECIMAL_STR_MAX(uid_t
) + 1] = "n/a";
65 char gid_buf
[DECIMAL_STR_MAX(gid_t
) + 1] = "n/a";
67 if (sd_bus_creds_get_audit_login_uid(audit
->creds
, &login_uid
) >= 0)
68 xsprintf(login_uid_buf
, UID_FMT
, login_uid
);
69 if (sd_bus_creds_get_euid(audit
->creds
, &uid
) >= 0)
70 xsprintf(uid_buf
, UID_FMT
, uid
);
71 if (sd_bus_creds_get_egid(audit
->creds
, &gid
) >= 0)
72 xsprintf(gid_buf
, GID_FMT
, gid
);
74 snprintf(msgbuf
, msgbufsize
,
75 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
76 login_uid_buf
, uid_buf
, gid_buf
,
77 audit
->path
? " path=\"" : "", strempty(audit
->path
), audit
->path
? "\"" : "",
78 audit
->cmdline
? " cmdline=\"" : "", strempty(audit
->cmdline
), audit
->cmdline
? "\"" : "");
83 static int callback_type_to_priority(int type
) {
85 case SELINUX_ERROR
: return LOG_ERR
;
86 case SELINUX_WARNING
: return LOG_WARNING
;
87 case SELINUX_INFO
: return LOG_INFO
;
89 default: return LOG_NOTICE
;
94 libselinux uses this callback when access gets denied or other
95 events happen. If audit is turned on, messages will be reported
96 using audit netlink, otherwise they will be logged using the usual
99 Code copied from dbus and modified.
101 _printf_(2, 3) static int log_callback(int type
, const char *fmt
, ...) {
110 _cleanup_free_
char *buf
= NULL
;
114 r
= vasprintf(&buf
, fmt
, ap
);
118 audit_log_user_avc_message(fd
, AUDIT_USER_AVC
, buf
, NULL
, NULL
, NULL
, 0);
125 log_internalv(LOG_AUTH
| callback_type_to_priority(type
),
126 0, __FILE__
, __LINE__
, __FUNCTION__
, fmt
, ap
);
133 Function must be called once to initialize the SELinux AVC environment.
135 If you want to cleanup memory you should need to call selinux_access_finish.
137 static int access_init(void) {
140 if (avc_open(NULL
, 0))
141 return log_error_errno(errno
, "avc_open() failed: %m");
143 selinux_set_callback(SELINUX_CB_AUDIT
, (union selinux_callback
) audit_callback
);
144 selinux_set_callback(SELINUX_CB_LOG
, (union selinux_callback
) log_callback
);
146 if (security_getenforce() < 0){
154 static int mac_selinux_access_init(sd_bus_error
*error
) {
160 if (!mac_selinux_use())
165 return sd_bus_error_set(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to initialize SELinux.");
172 void mac_selinux_access_free(void) {
184 This function communicates with the kernel to check whether or not it should
186 If the machine is in permissive mode it will return ok. Audit messages will
187 still be generated if the access would be denied in enforcing mode.
189 int mac_selinux_generic_access_check(
190 sd_bus_message
*message
,
192 const char *permission
,
193 sd_bus_error
*error
) {
196 _cleanup_bus_creds_unref_ sd_bus_creds
*creds
= NULL
;
197 const char *tclass
= NULL
, *scon
= NULL
;
198 struct audit_info audit_info
= {};
199 _cleanup_free_
char *cl
= NULL
;
200 security_context_t fcon
= NULL
;
201 char **cmdline
= NULL
;
208 if (!mac_selinux_use())
211 r
= mac_selinux_access_init(error
);
215 r
= sd_bus_query_sender_creds(
217 SD_BUS_CREDS_PID
|SD_BUS_CREDS_EUID
|SD_BUS_CREDS_EGID
|
218 SD_BUS_CREDS_CMDLINE
|SD_BUS_CREDS_AUDIT_LOGIN_UID
|
219 SD_BUS_CREDS_SELINUX_CONTEXT
|
220 SD_BUS_CREDS_AUGMENT
/* get more bits from /proc */,
225 /* The SELinux context is something we really should have
226 * gotten directly from the message or sender, and not be an
227 * augmented field. If it was augmented we cannot use it for
228 * authorization, since this is racy and vulnerable. Let's add
229 * an extra check, just in case, even though this really
230 * shouldn't be possible. */
231 assert_return((sd_bus_creds_get_augmented_mask(creds
) & SD_BUS_CREDS_SELINUX_CONTEXT
) == 0, -EPERM
);
233 r
= sd_bus_creds_get_selinux_context(creds
, &scon
);
238 /* Get the file context of the unit file */
240 r
= getfilecon(path
, &fcon
);
242 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get file context on %s.", path
);
250 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Failed to get current context.");
257 sd_bus_creds_get_cmdline(creds
, &cmdline
);
258 cl
= strv_join(cmdline
, " ");
260 audit_info
.creds
= creds
;
261 audit_info
.path
= path
;
262 audit_info
.cmdline
= cl
;
264 r
= selinux_check_access(scon
, fcon
, tclass
, permission
, &audit_info
);
266 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "SELinux policy denies access.");
268 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
);
273 if (r
< 0 && security_getenforce() != 1) {
274 sd_bus_error_free(error
);
284 int mac_selinux_unit_access_check_strv(char **units
,
285 sd_bus_message
*message
,
287 const char *permission
,
288 sd_bus_error
*error
) {
294 STRV_FOREACH(i
, units
) {
295 u
= manager_get_unit(m
, *i
);
297 r
= mac_selinux_unit_access_check(u
, message
, permission
, error
);