1 /* SELinux access controls for nscd.
2    Copyright (C) 2004-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
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.
9 
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.
14 
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/>.  */
18 
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>
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.  */
43 int selinux_enabled;
44 
45 /* Define mappings of request type to AVC permission name.  */
46 static const char *perms[LASTREQ] =
47 {
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",
70 };
71 
72 /* Store an entry ref to speed AVC decisions.  */
73 static struct avc_entry_ref aeref;
74 
75 /* Thread to listen for SELinux status changes via netlink.  */
76 static pthread_t avc_notify_thread;
77 
78 #ifdef HAVE_LIBAUDIT
79 /* Prototype for supporting the audit daemon */
80 static void log_callback (const char *fmt, ...);
81 #endif
82 
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);
90 
91 /* AVC callback structures for use in avc_init.  */
92 static const struct avc_log_callback log_cb =
93 {
94 #ifdef HAVE_LIBAUDIT
95   .func_log = log_callback,
96 #else
97   .func_log = dbg_log,
98 #endif
99   .func_audit = NULL
100 };
101 static const struct avc_thread_callback thread_cb =
102 {
103   .func_create_thread = avc_create_thread,
104   .func_stop_thread = avc_stop_thread
105 };
106 static 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 
114 #ifdef HAVE_LIBAUDIT
115 /* The audit system's netlink socket descriptor */
116 static int audit_fd = -1;
117 
118 /* When an avc denial occurs, log it to audit system */
119 static void
log_callback(const char * fmt,...)120 log_callback (const char *fmt, ...)
121 {
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 ());
138 
139       if (e >= 0)
140 	free (buf);
141 
142       va_end (ap);
143     }
144 }
145 
146 /* Initialize the connection to the audit system */
147 static void
audit_init(void)148 audit_init (void)
149 {
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"));
155 }
156 
157 
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]))
165 
166 cap_t
preserve_capabilities(void)167 preserve_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"));
176       do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
177       /* NOTREACHED */
178     }
179 
180   cap_t tmp_caps = cap_init ();
181   cap_t new_caps = NULL;
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)
188 	cap_free (tmp_caps);
189 
190       dbg_log (_("Failed to initialize drop of capabilities"));
191       do_exit (EXIT_FAILURE, 0, _("cap_init failed"));
192     }
193 
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);
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);
204 
205   int res = cap_set_proc (tmp_caps);
206 
207   cap_free (tmp_caps);
208 
209   if (__glibc_unlikely (res != 0))
210     {
211       cap_free (new_caps);
212       dbg_log (_("Failed to drop capabilities"));
213       do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
214     }
215 
216   return new_caps;
217 }
218 
219 void
install_real_capabilities(cap_t new_caps)220 install_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"));
230       do_exit (EXIT_FAILURE, 0, _("cap_set_proc failed"));
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"));
239       do_exit (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
240       /* NOTREACHED */
241     }
242 }
243 # endif /* HAVE_LIBCAP */
244 #endif /* HAVE_LIBAUDIT */
245 
246 /* Determine if we are running on an SELinux kernel. Set selinux_enabled
247    to the result.  */
248 void
nscd_selinux_enabled(int * selinux_enabled)249 nscd_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"));
255       do_exit (EXIT_FAILURE, 0, NULL);
256     }
257 }
258 
259 
260 /* Create thread for AVC netlink notification.  */
261 static void *
avc_create_thread(void (* run)(void))262 avc_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)
269     do_exit (EXIT_FAILURE, rc, _("Failed to start AVC thread"));
270 
271   return &avc_notify_thread;
272 }
273 
274 
275 /* Stop AVC netlink thread.  */
276 static void
avc_stop_thread(void * thread)277 avc_stop_thread (void *thread)
278 {
279   pthread_cancel (*(pthread_t *) thread);
280 }
281 
282 
283 /* Allocate a new AVC lock.  */
284 static void *
avc_alloc_lock(void)285 avc_alloc_lock (void)
286 {
287   pthread_mutex_t *avc_mutex;
288 
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);
293 
294   return avc_mutex;
295 }
296 
297 
298 /* Acquire an AVC lock.  */
299 static void
avc_get_lock(void * lock)300 avc_get_lock (void *lock)
301 {
302   pthread_mutex_lock (lock);
303 }
304 
305 
306 /* Release an AVC lock.  */
307 static void
avc_release_lock(void * lock)308 avc_release_lock (void *lock)
309 {
310   pthread_mutex_unlock (lock);
311 }
312 
313 
314 /* Free an AVC lock.  */
315 static void
avc_free_lock(void * lock)316 avc_free_lock (void *lock)
317 {
318   pthread_mutex_destroy (lock);
319   free (lock);
320 }
321 
322 
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");
328 
329 /* Initialize the user space access vector cache (AVC) for NSCD along with
330    log/thread/lock callbacks.  */
331 void
nscd_avc_init(void)332 nscd_avc_init (void)
333 {
334   avc_entry_ref_init (&aeref);
335 
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
343 }
344 DIAG_POP_NEEDS_COMMENT
345 
346 
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");
352 
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.
355 
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.  */
364 int
nscd_request_avc_has_perm(int fd,request_type req)365 nscd_request_avc_has_perm (int fd, request_type req)
366 {
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;
376 
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."));
383 
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."));
389 
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]);
395 
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;
400 
401   if (getpeercon (fd, &scon) < 0)
402     {
403       dbg_log (_("Error getting context of socket peer"));
404       goto out;
405     }
406   if (getcon (&tcon) < 0)
407     {
408       dbg_log (_("Error getting context of nscd"));
409       goto out;
410     }
411   if (avc_context_to_sid (scon, &ssid) < 0
412       || avc_context_to_sid (tcon, &tsid) < 0)
413     {
414       dbg_log (_("Error getting sid from context"));
415       goto out;
416     }
417 
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;
425 
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);
435 
436   return rc;
437 }
438 DIAG_POP_NEEDS_COMMENT
439 
440 
441 /* Wrapper to get AVC statistics.  */
442 void
nscd_avc_cache_stats(struct avc_cache_stats * cstats)443 nscd_avc_cache_stats (struct avc_cache_stats *cstats)
444 {
445   avc_cache_stats (cstats);
446 }
447 
448 
449 /* Print the AVC statistics to stdout.  */
450 void
nscd_avc_print_stats(struct avc_cache_stats * cstats)451 nscd_avc_print_stats (struct avc_cache_stats *cstats)
452 {
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);
465 }
466 
467 #endif /* HAVE_SELINUX */
468