]>
Commit | Line | Data |
---|---|---|
74a30a58 | 1 | /* SELinux access controls for nscd. |
64d64de6 | 2 | Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
74a30a58 UD |
3 | This file is part of the GNU C Library. |
4 | Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004. | |
5 | ||
6 | The GNU C Library is free software; you can redistribute it and/or | |
7 | modify it under the terms of the GNU Lesser General Public | |
8 | License as published by the Free Software Foundation; either | |
9 | version 2.1 of the License, or (at your option) any later version. | |
10 | ||
11 | The GNU C Library is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public | |
17 | License along with the GNU C Library; if not, write to the Free | |
18 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
19 | 02111-1307 USA. */ | |
20 | ||
ec23b9be | 21 | #include "config.h" |
74a30a58 UD |
22 | #include <error.h> |
23 | #include <errno.h> | |
24 | #include <libintl.h> | |
25 | #include <pthread.h> | |
26 | #include <stdarg.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <syslog.h> | |
62a8cefb | 30 | #include <unistd.h> |
74a30a58 UD |
31 | #include <selinux/av_permissions.h> |
32 | #include <selinux/avc.h> | |
33 | #include <selinux/flask.h> | |
34 | #include <selinux/selinux.h> | |
ec23b9be UD |
35 | #ifdef HAVE_LIBAUDIT |
36 | #include <libaudit.h> | |
37 | #endif | |
74a30a58 UD |
38 | |
39 | #include "dbg_log.h" | |
40 | #include "selinux.h" | |
41 | ||
42 | ||
43 | #ifdef HAVE_SELINUX | |
44 | /* Global variable to tell if the kernel has SELinux support. */ | |
45 | int selinux_enabled; | |
46 | ||
47 | /* Define mappings of access vector permissions to request types. */ | |
48 | static const int perms[LASTREQ] = | |
49 | { | |
50 | [GETPWBYNAME] = NSCD__GETPWD, | |
51 | [GETPWBYUID] = NSCD__GETPWD, | |
52 | [GETGRBYNAME] = NSCD__GETGRP, | |
53 | [GETGRBYGID] = NSCD__GETGRP, | |
54 | [GETHOSTBYNAME] = NSCD__GETHOST, | |
55 | [GETHOSTBYNAMEv6] = NSCD__GETHOST, | |
56 | [GETHOSTBYADDR] = NSCD__GETHOST, | |
57 | [GETHOSTBYADDRv6] = NSCD__GETHOST, | |
58 | [GETSTAT] = NSCD__GETSTAT, | |
59 | [SHUTDOWN] = NSCD__ADMIN, | |
60 | [INVALIDATE] = NSCD__ADMIN, | |
61 | [GETFDPW] = NSCD__SHMEMPWD, | |
62 | [GETFDGR] = NSCD__SHMEMGRP, | |
63 | [GETFDHST] = NSCD__SHMEMHOST, | |
f7e7a396 UD |
64 | [GETAI] = NSCD__GETHOST, |
65 | [INITGROUPS] = NSCD__GETGRP | |
74a30a58 UD |
66 | }; |
67 | ||
68 | /* Store an entry ref to speed AVC decisions. */ | |
69 | static struct avc_entry_ref aeref; | |
70 | ||
71 | /* Thread to listen for SELinux status changes via netlink. */ | |
72 | static pthread_t avc_notify_thread; | |
73 | ||
ec23b9be UD |
74 | #ifdef HAVE_LIBAUDIT |
75 | /* Prototype for supporting the audit daemon */ | |
76 | static void log_callback (const char *fmt, ...); | |
77 | #endif | |
78 | ||
74a30a58 UD |
79 | /* Prototypes for AVC callback functions. */ |
80 | static void *avc_create_thread (void (*run) (void)); | |
81 | static void avc_stop_thread (void *thread); | |
82 | static void *avc_alloc_lock (void); | |
83 | static void avc_get_lock (void *lock); | |
84 | static void avc_release_lock (void *lock); | |
85 | static void avc_free_lock (void *lock); | |
86 | ||
87 | /* AVC callback structures for use in avc_init. */ | |
88 | static const struct avc_log_callback log_cb = | |
89 | { | |
ec23b9be UD |
90 | #ifdef HAVE_LIBAUDIT |
91 | .func_log = log_callback, | |
92 | #else | |
74a30a58 | 93 | .func_log = dbg_log, |
ec23b9be | 94 | #endif |
74a30a58 UD |
95 | .func_audit = NULL |
96 | }; | |
97 | static const struct avc_thread_callback thread_cb = | |
98 | { | |
99 | .func_create_thread = avc_create_thread, | |
100 | .func_stop_thread = avc_stop_thread | |
101 | }; | |
102 | static const struct avc_lock_callback lock_cb = | |
103 | { | |
104 | .func_alloc_lock = avc_alloc_lock, | |
105 | .func_get_lock = avc_get_lock, | |
106 | .func_release_lock = avc_release_lock, | |
107 | .func_free_lock = avc_free_lock | |
108 | }; | |
109 | ||
ec23b9be UD |
110 | #ifdef HAVE_LIBAUDIT |
111 | /* The audit system's netlink socket descriptor */ | |
112 | static int audit_fd = -1; | |
113 | ||
114 | /* When an avc denial occurs, log it to audit system */ | |
64d64de6 | 115 | static void |
ec23b9be UD |
116 | log_callback (const char *fmt, ...) |
117 | { | |
62a8cefb UD |
118 | if (audit_fd >= 0) |
119 | { | |
120 | va_list ap; | |
121 | va_start (ap, fmt); | |
122 | ||
123 | char *buf; | |
124 | int e = vasprintf (&buf, fmt, ap); | |
125 | if (e < 0) | |
126 | { | |
127 | buf = alloca (BUFSIZ); | |
128 | vsnprintf (buf, BUFSIZ, fmt, ap); | |
129 | } | |
130 | ||
131 | /* FIXME: need to attribute this to real user, using getuid for now */ | |
132 | audit_log_user_avc_message (audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, | |
133 | NULL, getuid ()); | |
ec23b9be | 134 | |
62a8cefb UD |
135 | if (e >= 0) |
136 | free (buf); | |
137 | ||
138 | va_end (ap); | |
139 | } | |
ec23b9be UD |
140 | } |
141 | ||
142 | /* Initialize the connection to the audit system */ | |
64d64de6 | 143 | static void |
ec23b9be UD |
144 | audit_init (void) |
145 | { | |
146 | audit_fd = audit_open (); | |
62a8cefb UD |
147 | if (audit_fd < 0 |
148 | /* If kernel doesn't support audit, bail out */ | |
149 | && errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT) | |
150 | dbg_log (_("Failed opening connection to the audit subsystem")); | |
ec23b9be UD |
151 | } |
152 | #endif /* HAVE_LIBAUDIT */ | |
74a30a58 UD |
153 | |
154 | /* Determine if we are running on an SELinux kernel. Set selinux_enabled | |
155 | to the result. */ | |
156 | void | |
157 | nscd_selinux_enabled (int *selinux_enabled) | |
158 | { | |
159 | *selinux_enabled = is_selinux_enabled (); | |
160 | if (*selinux_enabled < 0) | |
161 | { | |
162 | dbg_log (_("Failed to determine if kernel supports SELinux")); | |
163 | exit (EXIT_FAILURE); | |
164 | } | |
165 | } | |
166 | ||
167 | ||
168 | /* Create thread for AVC netlink notification. */ | |
169 | static void * | |
170 | avc_create_thread (void (*run) (void)) | |
171 | { | |
172 | int rc; | |
173 | ||
174 | rc = | |
175 | pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL); | |
176 | if (rc != 0) | |
177 | error (EXIT_FAILURE, rc, _("Failed to start AVC thread")); | |
178 | ||
179 | return &avc_notify_thread; | |
180 | } | |
181 | ||
182 | ||
183 | /* Stop AVC netlink thread. */ | |
184 | static void | |
185 | avc_stop_thread (void *thread) | |
186 | { | |
187 | pthread_cancel (*(pthread_t *) thread); | |
188 | } | |
189 | ||
190 | ||
191 | /* Allocate a new AVC lock. */ | |
192 | static void * | |
193 | avc_alloc_lock (void) | |
194 | { | |
195 | pthread_mutex_t *avc_mutex; | |
196 | ||
197 | avc_mutex = malloc (sizeof (pthread_mutex_t)); | |
198 | if (avc_mutex == NULL) | |
199 | error (EXIT_FAILURE, errno, _("Failed to create AVC lock")); | |
200 | pthread_mutex_init (avc_mutex, NULL); | |
201 | ||
202 | return avc_mutex; | |
203 | } | |
204 | ||
205 | ||
206 | /* Acquire an AVC lock. */ | |
207 | static void | |
208 | avc_get_lock (void *lock) | |
209 | { | |
210 | pthread_mutex_lock (lock); | |
211 | } | |
212 | ||
213 | ||
214 | /* Release an AVC lock. */ | |
215 | static void | |
216 | avc_release_lock (void *lock) | |
217 | { | |
218 | pthread_mutex_unlock (lock); | |
219 | } | |
220 | ||
221 | ||
222 | /* Free an AVC lock. */ | |
223 | static void | |
224 | avc_free_lock (void *lock) | |
225 | { | |
226 | pthread_mutex_destroy (lock); | |
227 | free (lock); | |
228 | } | |
229 | ||
230 | ||
231 | /* Initialize the user space access vector cache (AVC) for NSCD along with | |
232 | log/thread/lock callbacks. */ | |
233 | void | |
234 | nscd_avc_init (void) | |
235 | { | |
236 | avc_entry_ref_init (&aeref); | |
237 | ||
238 | if (avc_init ("avc", NULL, &log_cb, &thread_cb, &lock_cb) < 0) | |
239 | error (EXIT_FAILURE, errno, _("Failed to start AVC")); | |
240 | else | |
241 | dbg_log (_("Access Vector Cache (AVC) started")); | |
ec23b9be UD |
242 | #ifdef HAVE_LIBAUDIT |
243 | audit_init (); | |
244 | #endif | |
74a30a58 UD |
245 | } |
246 | ||
247 | ||
248 | /* Check the permission from the caller (via getpeercon) to nscd. | |
249 | Returns 0 if access is allowed, 1 if denied, and -1 on error. */ | |
250 | int | |
251 | nscd_request_avc_has_perm (int fd, request_type req) | |
252 | { | |
253 | /* Initialize to NULL so we know what to free in case of failure. */ | |
254 | security_context_t scon = NULL; | |
255 | security_context_t tcon = NULL; | |
256 | security_id_t ssid = NULL; | |
257 | security_id_t tsid = NULL; | |
258 | int rc = -1; | |
259 | ||
260 | if (getpeercon (fd, &scon) < 0) | |
261 | { | |
262 | dbg_log (_("Error getting context of socket peer")); | |
263 | goto out; | |
264 | } | |
265 | if (getcon (&tcon) < 0) | |
266 | { | |
267 | dbg_log (_("Error getting context of nscd")); | |
268 | goto out; | |
269 | } | |
1945c96f UD |
270 | if (avc_context_to_sid (scon, &ssid) < 0 |
271 | || avc_context_to_sid (tcon, &tsid) < 0) | |
74a30a58 UD |
272 | { |
273 | dbg_log (_("Error getting sid from context")); | |
274 | goto out; | |
275 | } | |
276 | ||
277 | rc = avc_has_perm (ssid, tsid, SECCLASS_NSCD, perms[req], &aeref, NULL) < 0; | |
278 | ||
279 | out: | |
280 | if (scon) | |
281 | freecon (scon); | |
282 | if (tcon) | |
283 | freecon (tcon); | |
284 | if (ssid) | |
285 | sidput (ssid); | |
286 | if (tsid) | |
287 | sidput (tsid); | |
288 | ||
289 | return rc; | |
290 | } | |
291 | ||
292 | ||
293 | /* Wrapper to get AVC statistics. */ | |
294 | void | |
295 | nscd_avc_cache_stats (struct avc_cache_stats *cstats) | |
296 | { | |
297 | avc_cache_stats (cstats); | |
298 | } | |
299 | ||
300 | ||
301 | /* Print the AVC statistics to stdout. */ | |
302 | void | |
303 | nscd_avc_print_stats (struct avc_cache_stats *cstats) | |
304 | { | |
305 | printf (_("\nSELinux AVC Statistics:\n\n" | |
306 | "%15u entry lookups\n" | |
307 | "%15u entry hits\n" | |
308 | "%15u entry misses\n" | |
309 | "%15u entry discards\n" | |
310 | "%15u CAV lookups\n" | |
311 | "%15u CAV hits\n" | |
312 | "%15u CAV probes\n" | |
313 | "%15u CAV misses\n"), | |
314 | cstats->entry_lookups, cstats->entry_hits, cstats->entry_misses, | |
315 | cstats->entry_discards, cstats->cav_lookups, cstats->cav_hits, | |
316 | cstats->cav_probes, cstats->cav_misses); | |
317 | } | |
318 | ||
319 | ||
320 | /* Clean up the AVC before exiting. */ | |
321 | void | |
322 | nscd_avc_destroy (void) | |
323 | { | |
324 | avc_destroy (); | |
ec23b9be UD |
325 | #ifdef HAVE_LIBAUDIT |
326 | audit_close (audit_fd); | |
327 | #endif | |
74a30a58 UD |
328 | } |
329 | ||
330 | #endif /* HAVE_SELINUX */ |