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 | |
27 | int __nss_not_use_nscd_netgroup; |
28 | |
29 | |
30 | libc_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. */ |
34 | void |
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 | |
46 | int |
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 | |
165 | int |
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 | |