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