(root)/
glibc-2.38/
nss/
bug17079.c
       1  /* Test for bug 17079: heap overflow in NSS with small buffers.
       2     Copyright (C) 2015-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 <errno.h>
      20  #include <nss.h>
      21  #include <pwd.h>
      22  #include <stdbool.h>
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  
      27  #include <support/support.h>
      28  
      29  /* Check if two passwd structs contain the same data.  */
      30  static bool
      31  equal (const struct passwd *a, const struct passwd *b)
      32  {
      33    return strcmp (a->pw_name, b->pw_name) == 0
      34      && strcmp (a->pw_passwd, b->pw_passwd) == 0
      35      && a->pw_uid == b->pw_uid
      36      && a->pw_gid == b->pw_gid
      37      && strcmp (a->pw_gecos, b->pw_gecos) == 0
      38      && strcmp (a->pw_dir, b->pw_dir) == 0
      39      && strcmp (a->pw_shell, b->pw_shell) == 0;
      40  }
      41  
      42  enum { MAX_TEST_ITEMS = 10 };
      43  static struct passwd test_items[MAX_TEST_ITEMS];
      44  static int test_count;
      45  
      46  /* Initialize test_items and test_count above, with data from the
      47     passwd database.  */
      48  static bool
      49  init_test_items (void)
      50  {
      51    setpwent ();
      52    do
      53      {
      54        struct passwd *pwd = getpwent ();
      55        if (pwd == NULL)
      56          break;
      57        struct passwd *target = test_items + test_count;
      58        target->pw_name = xstrdup (pwd->pw_name);
      59        target->pw_passwd = xstrdup (pwd->pw_passwd);
      60        target->pw_uid = pwd->pw_uid;
      61        target->pw_gid = pwd->pw_gid;
      62        target->pw_gecos = xstrdup (pwd->pw_gecos);
      63        target->pw_dir = xstrdup (pwd->pw_dir);
      64        target->pw_shell = xstrdup (pwd->pw_shell);
      65      }
      66    while (++test_count < MAX_TEST_ITEMS);
      67    endpwent ();
      68  
      69    /* Filter out those test items which cannot be looked up by name or
      70       UID.  */
      71    bool found = false;
      72    for (int i = 0; i < test_count; ++i)
      73      {
      74        struct passwd *pwd1 = getpwnam (test_items[i].pw_name);
      75        struct passwd *pwd2 = getpwuid (test_items[i].pw_uid);
      76        if (pwd1 == NULL || !equal (pwd1, test_items + i)
      77            || pwd2 == NULL || !equal (pwd2, test_items + i))
      78          {
      79            printf ("info: skipping user \"%s\", UID %ld due to inconsistency\n",
      80                    test_items[i].pw_name, (long) test_items[i].pw_uid);
      81            test_items[i].pw_name = NULL;
      82          }
      83        else
      84          found = true;
      85      }
      86  
      87    if (!found)
      88      puts ("error: no accounts found which can be looked up by name and UID.");
      89    return found;
      90  }
      91  
      92  /* Set to true if an error is encountered.  */
      93  static bool errors;
      94  
      95  /* Return true if the padding has not been tampered with.  */
      96  static bool
      97  check_padding (char *buffer, size_t size, char pad)
      98  {
      99    char *end = buffer + size;
     100    while (buffer < end)
     101      {
     102        if (*buffer != pad)
     103          return false;
     104        ++buffer;
     105      }
     106    return true;
     107  }
     108  
     109  /* Test one buffer size and padding combination.  */
     110  static void
     111  test_one (const struct passwd *item, size_t buffer_size,
     112             char pad, size_t padding_size)
     113  {
     114    char *buffer = xmalloc (buffer_size + padding_size);
     115  
     116    struct passwd pwd;
     117    struct passwd *result;
     118    int ret;
     119  
     120    /* Test getpwname_r.  */
     121    memset (buffer, pad, buffer_size + padding_size);
     122    pwd = (struct passwd) {};
     123    ret = getpwnam_r (item->pw_name, &pwd, buffer, buffer_size, &result);
     124    if (!check_padding (buffer + buffer_size, padding_size, pad))
     125      {
     126        printf ("error: padding change: "
     127                "name \"%s\", buffer size %zu, padding size %zu, pad 0x%02x\n",
     128                item->pw_name, buffer_size, padding_size, (unsigned char) pad);
     129        errors = true;
     130      }
     131    if (ret == 0)
     132      {
     133        if (result == NULL)
     134          {
     135            printf ("error: no data: name \"%s\", buffer size %zu\n",
     136                    item->pw_name, buffer_size);
     137            errors = true;
     138          }
     139        else if (!equal (item, result))
     140          {
     141            printf ("error: lookup mismatch: name \"%s\", buffer size %zu\n",
     142                    item->pw_name, buffer_size);
     143            errors = true;
     144          }
     145      }
     146    else if (ret != ERANGE)
     147      {
     148        errno = ret;
     149        printf ("error: lookup failure for name \"%s\": %m (%d)\n",
     150                item->pw_name, ret);
     151        errors = true;
     152      }
     153  
     154    /* Test getpwuid_r.  */
     155    memset (buffer, pad, buffer_size + padding_size);
     156    pwd = (struct passwd) {};
     157    ret = getpwuid_r (item->pw_uid, &pwd, buffer, buffer_size, &result);
     158    if (!check_padding (buffer + buffer_size, padding_size, pad))
     159      {
     160        printf ("error: padding change: "
     161                "UID %ld, buffer size %zu, padding size %zu, pad 0x%02x\n",
     162                (long) item->pw_uid, buffer_size, padding_size,
     163                (unsigned char) pad);
     164        errors = true;
     165      }
     166    if (ret == 0)
     167      {
     168        if (result == NULL)
     169          {
     170            printf ("error: no data: UID %ld, buffer size %zu\n",
     171                    (long) item->pw_uid, buffer_size);
     172            errors = true;
     173          }
     174        else if (!equal (item, result))
     175          {
     176            printf ("error: lookup mismatch: UID %ld, buffer size %zu\n",
     177                    (long) item->pw_uid, buffer_size);
     178            errors = true;
     179          }
     180      }
     181    else if (ret != ERANGE)
     182      {
     183        errno = ret;
     184        printf ("error: lookup failure for UID \"%ld\": %m (%d)\n",
     185                (long) item->pw_uid, ret);
     186        errors = true;
     187      }
     188  
     189    free (buffer);
     190  }
     191  
     192  /* Test one buffer size with different paddings.  */
     193  static void
     194  test_buffer_size (size_t buffer_size)
     195  {
     196    for (int i = 0; i < test_count; ++i)
     197      for (size_t padding_size = 0; padding_size < 3; ++padding_size)
     198        {
     199          /* Skip entries with inconsistent name/UID lookups.  */
     200          if (test_items[i].pw_name == NULL)
     201            continue;
     202  
     203          test_one (test_items + i, buffer_size, '\0', padding_size);
     204          if (padding_size > 0)
     205            {
     206              test_one (test_items + i, buffer_size, ':', padding_size);
     207              test_one (test_items + i, buffer_size, '\n', padding_size);
     208              test_one (test_items + i, buffer_size, '\xff', padding_size);
     209              test_one (test_items + i, buffer_size, '@', padding_size);
     210            }
     211        }
     212  }
     213  
     214  int
     215  do_test (void)
     216  {
     217    __nss_configure_lookup ("passwd", "files");
     218  
     219    if (!init_test_items ())
     220      return 1;
     221    printf ("info: %d test items\n", test_count);
     222  
     223    for (size_t buffer_size = 0; buffer_size <= 65; ++buffer_size)
     224      test_buffer_size (buffer_size);
     225    for (size_t buffer_size = 64 + 4; buffer_size < 256; buffer_size += 4)
     226      test_buffer_size (buffer_size);
     227    test_buffer_size (255);
     228    test_buffer_size (257);
     229    for (size_t buffer_size = 256; buffer_size < 512; buffer_size += 8)
     230      test_buffer_size (buffer_size);
     231    test_buffer_size (511);
     232    test_buffer_size (513);
     233    test_buffer_size (1024);
     234    test_buffer_size (2048);
     235  
     236    if (errors)
     237      return 1;
     238    else
     239      return 0;
     240  }
     241  
     242  #include <support/test-driver.c>