chiark / gitweb /
3a244ad9ffab422c0bcbd829fe529d01dc778735
[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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "util.h"
23 #include "job.h"
24 #include "manager.h"
25 #include "selinux-access.h"
26
27 #ifdef HAVE_SELINUX
28 #include "dbus.h"
29 #include "log.h"
30 #include "dbus-unit.h"
31 #include "bus-errors.h"
32 #include "dbus-common.h"
33 #include "audit.h"
34 #include "selinux-util.h"
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <selinux/selinux.h>
40 #include <selinux/avc.h>
41 #ifdef HAVE_AUDIT
42 #include <libaudit.h>
43 #endif
44 #include <limits.h>
45
46 static bool initialized = false;
47 static int audit_fd = -1;
48
49 struct auditstruct {
50         const char *path;
51         char *cmdline;
52         uid_t loginuid;
53         uid_t uid;
54         gid_t gid;
55 };
56
57 static int bus_get_selinux_security_context(
58                 DBusConnection *connection,
59                 const char *name,
60                 char **scon,
61                 DBusError *error) {
62
63         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
64
65         m = dbus_message_new_method_call(
66                         DBUS_SERVICE_DBUS,
67                         DBUS_PATH_DBUS,
68                         DBUS_INTERFACE_DBUS,
69                         "GetConnectionSELinuxSecurityContext");
70         if (!m) {
71                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
72                 return -ENOMEM;
73         }
74
75         if (!dbus_message_append_args(
76                             m,
77                             DBUS_TYPE_STRING, &name,
78                             DBUS_TYPE_INVALID)) {
79                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
80                 return -ENOMEM;
81         }
82
83         reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
84         if (!reply)
85                 return -EIO;
86
87         if (dbus_set_error_from_message(error, reply))
88                 return -EIO;
89
90         if (!dbus_message_get_args(
91                             reply, error,
92                             DBUS_TYPE_STRING, scon,
93                             DBUS_TYPE_INVALID))
94                 return -EIO;
95
96         return 0;
97 }
98
99 static int bus_get_audit_data(
100                 DBusConnection *connection,
101                 const char *name,
102                 struct auditstruct *audit,
103                 DBusError *error) {
104
105         pid_t pid;
106         int r;
107
108         pid = bus_get_unix_process_id(connection, name, error);
109         if (pid <= 0)
110                 return -EIO;
111
112         r = audit_loginuid_from_pid(pid, &audit->loginuid);
113         if (r < 0)
114                 return r;
115
116         r = get_process_uid(pid, &audit->uid);
117         if (r < 0)
118                 return r;
119
120         r = get_process_gid(pid, &audit->gid);
121         if (r < 0)
122                 return r;
123
124         r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
125         if (r < 0)
126                 return r;
127
128         return 0;
129 }
130
131 /*
132    Any time an access gets denied this callback will be called
133    with the aduit data.  We then need to just copy the audit data into the msgbuf.
134 */
135 static int audit_callback(
136                 void *auditdata,
137                 security_class_t cls,
138                 char *msgbuf,
139                 size_t msgbufsize) {
140
141         struct auditstruct *audit = (struct auditstruct *) auditdata;
142
143         snprintf(msgbuf, msgbufsize,
144                  "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
145                  audit->loginuid,
146                  audit->uid,
147                  audit->gid,
148                  (audit->path ? " path=\"" : ""),
149                  strempty(audit->path),
150                  (audit->path ? "\"" : ""),
151                  (audit->cmdline ? " cmdline=\"" : ""),
152                  strempty(audit->cmdline),
153                  (audit->cmdline ? "\"" : ""));
154
155         msgbuf[msgbufsize-1] = 0;
156
157         return 0;
158 }
159
160 /*
161    Any time an access gets denied this callback will be called
162    code copied from dbus. If audit is turned on the messages will go as
163    user_avc's into the /var/log/audit/audit.log, otherwise they will be
164    sent to syslog.
165 */
166 static int log_callback(int type, const char *fmt, ...) {
167         va_list ap;
168
169         va_start(ap, fmt);
170
171 #ifdef HAVE_AUDIT
172         if (audit_fd >= 0) {
173                 char buf[LINE_MAX];
174
175                 vsnprintf(buf, sizeof(buf), fmt, ap);
176                 audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
177                 va_end(ap);
178
179                 return 0;
180         }
181 #endif
182         log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
183         va_end(ap);
184
185         return 0;
186 }
187
188 /*
189    Function must be called once to initialize the SELinux AVC environment.
190    Sets up callbacks.
191    If you want to cleanup memory you should need to call selinux_access_finish.
192 */
193 static int access_init(void) {
194         int r;
195
196         if (avc_open(NULL, 0)) {
197                 log_error("avc_open() failed: %m");
198                 return -errno;
199         }
200
201         selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
202         selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
203
204         if (security_getenforce() >= 0)
205                 return 0;
206
207         r = -errno;
208         avc_destroy();
209
210         return r;
211 }
212
213 static int selinux_init(Manager *m, DBusError *error) {
214         int r;
215
216 #ifdef HAVE_AUDIT
217         audit_fd = m->audit_fd;
218 #endif
219         if (initialized)
220                 return 0;
221
222         if (use_selinux()) {
223                 r = access_init();
224                 if (r < 0) {
225                         dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
226                         return r;
227                 }
228         }
229
230         initialized = true;
231         return 0;
232 }
233
234 static int get_audit_data(
235                 DBusConnection *connection,
236                 DBusMessage *message,
237                 struct auditstruct *audit,
238                 DBusError *error) {
239
240         const char *sender;
241         int r, fd;
242         struct ucred ucred;
243         socklen_t len;
244
245         sender = dbus_message_get_sender(message);
246         if (sender)
247                 return bus_get_audit_data(connection, sender, audit, error);
248
249         if (!dbus_connection_get_unix_fd(connection, &fd))
250                 return -EINVAL;
251
252         r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
253         if (r < 0) {
254                 log_error("Failed to determine peer credentials: %m");
255                 return -errno;
256         }
257
258         audit->uid = ucred.uid;
259         audit->gid = ucred.gid;
260
261         r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
262         if (r < 0)
263                 return r;
264
265         r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
266         if (r < 0)
267                 return r;
268
269         return 0;
270 }
271
272 /*
273    This function returns the security context of the remote end of the dbus
274    connections.  Whether it is on the bus or a local connection.
275 */
276 static int get_calling_context(
277                 DBusConnection *connection,
278                 DBusMessage *message,
279                 security_context_t *scon,
280                 DBusError *error) {
281
282         const char *sender;
283         int r;
284         int fd;
285
286         /*
287            If sender exists then
288            if sender is NULL this indicates a local connection.  Grab the fd
289            from dbus and do an getpeercon to peers process context
290         */
291         sender = dbus_message_get_sender(message);
292         if (sender) {
293                 r = bus_get_selinux_security_context(connection, sender, scon, error);
294                 if (r >= 0)
295                         return r;
296
297                 log_debug("bus_get_selinux_security_context failed %m");
298         }
299
300         if (!dbus_connection_get_unix_fd(connection, &fd)) {
301                 log_error("bus_connection_get_unix_fd failed %m");
302                 return -EINVAL;
303         }
304
305         r = getpeercon(fd, scon);
306         if (r < 0) {
307                 log_error("getpeercon failed %m");
308                 return -errno;
309         }
310
311         return 0;
312 }
313
314 /*
315    This function communicates with the kernel to check whether or not it should
316    allow the access.
317    If the machine is in permissive mode it will return ok.  Audit messages will
318    still be generated if the access would be denied in enforcing mode.
319 */
320 static int selinux_access_check(
321                 Manager *m,
322                 DBusConnection *connection,
323                 DBusMessage *message,
324                 const char *path,
325                 const char *permission,
326                 DBusError *error) {
327
328         security_context_t scon = NULL, fcon = NULL;
329         int r = 0;
330         const char *tclass = NULL;
331         struct auditstruct audit;
332
333         assert(m);
334         assert(connection);
335         assert(message);
336         assert(permission);
337         assert(error);
338
339         r = selinux_init(m, error);
340         if (r < 0)
341                 return r;
342
343         if (!use_selinux())
344                 return 0;
345
346         log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
347
348         audit.uid = audit.loginuid = (uid_t) -1;
349         audit.gid = (gid_t) -1;
350         audit.cmdline = NULL;
351         audit.path = path;
352
353         r = get_calling_context(connection, message, &scon, error);
354         if (r < 0) {
355                 log_error("Failed to get caller's security context on: %m");
356                 goto finish;
357         }
358
359         if (path) {
360                 tclass = "service";
361                 /* get the file context of the unit file */
362                 r = getfilecon(path, &fcon);
363                 if (r < 0) {
364                         dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
365                         r = -errno;
366                         log_error("Failed to get security context on %s: %m",path);
367                         goto finish;
368                 }
369
370         } else {
371                 tclass = "system";
372                 r = getcon(&fcon);
373                 if (r < 0) {
374                         dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
375                         r = -errno;
376                         log_error("Failed to get current process context on: %m");
377                         goto finish;
378                 }
379         }
380
381         (void) get_audit_data(connection, message, &audit, error);
382
383         errno = 0;
384         r = selinux_check_access(scon, fcon, tclass, permission, &audit);
385         if (r < 0) {
386                 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
387                 r = -errno;
388                 log_error("SELinux policy denies access.");
389         }
390
391         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);
392
393 finish:
394         free(audit.cmdline);
395         freecon(scon);
396         freecon(fcon);
397
398         if (r && security_getenforce() != 1) {
399                 dbus_error_init(error);
400                 r = 0;
401         }
402
403         return r;
404 }
405
406 int selinux_unit_access_check(
407                 Unit *u,
408                 DBusConnection *connection,
409                 DBusMessage *message,
410                 const char *permission,
411                 DBusError *error) {
412
413         assert(u);
414         assert(connection);
415         assert(message);
416         assert(permission);
417         assert(error);
418
419         return selinux_access_check(u->manager, connection, message, u->source_path ? u->source_path : u->fragment_path, permission, error);
420 }
421
422 int selinux_manager_access_check(
423                 Manager *m,
424                 DBusConnection *connection,
425                 DBusMessage *message,
426                 const char *permission,
427                 DBusError *error) {
428
429         assert(m);
430         assert(connection);
431         assert(message);
432         assert(permission);
433         assert(error);
434
435         return selinux_access_check(m, connection, message, NULL, permission, error);
436 }
437
438 void selinux_access_finish(void) {
439         if (!initialized)
440                 return;
441
442         avc_destroy();
443         initialized = false;
444 }
445
446 #else
447
448 int selinux_unit_access_check(
449                 Unit *u,
450                 DBusConnection *connection,
451                 DBusMessage *message,
452                 const char *permission,
453                 DBusError *error) {
454
455         return 0;
456 }
457
458 int selinux_manager_access_check(
459                 Manager *m,
460                 DBusConnection *connection,
461                 DBusMessage *message,
462                 const char *permission,
463                 DBusError *error) {
464
465         return 0;
466 }
467
468 void selinux_access_finish(void) {
469 }
470
471 #endif