]>
Commit | Line | Data |
---|---|---|
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 | 46 | static bool initialized = false; |
e2417e41 | 47 | |
5b12334d LP |
48 | struct 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 |
58 | static 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 | */ | |
120 | static 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 | 139 | static 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 | 156 | void 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 | 171 | int 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 | 244 | finish: |
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 | 257 | int 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 | 267 | void selinux_access_free(void) { |
c3090674 | 268 | } |
cad45ba1 | 269 | |
e2417e41 | 270 | #endif |