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