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