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