chiark / gitweb /
networkd: add basic dbus API
[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 #include "strv.h"
45
46 static bool initialized = false;
47
48 struct audit_info {
49         sd_bus_creds *creds;
50         const char *path;
51         const char *cmdline;
52 };
53
54 /*
55    Any time an access gets denied this callback will be called
56    with the audit data.  We then need to just copy the audit data into the msgbuf.
57 */
58 static int audit_callback(
59                 void *auditdata,
60                 security_class_t cls,
61                 char *msgbuf,
62                 size_t msgbufsize) {
63
64         const struct audit_info *audit = auditdata;
65         uid_t uid = 0, login_uid = 0;
66         gid_t gid = 0;
67         char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
68         char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
69         char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a";
70
71         if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
72                 xsprintf(login_uid_buf, UID_FMT, login_uid);
73         if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
74                 xsprintf(uid_buf, UID_FMT, uid);
75         if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
76                 xsprintf(gid_buf, GID_FMT, gid);
77
78         snprintf(msgbuf, msgbufsize,
79                  "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
80                  login_uid_buf, uid_buf, gid_buf,
81                  audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
82                  audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
83
84         return 0;
85 }
86
87 /*
88    Any time an access gets denied this callback will be called
89    code copied from dbus. If audit is turned on the messages will go as
90    user_avc's into the /var/log/audit/audit.log, otherwise they will be
91    sent to syslog.
92 */
93 _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
94         va_list ap;
95
96 #ifdef HAVE_AUDIT
97         if (get_audit_fd() >= 0) {
98                 _cleanup_free_ char *buf = NULL;
99                 int r;
100
101                 va_start(ap, fmt);
102                 r = vasprintf(&buf, fmt, ap);
103                 va_end(ap);
104
105                 if (r >= 0) {
106                         audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
107                         return 0;
108                 }
109         }
110 #endif
111
112         va_start(ap, fmt);
113         log_internalv(LOG_AUTH | LOG_INFO, 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
114         va_end(ap);
115
116         return 0;
117 }
118
119 /*
120    Function must be called once to initialize the SELinux AVC environment.
121    Sets up callbacks.
122    If you want to cleanup memory you should need to call selinux_access_finish.
123 */
124 static int access_init(void) {
125         int r = 0;
126
127         if (avc_open(NULL, 0))
128                 return log_error_errno(errno, "avc_open() failed: %m");
129
130         selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
131         selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
132
133         if (security_getenforce() < 0){
134                 r = -errno;
135                 avc_destroy();
136         }
137
138         return r;
139 }
140
141 static int mac_selinux_access_init(sd_bus_error *error) {
142         int r;
143
144         if (initialized)
145                 return 0;
146
147         if (!mac_selinux_use())
148                 return 0;
149
150         r = access_init();
151         if (r < 0)
152                 return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
153
154         initialized = true;
155         return 0;
156 }
157 #endif
158
159 void mac_selinux_access_free(void) {
160
161 #ifdef HAVE_SELINUX
162         if (!initialized)
163                 return;
164
165         avc_destroy();
166         initialized = false;
167 #endif
168 }
169
170 /*
171    This function communicates with the kernel to check whether or not it should
172    allow the access.
173    If the machine is in permissive mode it will return ok.  Audit messages will
174    still be generated if the access would be denied in enforcing mode.
175 */
176 int mac_selinux_generic_access_check(
177                 sd_bus_message *message,
178                 const char *path,
179                 const char *permission,
180                 sd_bus_error *error) {
181
182 #ifdef HAVE_SELINUX
183         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
184         const char *tclass = NULL, *scon = NULL;
185         struct audit_info audit_info = {};
186         _cleanup_free_ char *cl = NULL;
187         security_context_t fcon = NULL;
188         char **cmdline = NULL;
189         int r = 0;
190
191         assert(message);
192         assert(permission);
193         assert(error);
194
195         if (!mac_selinux_use())
196                 return 0;
197
198         r = mac_selinux_access_init(error);
199         if (r < 0)
200                 return r;
201
202         r = sd_bus_query_sender_creds(
203                         message,
204                         SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
205                         SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
206                         SD_BUS_CREDS_SELINUX_CONTEXT|
207                         SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
208                         &creds);
209         if (r < 0)
210                 goto finish;
211
212         r = sd_bus_creds_get_selinux_context(creds, &scon);
213         if (r < 0)
214                 goto finish;
215
216         if (path) {
217                 /* Get the file context of the unit file */
218
219                 r = getfilecon(path, &fcon);
220                 if (r < 0) {
221                         r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
222                         goto finish;
223                 }
224
225                 tclass = "service";
226         } else {
227                 r = getcon(&fcon);
228                 if (r < 0) {
229                         r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
230                         goto finish;
231                 }
232
233                 tclass = "system";
234         }
235
236         sd_bus_creds_get_cmdline(creds, &cmdline);
237         cl = strv_join(cmdline, " ");
238
239         audit_info.creds = creds;
240         audit_info.path = path;
241         audit_info.cmdline = cl;
242
243         r = selinux_check_access((security_context_t) scon, fcon, tclass, permission, &audit_info);
244         if (r < 0)
245                 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
246
247         log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r);
248
249 finish:
250         freecon(fcon);
251
252         if (r < 0 && security_getenforce() != 1) {
253                 sd_bus_error_free(error);
254                 r = 0;
255         }
256
257         return r;
258 #else
259         return 0;
260 #endif
261 }
262
263 int mac_selinux_unit_access_check_strv(char **units,
264                                 sd_bus_message *message,
265                                 Manager *m,
266                                 const char *permission,
267                                 sd_bus_error *error) {
268 #ifdef HAVE_SELINUX
269         char **i;
270         Unit *u;
271         int r;
272
273         STRV_FOREACH(i, units) {
274                 u = manager_get_unit(m, *i);
275                 if (u) {
276                         r = mac_selinux_unit_access_check(u, message, permission, error);
277                         if (r < 0)
278                                 return r;
279                 }
280         }
281 #endif
282         return 0;
283 }