]> git.proxmox.com Git - systemd.git/blob - src/core/selinux-access.c
Imported Upstream version 229
[systemd.git] / src / core / selinux-access.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2012 Dan Walsh
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include "selinux-access.h"
21
22 #ifdef HAVE_SELINUX
23
24 #include <errno.h>
25 #include <selinux/avc.h>
26 #include <selinux/selinux.h>
27 #include <stdio.h>
28 #ifdef HAVE_AUDIT
29 #include <libaudit.h>
30 #endif
31
32 #include "sd-bus.h"
33
34 #include "alloc-util.h"
35 #include "audit-fd.h"
36 #include "bus-util.h"
37 #include "log.h"
38 #include "path-util.h"
39 #include "selinux-util.h"
40 #include "stdio-util.h"
41 #include "strv.h"
42 #include "util.h"
43
44 static bool initialized = false;
45
46 struct audit_info {
47 sd_bus_creds *creds;
48 const char *path;
49 const char *cmdline;
50 };
51
52 /*
53 Any time an access gets denied this callback will be called
54 with the audit data. We then need to just copy the audit data into the msgbuf.
55 */
56 static int audit_callback(
57 void *auditdata,
58 security_class_t cls,
59 char *msgbuf,
60 size_t msgbufsize) {
61
62 const struct audit_info *audit = auditdata;
63 uid_t uid = 0, login_uid = 0;
64 gid_t gid = 0;
65 char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
66 char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
67 char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a";
68
69 if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
70 xsprintf(login_uid_buf, UID_FMT, login_uid);
71 if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
72 xsprintf(uid_buf, UID_FMT, uid);
73 if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
74 xsprintf(gid_buf, GID_FMT, gid);
75
76 snprintf(msgbuf, msgbufsize,
77 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
78 login_uid_buf, uid_buf, gid_buf,
79 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
80 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
81
82 return 0;
83 }
84
85 static int callback_type_to_priority(int type) {
86 switch(type) {
87
88 case SELINUX_ERROR:
89 return LOG_ERR;
90
91 case SELINUX_WARNING:
92 return LOG_WARNING;
93
94 case SELINUX_INFO:
95 return LOG_INFO;
96
97 case SELINUX_AVC:
98 default:
99 return LOG_NOTICE;
100 }
101 }
102
103 /*
104 libselinux uses this callback when access gets denied or other
105 events happen. If audit is turned on, messages will be reported
106 using audit netlink, otherwise they will be logged using the usual
107 channels.
108
109 Code copied from dbus and modified.
110 */
111 _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
112 va_list ap;
113
114 #ifdef HAVE_AUDIT
115 int fd;
116
117 fd = get_audit_fd();
118
119 if (fd >= 0) {
120 _cleanup_free_ char *buf = NULL;
121 int r;
122
123 va_start(ap, fmt);
124 r = vasprintf(&buf, fmt, ap);
125 va_end(ap);
126
127 if (r >= 0) {
128 audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
129 return 0;
130 }
131 }
132 #endif
133
134 va_start(ap, fmt);
135 log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
136 va_end(ap);
137
138 return 0;
139 }
140
141 static int access_init(sd_bus_error *error) {
142
143 if (!mac_selinux_use())
144 return 0;
145
146 if (initialized)
147 return 1;
148
149 if (avc_open(NULL, 0) != 0) {
150 int enforce, saved_errno = errno;
151
152 enforce = security_getenforce();
153 log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
154
155 /* If enforcement isn't on, then let's suppress this
156 * error, and just don't do any AVC checks. The
157 * warning we printed is hence all the admin will
158 * see. */
159 if (enforce == 0)
160 return 0;
161
162 /* Return an access denied error, if we couldn't load
163 * the AVC but enforcing mode was on, or we couldn't
164 * determine whether it is one. */
165 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror(saved_errno));
166 }
167
168 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
169 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
170
171 initialized = true;
172 return 1;
173 }
174
175 /*
176 This function communicates with the kernel to check whether or not it should
177 allow the access.
178 If the machine is in permissive mode it will return ok. Audit messages will
179 still be generated if the access would be denied in enforcing mode.
180 */
181 int mac_selinux_generic_access_check(
182 sd_bus_message *message,
183 const char *path,
184 const char *permission,
185 sd_bus_error *error) {
186
187 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
188 const char *tclass = NULL, *scon = NULL;
189 struct audit_info audit_info = {};
190 _cleanup_free_ char *cl = NULL;
191 security_context_t fcon = NULL;
192 char **cmdline = NULL;
193 int r = 0;
194
195 assert(message);
196 assert(permission);
197 assert(error);
198
199 r = access_init(error);
200 if (r <= 0)
201 return r;
202
203 r = sd_bus_query_sender_creds(
204 message,
205 SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
206 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
207 SD_BUS_CREDS_SELINUX_CONTEXT|
208 SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
209 &creds);
210 if (r < 0)
211 goto finish;
212
213 /* The SELinux context is something we really should have
214 * gotten directly from the message or sender, and not be an
215 * augmented field. If it was augmented we cannot use it for
216 * authorization, since this is racy and vulnerable. Let's add
217 * an extra check, just in case, even though this really
218 * shouldn't be possible. */
219 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
220
221 r = sd_bus_creds_get_selinux_context(creds, &scon);
222 if (r < 0)
223 goto finish;
224
225 if (path) {
226 /* Get the file context of the unit file */
227
228 r = getfilecon_raw(path, &fcon);
229 if (r < 0) {
230 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
231 goto finish;
232 }
233
234 tclass = "service";
235 } else {
236 r = getcon_raw(&fcon);
237 if (r < 0) {
238 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
239 goto finish;
240 }
241
242 tclass = "system";
243 }
244
245 sd_bus_creds_get_cmdline(creds, &cmdline);
246 cl = strv_join(cmdline, " ");
247
248 audit_info.creds = creds;
249 audit_info.path = path;
250 audit_info.cmdline = cl;
251
252 r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
253 if (r < 0)
254 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
255
256 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);
257
258 finish:
259 freecon(fcon);
260
261 if (r < 0 && security_getenforce() != 1) {
262 sd_bus_error_free(error);
263 r = 0;
264 }
265
266 return r;
267 }
268
269 #else
270
271 int mac_selinux_generic_access_check(
272 sd_bus_message *message,
273 const char *path,
274 const char *permission,
275 sd_bus_error *error) {
276
277 return 0;
278 }
279
280 #endif