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