Fix ifunc-impl-list.c build for s390
[glibc.git] / nscd / selinux.c
blobfbd1ac24e65176d4abef41ebfdf699af252e90d9
1 /* SELinux access controls for nscd.
2 Copyright (C) 2004-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include "config.h"
20 #include <error.h>
21 #include <errno.h>
22 #include <libintl.h>
23 #include <pthread.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <syslog.h>
28 #include <unistd.h>
29 #include <sys/prctl.h>
30 #include <selinux/avc.h>
31 #include <selinux/selinux.h>
32 #ifdef HAVE_LIBAUDIT
33 # include <libaudit.h>
34 #endif
35 #include <libc-diag.h>
37 #include "dbg_log.h"
38 #include "selinux.h"
41 #ifdef HAVE_SELINUX
42 /* Global variable to tell if the kernel has SELinux support. */
43 int selinux_enabled;
45 /* Define mappings of request type to AVC permission name. */
46 static const char *perms[LASTREQ] =
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",
72 /* Store an entry ref to speed AVC decisions. */
73 static struct avc_entry_ref aeref;
75 /* Thread to listen for SELinux status changes via netlink. */
76 static pthread_t avc_notify_thread;
78 #ifdef HAVE_LIBAUDIT
79 /* Prototype for supporting the audit daemon */
80 static void log_callback (const char *fmt, ...);
81 #endif
83 /* Prototypes for AVC callback functions. */
84 static void *avc_create_thread (void (*run) (void));
85 static void avc_stop_thread (void *thread);
86 static void *avc_alloc_lock (void);
87 static void avc_get_lock (void *lock);
88 static void avc_release_lock (void *lock);
89 static void avc_free_lock (void *lock);
91 /* AVC callback structures for use in avc_init. */
92 static const struct avc_log_callback log_cb =
94 #ifdef HAVE_LIBAUDIT
95 .func_log = log_callback,
96 #else
97 .func_log = dbg_log,
98 #endif
99 .func_audit = NULL
101 static const struct avc_thread_callback thread_cb =
103 .func_create_thread = avc_create_thread,
104 .func_stop_thread = avc_stop_thread
106 static const struct avc_lock_callback lock_cb =
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
114 #ifdef HAVE_LIBAUDIT
115 /* The audit system's netlink socket descriptor */
116 static int audit_fd = -1;
118 /* When an avc denial occurs, log it to audit system */
119 static void
120 log_callback (const char *fmt, ...)
122 if (audit_fd >= 0)
124 va_list ap;
125 va_start (ap, fmt);
127 char *buf;
128 int e = vasprintf (&buf, fmt, ap);
129 if (e < 0)
131 buf = alloca (BUFSIZ);
132 vsnprintf (buf, BUFSIZ, fmt, ap);
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 ());
139 if (e >= 0)
140 free (buf);
142 va_end (ap);
146 /* Initialize the connection to the audit system */
147 static void
148 audit_init (void)
150 audit_fd = audit_open ();
151 if (audit_fd < 0
152 /* If kernel doesn't support audit, bail out */
153 && errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT)
154 dbg_log (_("Failed opening connection to the audit subsystem: %m"));
158 # ifdef HAVE_LIBCAP
159 static 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]))
162 static 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]))
166 cap_t
167 preserve_capabilities (void)
169 if (getuid () != 0)
170 /* Not root, then we cannot preserve anything. */
171 return NULL;
173 if (prctl (PR_SET_KEEPCAPS, 1) == -1)
175 dbg_log (_("Failed to set keep-capabilities"));
176 do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
177 /* NOTREACHED */
180 cap_t tmp_caps = cap_init ();
181 cap_t new_caps = NULL;
182 if (tmp_caps != NULL)
183 new_caps = cap_init ();
185 if (tmp_caps == NULL || new_caps == NULL)
187 if (tmp_caps != NULL)
188 cap_free (tmp_caps);
190 dbg_log (_("Failed to initialize drop of capabilities"));
191 do_exit (EXIT_FAILURE, 0, _("cap_init failed"));
194 /* There is no reason why these should not work. */
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);
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);
205 int res = cap_set_proc (tmp_caps);
207 cap_free (tmp_caps);
209 if (__glibc_unlikely (res != 0))
211 cap_free (new_caps);
212 dbg_log (_("Failed to drop capabilities"));
213 do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
216 return new_caps;
219 void
220 install_real_capabilities (cap_t new_caps)
222 /* If we have no capabilities there is nothing to do here. */
223 if (new_caps == NULL)
224 return;
226 if (cap_set_proc (new_caps))
228 cap_free (new_caps);
229 dbg_log (_("Failed to drop capabilities"));
230 do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
231 /* NOTREACHED */
234 cap_free (new_caps);
236 if (prctl (PR_SET_KEEPCAPS, 0) == -1)
238 dbg_log (_("Failed to unset keep-capabilities"));
239 do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
240 /* NOTREACHED */
243 # endif /* HAVE_LIBCAP */
244 #endif /* HAVE_LIBAUDIT */
246 /* Determine if we are running on an SELinux kernel. Set selinux_enabled
247 to the result. */
248 void
249 nscd_selinux_enabled (int *selinux_enabled)
251 *selinux_enabled = is_selinux_enabled ();
252 if (*selinux_enabled < 0)
254 dbg_log (_("Failed to determine if kernel supports SELinux"));
255 do_exit (EXIT_FAILURE, 0, NULL);
260 /* Create thread for AVC netlink notification. */
261 static void *
262 avc_create_thread (void (*run) (void))
264 int rc;
266 rc =
267 pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
268 if (rc != 0)
269 do_exit (EXIT_FAILURE, rc, _("Failed to start AVC thread"));
271 return &avc_notify_thread;
275 /* Stop AVC netlink thread. */
276 static void
277 avc_stop_thread (void *thread)
279 pthread_cancel (*(pthread_t *) thread);
283 /* Allocate a new AVC lock. */
284 static void *
285 avc_alloc_lock (void)
287 pthread_mutex_t *avc_mutex;
289 avc_mutex = malloc (sizeof (pthread_mutex_t));
290 if (avc_mutex == NULL)
291 do_exit (EXIT_FAILURE, errno, _("Failed to create AVC lock"));
292 pthread_mutex_init (avc_mutex, NULL);
294 return avc_mutex;
298 /* Acquire an AVC lock. */
299 static void
300 avc_get_lock (void *lock)
302 pthread_mutex_lock (lock);
306 /* Release an AVC lock. */
307 static void
308 avc_release_lock (void *lock)
310 pthread_mutex_unlock (lock);
314 /* Free an AVC lock. */
315 static void
316 avc_free_lock (void *lock)
318 pthread_mutex_destroy (lock);
319 free (lock);
323 /* avc_init (along with several other symbols) was marked as deprecated by the
324 SELinux API starting from version 3.1. We use it here, but should
325 eventually switch to the newer API. */
326 DIAG_PUSH_NEEDS_COMMENT
327 DIAG_IGNORE_NEEDS_COMMENT (10, "-Wdeprecated-declarations");
329 /* Initialize the user space access vector cache (AVC) for NSCD along with
330 log/thread/lock callbacks. */
331 void
332 nscd_avc_init (void)
334 avc_entry_ref_init (&aeref);
336 if (avc_init ("avc", NULL, &log_cb, &thread_cb, &lock_cb) < 0)
337 do_exit (EXIT_FAILURE, errno, _("Failed to start AVC"));
338 else
339 dbg_log (_("Access Vector Cache (AVC) started"));
340 #ifdef HAVE_LIBAUDIT
341 audit_init ();
342 #endif
344 DIAG_POP_NEEDS_COMMENT
347 /* security_context_t and sidput (along with several other symbols) were marked
348 as deprecated by the SELinux API starting from version 3.1. We use them
349 here, but should eventually switch to the newer API. */
350 DIAG_PUSH_NEEDS_COMMENT
351 DIAG_IGNORE_NEEDS_COMMENT (10, "-Wdeprecated-declarations");
353 /* Check the permission from the caller (via getpeercon) to nscd.
354 Returns 0 if access is allowed, 1 if denied, and -1 on error.
356 The SELinux policy, enablement, and permission bits are all dynamic and the
357 caching done by glibc is not entirely correct. This nscd support should be
358 rewritten to use selinux_check_permission. A rewrite is risky though and
359 requires some refactoring. Currently we use symbolic mappings instead of
360 compile time constants (which SELinux upstream says are going away), and we
361 use security_deny_unknown to determine what to do if selinux-policy* doesn't
362 have a definition for the the permission or object class we are looking
363 up. */
365 nscd_request_avc_has_perm (int fd, request_type req)
367 /* Initialize to NULL so we know what to free in case of failure. */
368 security_context_t scon = NULL;
369 security_context_t tcon = NULL;
370 security_id_t ssid = NULL;
371 security_id_t tsid = NULL;
372 int rc = -1;
373 security_class_t sc_nscd;
374 access_vector_t perm;
375 int avc_deny_unknown;
377 /* Check if SELinux denys or allows unknown object classes
378 and permissions. It is 0 if they are allowed, 1 if they
379 are not allowed and -1 on error. */
380 if ((avc_deny_unknown = security_deny_unknown ()) == -1)
381 dbg_log (_("Error querying policy for undefined object classes "
382 "or permissions."));
384 /* Get the security class for nscd. If this fails we will likely be
385 unable to do anything unless avc_deny_unknown is 0. */
386 sc_nscd = string_to_security_class ("nscd");
387 if (sc_nscd == 0 && avc_deny_unknown == 1)
388 dbg_log (_("Error getting security class for nscd."));
390 /* Convert permission to AVC bits. */
391 perm = string_to_av_perm (sc_nscd, perms[req]);
392 if (perm == 0 && avc_deny_unknown == 1)
393 dbg_log (_("Error translating permission name "
394 "\"%s\" to access vector bit."), perms[req]);
396 /* If the nscd security class was not found or perms were not
397 found and AVC does not deny unknown values then allow it. */
398 if ((sc_nscd == 0 || perm == 0) && avc_deny_unknown == 0)
399 return 0;
401 if (getpeercon (fd, &scon) < 0)
403 dbg_log (_("Error getting context of socket peer"));
404 goto out;
406 if (getcon (&tcon) < 0)
408 dbg_log (_("Error getting context of nscd"));
409 goto out;
411 if (avc_context_to_sid (scon, &ssid) < 0
412 || avc_context_to_sid (tcon, &tsid) < 0)
414 dbg_log (_("Error getting sid from context"));
415 goto out;
418 /* The SELinux API for avc_has_perm conflates access denied and error into
419 the return code -1, while nscd_request_avs_has_perm has distinct error
420 (-1) and denied (1) return codes. We map the avc_has_perm access denied or
421 error into an access denied at the nscd interface level (we do accurately
422 report error for the getpeercon, getcon, and avc_context_to_sid interfaces
423 used above). */
424 rc = avc_has_perm (ssid, tsid, sc_nscd, perm, &aeref, NULL) < 0;
426 out:
427 if (scon)
428 freecon (scon);
429 if (tcon)
430 freecon (tcon);
431 if (ssid)
432 sidput (ssid);
433 if (tsid)
434 sidput (tsid);
436 return rc;
438 DIAG_POP_NEEDS_COMMENT
441 /* Wrapper to get AVC statistics. */
442 void
443 nscd_avc_cache_stats (struct avc_cache_stats *cstats)
445 avc_cache_stats (cstats);
449 /* Print the AVC statistics to stdout. */
450 void
451 nscd_avc_print_stats (struct avc_cache_stats *cstats)
453 printf (_("\nSELinux AVC Statistics:\n\n"
454 "%15u entry lookups\n"
455 "%15u entry hits\n"
456 "%15u entry misses\n"
457 "%15u entry discards\n"
458 "%15u CAV lookups\n"
459 "%15u CAV hits\n"
460 "%15u CAV probes\n"
461 "%15u CAV misses\n"),
462 cstats->entry_lookups, cstats->entry_hits, cstats->entry_misses,
463 cstats->entry_discards, cstats->cav_lookups, cstats->cav_hits,
464 cstats->cav_probes, cstats->cav_misses);
467 #endif /* HAVE_SELINUX */