(CFLAGS-tst-align.c): Add -mpreferred-stack-boundary=4.
[glibc.git] / nis / nss_nis / nis-initgroups.c
blob33a9662b4c2359de1445678e5511ac1ed4efb36f
1 /* Copyright (C) 1998-2000, 2002, 2003, 2004 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
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.
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.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <alloca.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <grp.h>
24 #include <nss.h>
25 #include <pwd.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <rpcsvc/yp.h>
29 #include <rpcsvc/ypclnt.h>
30 #include <sys/param.h>
32 #include "nss-nis.h"
34 /* Get the declaration of the parser function. */
35 #define ENTNAME grent
36 #define STRUCTURE group
37 #define EXTERN_PARSER
38 #include <nss/nss_files/files-parse.c>
40 struct response_t
42 struct response_t *next;
43 char val[0];
46 struct intern_t
48 struct response_t *start;
49 struct response_t *next;
51 typedef struct intern_t intern_t;
53 static int
54 saveit (int instatus, char *inkey, int inkeylen, char *inval,
55 int invallen, char *indata)
57 intern_t *intern = (intern_t *) indata;
59 if (instatus != YP_TRUE)
60 return 1;
62 if (inkey && inkeylen > 0 && inval && invallen > 0)
64 struct response_t *newp = malloc (sizeof (struct response_t)
65 + invallen + 1);
66 if (newp == NULL)
67 return 1; /* We have no error code for out of memory */
69 if (intern->start == NULL)
70 intern->start = newp;
71 else
72 intern->next->next = newp;
73 intern->next = newp;
75 newp->next = NULL;
76 *((char *) mempcpy (newp->val, inval, invallen)) = '\0';
79 return 0;
82 static enum nss_status
83 internal_setgrent (char *domainname, intern_t *intern)
85 struct ypall_callback ypcb;
86 enum nss_status status;
88 intern->start = NULL;
90 ypcb.foreach = saveit;
91 ypcb.data = (char *) intern;
92 status = yperr2nss (yp_all (domainname, "group.byname", &ypcb));
93 intern->next = intern->start;
95 return status;
98 static enum nss_status
99 internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
100 int *errnop, intern_t *intern)
102 struct parser_data *data = (void *) buffer;
103 int parse_res;
104 char *p;
106 if (intern->start == NULL)
107 return NSS_STATUS_NOTFOUND;
109 /* Get the next entry until we found a correct one. */
112 if (intern->next == NULL)
113 return NSS_STATUS_NOTFOUND;
115 p = strncpy (buffer, intern->next->val, buflen);
116 while (isspace (*p))
117 ++p;
119 parse_res = _nss_files_parse_grent (p, grp, data, buflen, errnop);
120 if (parse_res == -1)
121 return NSS_STATUS_TRYAGAIN;
122 intern->next = intern->next->next;
124 while (!parse_res);
126 return NSS_STATUS_SUCCESS;
130 static int
131 get_uid (const char *user, uid_t *uidp)
133 size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
134 char *buf = (char *) alloca (buflen);
136 while (1)
138 struct passwd result;
139 struct passwd *resp;
141 int r = getpwnam_r (user, &result, buf, buflen, &resp);
142 if (r == 0 && resp != NULL)
144 *uidp = resp->pw_uid;
145 return 0;
148 if (r != ERANGE)
149 break;
151 extend_alloca (buf, buflen, 2 * buflen);
154 return 1;
158 static enum nss_status
159 initgroups_netid (uid_t uid, gid_t group, long int *start, long int *size,
160 gid_t **groupsp, long int limit, int *errnop,
161 const char *domainname)
163 /* Prepare the key. The form is "unix.UID@DOMAIN" with the UID and
164 DOMAIN field filled in appropriately. */
165 char key[sizeof ("unix.@") + sizeof (uid_t) * 3 + strlen (domainname)];
166 ssize_t keylen = snprintf (key, sizeof (key), "unix.%lu@%s",
167 (unsigned long int) uid, domainname);
169 enum nss_status retval;
170 char *result;
171 int reslen;
172 retval = yperr2nss (yp_match (domainname, "netid.byname", key, keylen,
173 &result, &reslen));
174 if (retval != NSS_STATUS_SUCCESS)
175 return retval;
177 /* Parse the result: following the colon is a comma separated list of
178 group IDs. */
179 char *cp = strchr (result, ':');
180 if (cp == NULL)
182 errout:
183 free (result);
184 return NSS_STATUS_NOTFOUND;
186 /* Skip the colon. */
187 ++cp;
189 gid_t *groups = *groupsp;
190 while (*cp != '\0')
192 char *endp;
193 unsigned long int gid = strtoul (cp, &endp, 0);
194 if (cp == endp)
195 goto errout;
196 if (*endp == ',')
197 ++endp;
198 else if (*endp != '\0')
199 goto errout;
200 cp = endp;
202 if (gid == group)
203 /* We do not need this group again. */
204 continue;
206 /* Insert this group. */
207 if (*start == *size)
209 /* Need a bigger buffer. */
210 gid_t *newgroups;
211 long int newsize;
213 if (limit > 0 && *size == limit)
214 /* We reached the maximum. */
215 break;
217 if (limit <= 0)
218 newsize = 2 * *size;
219 else
220 newsize = MIN (limit, 2 * *size);
222 newgroups = realloc (groups, newsize * sizeof (*groups));
223 if (newgroups == NULL)
224 goto errout;
225 *groupsp = groups = newgroups;
226 *size = newsize;
229 groups[*start] = gid;
230 *start += 1;
233 free (result);
235 return NSS_STATUS_SUCCESS;
239 enum nss_status
240 _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
241 long int *size, gid_t **groupsp, long int limit,
242 int *errnop)
244 /* We always need the domain name. */
245 char *domainname;
246 if (yp_get_default_domain (&domainname))
247 return NSS_STATUS_UNAVAIL;
249 /* Check whether we are supposed to use the netid.byname map. */
250 if (_nis_default_nss () & NSS_FLAG_NETID_AUTHORITATIVE)
252 /* We need the user ID. */
253 uid_t uid;
255 if (get_uid (user, &uid) == 0
256 && initgroups_netid (uid, group, start, size, groupsp, limit,
257 errnop, domainname) == NSS_STATUS_SUCCESS)
258 return NSS_STATUS_SUCCESS;
261 struct group grpbuf, *g;
262 size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
263 char *tmpbuf;
264 enum nss_status status;
265 intern_t intern = { NULL, NULL };
266 gid_t *groups = *groupsp;
268 status = internal_setgrent (domainname, &intern);
269 if (status != NSS_STATUS_SUCCESS)
270 return status;
272 tmpbuf = __alloca (buflen);
276 while ((status =
277 internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop,
278 &intern)) == NSS_STATUS_TRYAGAIN
279 && *errnop == ERANGE)
280 tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
282 if (status != NSS_STATUS_SUCCESS)
283 goto done;
286 g = &grpbuf;
287 if (g->gr_gid != group)
289 char **m;
291 for (m = g->gr_mem; *m != NULL; ++m)
292 if (strcmp (*m, user) == 0)
294 /* Matches user. Insert this group. */
295 if (*start == *size)
297 /* Need a bigger buffer. */
298 gid_t *newgroups;
299 long int newsize;
301 if (limit > 0 && *size == limit)
302 /* We reached the maximum. */
303 goto done;
305 if (limit <= 0)
306 newsize = 2 * *size;
307 else
308 newsize = MIN (limit, 2 * *size);
310 newgroups = realloc (groups, newsize * sizeof (*groups));
311 if (newgroups == NULL)
312 goto done;
313 *groupsp = groups = newgroups;
314 *size = newsize;
317 groups[*start] = g->gr_gid;
318 *start += 1;
320 break;
324 while (status == NSS_STATUS_SUCCESS);
326 done:
327 while (intern.start != NULL)
329 intern.next = intern.start;
330 intern.start = intern.start->next;
331 free (intern.next);
334 return NSS_STATUS_SUCCESS;