1/* Copyright (C) 2011-2018 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19#include <alloca.h>
20#include <errno.h>
21#include <stdlib.h>
22#include <string.h>
23#include <not-cancel.h>
24
25#include "nscd-client.h"
26#include "nscd_proto.h"
27
28int __nss_not_use_nscd_netgroup;
29
30
31libc_locked_map_ptr (static, map_handle);
32/* Note that we only free the structure if necessary. The memory
33 mapping is not removed since it is not visible to the malloc
34 handling. */
35libc_freeres_fn (pw_map_free)
36{
37 if (map_handle.mapped != NO_MAPPING)
38 {
39 void *p = map_handle.mapped;
40 map_handle.mapped = NO_MAPPING;
41 free (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 (GETFDNETGR, "netgroup", &map_handle, &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 (GETNETGRENT, group,
66 group_len, mapped,
67 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 (group, group_len, GETNETGRENT,
86 &netgroup_resp, 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 (datalen);
105 if (respdata == NULL)
106 goto out_close;
107
108 if ((size_t) __readall (sock, respdata, datalen) != datalen)
109 {
110 free (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 = (service_user *) -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 (sock);
143 out:
144 if (__nscd_drop_map_ref (mapped, &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_decrement_val (&mapped->counter) == 0)
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 (key_len);
173 if (use_alloca)
174 key = alloca (key_len);
175 else
176 {
177 key = malloc (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 (GETFDNETGR, "netgroup", &map_handle, &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 (INNETGR, key,
220 key_len, mapped,
221 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, key_len, INNETGR,
238 &innetgroup_resp, 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 (sock);
267 out:
268 if (__nscd_drop_map_ref (mapped, &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_decrement_val (&mapped->counter) == 0)
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 (key);
287
288 return retval;
289}
290