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