1/* Copyright (C) 2011-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
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
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18#include <alloca.h>
19#include <errno.h>
20#include <stdlib.h>
21#include <string.h>
22#include <not-cancel.h>
23
24#include "nscd-client.h"
25#include "nscd_proto.h"
26
27int __nss_not_use_nscd_netgroup;
28
29
30libc_locked_map_ptr (static, map_handle);
31/* Note that we only free the structure if necessary. The memory
32 mapping is not removed since it is not visible to the malloc
33 handling. */
34void
35__nscd_group_map_freemem (void)
36{
37 if (map_handle.mapped != NO_MAPPING)
38 {
39 void *p = map_handle.mapped;
40 map_handle.mapped = NO_MAPPING;
41 free (ptr: p);
42 }
43}
44
45
46int
47__nscd_setnetgrent (const char *group, struct __netgrent *datap)
48{
49 int gc_cycle;
50 int nretries = 0;
51 size_t group_len = strlen (group) + 1;
52
53 /* If the mapping is available, try to search there instead of
54 communicating with the nscd. */
55 struct mapped_database *mapped;
56 mapped = __nscd_get_map_ref (type: GETFDNETGR, name: "netgroup", mapptr: &map_handle, gc_cyclep: &gc_cycle);
57
58 retry:;
59 char *respdata = NULL;
60 int retval = -1;
61 netgroup_response_header netgroup_resp;
62
63 if (mapped != NO_MAPPING)
64 {
65 struct datahead *found = __nscd_cache_search (type: GETNETGRENT, key: group,
66 keylen: group_len, mapped,
67 datalen: sizeof netgroup_resp);
68 if (found != NULL)
69 {
70 respdata = (char *) (&found->data[0].netgroupdata + 1);
71 netgroup_resp = found->data[0].netgroupdata;
72 /* Now check if we can trust pw_resp fields. If GC is
73 in progress, it can contain anything. */
74 if (mapped->head->gc_cycle != gc_cycle)
75 {
76 retval = -2;
77 goto out;
78 }
79 }
80 }
81
82 int sock = -1;
83 if (respdata == NULL)
84 {
85 sock = __nscd_open_socket (key: group, keylen: group_len, type: GETNETGRENT,
86 response: &netgroup_resp, responselen: sizeof (netgroup_resp));
87 if (sock == -1)
88 {
89 /* nscd not running or wrong version. */
90 __nss_not_use_nscd_netgroup = 1;
91 goto out;
92 }
93 }
94
95 if (netgroup_resp.found == 1)
96 {
97 size_t datalen = netgroup_resp.result_len;
98
99 /* If we do not have to read the data here it comes from the
100 mapped data and does not have to be freed. */
101 if (respdata == NULL)
102 {
103 /* The data will come via the socket. */
104 respdata = malloc (size: datalen);
105 if (respdata == NULL)
106 goto out_close;
107
108 if ((size_t) __readall (fd: sock, buf: respdata, len: datalen) != datalen)
109 {
110 free (ptr: respdata);
111 goto out_close;
112 }
113 }
114
115 datap->data = respdata;
116 datap->data_size = datalen;
117 datap->cursor = respdata;
118 datap->first = 1;
119 datap->nip = (nss_action_list) -1l;
120 datap->known_groups = NULL;
121 datap->needed_groups = NULL;
122
123 retval = 1;
124 }
125 else
126 {
127 if (__glibc_unlikely (netgroup_resp.found == -1))
128 {
129 /* The daemon does not cache this database. */
130 __nss_not_use_nscd_netgroup = 1;
131 goto out_close;
132 }
133
134 /* Set errno to 0 to indicate no error, just no found record. */
135 __set_errno (0);
136 /* Even though we have not found anything, the result is zero. */
137 retval = 0;
138 }
139
140 out_close:
141 if (sock != -1)
142 __close_nocancel_nostatus (fd: sock);
143 out:
144 if (__nscd_drop_map_ref (map: mapped, gc_cycle: &gc_cycle) != 0)
145 {
146 /* When we come here this means there has been a GC cycle while we
147 were looking for the data. This means the data might have been
148 inconsistent. Retry if possible. */
149 if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
150 {
151 /* nscd is just running gc now. Disable using the mapping. */
152 if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
153 __nscd_unmap (mapped);
154 mapped = NO_MAPPING;
155 }
156
157 if (retval != -1)
158 goto retry;
159 }
160
161 return retval;
162}
163
164
165int
166__nscd_innetgr (const char *netgroup, const char *host, const char *user,
167 const char *domain)
168{
169 size_t key_len = (strlen (netgroup) + strlen (host ?: "")
170 + strlen (user ?: "") + strlen (domain ?: "") + 7);
171 char *key;
172 bool use_alloca = __libc_use_alloca (size: key_len);
173 if (use_alloca)
174 key = alloca (key_len);
175 else
176 {
177 key = malloc (size: key_len);
178 if (key == NULL)
179 return -1;
180 }
181 char *wp = stpcpy (key, netgroup) + 1;
182 if (host != NULL)
183 {
184 *wp++ = '\1';
185 wp = stpcpy (wp, host) + 1;
186 }
187 else
188 *wp++ = '\0';
189 if (user != NULL)
190 {
191 *wp++ = '\1';
192 wp = stpcpy (wp, user) + 1;
193 }
194 else
195 *wp++ = '\0';
196 if (domain != NULL)
197 {
198 *wp++ = '\1';
199 wp = stpcpy (wp, domain) + 1;
200 }
201 else
202 *wp++ = '\0';
203 key_len = wp - key;
204
205 /* If the mapping is available, try to search there instead of
206 communicating with the nscd. */
207 int gc_cycle;
208 int nretries = 0;
209 struct mapped_database *mapped;
210 mapped = __nscd_get_map_ref (type: GETFDNETGR, name: "netgroup", mapptr: &map_handle, gc_cyclep: &gc_cycle);
211
212 retry:;
213 int retval = -1;
214 innetgroup_response_header innetgroup_resp;
215 int sock = -1;
216
217 if (mapped != NO_MAPPING)
218 {
219 struct datahead *found = __nscd_cache_search (type: INNETGR, key,
220 keylen: key_len, mapped,
221 datalen: sizeof innetgroup_resp);
222 if (found != NULL)
223 {
224 innetgroup_resp = found->data[0].innetgroupdata;
225 /* Now check if we can trust pw_resp fields. If GC is
226 in progress, it can contain anything. */
227 if (mapped->head->gc_cycle != gc_cycle)
228 {
229 retval = -2;
230 goto out;
231 }
232
233 goto found_entry;
234 }
235 }
236
237 sock = __nscd_open_socket (key, keylen: key_len, type: INNETGR,
238 response: &innetgroup_resp, responselen: sizeof (innetgroup_resp));
239 if (sock == -1)
240 {
241 /* nscd not running or wrong version. */
242 __nss_not_use_nscd_netgroup = 1;
243 goto out;
244 }
245
246 found_entry:
247 if (innetgroup_resp.found == 1)
248 retval = innetgroup_resp.result;
249 else
250 {
251 if (__glibc_unlikely (innetgroup_resp.found == -1))
252 {
253 /* The daemon does not cache this database. */
254 __nss_not_use_nscd_netgroup = 1;
255 goto out_close;
256 }
257
258 /* Set errno to 0 to indicate no error, just no found record. */
259 __set_errno (0);
260 /* Even though we have not found anything, the result is zero. */
261 retval = 0;
262 }
263
264 out_close:
265 if (sock != -1)
266 __close_nocancel_nostatus (fd: sock);
267 out:
268 if (__nscd_drop_map_ref (map: mapped, gc_cycle: &gc_cycle) != 0)
269 {
270 /* When we come here this means there has been a GC cycle while we
271 were looking for the data. This means the data might have been
272 inconsistent. Retry if possible. */
273 if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
274 {
275 /* nscd is just running gc now. Disable using the mapping. */
276 if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
277 __nscd_unmap (mapped);
278 mapped = NO_MAPPING;
279 }
280
281 if (retval != -1)
282 goto retry;
283 }
284
285 if (! use_alloca)
286 free (ptr: key);
287
288 return retval;
289}
290

source code of glibc/nscd/nscd_netgroup.c