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