]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
test-unit-name: check that unexpanded specifiers not valid unit name make
[thirdparty/systemd.git] / src / core / selinux-access.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e2417e41 2
e2417e41
DW
3#include "selinux-access.h"
4
349cc4a5 5#if HAVE_SELINUX
e2417e41 6
e2417e41 7#include <errno.h>
e2417e41 8#include <selinux/avc.h>
cf0fbc49 9#include <selinux/selinux.h>
349cc4a5 10#if HAVE_AUDIT
e2417e41
DW
11#include <libaudit.h>
12#endif
ffc227c9 13
718db961 14#include "sd-bus.h"
15a5e950 15
b5efdb8a 16#include "alloc-util.h"
15a5e950 17#include "audit-fd.h"
718db961 18#include "bus-util.h"
4bbccb02 19#include "errno-util.h"
ca78ad1d 20#include "format-util.h"
ffc227c9 21#include "log.h"
15a5e950 22#include "path-util.h"
ffc227c9 23#include "selinux-util.h"
15a5e950 24#include "stdio-util.h"
5b12334d 25#include "strv.h"
15a5e950 26#include "util.h"
e2417e41 27
cad45ba1 28static bool initialized = false;
e2417e41 29
5b12334d
LP
30struct audit_info {
31 sd_bus_creds *creds;
e2417e41 32 const char *path;
5b12334d 33 const char *cmdline;
e2417e41
DW
34};
35
e2417e41
DW
36/*
37 Any time an access gets denied this callback will be called
dec23413 38 with the audit data. We then need to just copy the audit data into the msgbuf.
e2417e41 39*/
cad45ba1
LP
40static int audit_callback(
41 void *auditdata,
42 security_class_t cls,
43 char *msgbuf,
44 size_t msgbufsize) {
45
5b12334d
LP
46 const struct audit_info *audit = auditdata;
47 uid_t uid = 0, login_uid = 0;
48 gid_t gid = 0;
5ffa8c81
ZJS
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";
5b12334d 52
dec23413 53 if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
5ffa8c81 54 xsprintf(login_uid_buf, UID_FMT, login_uid);
05bae4a6 55 if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
5ffa8c81 56 xsprintf(uid_buf, UID_FMT, uid);
05bae4a6 57 if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
5ffa8c81 58 xsprintf(gid_buf, GID_FMT, gid);
cad45ba1 59
e2417e41 60 snprintf(msgbuf, msgbufsize,
dec23413
ZJS
61 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
62 login_uid_buf, uid_buf, gid_buf,
5b12334d
LP
63 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
64 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
d67227c8 65
e2417e41
DW
66 return 0;
67}
68
17af49f2
ZJS
69static int callback_type_to_priority(int type) {
70 switch(type) {
c04754bc
LP
71
72 case SELINUX_ERROR:
73 return LOG_ERR;
74
75 case SELINUX_WARNING:
76 return LOG_WARNING;
77
78 case SELINUX_INFO:
79 return LOG_INFO;
80
17af49f2 81 case SELINUX_AVC:
c04754bc
LP
82 default:
83 return LOG_NOTICE;
17af49f2
ZJS
84 }
85}
86
e2417e41 87/*
17af49f2
ZJS
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
91 channels.
92
93 Code copied from dbus and modified.
e2417e41 94*/
44b601bc 95_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41 96 va_list ap;
c8a806f2 97 const char *fmt2;
e2417e41 98
349cc4a5 99#if HAVE_AUDIT
17af49f2
ZJS
100 int fd;
101
102 fd = get_audit_fd();
103
104 if (fd >= 0) {
ace188cf
LP
105 _cleanup_free_ char *buf = NULL;
106 int r;
e2417e41 107
5b12334d 108 va_start(ap, fmt);
ace188cf 109 r = vasprintf(&buf, fmt, ap);
7f1736f7 110 va_end(ap);
cad45ba1 111
ace188cf 112 if (r >= 0) {
6227fc14
MS
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);
117
ace188cf
LP
118 return 0;
119 }
e2417e41
DW
120 }
121#endif
5b12334d 122
c8a806f2
ZJS
123 fmt2 = strjoina("selinux: ", fmt);
124
5b12334d 125 va_start(ap, fmt);
032b7541
ZJS
126#pragma GCC diagnostic push
127#pragma GCC diagnostic ignored "-Wformat-nonliteral"
128 log_internalv(LOG_AUTH | callback_type_to_priority(type),
62c6bbbc 129 0, PROJECT_FILE, __LINE__, __FUNCTION__,
032b7541
ZJS
130 fmt2, ap);
131#pragma GCC diagnostic pop
e2417e41 132 va_end(ap);
cad45ba1 133
e2417e41
DW
134 return 0;
135}
136
6344f3e2 137static int access_init(sd_bus_error *error) {
e2417e41 138
6344f3e2
LP
139 if (!mac_selinux_use())
140 return 0;
e2417e41 141
6344f3e2
LP
142 if (initialized)
143 return 1;
cad45ba1 144
6344f3e2
LP
145 if (avc_open(NULL, 0) != 0) {
146 int enforce, saved_errno = errno;
e2417e41 147
6344f3e2
LP
148 enforce = security_getenforce();
149 log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
e2417e41 150
6344f3e2
LP
151 /* If enforcement isn't on, then let's suppress this
152 * error, and just don't do any AVC checks. The
153 * warning we printed is hence all the admin will
154 * see. */
155 if (enforce == 0)
156 return 0;
e2417e41 157
6344f3e2
LP
158 /* Return an access denied error, if we couldn't load
159 * the AVC but enforcing mode was on, or we couldn't
160 * determine whether it is one. */
4bbccb02 161 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror_safe(saved_errno));
6344f3e2 162 }
718db961 163
6344f3e2
LP
164 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
165 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
e2417e41 166
cad45ba1 167 initialized = true;
6344f3e2 168 return 1;
e2417e41
DW
169}
170
e2417e41
DW
171/*
172 This function communicates with the kernel to check whether or not it should
173 allow the access.
174 If the machine is in permissive mode it will return ok. Audit messages will
175 still be generated if the access would be denied in enforcing mode.
176*/
8a188de9 177int mac_selinux_generic_access_check(
718db961 178 sd_bus_message *message,
cad45ba1
LP
179 const char *path,
180 const char *permission,
718db961 181 sd_bus_error *error) {
cad45ba1 182
4afd3348 183 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
5b12334d
LP
184 const char *tclass = NULL, *scon = NULL;
185 struct audit_info audit_info = {};
186 _cleanup_free_ char *cl = NULL;
2ed96880 187 char *fcon = NULL;
5b12334d 188 char **cmdline = NULL;
718db961 189 int r = 0;
c3090674 190
cad45ba1
LP
191 assert(message);
192 assert(permission);
193 assert(error);
194
6344f3e2
LP
195 r = access_init(error);
196 if (r <= 0)
ffc227c9
LP
197 return r;
198
5b12334d
LP
199 r = sd_bus_query_sender_creds(
200 message,
05bae4a6 201 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
5b12334d 202 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
8fd00193
LP
203 SD_BUS_CREDS_SELINUX_CONTEXT|
204 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
5b12334d
LP
205 &creds);
206 if (r < 0)
207 goto finish;
e2417e41 208
0f514420
LP
209 /* The SELinux context is something we really should have
210 * gotten directly from the message or sender, and not be an
211 * augmented field. If it was augmented we cannot use it for
212 * authorization, since this is racy and vulnerable. Let's add
213 * an extra check, just in case, even though this really
214 * shouldn't be possible. */
215 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
216
5b12334d 217 r = sd_bus_creds_get_selinux_context(creds, &scon);
718db961 218 if (r < 0)
e2417e41 219 goto finish;
cad45ba1 220
e2417e41 221 if (path) {
718db961
LP
222 /* Get the file context of the unit file */
223
24154879 224 r = getfilecon_raw(path, &fcon);
e2417e41 225 if (r < 0) {
718db961 226 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
e2417e41
DW
227 goto finish;
228 }
229
718db961 230 tclass = "service";
e2417e41 231 } else {
24154879 232 r = getcon_raw(&fcon);
e2417e41 233 if (r < 0) {
718db961 234 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
e2417e41
DW
235 goto finish;
236 }
718db961
LP
237
238 tclass = "system";
e2417e41
DW
239 }
240
5b12334d
LP
241 sd_bus_creds_get_cmdline(creds, &cmdline);
242 cl = strv_join(cmdline, " ");
243
244 audit_info.creds = creds;
245 audit_info.path = path;
246 audit_info.cmdline = cl;
e2417e41 247
236f83af 248 r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
718db961
LP
249 if (r < 0)
250 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41 251
5b12334d 252 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);
cad45ba1 253
e2417e41 254finish:
e2417e41
DW
255 freecon(fcon);
256
5b12334d 257 if (r < 0 && security_getenforce() != 1) {
718db961 258 sd_bus_error_free(error);
e2417e41
DW
259 r = 0;
260 }
261
262 return r;
6344f3e2
LP
263}
264
8a188de9 265#else
6344f3e2
LP
266
267int mac_selinux_generic_access_check(
268 sd_bus_message *message,
269 const char *path,
270 const char *permission,
271 sd_bus_error *error) {
272
8a188de9 273 return 0;
e2417e41 274}
6344f3e2
LP
275
276#endif