Line data Source code
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 <errno.h>
28 : #include <selinux/selinux.h>
29 : #include <selinux/avc.h>
30 : #ifdef HAVE_AUDIT
31 : #include <libaudit.h>
32 : #endif
33 :
34 : #include "sd-bus.h"
35 : #include "bus-util.h"
36 : #include "util.h"
37 : #include "log.h"
38 : #include "selinux-util.h"
39 : #include "audit-fd.h"
40 : #include "strv.h"
41 :
42 : static bool initialized = false;
43 :
44 : struct audit_info {
45 : sd_bus_creds *creds;
46 : const char *path;
47 : const char *cmdline;
48 : };
49 :
50 : /*
51 : Any time an access gets denied this callback will be called
52 : with the audit data. We then need to just copy the audit data into the msgbuf.
53 : */
54 : static int audit_callback(
55 : void *auditdata,
56 : security_class_t cls,
57 : char *msgbuf,
58 : size_t msgbufsize) {
59 :
60 : const struct audit_info *audit = auditdata;
61 : uid_t uid = 0, login_uid = 0;
62 : gid_t gid = 0;
63 : char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
64 : char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
65 : char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a";
66 :
67 : if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
68 : xsprintf(login_uid_buf, UID_FMT, login_uid);
69 : if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
70 : xsprintf(uid_buf, UID_FMT, uid);
71 : if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
72 : xsprintf(gid_buf, GID_FMT, gid);
73 :
74 : snprintf(msgbuf, msgbufsize,
75 : "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
76 : login_uid_buf, uid_buf, gid_buf,
77 : audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
78 : audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
79 :
80 : return 0;
81 : }
82 :
83 : static int callback_type_to_priority(int type) {
84 : switch(type) {
85 :
86 : case SELINUX_ERROR:
87 : return LOG_ERR;
88 :
89 : case SELINUX_WARNING:
90 : return LOG_WARNING;
91 :
92 : case SELINUX_INFO:
93 : return LOG_INFO;
94 :
95 : case SELINUX_AVC:
96 : default:
97 : return LOG_NOTICE;
98 : }
99 : }
100 :
101 : /*
102 : libselinux uses this callback when access gets denied or other
103 : events happen. If audit is turned on, messages will be reported
104 : using audit netlink, otherwise they will be logged using the usual
105 : channels.
106 :
107 : Code copied from dbus and modified.
108 : */
109 : _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
110 : va_list ap;
111 :
112 : #ifdef HAVE_AUDIT
113 : int fd;
114 :
115 : fd = get_audit_fd();
116 :
117 : if (fd >= 0) {
118 : _cleanup_free_ char *buf = NULL;
119 : int r;
120 :
121 : va_start(ap, fmt);
122 : r = vasprintf(&buf, fmt, ap);
123 : va_end(ap);
124 :
125 : if (r >= 0) {
126 : audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
127 : return 0;
128 : }
129 : }
130 : #endif
131 :
132 : va_start(ap, fmt);
133 : log_internalv(LOG_AUTH | callback_type_to_priority(type),
134 : 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
135 : va_end(ap);
136 :
137 : return 0;
138 : }
139 :
140 : /*
141 : Function must be called once to initialize the SELinux AVC environment.
142 : Sets up callbacks.
143 : If you want to cleanup memory you should need to call selinux_access_finish.
144 : */
145 : static int access_init(void) {
146 : int r = 0;
147 :
148 : if (avc_open(NULL, 0))
149 : return log_error_errno(errno, "avc_open() failed: %m");
150 :
151 : selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
152 : selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
153 :
154 : if (security_getenforce() < 0){
155 : r = -errno;
156 : avc_destroy();
157 : }
158 :
159 : return r;
160 : }
161 :
162 : static int mac_selinux_access_init(sd_bus_error *error) {
163 : int r;
164 :
165 : if (initialized)
166 : return 0;
167 :
168 : if (!mac_selinux_use())
169 : return 0;
170 :
171 : r = access_init();
172 : if (r < 0)
173 : return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
174 :
175 : initialized = true;
176 : return 0;
177 : }
178 : #endif
179 :
180 0 : void mac_selinux_access_free(void) {
181 :
182 : #ifdef HAVE_SELINUX
183 : if (!initialized)
184 : return;
185 :
186 : avc_destroy();
187 : initialized = false;
188 : #endif
189 0 : }
190 :
191 : /*
192 : This function communicates with the kernel to check whether or not it should
193 : allow the access.
194 : If the machine is in permissive mode it will return ok. Audit messages will
195 : still be generated if the access would be denied in enforcing mode.
196 : */
197 0 : int mac_selinux_generic_access_check(
198 : sd_bus_message *message,
199 : const char *path,
200 : const char *permission,
201 : sd_bus_error *error) {
202 :
203 : #ifdef HAVE_SELINUX
204 : _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
205 : const char *tclass = NULL, *scon = NULL;
206 : struct audit_info audit_info = {};
207 : _cleanup_free_ char *cl = NULL;
208 : security_context_t fcon = NULL;
209 : char **cmdline = NULL;
210 : int r = 0;
211 :
212 : assert(message);
213 : assert(permission);
214 : assert(error);
215 :
216 : if (!mac_selinux_use())
217 : return 0;
218 :
219 : r = mac_selinux_access_init(error);
220 : if (r < 0)
221 : return r;
222 :
223 : r = sd_bus_query_sender_creds(
224 : message,
225 : SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
226 : SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
227 : SD_BUS_CREDS_SELINUX_CONTEXT|
228 : SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
229 : &creds);
230 : if (r < 0)
231 : goto finish;
232 :
233 : /* The SELinux context is something we really should have
234 : * gotten directly from the message or sender, and not be an
235 : * augmented field. If it was augmented we cannot use it for
236 : * authorization, since this is racy and vulnerable. Let's add
237 : * an extra check, just in case, even though this really
238 : * shouldn't be possible. */
239 : assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
240 :
241 : r = sd_bus_creds_get_selinux_context(creds, &scon);
242 : if (r < 0)
243 : goto finish;
244 :
245 : if (path) {
246 : /* Get the file context of the unit file */
247 :
248 : r = getfilecon(path, &fcon);
249 : if (r < 0) {
250 : r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
251 : goto finish;
252 : }
253 :
254 : tclass = "service";
255 : } else {
256 : r = getcon(&fcon);
257 : if (r < 0) {
258 : r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
259 : goto finish;
260 : }
261 :
262 : tclass = "system";
263 : }
264 :
265 : sd_bus_creds_get_cmdline(creds, &cmdline);
266 : cl = strv_join(cmdline, " ");
267 :
268 : audit_info.creds = creds;
269 : audit_info.path = path;
270 : audit_info.cmdline = cl;
271 :
272 : r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
273 : if (r < 0)
274 : r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
275 :
276 : 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);
277 :
278 : finish:
279 : freecon(fcon);
280 :
281 : if (r < 0 && security_getenforce() != 1) {
282 : sd_bus_error_free(error);
283 : r = 0;
284 : }
285 :
286 : return r;
287 : #else
288 0 : return 0;
289 : #endif
290 : }
291 :
292 0 : int mac_selinux_unit_access_check_strv(
293 : char **units,
294 : sd_bus_message *message,
295 : Manager *m,
296 : const char *permission,
297 : sd_bus_error *error) {
298 :
299 : #ifdef HAVE_SELINUX
300 : char **i;
301 : Unit *u;
302 : int r;
303 :
304 : STRV_FOREACH(i, units) {
305 : r = manager_load_unit(m, *i, NULL, error, &u);
306 : if (r < 0)
307 : return r;
308 : r = mac_selinux_unit_access_check(u, message, permission, error);
309 : if (r < 0)
310 : return r;
311 : }
312 : #endif
313 0 : return 0;
314 : }
|