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