]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/selinux.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / nscd / selinux.c
CommitLineData
74a30a58 1/* SELinux access controls for nscd.
04277e02 2 Copyright (C) 2004-2019 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
59ba27a6
PE
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
74a30a58 19
ec23b9be 20#include "config.h"
74a30a58
UD
21#include <error.h>
22#include <errno.h>
23#include <libintl.h>
24#include <pthread.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <syslog.h>
62a8cefb 29#include <unistd.h>
1f063dca 30#include <sys/prctl.h>
74a30a58 31#include <selinux/avc.h>
74a30a58 32#include <selinux/selinux.h>
ec23b9be 33#ifdef HAVE_LIBAUDIT
1f063dca 34# include <libaudit.h>
ec23b9be 35#endif
74a30a58
UD
36
37#include "dbg_log.h"
38#include "selinux.h"
39
40
41#ifdef HAVE_SELINUX
42/* Global variable to tell if the kernel has SELinux support. */
43int selinux_enabled;
44
0699f766
CD
45/* Define mappings of request type to AVC permission name. */
46static const char *perms[LASTREQ] =
74a30a58 47{
0699f766
CD
48 [GETPWBYNAME] = "getpwd",
49 [GETPWBYUID] = "getpwd",
50 [GETGRBYNAME] = "getgrp",
51 [GETGRBYGID] = "getgrp",
52 [GETHOSTBYNAME] = "gethost",
53 [GETHOSTBYNAMEv6] = "gethost",
54 [GETHOSTBYADDR] = "gethost",
55 [GETHOSTBYADDRv6] = "gethost",
56 [SHUTDOWN] = "admin",
57 [GETSTAT] = "getstat",
58 [INVALIDATE] = "admin",
59 [GETFDPW] = "shmempwd",
60 [GETFDGR] = "shmemgrp",
61 [GETFDHST] = "shmemhost",
62 [GETAI] = "gethost",
63 [INITGROUPS] = "getgrp",
64 [GETSERVBYNAME] = "getserv",
65 [GETSERVBYPORT] = "getserv",
66 [GETFDSERV] = "shmemserv",
67 [GETNETGRENT] = "getnetgrp",
68 [INNETGR] = "getnetgrp",
69 [GETFDNETGR] = "shmemnetgrp",
74a30a58
UD
70};
71
72/* Store an entry ref to speed AVC decisions. */
73static struct avc_entry_ref aeref;
74
75/* Thread to listen for SELinux status changes via netlink. */
76static pthread_t avc_notify_thread;
77
ec23b9be
UD
78#ifdef HAVE_LIBAUDIT
79/* Prototype for supporting the audit daemon */
80static void log_callback (const char *fmt, ...);
81#endif
82
74a30a58
UD
83/* Prototypes for AVC callback functions. */
84static void *avc_create_thread (void (*run) (void));
85static void avc_stop_thread (void *thread);
86static void *avc_alloc_lock (void);
87static void avc_get_lock (void *lock);
88static void avc_release_lock (void *lock);
89static void avc_free_lock (void *lock);
90
91/* AVC callback structures for use in avc_init. */
92static const struct avc_log_callback log_cb =
93{
ec23b9be
UD
94#ifdef HAVE_LIBAUDIT
95 .func_log = log_callback,
96#else
74a30a58 97 .func_log = dbg_log,
ec23b9be 98#endif
74a30a58
UD
99 .func_audit = NULL
100};
101static const struct avc_thread_callback thread_cb =
102{
103 .func_create_thread = avc_create_thread,
104 .func_stop_thread = avc_stop_thread
105};
106static const struct avc_lock_callback lock_cb =
107{
108 .func_alloc_lock = avc_alloc_lock,
109 .func_get_lock = avc_get_lock,
110 .func_release_lock = avc_release_lock,
111 .func_free_lock = avc_free_lock
112};
113
ec23b9be
UD
114#ifdef HAVE_LIBAUDIT
115/* The audit system's netlink socket descriptor */
116static int audit_fd = -1;
117
118/* When an avc denial occurs, log it to audit system */
64d64de6 119static void
ec23b9be
UD
120log_callback (const char *fmt, ...)
121{
62a8cefb
UD
122 if (audit_fd >= 0)
123 {
124 va_list ap;
125 va_start (ap, fmt);
126
127 char *buf;
128 int e = vasprintf (&buf, fmt, ap);
129 if (e < 0)
130 {
131 buf = alloca (BUFSIZ);
132 vsnprintf (buf, BUFSIZ, fmt, ap);
133 }
134
135 /* FIXME: need to attribute this to real user, using getuid for now */
136 audit_log_user_avc_message (audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
137 NULL, getuid ());
ec23b9be 138
62a8cefb
UD
139 if (e >= 0)
140 free (buf);
141
142 va_end (ap);
143 }
ec23b9be
UD
144}
145
146/* Initialize the connection to the audit system */
64d64de6 147static void
ec23b9be
UD
148audit_init (void)
149{
150 audit_fd = audit_open ();
62a8cefb
UD
151 if (audit_fd < 0
152 /* If kernel doesn't support audit, bail out */
153 && errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT)
9b07a801 154 dbg_log (_("Failed opening connection to the audit subsystem: %m"));
ec23b9be 155}
1f063dca
UD
156
157
158# ifdef HAVE_LIBCAP
159static const cap_value_t new_cap_list[] =
160 { CAP_AUDIT_WRITE };
161# define nnew_cap_list (sizeof (new_cap_list) / sizeof (new_cap_list[0]))
162static const cap_value_t tmp_cap_list[] =
163 { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID };
164# define ntmp_cap_list (sizeof (tmp_cap_list) / sizeof (tmp_cap_list[0]))
165
166cap_t
167preserve_capabilities (void)
168{
169 if (getuid () != 0)
170 /* Not root, then we cannot preserve anything. */
171 return NULL;
172
173 if (prctl (PR_SET_KEEPCAPS, 1) == -1)
174 {
175 dbg_log (_("Failed to set keep-capabilities"));
532a6035 176 do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
1f063dca
UD
177 /* NOTREACHED */
178 }
179
180 cap_t tmp_caps = cap_init ();
9d9febc7 181 cap_t new_caps = NULL;
1f063dca
UD
182 if (tmp_caps != NULL)
183 new_caps = cap_init ();
184
185 if (tmp_caps == NULL || new_caps == NULL)
186 {
187 if (tmp_caps != NULL)
e1f0c5bc 188 cap_free (tmp_caps);
1f063dca
UD
189
190 dbg_log (_("Failed to initialize drop of capabilities"));
532a6035 191 do_exit (EXIT_FAILURE, 0, _("cap_init failed"));
1f063dca
UD
192 }
193
194 /* There is no reason why these should not work. */
e1f0c5bc
UD
195 cap_set_flag (new_caps, CAP_PERMITTED, nnew_cap_list,
196 (cap_value_t *) new_cap_list, CAP_SET);
197 cap_set_flag (new_caps, CAP_EFFECTIVE, nnew_cap_list,
198 (cap_value_t *) new_cap_list, CAP_SET);
199
200 cap_set_flag (tmp_caps, CAP_PERMITTED, ntmp_cap_list,
201 (cap_value_t *) tmp_cap_list, CAP_SET);
202 cap_set_flag (tmp_caps, CAP_EFFECTIVE, ntmp_cap_list,
203 (cap_value_t *) tmp_cap_list, CAP_SET);
1f063dca
UD
204
205 int res = cap_set_proc (tmp_caps);
206
207 cap_free (tmp_caps);
208
a1ffb40e 209 if (__glibc_unlikely (res != 0))
1f063dca
UD
210 {
211 cap_free (new_caps);
11bf311e 212 dbg_log (_("Failed to drop capabilities"));
532a6035 213 do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
1f063dca
UD
214 }
215
216 return new_caps;
217}
218
219void
220install_real_capabilities (cap_t new_caps)
221{
222 /* If we have no capabilities there is nothing to do here. */
223 if (new_caps == NULL)
224 return;
225
226 if (cap_set_proc (new_caps))
227 {
228 cap_free (new_caps);
229 dbg_log (_("Failed to drop capabilities"));
532a6035 230 do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
1f063dca
UD
231 /* NOTREACHED */
232 }
233
234 cap_free (new_caps);
235
236 if (prctl (PR_SET_KEEPCAPS, 0) == -1)
237 {
238 dbg_log (_("Failed to unset keep-capabilities"));
532a6035 239 do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
1f063dca
UD
240 /* NOTREACHED */
241 }
242}
243# endif /* HAVE_LIBCAP */
ec23b9be 244#endif /* HAVE_LIBAUDIT */
74a30a58
UD
245
246/* Determine if we are running on an SELinux kernel. Set selinux_enabled
247 to the result. */
248void
249nscd_selinux_enabled (int *selinux_enabled)
250{
251 *selinux_enabled = is_selinux_enabled ();
252 if (*selinux_enabled < 0)
253 {
254 dbg_log (_("Failed to determine if kernel supports SELinux"));
532a6035 255 do_exit (EXIT_FAILURE, 0, NULL);
74a30a58
UD
256 }
257}
258
259
260/* Create thread for AVC netlink notification. */
261static void *
262avc_create_thread (void (*run) (void))
263{
264 int rc;
265
266 rc =
267 pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
268 if (rc != 0)
532a6035 269 do_exit (EXIT_FAILURE, rc, _("Failed to start AVC thread"));
74a30a58
UD
270
271 return &avc_notify_thread;
272}
273
274
275/* Stop AVC netlink thread. */
276static void
277avc_stop_thread (void *thread)
278{
279 pthread_cancel (*(pthread_t *) thread);
280}
281
282
283/* Allocate a new AVC lock. */
284static void *
285avc_alloc_lock (void)
286{
287 pthread_mutex_t *avc_mutex;
288
289 avc_mutex = malloc (sizeof (pthread_mutex_t));
290 if (avc_mutex == NULL)
532a6035 291 do_exit (EXIT_FAILURE, errno, _("Failed to create AVC lock"));
74a30a58
UD
292 pthread_mutex_init (avc_mutex, NULL);
293
294 return avc_mutex;
295}
296
297
298/* Acquire an AVC lock. */
299static void
300avc_get_lock (void *lock)
301{
302 pthread_mutex_lock (lock);
303}
304
305
306/* Release an AVC lock. */
307static void
308avc_release_lock (void *lock)
309{
310 pthread_mutex_unlock (lock);
311}
312
313
314/* Free an AVC lock. */
315static void
316avc_free_lock (void *lock)
317{
318 pthread_mutex_destroy (lock);
319 free (lock);
320}
321
322
323/* Initialize the user space access vector cache (AVC) for NSCD along with
324 log/thread/lock callbacks. */
325void
326nscd_avc_init (void)
327{
328 avc_entry_ref_init (&aeref);
329
330 if (avc_init ("avc", NULL, &log_cb, &thread_cb, &lock_cb) < 0)
532a6035 331 do_exit (EXIT_FAILURE, errno, _("Failed to start AVC"));
74a30a58
UD
332 else
333 dbg_log (_("Access Vector Cache (AVC) started"));
ec23b9be
UD
334#ifdef HAVE_LIBAUDIT
335 audit_init ();
336#endif
74a30a58
UD
337}
338
339
340/* Check the permission from the caller (via getpeercon) to nscd.
0699f766
CD
341 Returns 0 if access is allowed, 1 if denied, and -1 on error.
342
343 The SELinux policy, enablement, and permission bits are all dynamic and the
344 caching done by glibc is not entirely correct. This nscd support should be
345 rewritten to use selinux_check_permission. A rewrite is risky though and
346 requires some refactoring. Currently we use symbolic mappings instead of
347 compile time constants (which SELinux upstream says are going away), and we
348 use security_deny_unknown to determine what to do if selinux-policy* doesn't
349 have a definition for the the permission or object class we are looking
350 up. */
74a30a58
UD
351int
352nscd_request_avc_has_perm (int fd, request_type req)
353{
354 /* Initialize to NULL so we know what to free in case of failure. */
355 security_context_t scon = NULL;
356 security_context_t tcon = NULL;
357 security_id_t ssid = NULL;
358 security_id_t tsid = NULL;
359 int rc = -1;
0699f766
CD
360 security_class_t sc_nscd;
361 access_vector_t perm;
362 int avc_deny_unknown;
363
364 /* Check if SELinux denys or allows unknown object classes
365 and permissions. It is 0 if they are allowed, 1 if they
366 are not allowed and -1 on error. */
367 if ((avc_deny_unknown = security_deny_unknown ()) == -1)
368 dbg_log (_("Error querying policy for undefined object classes "
369 "or permissions."));
370
371 /* Get the security class for nscd. If this fails we will likely be
372 unable to do anything unless avc_deny_unknown is 0. */
373 sc_nscd = string_to_security_class ("nscd");
a1189263 374 if (sc_nscd == 0 && avc_deny_unknown == 1)
0699f766
CD
375 dbg_log (_("Error getting security class for nscd."));
376
377 /* Convert permission to AVC bits. */
378 perm = string_to_av_perm (sc_nscd, perms[req]);
379 if (perm == 0 && avc_deny_unknown == 1)
380 dbg_log (_("Error translating permission name "
381 "\"%s\" to access vector bit."), perms[req]);
382
383 /* If the nscd security class was not found or perms were not
384 found and AVC does not deny unknown values then allow it. */
385 if ((sc_nscd == 0 || perm == 0) && avc_deny_unknown == 0)
386 return 0;
74a30a58
UD
387
388 if (getpeercon (fd, &scon) < 0)
389 {
390 dbg_log (_("Error getting context of socket peer"));
391 goto out;
392 }
393 if (getcon (&tcon) < 0)
394 {
395 dbg_log (_("Error getting context of nscd"));
396 goto out;
397 }
1945c96f
UD
398 if (avc_context_to_sid (scon, &ssid) < 0
399 || avc_context_to_sid (tcon, &tsid) < 0)
74a30a58
UD
400 {
401 dbg_log (_("Error getting sid from context"));
402 goto out;
403 }
404
0699f766
CD
405 /* The SELinux API for avc_has_perm conflates access denied and error into
406 the return code -1, while nscd_request_avs_has_perm has distinct error
407 (-1) and denied (1) return codes. We map the avc_has_perm access denied or
408 error into an access denied at the nscd interface level (we do accurately
409 report error for the getpeercon, getcon, and avc_context_to_sid interfaces
410 used above). */
411 rc = avc_has_perm (ssid, tsid, sc_nscd, perm, &aeref, NULL) < 0;
74a30a58
UD
412
413out:
414 if (scon)
415 freecon (scon);
416 if (tcon)
417 freecon (tcon);
418 if (ssid)
419 sidput (ssid);
420 if (tsid)
421 sidput (tsid);
422
423 return rc;
424}
425
426
427/* Wrapper to get AVC statistics. */
428void
429nscd_avc_cache_stats (struct avc_cache_stats *cstats)
430{
431 avc_cache_stats (cstats);
432}
433
434
435/* Print the AVC statistics to stdout. */
436void
437nscd_avc_print_stats (struct avc_cache_stats *cstats)
438{
439 printf (_("\nSELinux AVC Statistics:\n\n"
440 "%15u entry lookups\n"
441 "%15u entry hits\n"
442 "%15u entry misses\n"
443 "%15u entry discards\n"
444 "%15u CAV lookups\n"
445 "%15u CAV hits\n"
446 "%15u CAV probes\n"
447 "%15u CAV misses\n"),
448 cstats->entry_lookups, cstats->entry_hits, cstats->entry_misses,
449 cstats->entry_discards, cstats->cav_lookups, cstats->cav_hits,
450 cstats->cav_probes, cstats->cav_misses);
451}
452
74a30a58 453#endif /* HAVE_SELINUX */