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