chiark / gitweb /
audit: turn the audit fd into a static variable
[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 #include "audit-fd.h"
36
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <selinux/selinux.h>
41 #include <selinux/avc.h>
42 #ifdef HAVE_AUDIT
43 #include <libaudit.h>
44 #endif
45 #include <limits.h>
46
47 static bool initialized = false;
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 (get_audit_fd() >= 0) {
173                 char buf[LINE_MAX];
174
175                 vsnprintf(buf, sizeof(buf), fmt, ap);
176                 audit_log_user_avc_message(get_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(DBusError *error) {
214         int r;
215
216         if (initialized)
217                 return 0;
218
219         if (use_selinux()) {
220                 r = access_init();
221                 if (r < 0) {
222                         dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
223                         return r;
224                 }
225         }
226
227         initialized = true;
228         return 0;
229 }
230
231 static int get_audit_data(
232                 DBusConnection *connection,
233                 DBusMessage *message,
234                 struct auditstruct *audit,
235                 DBusError *error) {
236
237         const char *sender;
238         int r, fd;
239         struct ucred ucred;
240         socklen_t len;
241
242         sender = dbus_message_get_sender(message);
243         if (sender)
244                 return bus_get_audit_data(connection, sender, audit, error);
245
246         if (!dbus_connection_get_unix_fd(connection, &fd))
247                 return -EINVAL;
248
249         r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
250         if (r < 0) {
251                 log_error("Failed to determine peer credentials: %m");
252                 return -errno;
253         }
254
255         audit->uid = ucred.uid;
256         audit->gid = ucred.gid;
257
258         r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
259         if (r < 0)
260                 return r;
261
262         r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
263         if (r < 0)
264                 return r;
265
266         return 0;
267 }
268
269 /*
270    This function returns the security context of the remote end of the dbus
271    connections.  Whether it is on the bus or a local connection.
272 */
273 static int get_calling_context(
274                 DBusConnection *connection,
275                 DBusMessage *message,
276                 security_context_t *scon,
277                 DBusError *error) {
278
279         const char *sender;
280         int r;
281         int fd;
282
283         /*
284            If sender exists then
285            if sender is NULL this indicates a local connection.  Grab the fd
286            from dbus and do an getpeercon to peers process context
287         */
288         sender = dbus_message_get_sender(message);
289         if (sender) {
290                 r = bus_get_selinux_security_context(connection, sender, scon, error);
291                 if (r >= 0)
292                         return r;
293
294                 log_debug("bus_get_selinux_security_context failed %m");
295         }
296
297         if (!dbus_connection_get_unix_fd(connection, &fd)) {
298                 log_error("bus_connection_get_unix_fd failed %m");
299                 return -EINVAL;
300         }
301
302         r = getpeercon(fd, scon);
303         if (r < 0) {
304                 log_error("getpeercon failed %m");
305                 return -errno;
306         }
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 static int selinux_access_check(
318                 DBusConnection *connection,
319                 DBusMessage *message,
320                 const char *path,
321                 const char *permission,
322                 DBusError *error) {
323
324         security_context_t scon = NULL, fcon = NULL;
325         int r = 0;
326         const char *tclass = NULL;
327         struct auditstruct audit;
328
329         assert(connection);
330         assert(message);
331         assert(permission);
332         assert(error);
333
334         r = selinux_init(error);
335         if (r < 0)
336                 return r;
337
338         if (!use_selinux())
339                 return 0;
340
341         log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
342
343         audit.uid = audit.loginuid = (uid_t) -1;
344         audit.gid = (gid_t) -1;
345         audit.cmdline = NULL;
346         audit.path = path;
347
348         r = get_calling_context(connection, message, &scon, error);
349         if (r < 0) {
350                 log_error("Failed to get caller's security context on: %m");
351                 goto finish;
352         }
353
354         if (path) {
355                 tclass = "service";
356                 /* get the file context of the unit file */
357                 r = getfilecon(path, &fcon);
358                 if (r < 0) {
359                         dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
360                         r = -errno;
361                         log_error("Failed to get security context on %s: %m",path);
362                         goto finish;
363                 }
364
365         } else {
366                 tclass = "system";
367                 r = getcon(&fcon);
368                 if (r < 0) {
369                         dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
370                         r = -errno;
371                         log_error("Failed to get current process context on: %m");
372                         goto finish;
373                 }
374         }
375
376         (void) get_audit_data(connection, message, &audit, error);
377
378         errno = 0;
379         r = selinux_check_access(scon, fcon, tclass, permission, &audit);
380         if (r < 0) {
381                 dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
382                 r = -errno;
383                 log_error("SELinux policy denies access.");
384         }
385
386         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);
387
388 finish:
389         free(audit.cmdline);
390         freecon(scon);
391         freecon(fcon);
392
393         if (r && security_getenforce() != 1) {
394                 dbus_error_init(error);
395                 r = 0;
396         }
397
398         return r;
399 }
400
401 int selinux_unit_access_check(
402                 Unit *u,
403                 DBusConnection *connection,
404                 DBusMessage *message,
405                 const char *permission,
406                 DBusError *error) {
407
408         assert(u);
409         assert(connection);
410         assert(message);
411         assert(permission);
412         assert(error);
413
414         return selinux_access_check(connection, message, u->source_path ? u->source_path : u->fragment_path, permission, error);
415 }
416
417 int selinux_manager_access_check(
418                 Manager *m,
419                 DBusConnection *connection,
420                 DBusMessage *message,
421                 const char *permission,
422                 DBusError *error) {
423
424         assert(m);
425         assert(connection);
426         assert(message);
427         assert(permission);
428         assert(error);
429
430         return selinux_access_check(connection, message, NULL, permission, error);
431 }
432
433 void selinux_access_finish(void) {
434         if (!initialized)
435                 return;
436
437         avc_destroy();
438         initialized = false;
439 }
440
441 #else
442
443 int selinux_unit_access_check(
444                 Unit *u,
445                 DBusConnection *connection,
446                 DBusMessage *message,
447                 const char *permission,
448                 DBusError *error) {
449
450         return 0;
451 }
452
453 int selinux_manager_access_check(
454                 Manager *m,
455                 DBusConnection *connection,
456                 DBusMessage *message,
457                 const char *permission,
458                 DBusError *error) {
459
460         return 0;
461 }
462
463 void selinux_access_finish(void) {
464 }
465
466 #endif