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