]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/selinux-access.c
login: replace readdir_r with readdir
[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>
27#include <string.h>
28#include <errno.h>
ffc227c9 29#include <limits.h>
e2417e41
DW
30#include <selinux/selinux.h>
31#include <selinux/avc.h>
94d56326 32#include <sys/socket.h>
e2417e41
DW
33#ifdef HAVE_AUDIT
34#include <libaudit.h>
35#endif
ffc227c9 36
718db961
LP
37#include "sd-bus.h"
38#include "bus-util.h"
ffc227c9
LP
39#include "util.h"
40#include "log.h"
ffc227c9
LP
41#include "audit.h"
42#include "selinux-util.h"
43#include "audit-fd.h"
5b12334d 44#include "strv.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
56 with the aduit data. We then need to just copy the audit data into the msgbuf.
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;
67
68 sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid);
69 sd_bus_creds_get_uid(audit->creds, &uid);
70 sd_bus_creds_get_gid(audit->creds, &gid);
cad45ba1 71
e2417e41 72 snprintf(msgbuf, msgbufsize,
cad45ba1 73 "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
5b12334d
LP
74 login_uid, uid, gid,
75 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
76 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
d67227c8 77
cad45ba1 78 msgbuf[msgbufsize-1] = 0;
d67227c8 79
e2417e41
DW
80 return 0;
81}
82
83/*
84 Any time an access gets denied this callback will be called
85 code copied from dbus. If audit is turned on the messages will go as
86 user_avc's into the /var/log/audit/audit.log, otherwise they will be
87 sent to syslog.
88*/
44b601bc 89_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
e2417e41
DW
90 va_list ap;
91
e2417e41 92#ifdef HAVE_AUDIT
c1165f82 93 if (get_audit_fd() >= 0) {
ace188cf
LP
94 _cleanup_free_ char *buf = NULL;
95 int r;
e2417e41 96
5b12334d 97 va_start(ap, fmt);
ace188cf 98 r = vasprintf(&buf, fmt, ap);
7f1736f7 99 va_end(ap);
cad45ba1 100
ace188cf
LP
101 if (r >= 0) {
102 audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
103 return 0;
104 }
e2417e41
DW
105 }
106#endif
5b12334d
LP
107
108 va_start(ap, fmt);
e2417e41
DW
109 log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
110 va_end(ap);
cad45ba1 111
e2417e41
DW
112 return 0;
113}
114
115/*
116 Function must be called once to initialize the SELinux AVC environment.
117 Sets up callbacks.
118 If you want to cleanup memory you should need to call selinux_access_finish.
119*/
120static int access_init(void) {
718db961 121 int r = 0;
e2417e41
DW
122
123 if (avc_open(NULL, 0)) {
cad45ba1 124 log_error("avc_open() failed: %m");
e2417e41
DW
125 return -errno;
126 }
127
cad45ba1
LP
128 selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
129 selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
e2417e41 130
718db961
LP
131 if (security_getenforce() < 0){
132 r = -errno;
133 avc_destroy();
134 }
cad45ba1 135
e2417e41
DW
136 return r;
137}
138
718db961 139static int selinux_access_init(sd_bus_error *error) {
e2417e41
DW
140 int r;
141
cad45ba1 142 if (initialized)
e2417e41
DW
143 return 0;
144
718db961
LP
145 if (!use_selinux())
146 return 0;
147
148 r = access_init();
149 if (r < 0)
150 return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
e2417e41 151
cad45ba1 152 initialized = true;
e2417e41
DW
153 return 0;
154}
155
ffc227c9 156void selinux_access_free(void) {
718db961 157
ffc227c9
LP
158 if (!initialized)
159 return;
160
161 avc_destroy();
162 initialized = false;
163}
164
e2417e41
DW
165/*
166 This function communicates with the kernel to check whether or not it should
167 allow the access.
168 If the machine is in permissive mode it will return ok. Audit messages will
169 still be generated if the access would be denied in enforcing mode.
170*/
ebcf1f97 171int selinux_generic_access_check(
718db961
LP
172 sd_bus *bus,
173 sd_bus_message *message,
cad45ba1
LP
174 const char *path,
175 const char *permission,
718db961 176 sd_bus_error *error) {
cad45ba1 177
5b12334d
LP
178 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
179 const char *tclass = NULL, *scon = NULL;
180 struct audit_info audit_info = {};
181 _cleanup_free_ char *cl = NULL;
182 security_context_t fcon = NULL;
183 char **cmdline = NULL;
718db961 184 int r = 0;
c3090674 185
718db961 186 assert(bus);
cad45ba1
LP
187 assert(message);
188 assert(permission);
189 assert(error);
190
cad45ba1
LP
191 if (!use_selinux())
192 return 0;
193
ffc227c9
LP
194 r = selinux_access_init(error);
195 if (r < 0)
196 return r;
197
5b12334d
LP
198 r = sd_bus_query_sender_creds(
199 message,
200 SD_BUS_CREDS_PID|SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|
201 SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
202 SD_BUS_CREDS_SELINUX_CONTEXT,
203 &creds);
204 if (r < 0)
205 goto finish;
e2417e41 206
5b12334d 207 r = sd_bus_creds_get_selinux_context(creds, &scon);
718db961 208 if (r < 0)
e2417e41 209 goto finish;
cad45ba1 210
e2417e41 211 if (path) {
718db961
LP
212 /* Get the file context of the unit file */
213
e2417e41
DW
214 r = getfilecon(path, &fcon);
215 if (r < 0) {
718db961 216 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
e2417e41
DW
217 goto finish;
218 }
219
718db961 220 tclass = "service";
e2417e41 221 } else {
e2417e41
DW
222 r = getcon(&fcon);
223 if (r < 0) {
718db961 224 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
e2417e41
DW
225 goto finish;
226 }
718db961
LP
227
228 tclass = "system";
e2417e41
DW
229 }
230
5b12334d
LP
231 sd_bus_creds_get_cmdline(creds, &cmdline);
232 cl = strv_join(cmdline, " ");
233
234 audit_info.creds = creds;
235 audit_info.path = path;
236 audit_info.cmdline = cl;
e2417e41 237
5b12334d 238 r = selinux_check_access((security_context_t) scon, fcon, tclass, permission, &audit_info);
718db961
LP
239 if (r < 0)
240 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
e2417e41 241
5b12334d 242 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 243
e2417e41 244finish:
e2417e41
DW
245 freecon(fcon);
246
5b12334d 247 if (r < 0 && security_getenforce() != 1) {
718db961 248 sd_bus_error_free(error);
e2417e41
DW
249 r = 0;
250 }
251
252 return r;
253}
254
e2417e41 255#else
cad45ba1 256
ebcf1f97 257int selinux_generic_access_check(
718db961
LP
258 sd_bus *bus,
259 sd_bus_message *message,
ffc227c9 260 const char *path,
cad45ba1 261 const char *permission,
718db961 262 sd_bus_error *error) {
cad45ba1 263
e2417e41
DW
264 return 0;
265}
266
ffc227c9 267void selinux_access_free(void) {
c3090674 268}
cad45ba1 269
e2417e41 270#endif