chiark / gitweb /
core: convert PID 1 to libsystemd-bus
[elogind.git] / src / core / selinux-access.c
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
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
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
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "selinux-access.h"
23
24 #ifdef HAVE_SELINUX
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <limits.h>
30 #include <selinux/selinux.h>
31 #include <selinux/avc.h>
32 #ifdef HAVE_AUDIT
33 #include <libaudit.h>
34 #endif
35
36 #include "sd-bus.h"
37 #include "bus-util.h"
38 #include "util.h"
39 #include "log.h"
40 #include "audit.h"
41 #include "selinux-util.h"
42 #include "audit-fd.h"
43
44 static bool initialized = false;
45
46 struct auditstruct {
47         const char *path;
48         char *cmdline;
49         uid_t loginuid;
50         uid_t uid;
51         gid_t gid;
52 };
53
54 static int bus_get_selinux_security_context(
55                 sd_bus *bus,
56                 const char *name,
57                 sd_bus_error *error,
58                 char **ret) {
59
60         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
61         const void *p;
62         size_t sz;
63         char *b;
64         int r;
65
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;
80
81         r = sd_bus_message_read_array(m, 'y', &p, &sz);
82         if (r < 0)
83                 return r;
84
85         b = strndup(p, sz);
86         if (!b)
87                 return -ENOMEM;
88
89         *ret = b;
90         return 0;
91 }
92
93 static int bus_get_audit_data(
94                 sd_bus *bus,
95                 const char *name,
96                 struct auditstruct *audit) {
97
98         pid_t pid;
99         int r;
100
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;
108
109         r = audit_loginuid_from_pid(pid, &audit->loginuid);
110         if (r < 0)
111                 return r;
112
113         r = get_process_uid(pid, &audit->uid);
114         if (r < 0)
115                 return r;
116
117         r = get_process_gid(pid, &audit->gid);
118         if (r < 0)
119                 return r;
120
121         r = get_process_cmdline(pid, 0, true, &audit->cmdline);
122         if (r < 0)
123                 return r;
124
125         return 0;
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 */
132 static int audit_callback(
133                 void *auditdata,
134                 security_class_t cls,
135                 char *msgbuf,
136                 size_t msgbufsize) {
137
138         struct auditstruct *audit = (struct auditstruct *) auditdata;
139
140         snprintf(msgbuf, msgbufsize,
141                  "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
142                  audit->loginuid,
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 ? "\"" : ""));
151
152         msgbuf[msgbufsize-1] = 0;
153
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 */
163 _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
164         va_list ap;
165
166         va_start(ap, fmt);
167
168 #ifdef HAVE_AUDIT
169         if (get_audit_fd() >= 0) {
170                 _cleanup_free_ char *buf = NULL;
171                 int r;
172
173                 r = vasprintf(&buf, fmt, ap);
174                 va_end(ap);
175
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);
182         }
183 #endif
184         log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
185         va_end(ap);
186
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 */
195 static int access_init(void) {
196         int r = 0;
197
198         if (avc_open(NULL, 0)) {
199                 log_error("avc_open() failed: %m");
200                 return -errno;
201         }
202
203         selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
204         selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
205
206         if (security_getenforce() < 0){
207                 r = -errno;
208                 avc_destroy();
209         }
210
211         return r;
212 }
213
214 static int selinux_access_init(sd_bus_error *error) {
215         int r;
216
217         if (initialized)
218                 return 0;
219
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.");
226
227         initialized = true;
228         return 0;
229 }
230
231 void selinux_access_free(void) {
232
233         if (!initialized)
234                 return;
235
236         avc_destroy();
237         initialized = false;
238 }
239
240 static int get_audit_data(
241                 sd_bus *bus,
242                 sd_bus_message *message,
243                 struct auditstruct *audit) {
244
245         struct ucred ucred;
246         const char *sender;
247         socklen_t len;
248         int r, fd;
249
250         sender = sd_bus_message_get_sender(message);
251         if (sender)
252                 return bus_get_audit_data(bus, sender, audit);
253
254         fd = sd_bus_get_fd(bus);
255         if (fd < 0)
256                 return fd;
257
258         len = sizeof(ucred);
259         r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
260         if (r < 0)
261                 return -errno;
262
263         audit->uid = ucred.uid;
264         audit->gid = ucred.gid;
265
266         r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
267         if (r < 0)
268                 return r;
269
270         r = get_process_cmdline(ucred.pid, 0, true, &audit->cmdline);
271         if (r < 0)
272                 return r;
273
274         return 0;
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 */
281 static int get_calling_context(
282                 sd_bus *bus,
283                 sd_bus_message *message,
284                 sd_bus_error *error,
285                 security_context_t *ret) {
286
287         const char *sender;
288         int r, fd;
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         */
295         sender = sd_bus_message_get_sender(message);
296         if (sender)
297                 return bus_get_selinux_security_context(bus, sender, error, ret);
298
299         fd = sd_bus_get_fd(bus);
300         if (fd < 0)
301                 return fd;
302
303         r = getpeercon(fd, ret);
304         if (r < 0)
305                 return -errno;
306
307         return 0;
308 }
309
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 */
316 int selinux_access_check(
317                 sd_bus *bus,
318                 sd_bus_message *message,
319                 const char *path,
320                 const char *permission,
321                 sd_bus_error *error) {
322
323         security_context_t scon = NULL, fcon = NULL;
324         const char *tclass = NULL;
325         struct auditstruct audit;
326         int r = 0;
327
328         assert(bus);
329         assert(message);
330         assert(permission);
331         assert(error);
332
333         if (!use_selinux())
334                 return 0;
335
336         r = selinux_access_init(error);
337         if (r < 0)
338                 return r;
339
340         audit.uid = audit.loginuid = (uid_t) -1;
341         audit.gid = (gid_t) -1;
342         audit.cmdline = NULL;
343         audit.path = path;
344
345         r = get_calling_context(bus, message, error, &scon);
346         if (r < 0)
347                 goto finish;
348
349         if (path) {
350                 /* Get the file context of the unit file */
351
352                 r = getfilecon(path, &fcon);
353                 if (r < 0) {
354                         r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
355                         goto finish;
356                 }
357
358                 tclass = "service";
359         } else {
360                 r = getcon(&fcon);
361                 if (r < 0) {
362                         r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
363                         goto finish;
364                 }
365
366                 tclass = "system";
367         }
368
369         get_audit_data(bus, message, &audit);
370
371         errno = 0;
372         r = selinux_check_access(scon, fcon, tclass, permission, &audit);
373         if (r < 0)
374                 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
375
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
378 finish:
379         free(audit.cmdline);
380         freecon(scon);
381         freecon(fcon);
382
383         if (r && security_getenforce() != 1) {
384                 sd_bus_error_free(error);
385                 r = 0;
386         }
387
388         return r;
389 }
390
391 #else
392
393 int selinux_access_check(
394                 sd_bus *bus,
395                 sd_bus_message *message,
396                 const char *path,
397                 const char *permission,
398                 sd_bus_error *error) {
399
400         return 0;
401 }
402
403 void selinux_access_free(void) {
404 }
405
406 #endif