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