]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
bus: update kdbus.h
[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>
27#include <string.h>
28#include <errno.h>
ffc227c9 29#include <limits.h>
e2417e41
DW
30#include <selinux/selinux.h>
31#include <selinux/avc.h>
32#ifdef HAVE_AUDIT
33#include <libaudit.h>
34#endif
ffc227c9 35
718db961
LP
36#include "sd-bus.h"
37#include "bus-util.h"
ffc227c9
LP
38#include "util.h"
39#include "log.h"
ffc227c9
LP
40#include "audit.h"
41#include "selinux-util.h"
42#include "audit-fd.h"
e2417e41 43
cad45ba1 44static bool initialized = false;
e2417e41
DW
45
46struct auditstruct {
47 const char *path;
48 char *cmdline;
49 uid_t loginuid;
50 uid_t uid;
51 gid_t gid;
52};
53
e2417e41 54static int bus_get_selinux_security_context(
718db961 55 sd_bus *bus,
e2417e41 56 const char *name,
718db961
LP
57 sd_bus_error *error,
58 char **ret) {
e2417e41 59
718db961
LP
60 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
61 const void *p;
62 size_t sz;
a33c48d8 63 char *b;
718db961 64 int r;
e2417e41 65
718db961
LP
66 assert(bus);
67 assert(name);
68 assert(ret);
69
70 r = sd_bus_call_method(
71 bus,
72 "org.freedesktop.DBus",
73 "/org/freedesktop/DBus",
74 "org.freedesktop.DBus",
75 "GetConnectionSELinuxSecurityContext",
76 error, &m,
77 "s", name);
78 if (r < 0)
79 return r;
a33c48d8 80
718db961
LP
81 r = sd_bus_message_read_array(m, 'y', &p, &sz);
82 if (r < 0)
83 return r;
a33c48d8 84
718db961 85 b = strndup(p, sz);
a33c48d8
DW
86 if (!b)
87 return -ENOMEM;
88
718db961 89 *ret = b;
cad45ba1 90 return 0;
e2417e41
DW
91}
92
e2417e41 93static int bus_get_audit_data(
718db961 94 sd_bus *bus,
e2417e41 95 const char *name,
718db961 96 struct auditstruct *audit) {
e2417e41
DW
97
98 pid_t pid;
c3090674 99 int r;
e2417e41 100
718db961
LP
101 assert(bus);
102 assert(name);
103 assert(audit);
104
105 r = sd_bus_get_owner_pid(bus, name, &pid);
106 if (r < 0)
107 return r;
e2417e41 108
c3090674
LP
109 r = audit_loginuid_from_pid(pid, &audit->loginuid);
110 if (r < 0)
111 return r;
e2417e41 112
c3090674
LP
113 r = get_process_uid(pid, &audit->uid);
114 if (r < 0)
115 return r;
e2417e41 116
c3090674
LP
117 r = get_process_gid(pid, &audit->gid);
118 if (r < 0)
119 return r;
e2417e41 120
9bdbc2e2 121 r = get_process_cmdline(pid, 0, true, &audit->cmdline);
c3090674
LP
122 if (r < 0)
123 return r;
e2417e41 124
c3090674 125 return 0;
e2417e41
DW
126}
127
128/*
129 Any time an access gets denied this callback will be called
130 with the aduit data. We then need to just copy the audit data into the msgbuf.
131*/
cad45ba1
LP
132static int audit_callback(
133 void *auditdata,
134 security_class_t cls,
135 char *msgbuf,
136 size_t msgbufsize) {
137
e2417e41 138 struct auditstruct *audit = (struct auditstruct *) auditdata;
cad45ba1 139
e2417e41 140 snprintf(msgbuf, msgbufsize,
cad45ba1 141 "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
d67227c8 142 audit->loginuid,
cad45ba1
LP
143 audit->uid,
144 audit->gid,
145 (audit->path ? " path=\"" : ""),
146 strempty(audit->path),
147 (audit->path ? "\"" : ""),
148 (audit->cmdline ? " cmdline=\"" : ""),
149 strempty(audit->cmdline),
150 (audit->cmdline ? "\"" : ""));
d67227c8 151
cad45ba1 152 msgbuf[msgbufsize-1] = 0;
d67227c8 153
e2417e41
DW
154 return 0;
155}
156
157/*
158 Any time an access gets denied this callback will be called
159 code copied from dbus. If audit is turned on the messages will go as
160 user_avc's into the /var/log/audit/audit.log, otherwise they will be
161 sent to syslog.
162*/
44b601bc 163_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41
DW
164 va_list ap;
165
166 va_start(ap, fmt);
cad45ba1 167
e2417e41 168#ifdef HAVE_AUDIT
c1165f82 169 if (get_audit_fd() >= 0) {
ace188cf
LP
170 _cleanup_free_ char *buf = NULL;
171 int r;
e2417e41 172
ace188cf 173 r = vasprintf(&buf, fmt, ap);
7f1736f7 174 va_end(ap);
cad45ba1 175
ace188cf
LP
176 if (r >= 0) {
177 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
178 return 0;
179 }
180
181 va_start(ap, fmt);
e2417e41
DW
182 }
183#endif
184 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
185 va_end(ap);
cad45ba1 186
e2417e41
DW
187 return 0;
188}
189
190/*
191 Function must be called once to initialize the SELinux AVC environment.
192 Sets up callbacks.
193 If you want to cleanup memory you should need to call selinux_access_finish.
194*/
195static int access_init(void) {
718db961 196 int r = 0;
e2417e41
DW
197
198 if (avc_open(NULL, 0)) {
cad45ba1 199 log_error("avc_open() failed: %m");
e2417e41
DW
200 return -errno;
201 }
202
cad45ba1
LP
203 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
204 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
e2417e41 205
718db961
LP
206 if (security_getenforce() < 0){
207 r = -errno;
208 avc_destroy();
209 }
cad45ba1 210
e2417e41
DW
211 return r;
212}
213
718db961 214static int selinux_access_init(sd_bus_error *error) {
e2417e41
DW
215 int r;
216
cad45ba1 217 if (initialized)
e2417e41
DW
218 return 0;
219
718db961
LP
220 if (!use_selinux())
221 return 0;
222
223 r = access_init();
224 if (r < 0)
225 return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
e2417e41 226
cad45ba1 227 initialized = true;
e2417e41
DW
228 return 0;
229}
230
ffc227c9 231void selinux_access_free(void) {
718db961 232
ffc227c9
LP
233 if (!initialized)
234 return;
235
236 avc_destroy();
237 initialized = false;
238}
239
e2417e41 240static int get_audit_data(
718db961
LP
241 sd_bus *bus,
242 sd_bus_message *message,
243 struct auditstruct *audit) {
e2417e41 244
718db961 245 struct ucred ucred;
e2417e41 246 const char *sender;
718db961 247 socklen_t len;
cad45ba1 248 int r, fd;
e2417e41 249
718db961 250 sender = sd_bus_message_get_sender(message);
c3090674 251 if (sender)
718db961 252 return bus_get_audit_data(bus, sender, audit);
c3090674 253
718db961
LP
254 fd = sd_bus_get_fd(bus);
255 if (fd < 0)
256 return fd;
e2417e41 257
718db961 258 len = sizeof(ucred);
cad45ba1 259 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
718db961 260 if (r < 0)
cad45ba1 261 return -errno;
c3090674 262
cad45ba1
LP
263 audit->uid = ucred.uid;
264 audit->gid = ucred.gid;
e2417e41 265
cad45ba1
LP
266 r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
267 if (r < 0)
268 return r;
e2417e41 269
9bdbc2e2 270 r = get_process_cmdline(ucred.pid, 0, true, &audit->cmdline);
cad45ba1
LP
271 if (r < 0)
272 return r;
e2417e41 273
cad45ba1 274 return 0;
e2417e41
DW
275}
276
277/*
278 This function returns the security context of the remote end of the dbus
279 connections. Whether it is on the bus or a local connection.
280*/
281static int get_calling_context(
718db961
LP
282 sd_bus *bus,
283 sd_bus_message *message,
284 sd_bus_error *error,
285 security_context_t *ret) {
e2417e41
DW
286
287 const char *sender;
718db961 288 int r, fd;
e2417e41
DW
289
290 /*
291 If sender exists then
292 if sender is NULL this indicates a local connection. Grab the fd
293 from dbus and do an getpeercon to peers process context
294 */
718db961
LP
295 sender = sd_bus_message_get_sender(message);
296 if (sender)
297 return bus_get_selinux_security_context(bus, sender, error, ret);
d67227c8 298
718db961
LP
299 fd = sd_bus_get_fd(bus);
300 if (fd < 0)
301 return fd;
d67227c8 302
718db961
LP
303 r = getpeercon(fd, ret);
304 if (r < 0)
d67227c8 305 return -errno;
e2417e41
DW
306
307 return 0;
308}
309
e2417e41
DW
310/*
311 This function communicates with the kernel to check whether or not it should
312 allow the access.
313 If the machine is in permissive mode it will return ok. Audit messages will
314 still be generated if the access would be denied in enforcing mode.
315*/
ffc227c9 316int selinux_access_check(
718db961
LP
317 sd_bus *bus,
318 sd_bus_message *message,
cad45ba1
LP
319 const char *path,
320 const char *permission,
718db961 321 sd_bus_error *error) {
cad45ba1
LP
322
323 security_context_t scon = NULL, fcon = NULL;
e2417e41
DW
324 const char *tclass = NULL;
325 struct auditstruct audit;
718db961 326 int r = 0;
c3090674 327
718db961 328 assert(bus);
cad45ba1
LP
329 assert(message);
330 assert(permission);
331 assert(error);
332
cad45ba1
LP
333 if (!use_selinux())
334 return 0;
335
ffc227c9
LP
336 r = selinux_access_init(error);
337 if (r < 0)
338 return r;
339
c3090674
LP
340 audit.uid = audit.loginuid = (uid_t) -1;
341 audit.gid = (gid_t) -1;
e2417e41
DW
342 audit.cmdline = NULL;
343 audit.path = path;
344
718db961
LP
345 r = get_calling_context(bus, message, error, &scon);
346 if (r < 0)
e2417e41 347 goto finish;
cad45ba1 348
e2417e41 349 if (path) {
718db961
LP
350 /* Get the file context of the unit file */
351
e2417e41
DW
352 r = getfilecon(path, &fcon);
353 if (r < 0) {
718db961 354 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
e2417e41
DW
355 goto finish;
356 }
357
718db961 358 tclass = "service";
e2417e41 359 } else {
e2417e41
DW
360 r = getcon(&fcon);
361 if (r < 0) {
718db961 362 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
e2417e41
DW
363 goto finish;
364 }
718db961
LP
365
366 tclass = "system";
e2417e41
DW
367 }
368
718db961 369 get_audit_data(bus, message, &audit);
e2417e41 370
cad45ba1
LP
371 errno = 0;
372 r = selinux_check_access(scon, fcon, tclass, permission, &audit);
718db961
LP
373 if (r < 0)
374 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41 375
cad45ba1
LP
376 log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, audit.cmdline, r);
377
e2417e41 378finish:
e2417e41
DW
379 free(audit.cmdline);
380 freecon(scon);
381 freecon(fcon);
382
cad45ba1 383 if (r && security_getenforce() != 1) {
718db961 384 sd_bus_error_free(error);
e2417e41
DW
385 r = 0;
386 }
387
388 return r;
389}
390
e2417e41 391#else
cad45ba1 392
ffc227c9 393int selinux_access_check(
718db961
LP
394 sd_bus *bus,
395 sd_bus_message *message,
ffc227c9 396 const char *path,
cad45ba1 397 const char *permission,
718db961 398 sd_bus_error *error) {
cad45ba1 399
e2417e41
DW
400 return 0;
401}
402
ffc227c9 403void selinux_access_free(void) {
c3090674 404}
cad45ba1 405
e2417e41 406#endif