1 /* Copyright (C) 2011-2022 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.  */
libc_freeres_fn(pw_map_free)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
__nscd_setnetgrent(const char * group,struct __netgrent * datap)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_decrement_val (&mapped->counter) == 0)
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
__nscd_innetgr(const char * netgroup,const char * host,const char * user,const char * domain)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_decrement_val (&mapped->counter) == 0)
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