1 /* Copyright (C) 2004-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 <assert.h>
19 #include <errno.h>
20 #include <grp.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <not-cancel.h>
25 
26 #include "nscd-client.h"
27 #include "nscd_proto.h"
28 
29 
30 /* We use the same mapping as in nscd_getgr.   */
31 libc_locked_map_ptr (extern, __gr_map_handle) attribute_hidden;
32 
33 
34 int
__nscd_getgrouplist(const char * user,gid_t group,long int * size,gid_t ** groupsp,long int limit)35 __nscd_getgrouplist (const char *user, gid_t group, long int *size,
36 		     gid_t **groupsp, long int limit)
37 {
38   size_t userlen = strlen (user) + 1;
39   int gc_cycle;
40   int nretries = 0;
41 
42   /* If the mapping is available, try to search there instead of
43      communicating with the nscd.  */
44   struct mapped_database *mapped;
45   mapped = __nscd_get_map_ref (GETFDGR, "group", &__gr_map_handle, &gc_cycle);
46 
47  retry:;
48   char *respdata = NULL;
49   int retval = -1;
50   int sock = -1;
51   initgr_response_header initgr_resp;
52 
53   if (mapped != NO_MAPPING)
54     {
55       struct datahead *found = __nscd_cache_search (INITGROUPS, user,
56 						    userlen, mapped,
57 						    sizeof initgr_resp);
58       if (found != NULL)
59 	{
60 	  respdata = (char *) (&found->data[0].initgrdata + 1);
61 	  initgr_resp = found->data[0].initgrdata;
62 	  char *recend = (char *) found->data + found->recsize;
63 
64 	  /* Now check if we can trust initgr_resp fields.  If GC is
65 	     in progress, it can contain anything.  */
66 	  if (mapped->head->gc_cycle != gc_cycle)
67 	    {
68 	      retval = -2;
69 	      goto out;
70 	    }
71 
72 	  if (respdata + initgr_resp.ngrps * sizeof (int32_t) > recend)
73 	    goto out;
74 	}
75     }
76 
77   /* If we do not have the cache mapped, try to get the data over the
78      socket.  */
79   if (respdata == NULL)
80     {
81       sock = __nscd_open_socket (user, userlen, INITGROUPS, &initgr_resp,
82 				 sizeof (initgr_resp));
83       if (sock == -1)
84 	{
85 	  /* nscd not running or wrong version.  */
86 	  __nss_not_use_nscd_group = 1;
87 	  goto out;
88 	}
89     }
90 
91   if (initgr_resp.found == 1)
92     {
93       /* The following code assumes that gid_t and int32_t are the
94 	 same size.  This is the case for al existing implementation.
95 	 If this should change some code needs to be added which
96 	 doesn't use memcpy but instead copies each array element one
97 	 by one.  */
98       assert (sizeof (int32_t) == sizeof (gid_t));
99       assert (initgr_resp.ngrps >= 0);
100 
101       /* Make sure we have enough room.  We always count GROUP in even
102 	 though we might not end up adding it.  */
103       if (*size < initgr_resp.ngrps + 1)
104 	{
105 	  gid_t *newp = realloc (*groupsp,
106 				 (initgr_resp.ngrps + 1) * sizeof (gid_t));
107 	  if (newp == NULL)
108 	    /* We cannot increase the buffer size.  */
109 	    goto out_close;
110 
111 	  *groupsp = newp;
112 	  *size = initgr_resp.ngrps + 1;
113 	}
114 
115       if (respdata == NULL)
116 	{
117 	  /* Read the data from the socket.  */
118 	  if ((size_t) __readall (sock, *groupsp, initgr_resp.ngrps
119 						  * sizeof (gid_t))
120 	      == initgr_resp.ngrps * sizeof (gid_t))
121 	    retval = initgr_resp.ngrps;
122 	}
123       else
124 	{
125 	  /* Just copy the data.  */
126 	  retval = initgr_resp.ngrps;
127 	  memcpy (*groupsp, respdata, retval * sizeof (gid_t));
128 	}
129     }
130   else
131     {
132       if (__glibc_unlikely (initgr_resp.found == -1))
133 	{
134 	  /* The daemon does not cache this database.  */
135 	  __nss_not_use_nscd_group = 1;
136 	  goto out_close;
137 	}
138 
139       /* No group found yet.   */
140       retval = 0;
141 
142       assert (*size >= 1);
143     }
144 
145   /* Check whether GROUP is part of the mix.  If not, add it.  */
146   if (retval >= 0)
147     {
148       int cnt;
149       for (cnt = 0; cnt < retval; ++cnt)
150 	if ((*groupsp)[cnt] == group)
151 	  break;
152 
153       if (cnt == retval)
154 	(*groupsp)[retval++] = group;
155     }
156 
157  out_close:
158   if (sock != -1)
159     __close_nocancel_nostatus (sock);
160  out:
161   if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
162     {
163       /* When we come here this means there has been a GC cycle while we
164 	 were looking for the data.  This means the data might have been
165 	 inconsistent.  Retry if possible.  */
166       if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
167 	{
168 	  /* nscd is just running gc now.  Disable using the mapping.  */
169 	  if (atomic_decrement_val (&mapped->counter) == 0)
170 	    __nscd_unmap (mapped);
171 	  mapped = NO_MAPPING;
172 	}
173 
174       if (retval != -1)
175 	goto retry;
176     }
177 
178   return retval;
179 }
180