1 /* Copyright (C) 1997-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 <ctype.h>
19 #include <errno.h>
20 #include <grp.h>
21 #include <hesiod.h>
22 #include <nss.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/param.h>
27 
NSS_DECLARE_MODULE_FUNCTIONS(hesiod)28 NSS_DECLARE_MODULE_FUNCTIONS (hesiod)
29 
30 /* Get the declaration of the parser function.  */
31 #define ENTNAME grent
32 #define STRUCTURE group
33 #define EXTERN_PARSER
34 #include <nss/nss_files/files-parse.c>
35 
36 enum nss_status
37 _nss_hesiod_setgrent (int stayopen)
38 {
39   return NSS_STATUS_SUCCESS;
40 }
41 
42 enum nss_status
_nss_hesiod_endgrent(void)43 _nss_hesiod_endgrent (void)
44 {
45   return NSS_STATUS_SUCCESS;
46 }
47 
48 static enum nss_status
lookup(const char * name,const char * type,struct group * grp,char * buffer,size_t buflen,int * errnop)49 lookup (const char *name, const char *type, struct group *grp,
50 	char *buffer, size_t buflen, int *errnop)
51 {
52   struct parser_data *data = (void *) buffer;
53   size_t linebuflen;
54   void *context;
55   char **list;
56   int parse_res;
57   size_t len;
58   int olderr = errno;
59 
60   if (hesiod_init (&context) < 0)
61     return NSS_STATUS_UNAVAIL;
62 
63   list = hesiod_resolve (context, name, type);
64   if (list == NULL)
65     {
66       int err = errno;
67       hesiod_end (context);
68       __set_errno (olderr);
69       return err == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL;
70     }
71 
72   linebuflen = buffer + buflen - data->linebuffer;
73   len = strlen (*list) + 1;
74   if (linebuflen < len)
75     {
76       hesiod_free_list (context, list);
77       hesiod_end (context);
78       *errnop = ERANGE;
79       return NSS_STATUS_TRYAGAIN;
80     }
81 
82   memcpy (data->linebuffer, *list, len);
83   hesiod_free_list (context, list);
84   hesiod_end (context);
85 
86   parse_res = _nss_files_parse_grent (buffer, grp, data, buflen, errnop);
87   if (parse_res < 1)
88     {
89       __set_errno (olderr);
90       return parse_res == -1 ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
91     }
92 
93   return NSS_STATUS_SUCCESS;
94 }
95 
96 enum nss_status
_nss_hesiod_getgrnam_r(const char * name,struct group * grp,char * buffer,size_t buflen,int * errnop)97 _nss_hesiod_getgrnam_r (const char *name, struct group *grp,
98 			char *buffer, size_t buflen, int *errnop)
99 {
100   return lookup (name, "group", grp, buffer, buflen, errnop);
101 }
102 
103 enum nss_status
_nss_hesiod_getgrgid_r(gid_t gid,struct group * grp,char * buffer,size_t buflen,int * errnop)104 _nss_hesiod_getgrgid_r (gid_t gid, struct group *grp,
105 			char *buffer, size_t buflen, int *errnop)
106 {
107   char gidstr[21];	/* We will probably never have a gid_t with more
108 			   than 64 bits.  */
109 
110   snprintf (gidstr, sizeof gidstr, "%d", gid);
111 
112   return lookup (gidstr, "gid", grp, buffer, buflen, errnop);
113 }
114 
115 static int
internal_gid_in_list(const gid_t * list,const gid_t g,long int len)116 internal_gid_in_list (const gid_t *list, const gid_t g, long int len)
117 {
118   while (len > 0)
119     {
120       if (*list == g)
121 	return 1;
122       --len;
123       ++list;
124     }
125   return 0;
126 }
127 
128 static enum nss_status
internal_gid_from_group(void * context,const char * groupname,gid_t * group)129 internal_gid_from_group (void *context, const char *groupname, gid_t *group)
130 {
131   char **grp_res;
132   enum nss_status status = NSS_STATUS_NOTFOUND;
133 
134   grp_res = hesiod_resolve (context, groupname, "group");
135   if (grp_res != NULL && *grp_res != NULL)
136     {
137       char *p = *grp_res;
138 
139       /* Skip to third field.  */
140       while (*p != '\0' && *p != ':')
141 	++p;
142       if (*p != '\0')
143 	++p;
144       while (*p != '\0' && *p != ':')
145 	++p;
146       if (*p != '\0')
147 	{
148 	  char *endp;
149 	  char *q = ++p;
150 	  long int val;
151 
152 	  while (*q != '\0' && *q != ':')
153 	    ++q;
154 
155 	  val = strtol (p, &endp, 10);
156 	  if (sizeof (gid_t) == sizeof (long int) || (gid_t) val == val)
157 	    {
158 	      *group = val;
159 	      if (endp == q && endp != p)
160 		status = NSS_STATUS_SUCCESS;
161 	    }
162         }
163       hesiod_free_list (context, grp_res);
164     }
165   return status;
166 }
167 
168 enum nss_status
_nss_hesiod_initgroups_dyn(const char * user,gid_t group,long int * start,long int * size,gid_t ** groupsp,long int limit,int * errnop)169 _nss_hesiod_initgroups_dyn (const char *user, gid_t group, long int *start,
170 			    long int *size, gid_t **groupsp, long int limit,
171 			    int *errnop)
172 {
173   enum nss_status status = NSS_STATUS_SUCCESS;
174   char **list = NULL;
175   char *p;
176   void *context;
177   gid_t *groups = *groupsp;
178   int save_errno;
179 
180   if (hesiod_init (&context) < 0)
181     return NSS_STATUS_UNAVAIL;
182 
183   list = hesiod_resolve (context, user, "grplist");
184 
185   if (list == NULL)
186     {
187       hesiod_end (context);
188       return errno == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL;
189     }
190 
191   save_errno = errno;
192 
193   p = *list;
194   while (*p != '\0')
195     {
196       char *endp;
197       char *q;
198       long int val;
199 
200       status = NSS_STATUS_NOTFOUND;
201 
202       q = p;
203       while (*q != '\0' && *q != ':' && *q != ',')
204 	++q;
205 
206       if (*q != '\0')
207 	*q++ = '\0';
208 
209       __set_errno (0);
210       val = strtol (p, &endp, 10);
211       /* Test whether the number is representable in a variable of
212          type `gid_t'.  If not ignore the number.  */
213       if ((sizeof (gid_t) == sizeof (long int) || (gid_t) val == val)
214 	  && errno == 0)
215 	{
216 	  if (*endp == '\0' && endp != p)
217 	    {
218 	      group = val;
219 	      status = NSS_STATUS_SUCCESS;
220 	    }
221 	  else
222 	    status = internal_gid_from_group (context, p, &group);
223 
224 	  if (status == NSS_STATUS_SUCCESS
225 	      && !internal_gid_in_list (groups, group, *start))
226 	    {
227 	      if (__glibc_unlikely (*start == *size))
228 		{
229 		  /* Need a bigger buffer.  */
230 		  gid_t *newgroups;
231 		  long int newsize;
232 
233 		  if (limit > 0 && *size == limit)
234 		    /* We reached the maximum.  */
235 		    goto done;
236 
237 		  if (limit <= 0)
238 		    newsize = 2 * *size;
239 		  else
240 		    newsize = MIN (limit, 2 * *size);
241 
242 		  newgroups = realloc (groups, newsize * sizeof (*groups));
243 		  if (newgroups == NULL)
244 		    goto done;
245 		  *groupsp = groups = newgroups;
246 		  *size = newsize;
247 		}
248 
249 	      groups[(*start)++] = group;
250 	    }
251 	}
252 
253       p = q;
254     }
255 
256   __set_errno (save_errno);
257 
258  done:
259   hesiod_free_list (context, list);
260   hesiod_end (context);
261 
262   return NSS_STATUS_SUCCESS;
263 }
264