1  /* NSS actions, elements in a nsswitch.conf configuration line.
       2     Copyright (c) 2020-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 <nsswitch.h>
      20  
      21  #include <string.h>
      22  #include <libc-lock.h>
      23  
      24  /* Maintain a global list of NSS action lists.  Since most databases
      25     use the same list of actions, this list is usually short.
      26     Deduplication in __nss_action_allocate ensures that the list does
      27     not grow without bounds.  */
      28  
      29  struct nss_action_list_wrapper
      30  {
      31    /* The next element of the list.  */
      32    struct nss_action_list_wrapper *next;
      33  
      34    /* Number of elements in the list (excluding the terminator).  */
      35    size_t count;
      36  
      37    /* NULL-terminated list of actions.  */
      38    struct nss_action actions[];
      39  };
      40  
      41  /* Toplevel list of allocated NSS action lists.  */
      42  static struct nss_action_list_wrapper *nss_actions;
      43  
      44  /* Lock covers the nss_actions list.  */
      45  __libc_lock_define (static, nss_actions_lock);
      46  
      47  /* Returns true if the actions are equal (same module, same actions
      48     array).  */
      49  static bool
      50  actions_equal (const struct nss_action *a, const struct nss_action *b)
      51  {
      52    return a->module == b->module && a->action_bits == b->action_bits;
      53  }
      54  
      55  
      56  /* Returns true if COUNT actions at A and B are equal (according to
      57     actions_equal above). Caller must ensure that either A or B have at
      58     least COUNT actions.  */
      59  static bool
      60  action_lists_equal (const struct nss_action *a, const struct nss_action *b,
      61                      size_t count)
      62  {
      63    for (size_t i = 0; i < count; ++i)
      64      if (!actions_equal (a + i, b + i))
      65        return false;
      66    return true;
      67  }
      68  
      69  /* Returns a pre-allocated action list for COUNT actions at ACTIONS,
      70     or NULL if no such list exists.  */
      71  static nss_action_list
      72  find_allocated (struct nss_action *actions, size_t count)
      73  {
      74    for (struct nss_action_list_wrapper *p = nss_actions; p != NULL; p = p->next)
      75      if (p->count == count && action_lists_equal (p->actions, actions, count))
      76        return p->actions;
      77    return NULL;
      78  }
      79  
      80  nss_action_list
      81  __nss_action_allocate (struct nss_action *actions, size_t count)
      82  {
      83    nss_action_list result = NULL;
      84    __libc_lock_lock (nss_actions_lock);
      85  
      86    result = find_allocated (actions, count);
      87    if (result == NULL)
      88      {
      89        struct nss_action_list_wrapper *wrapper
      90          = malloc (sizeof (*wrapper) + sizeof (*actions) * count);
      91        if (wrapper != NULL)
      92          {
      93            wrapper->next = nss_actions;
      94            wrapper->count = count;
      95            memcpy (wrapper->actions, actions, sizeof (*actions) * count);
      96            nss_actions = wrapper;
      97            result = wrapper->actions;
      98          }
      99      }
     100  
     101    __libc_lock_unlock (nss_actions_lock);
     102    return result;
     103  }
     104  
     105  void
     106  __nss_action_freeres (void)
     107  {
     108    struct nss_action_list_wrapper *current = nss_actions;
     109    while (current != NULL)
     110      {
     111        struct nss_action_list_wrapper *next = current->next;
     112        free (current);
     113        current = next;
     114      }
     115    nss_actions = NULL;
     116  }