]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/selinux-access.c
tty-ask-password: Split out password sending
[thirdparty/systemd.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 <errno.h>
27 #include <selinux/avc.h>
28 #include <selinux/selinux.h>
29 #include <stdio.h>
30 #ifdef HAVE_AUDIT
31 #include <libaudit.h>
32 #endif
33
34 #include "sd-bus.h"
35
36 #include "alloc-util.h"
37 #include "audit-fd.h"
38 #include "bus-util.h"
39 #include "log.h"
40 #include "path-util.h"
41 #include "selinux-util.h"
42 #include "stdio-util.h"
43 #include "strv.h"
44 #include "util.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 static int callback_type_to_priority(int type) {
88 switch(type) {
89
90 case SELINUX_ERROR:
91 return LOG_ERR;
92
93 case SELINUX_WARNING:
94 return LOG_WARNING;
95
96 case SELINUX_INFO:
97 return LOG_INFO;
98
99 case SELINUX_AVC:
100 default:
101 return LOG_NOTICE;
102 }
103 }
104
105 /*
106 libselinux uses this callback when access gets denied or other
107 events happen. If audit is turned on, messages will be reported
108 using audit netlink, otherwise they will be logged using the usual
109 channels.
110
111 Code copied from dbus and modified.
112 */
113 _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
114 va_list ap;
115
116 #ifdef HAVE_AUDIT
117 int fd;
118
119 fd = get_audit_fd();
120
121 if (fd >= 0) {
122 _cleanup_free_ char *buf = NULL;
123 int r;
124
125 va_start(ap, fmt);
126 r = vasprintf(&buf, fmt, ap);
127 va_end(ap);
128
129 if (r >= 0) {
130 audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
131 return 0;
132 }
133 }
134 #endif
135
136 va_start(ap, fmt);
137 log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
138 va_end(ap);
139
140 return 0;
141 }
142
143 static int access_init(sd_bus_error *error) {
144
145 if (!mac_selinux_use())
146 return 0;
147
148 if (initialized)
149 return 1;
150
151 if (avc_open(NULL, 0) != 0) {
152 int enforce, saved_errno = errno;
153
154 enforce = security_getenforce();
155 log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
156
157 /* If enforcement isn't on, then let's suppress this
158 * error, and just don't do any AVC checks. The
159 * warning we printed is hence all the admin will
160 * see. */
161 if (enforce == 0)
162 return 0;
163
164 /* Return an access denied error, if we couldn't load
165 * the AVC but enforcing mode was on, or we couldn't
166 * determine whether it is one. */
167 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror(saved_errno));
168 }
169
170 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
171 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
172
173 initialized = true;
174 return 1;
175 }
176
177 /*
178 This function communicates with the kernel to check whether or not it should
179 allow the access.
180 If the machine is in permissive mode it will return ok. Audit messages will
181 still be generated if the access would be denied in enforcing mode.
182 */
183 int mac_selinux_generic_access_check(
184 sd_bus_message *message,
185 const char *path,
186 const char *permission,
187 sd_bus_error *error) {
188
189 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
190 const char *tclass = NULL, *scon = NULL;
191 struct audit_info audit_info = {};
192 _cleanup_free_ char *cl = NULL;
193 security_context_t fcon = NULL;
194 char **cmdline = NULL;
195 int r = 0;
196
197 assert(message);
198 assert(permission);
199 assert(error);
200
201 r = access_init(error);
202 if (r <= 0)
203 return r;
204
205 r = sd_bus_query_sender_creds(
206 message,
207 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
208 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
209 SD_BUS_CREDS_SELINUX_CONTEXT|
210 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
211 &creds);
212 if (r < 0)
213 goto finish;
214
215 /* The SELinux context is something we really should have
216 * gotten directly from the message or sender, and not be an
217 * augmented field. If it was augmented we cannot use it for
218 * authorization, since this is racy and vulnerable. Let's add
219 * an extra check, just in case, even though this really
220 * shouldn't be possible. */
221 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
222
223 r = sd_bus_creds_get_selinux_context(creds, &scon);
224 if (r < 0)
225 goto finish;
226
227 if (path) {
228 /* Get the file context of the unit file */
229
230 r = getfilecon_raw(path, &fcon);
231 if (r < 0) {
232 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
233 goto finish;
234 }
235
236 tclass = "service";
237 } else {
238 r = getcon_raw(&fcon);
239 if (r < 0) {
240 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
241 goto finish;
242 }
243
244 tclass = "system";
245 }
246
247 sd_bus_creds_get_cmdline(creds, &cmdline);
248 cl = strv_join(cmdline, " ");
249
250 audit_info.creds = creds;
251 audit_info.path = path;
252 audit_info.cmdline = cl;
253
254 r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
255 if (r < 0)
256 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
257
258 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);
259
260 finish:
261 freecon(fcon);
262
263 if (r < 0 && security_getenforce() != 1) {
264 sd_bus_error_free(error);
265 r = 0;
266 }
267
268 return r;
269 }
270
271 #else
272
273 int mac_selinux_generic_access_check(
274 sd_bus_message *message,
275 const char *path,
276 const char *permission,
277 sd_bus_error *error) {
278
279 return 0;
280 }
281
282 #endif