bug17079.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /* Test for bug 17079: heap overflow in NSS with small buffers.
  2. Copyright (C) 2015-2019 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <http://www.gnu.org/licenses/>. */
  15. #include <errno.h>
  16. #include <nss.h>
  17. #include <pwd.h>
  18. #include <stdbool.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <support/support.h>
  23. /* Check if two passwd structs contain the same data. */
  24. static bool
  25. equal (const struct passwd *a, const struct passwd *b)
  26. {
  27. return strcmp (a->pw_name, b->pw_name) == 0
  28. && strcmp (a->pw_passwd, b->pw_passwd) == 0
  29. && a->pw_uid == b->pw_uid
  30. && a->pw_gid == b->pw_gid
  31. && strcmp (a->pw_gecos, b->pw_gecos) == 0
  32. && strcmp (a->pw_dir, b->pw_dir) == 0
  33. && strcmp (a->pw_shell, b->pw_shell) == 0;
  34. }
  35. enum { MAX_TEST_ITEMS = 10 };
  36. static struct passwd test_items[MAX_TEST_ITEMS];
  37. static int test_count;
  38. /* Initialize test_items and test_count above, with data from the
  39. passwd database. */
  40. static bool
  41. init_test_items (void)
  42. {
  43. setpwent ();
  44. do
  45. {
  46. struct passwd *pwd = getpwent ();
  47. if (pwd == NULL)
  48. break;
  49. struct passwd *target = test_items + test_count;
  50. target->pw_name = xstrdup (pwd->pw_name);
  51. target->pw_passwd = xstrdup (pwd->pw_passwd);
  52. target->pw_uid = pwd->pw_uid;
  53. target->pw_gid = pwd->pw_gid;
  54. target->pw_gecos = xstrdup (pwd->pw_gecos);
  55. target->pw_dir = xstrdup (pwd->pw_dir);
  56. target->pw_shell = xstrdup (pwd->pw_shell);
  57. }
  58. while (++test_count < MAX_TEST_ITEMS);
  59. endpwent ();
  60. /* Filter out those test items which cannot be looked up by name or
  61. UID. */
  62. bool found = false;
  63. for (int i = 0; i < test_count; ++i)
  64. {
  65. struct passwd *pwd1 = getpwnam (test_items[i].pw_name);
  66. struct passwd *pwd2 = getpwuid (test_items[i].pw_uid);
  67. if (pwd1 == NULL || !equal (pwd1, test_items + i)
  68. || pwd2 == NULL || !equal (pwd2, test_items + i))
  69. {
  70. printf ("info: skipping user \"%s\", UID %ld due to inconsistency\n",
  71. test_items[i].pw_name, (long) test_items[i].pw_uid);
  72. test_items[i].pw_name = NULL;
  73. }
  74. else
  75. found = true;
  76. }
  77. if (!found)
  78. puts ("error: no accounts found which can be looked up by name and UID.");
  79. return found;
  80. }
  81. /* Set to true if an error is encountered. */
  82. static bool errors;
  83. /* Return true if the padding has not been tampered with. */
  84. static bool
  85. check_padding (char *buffer, size_t size, char pad)
  86. {
  87. char *end = buffer + size;
  88. while (buffer < end)
  89. {
  90. if (*buffer != pad)
  91. return false;
  92. ++buffer;
  93. }
  94. return true;
  95. }
  96. /* Test one buffer size and padding combination. */
  97. static void
  98. test_one (const struct passwd *item, size_t buffer_size,
  99. char pad, size_t padding_size)
  100. {
  101. char *buffer = xmalloc (buffer_size + padding_size);
  102. struct passwd pwd;
  103. struct passwd *result;
  104. int ret;
  105. /* Test getpwname_r. */
  106. memset (buffer, pad, buffer_size + padding_size);
  107. pwd = (struct passwd) {};
  108. ret = getpwnam_r (item->pw_name, &pwd, buffer, buffer_size, &result);
  109. if (!check_padding (buffer + buffer_size, padding_size, pad))
  110. {
  111. printf ("error: padding change: "
  112. "name \"%s\", buffer size %zu, padding size %zu, pad 0x%02x\n",
  113. item->pw_name, buffer_size, padding_size, (unsigned char) pad);
  114. errors = true;
  115. }
  116. if (ret == 0)
  117. {
  118. if (result == NULL)
  119. {
  120. printf ("error: no data: name \"%s\", buffer size %zu\n",
  121. item->pw_name, buffer_size);
  122. errors = true;
  123. }
  124. else if (!equal (item, result))
  125. {
  126. printf ("error: lookup mismatch: name \"%s\", buffer size %zu\n",
  127. item->pw_name, buffer_size);
  128. errors = true;
  129. }
  130. }
  131. else if (ret != ERANGE)
  132. {
  133. errno = ret;
  134. printf ("error: lookup failure for name \"%s\": %m (%d)\n",
  135. item->pw_name, ret);
  136. errors = true;
  137. }
  138. /* Test getpwuid_r. */
  139. memset (buffer, pad, buffer_size + padding_size);
  140. pwd = (struct passwd) {};
  141. ret = getpwuid_r (item->pw_uid, &pwd, buffer, buffer_size, &result);
  142. if (!check_padding (buffer + buffer_size, padding_size, pad))
  143. {
  144. printf ("error: padding change: "
  145. "UID %ld, buffer size %zu, padding size %zu, pad 0x%02x\n",
  146. (long) item->pw_uid, buffer_size, padding_size,
  147. (unsigned char) pad);
  148. errors = true;
  149. }
  150. if (ret == 0)
  151. {
  152. if (result == NULL)
  153. {
  154. printf ("error: no data: UID %ld, buffer size %zu\n",
  155. (long) item->pw_uid, buffer_size);
  156. errors = true;
  157. }
  158. else if (!equal (item, result))
  159. {
  160. printf ("error: lookup mismatch: UID %ld, buffer size %zu\n",
  161. (long) item->pw_uid, buffer_size);
  162. errors = true;
  163. }
  164. }
  165. else if (ret != ERANGE)
  166. {
  167. errno = ret;
  168. printf ("error: lookup failure for UID \"%ld\": %m (%d)\n",
  169. (long) item->pw_uid, ret);
  170. errors = true;
  171. }
  172. free (buffer);
  173. }
  174. /* Test one buffer size with different paddings. */
  175. static void
  176. test_buffer_size (size_t buffer_size)
  177. {
  178. for (int i = 0; i < test_count; ++i)
  179. for (size_t padding_size = 0; padding_size < 3; ++padding_size)
  180. {
  181. /* Skip entries with inconsistent name/UID lookups. */
  182. if (test_items[i].pw_name == NULL)
  183. continue;
  184. test_one (test_items + i, buffer_size, '\0', padding_size);
  185. if (padding_size > 0)
  186. {
  187. test_one (test_items + i, buffer_size, ':', padding_size);
  188. test_one (test_items + i, buffer_size, '\n', padding_size);
  189. test_one (test_items + i, buffer_size, '\xff', padding_size);
  190. test_one (test_items + i, buffer_size, '@', padding_size);
  191. }
  192. }
  193. }
  194. int
  195. do_test (void)
  196. {
  197. __nss_configure_lookup ("passwd", "files");
  198. if (!init_test_items ())
  199. return 1;
  200. printf ("info: %d test items\n", test_count);
  201. for (size_t buffer_size = 0; buffer_size <= 65; ++buffer_size)
  202. test_buffer_size (buffer_size);
  203. for (size_t buffer_size = 64 + 4; buffer_size < 256; buffer_size += 4)
  204. test_buffer_size (buffer_size);
  205. test_buffer_size (255);
  206. test_buffer_size (257);
  207. for (size_t buffer_size = 256; buffer_size < 512; buffer_size += 8)
  208. test_buffer_size (buffer_size);
  209. test_buffer_size (511);
  210. test_buffer_size (513);
  211. test_buffer_size (1024);
  212. test_buffer_size (2048);
  213. if (errors)
  214. return 1;
  215. else
  216. return 0;
  217. }
  218. #include <support/test-driver.c>