(root)/
glibc-2.38/
nscd/
selinux.c
       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.
       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
     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
     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
     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
     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
     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 *
     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
     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 *
     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
     300  avc_get_lock (void *lock)
     301  {
     302    pthread_mutex_lock (lock);
     303  }
     304  
     305  
     306  /* Release an AVC lock.  */
     307  static void
     308  avc_release_lock (void *lock)
     309  {
     310    pthread_mutex_unlock (lock);
     311  }
     312  
     313  
     314  /* Free an AVC lock.  */
     315  static void
     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
     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
     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
     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
     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 */