nscd_getgr_r.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /* Copyright (C) 1998-2026 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3. The GNU C Library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. The GNU C Library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with the GNU C Library; if not, see
  13. <https://www.gnu.org/licenses/>. */
  14. #include <alloca.h>
  15. #include <assert.h>
  16. #include <errno.h>
  17. #include <grp.h>
  18. #include <stdint.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <sys/mman.h>
  24. #include <sys/socket.h>
  25. #include <sys/uio.h>
  26. #include <sys/un.h>
  27. #include <not-cancel.h>
  28. #include <_itoa.h>
  29. #include <scratch_buffer.h>
  30. #include "nscd-client.h"
  31. #include "nscd_proto.h"
  32. int __nss_not_use_nscd_group;
  33. static int nscd_getgr_r (const char *key, size_t keylen, request_type type,
  34. struct group *resultbuf, char *buffer,
  35. size_t buflen, struct group **result);
  36. int
  37. __nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer,
  38. size_t buflen, struct group **result)
  39. {
  40. return nscd_getgr_r (name, strlen (name) + 1, GETGRBYNAME, resultbuf,
  41. buffer, buflen, result);
  42. }
  43. int
  44. __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
  45. size_t buflen, struct group **result)
  46. {
  47. char buf[3 * sizeof (gid_t)];
  48. buf[sizeof (buf) - 1] = '\0';
  49. char *cp = _itoa_word (gid, buf + sizeof (buf) - 1, 10, 0);
  50. return nscd_getgr_r (cp, buf + sizeof (buf) - cp, GETGRBYGID, resultbuf,
  51. buffer, buflen, result);
  52. }
  53. libc_locked_map_ptr (,__gr_map_handle) attribute_hidden;
  54. /* Note that we only free the structure if necessary. The memory
  55. mapping is not removed since it is not visible to the malloc
  56. handling. */
  57. void
  58. __nscd_gr_map_freemem (void)
  59. {
  60. if (__gr_map_handle.mapped != NO_MAPPING)
  61. {
  62. void *p = __gr_map_handle.mapped;
  63. __gr_map_handle.mapped = NO_MAPPING;
  64. free (p);
  65. }
  66. }
  67. static int
  68. nscd_getgr_r (const char *key, size_t keylen, request_type type,
  69. struct group *resultbuf, char *buffer, size_t buflen,
  70. struct group **result)
  71. {
  72. int gc_cycle;
  73. int nretries = 0;
  74. const uint32_t *len = NULL;
  75. struct scratch_buffer lenbuf;
  76. scratch_buffer_init (&lenbuf);
  77. /* If the mapping is available, try to search there instead of
  78. communicating with the nscd. */
  79. struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
  80. &__gr_map_handle,
  81. &gc_cycle);
  82. retry:;
  83. const char *gr_name = NULL;
  84. size_t gr_name_len = 0;
  85. int retval = -1;
  86. const char *recend = (const char *) ~UINTMAX_C (0);
  87. gr_response_header gr_resp;
  88. if (mapped != NO_MAPPING)
  89. {
  90. struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
  91. sizeof gr_resp);
  92. if (found != NULL)
  93. {
  94. len = (const uint32_t *) (&found->data[0].grdata + 1);
  95. gr_resp = found->data[0].grdata;
  96. gr_name = ((const char *) len
  97. + gr_resp.gr_mem_cnt * sizeof (uint32_t));
  98. gr_name_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
  99. recend = (const char *) found->data + found->recsize;
  100. /* Now check if we can trust gr_resp fields. If GC is
  101. in progress, it can contain anything. */
  102. if (mapped->head->gc_cycle != gc_cycle)
  103. {
  104. retval = -2;
  105. goto out;
  106. }
  107. /* The alignment is always sufficient, unless GC is in progress. */
  108. assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
  109. }
  110. }
  111. int sock = -1;
  112. if (gr_name == NULL)
  113. {
  114. sock = __nscd_open_socket (key, keylen, type, &gr_resp,
  115. sizeof (gr_resp));
  116. if (sock == -1)
  117. {
  118. __nss_not_use_nscd_group = 1;
  119. goto out;
  120. }
  121. }
  122. /* No value found so far. */
  123. *result = NULL;
  124. if (__glibc_unlikely (gr_resp.found == -1))
  125. {
  126. /* The daemon does not cache this database. */
  127. __nss_not_use_nscd_group = 1;
  128. goto out_close;
  129. }
  130. if (gr_resp.found == 1)
  131. {
  132. struct iovec vec[2];
  133. char *p = buffer;
  134. size_t total_len;
  135. uintptr_t align;
  136. nscd_ssize_t cnt;
  137. /* Now allocate the buffer the array for the group members. We must
  138. align the pointer. */
  139. align = ((__alignof__ (char *) - ((uintptr_t) p))
  140. & (__alignof__ (char *) - 1));
  141. total_len = (align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *)
  142. + gr_resp.gr_name_len + gr_resp.gr_passwd_len);
  143. if (__glibc_unlikely (buflen < total_len))
  144. {
  145. no_room:
  146. __set_errno (ERANGE);
  147. retval = ERANGE;
  148. goto out_close;
  149. }
  150. buflen -= total_len;
  151. p += align;
  152. resultbuf->gr_mem = (char **) p;
  153. p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
  154. /* Set pointers for strings. */
  155. resultbuf->gr_name = p;
  156. p += gr_resp.gr_name_len;
  157. resultbuf->gr_passwd = p;
  158. p += gr_resp.gr_passwd_len;
  159. /* Fill in what we know now. */
  160. resultbuf->gr_gid = gr_resp.gr_gid;
  161. /* Read the length information, group name, and password. */
  162. if (gr_name == NULL)
  163. {
  164. /* Handle a simple, usual case: no group members. */
  165. if (__glibc_likely (gr_resp.gr_mem_cnt == 0))
  166. {
  167. size_t n = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
  168. if (__builtin_expect (__readall (sock, resultbuf->gr_name, n)
  169. != (ssize_t) n, 0))
  170. goto out_close;
  171. }
  172. else
  173. {
  174. /* Allocate array to store lengths. */
  175. if (!scratch_buffer_set_array_size
  176. (&lenbuf, gr_resp.gr_mem_cnt, sizeof (uint32_t)))
  177. goto out_close;
  178. len = lenbuf.data;
  179. vec[0].iov_base = (void *) len;
  180. vec[0].iov_len = gr_resp.gr_mem_cnt * sizeof (uint32_t);
  181. vec[1].iov_base = resultbuf->gr_name;
  182. vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
  183. total_len = vec[0].iov_len + vec[1].iov_len;
  184. /* Get this data. */
  185. size_t n = __readvall (sock, vec, 2);
  186. if (__glibc_unlikely (n != total_len))
  187. goto out_close;
  188. }
  189. }
  190. else
  191. /* We already have the data. Just copy the group name and
  192. password. */
  193. memcpy (resultbuf->gr_name, gr_name,
  194. gr_resp.gr_name_len + gr_resp.gr_passwd_len);
  195. /* Clear the terminating entry. */
  196. resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
  197. /* Prepare reading the group members. */
  198. total_len = 0;
  199. for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
  200. {
  201. resultbuf->gr_mem[cnt] = p;
  202. total_len += len[cnt];
  203. p += len[cnt];
  204. }
  205. if (__glibc_unlikely (gr_name + gr_name_len + total_len > recend))
  206. {
  207. /* len array might contain garbage during nscd GC cycle,
  208. retry rather than fail in that case. */
  209. if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
  210. retval = -2;
  211. goto out_close;
  212. }
  213. if (__glibc_unlikely (total_len > buflen))
  214. {
  215. /* len array might contain garbage during nscd GC cycle,
  216. retry rather than fail in that case. */
  217. if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
  218. {
  219. retval = -2;
  220. goto out_close;
  221. }
  222. else
  223. goto no_room;
  224. }
  225. retval = 0;
  226. /* If there are no group members TOTAL_LEN is zero. */
  227. if (gr_name == NULL)
  228. {
  229. if (total_len > 0
  230. && __builtin_expect (__readall (sock, resultbuf->gr_mem[0],
  231. total_len) != total_len, 0))
  232. {
  233. /* The `errno' to some value != ERANGE. */
  234. __set_errno (ENOENT);
  235. retval = ENOENT;
  236. }
  237. else
  238. *result = resultbuf;
  239. }
  240. else
  241. {
  242. /* Copy the group member names. */
  243. memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
  244. /* Try to detect corrupt databases. */
  245. if (resultbuf->gr_name[gr_name_len - 1] != '\0'
  246. || resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0'
  247. || ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
  248. if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0')
  249. break;
  250. cnt < gr_resp.gr_mem_cnt; }))
  251. {
  252. /* We cannot use the database. */
  253. retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
  254. goto out_close;
  255. }
  256. *result = resultbuf;
  257. }
  258. }
  259. else
  260. {
  261. /* Set errno to 0 to indicate no error, just no found record. */
  262. __set_errno (0);
  263. /* Even though we have not found anything, the result is zero. */
  264. retval = 0;
  265. }
  266. out_close:
  267. if (sock != -1)
  268. __close_nocancel_nostatus (sock);
  269. out:
  270. if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
  271. {
  272. /* When we come here this means there has been a GC cycle while we
  273. were looking for the data. This means the data might have been
  274. inconsistent. Retry if possible. */
  275. if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
  276. {
  277. /* nscd is just running gc now. Disable using the mapping. */
  278. if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
  279. __nscd_unmap (mapped);
  280. mapped = NO_MAPPING;
  281. }
  282. if (retval != -1)
  283. goto retry;
  284. }
  285. scratch_buffer_free (&lenbuf);
  286. return retval;
  287. }