]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
Merge pull request #179 from l10n-tw/master
[thirdparty/systemd.git] / src / core / selinux-access.c
CommitLineData
e2417e41
DW
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Dan Walsh
7
8 systemd is free software; you can redistribute it and/or modify it
03e22642
KS
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
e2417e41
DW
11 (at your option) any later version.
12
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
03e22642 16 Lesser General Public License for more details.
e2417e41 17
03e22642 18 You should have received a copy of the GNU Lesser General Public License
e2417e41
DW
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
e2417e41
DW
22#include "selinux-access.h"
23
24#ifdef HAVE_SELINUX
e2417e41
DW
25
26#include <stdio.h>
e2417e41
DW
27#include <errno.h>
28#include <selinux/selinux.h>
29#include <selinux/avc.h>
30#ifdef HAVE_AUDIT
31#include <libaudit.h>
32#endif
ffc227c9 33
718db961
LP
34#include "sd-bus.h"
35#include "bus-util.h"
ffc227c9
LP
36#include "util.h"
37#include "log.h"
ffc227c9
LP
38#include "selinux-util.h"
39#include "audit-fd.h"
5b12334d 40#include "strv.h"
e2417e41 41
cad45ba1 42static bool initialized = false;
e2417e41 43
5b12334d
LP
44struct audit_info {
45 sd_bus_creds *creds;
e2417e41 46 const char *path;
5b12334d 47 const char *cmdline;
e2417e41
DW
48};
49
e2417e41
DW
50/*
51 Any time an access gets denied this callback will be called
dec23413 52 with the audit data. We then need to just copy the audit data into the msgbuf.
e2417e41 53*/
cad45ba1
LP
54static int audit_callback(
55 void *auditdata,
56 security_class_t cls,
57 char *msgbuf,
58 size_t msgbufsize) {
59
5b12334d
LP
60 const struct audit_info *audit = auditdata;
61 uid_t uid = 0, login_uid = 0;
62 gid_t gid = 0;
5ffa8c81
ZJS
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";
5b12334d 66
dec23413 67 if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
5ffa8c81 68 xsprintf(login_uid_buf, UID_FMT, login_uid);
05bae4a6 69 if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
5ffa8c81 70 xsprintf(uid_buf, UID_FMT, uid);
05bae4a6 71 if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
5ffa8c81 72 xsprintf(gid_buf, GID_FMT, gid);
cad45ba1 73
e2417e41 74 snprintf(msgbuf, msgbufsize,
dec23413
ZJS
75 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
76 login_uid_buf, uid_buf, gid_buf,
5b12334d
LP
77 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
78 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
d67227c8 79
e2417e41
DW
80 return 0;
81}
82
17af49f2
ZJS
83static int callback_type_to_priority(int type) {
84 switch(type) {
c04754bc
LP
85
86 case SELINUX_ERROR:
87 return LOG_ERR;
88
89 case SELINUX_WARNING:
90 return LOG_WARNING;
91
92 case SELINUX_INFO:
93 return LOG_INFO;
94
17af49f2 95 case SELINUX_AVC:
c04754bc
LP
96 default:
97 return LOG_NOTICE;
17af49f2
ZJS
98 }
99}
100
e2417e41 101/*
17af49f2
ZJS
102 libselinux uses this callback when access gets denied or other
103 events happen. If audit is turned on, messages will be reported
104 using audit netlink, otherwise they will be logged using the usual
105 channels.
106
107 Code copied from dbus and modified.
e2417e41 108*/
44b601bc 109_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41
DW
110 va_list ap;
111
e2417e41 112#ifdef HAVE_AUDIT
17af49f2
ZJS
113 int fd;
114
115 fd = get_audit_fd();
116
117 if (fd >= 0) {
ace188cf
LP
118 _cleanup_free_ char *buf = NULL;
119 int r;
e2417e41 120
5b12334d 121 va_start(ap, fmt);
ace188cf 122 r = vasprintf(&buf, fmt, ap);
7f1736f7 123 va_end(ap);
cad45ba1 124
ace188cf 125 if (r >= 0) {
17af49f2 126 audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
ace188cf
LP
127 return 0;
128 }
e2417e41
DW
129 }
130#endif
5b12334d
LP
131
132 va_start(ap, fmt);
17af49f2
ZJS
133 log_internalv(LOG_AUTH | callback_type_to_priority(type),
134 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
e2417e41 135 va_end(ap);
cad45ba1 136
e2417e41
DW
137 return 0;
138}
139
140/*
141 Function must be called once to initialize the SELinux AVC environment.
142 Sets up callbacks.
143 If you want to cleanup memory you should need to call selinux_access_finish.
144*/
145static int access_init(void) {
718db961 146 int r = 0;
e2417e41 147
4a62c710
MS
148 if (avc_open(NULL, 0))
149 return log_error_errno(errno, "avc_open() failed: %m");
e2417e41 150
cad45ba1
LP
151 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
152 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
e2417e41 153
718db961
LP
154 if (security_getenforce() < 0){
155 r = -errno;
156 avc_destroy();
157 }
cad45ba1 158
e2417e41
DW
159 return r;
160}
161
8a188de9 162static int mac_selinux_access_init(sd_bus_error *error) {
e2417e41
DW
163 int r;
164
cad45ba1 165 if (initialized)
e2417e41
DW
166 return 0;
167
6baa7db0 168 if (!mac_selinux_use())
718db961
LP
169 return 0;
170
171 r = access_init();
172 if (r < 0)
173 return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
e2417e41 174
cad45ba1 175 initialized = true;
e2417e41
DW
176 return 0;
177}
8a188de9 178#endif
e2417e41 179
8a188de9 180void mac_selinux_access_free(void) {
718db961 181
8a188de9 182#ifdef HAVE_SELINUX
ffc227c9
LP
183 if (!initialized)
184 return;
185
186 avc_destroy();
187 initialized = false;
8a188de9 188#endif
ffc227c9
LP
189}
190
e2417e41
DW
191/*
192 This function communicates with the kernel to check whether or not it should
193 allow the access.
194 If the machine is in permissive mode it will return ok. Audit messages will
195 still be generated if the access would be denied in enforcing mode.
196*/
8a188de9 197int mac_selinux_generic_access_check(
718db961 198 sd_bus_message *message,
cad45ba1
LP
199 const char *path,
200 const char *permission,
718db961 201 sd_bus_error *error) {
cad45ba1 202
8a188de9 203#ifdef HAVE_SELINUX
5b12334d
LP
204 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
205 const char *tclass = NULL, *scon = NULL;
206 struct audit_info audit_info = {};
207 _cleanup_free_ char *cl = NULL;
208 security_context_t fcon = NULL;
209 char **cmdline = NULL;
718db961 210 int r = 0;
c3090674 211
cad45ba1
LP
212 assert(message);
213 assert(permission);
214 assert(error);
215
6baa7db0 216 if (!mac_selinux_use())
cad45ba1
LP
217 return 0;
218
8a188de9 219 r = mac_selinux_access_init(error);
ffc227c9
LP
220 if (r < 0)
221 return r;
222
5b12334d
LP
223 r = sd_bus_query_sender_creds(
224 message,
05bae4a6 225 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
5b12334d 226 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
8fd00193
LP
227 SD_BUS_CREDS_SELINUX_CONTEXT|
228 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
5b12334d
LP
229 &creds);
230 if (r < 0)
231 goto finish;
e2417e41 232
0f514420
LP
233 /* The SELinux context is something we really should have
234 * gotten directly from the message or sender, and not be an
235 * augmented field. If it was augmented we cannot use it for
236 * authorization, since this is racy and vulnerable. Let's add
237 * an extra check, just in case, even though this really
238 * shouldn't be possible. */
239 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
240
5b12334d 241 r = sd_bus_creds_get_selinux_context(creds, &scon);
718db961 242 if (r < 0)
e2417e41 243 goto finish;
cad45ba1 244
e2417e41 245 if (path) {
718db961
LP
246 /* Get the file context of the unit file */
247
e2417e41
DW
248 r = getfilecon(path, &fcon);
249 if (r < 0) {
718db961 250 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
e2417e41
DW
251 goto finish;
252 }
253
718db961 254 tclass = "service";
e2417e41 255 } else {
e2417e41
DW
256 r = getcon(&fcon);
257 if (r < 0) {
718db961 258 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
e2417e41
DW
259 goto finish;
260 }
718db961
LP
261
262 tclass = "system";
e2417e41
DW
263 }
264
5b12334d
LP
265 sd_bus_creds_get_cmdline(creds, &cmdline);
266 cl = strv_join(cmdline, " ");
267
268 audit_info.creds = creds;
269 audit_info.path = path;
270 audit_info.cmdline = cl;
e2417e41 271
236f83af 272 r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
718db961
LP
273 if (r < 0)
274 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41 275
5b12334d 276 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 277
e2417e41 278finish:
e2417e41
DW
279 freecon(fcon);
280
5b12334d 281 if (r < 0 && security_getenforce() != 1) {
718db961 282 sd_bus_error_free(error);
e2417e41
DW
283 r = 0;
284 }
285
286 return r;
8a188de9
WC
287#else
288 return 0;
289#endif
e2417e41
DW
290}
291
c04754bc
LP
292int mac_selinux_unit_access_check_strv(
293 char **units,
294 sd_bus_message *message,
295 Manager *m,
296 const char *permission,
297 sd_bus_error *error) {
298
8a188de9 299#ifdef HAVE_SELINUX
e94937df
LN
300 char **i;
301 Unit *u;
302 int r;
303
304 STRV_FOREACH(i, units) {
305 u = manager_get_unit(m, *i);
306 if (u) {
8a188de9 307 r = mac_selinux_unit_access_check(u, message, permission, error);
e94937df
LN
308 if (r < 0)
309 return r;
310 }
311 }
8a188de9 312#endif
e94937df
LN
313 return 0;
314}