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