Implement getting the information from the netid.byname map if the
[kopensolaris-gnu/glibc.git] / nis / nss_nis / nis-initgroups.c
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.
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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
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 <stdio.h>
27 #include <stdio_ext.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <rpcsvc/yp.h>
31 #include <rpcsvc/ypclnt.h>
32 #include <sys/param.h>
33
34 #include "nss-nis.h"
35
36 /* Get the declaration of the parser function.  */
37 #define ENTNAME grent
38 #define STRUCTURE group
39 #define EXTERN_PARSER
40 #include <nss/nss_files/files-parse.c>
41
42 struct response_t
43 {
44   struct response_t *next;
45   char val[0];
46 };
47
48 struct intern_t
49 {
50   struct response_t *start;
51   struct response_t *next;
52 };
53 typedef struct intern_t intern_t;
54
55 static int
56 saveit (int instatus, char *inkey, int inkeylen, char *inval,
57         int invallen, char *indata)
58 {
59   intern_t *intern = (intern_t *) indata;
60
61   if (instatus != YP_TRUE)
62     return instatus;
63
64   if (inkey && inkeylen > 0 && inval && invallen > 0)
65     {
66       struct response_t *newp = malloc (sizeof (struct response_t)
67                                         + invallen + 1);
68       if (newp == NULL)
69         return YP_FALSE; /* We have no error code for out of memory */
70
71       if (intern->start == NULL)
72         intern->start = newp;
73       else
74         intern->next->next = newp;
75       intern->next = newp;
76
77       newp->next = NULL;
78       *((char *) mempcpy (newp->val, inval, invallen)) = '\0';
79     }
80
81   return 0;
82 }
83
84 static enum nss_status
85 internal_setgrent (char *domainname, intern_t *intern)
86 {
87   struct ypall_callback ypcb;
88   enum nss_status status;
89
90   intern->start = NULL;
91
92   ypcb.foreach = saveit;
93   ypcb.data = (char *) intern;
94   status = yperr2nss (yp_all (domainname, "group.byname", &ypcb));
95   intern->next = intern->start;
96
97   return status;
98 }
99
100 static enum nss_status
101 internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
102                      int *errnop, intern_t *intern)
103 {
104   struct parser_data *data = (void *) buffer;
105   int parse_res;
106   char *p;
107
108   if (intern->start == NULL)
109     return NSS_STATUS_NOTFOUND;
110
111   /* Get the next entry until we found a correct one. */
112   do
113     {
114       if (intern->next == NULL)
115         return NSS_STATUS_NOTFOUND;
116
117       p = strncpy (buffer, intern->next->val, buflen);
118       while (isspace (*p))
119         ++p;
120
121       parse_res = _nss_files_parse_grent (p, grp, data, buflen, errnop);
122       if (parse_res == -1)
123         return NSS_STATUS_TRYAGAIN;
124       intern->next = intern->next->next;
125     }
126   while (!parse_res);
127
128   return NSS_STATUS_SUCCESS;
129 }
130
131
132 static int init;
133 static int use_netid;
134
135
136 static const char default_nss[] = "/etc/default/nss";
137
138 static void
139 check_default_nss (void)
140 {
141   FILE *fp = fopen (default_nss, "rc");
142   if (fp != NULL)
143     {
144       char *line = NULL;
145       size_t linelen = 0;
146
147       __fsetlocking (fp, FSETLOCKING_BYCALLER);
148
149       while (!feof_unlocked (fp))
150         {
151           ssize_t n = getline (&line, &linelen, fp);
152           if (n <= 0)
153             break;
154
155           /* There currently is only one variable we expect, so
156              simplify the parsing.  Recognize only
157
158                NETID_AUTHORITATIVE = TRUE
159
160              with arbitrary white spaces.  */
161           char *cp = line;
162           while (isspace (*cp))
163             ++cp;
164
165           static const char netid_authoritative[] = "NETID_AUTHORITATIVE";
166           if (strncmp (cp, netid_authoritative,
167                        sizeof (netid_authoritative) - 1) != 0)
168             continue;
169
170           cp += sizeof (netid_authoritative) - 1;
171           while (isspace (*cp))
172             ++cp;
173           if (*cp++ != '=')
174             continue;
175           while (isspace (*cp))
176             ++cp;
177
178           if (strncmp (cp, "TRUE", 4) != 0)
179             continue;
180           cp +=4;
181
182           while (isspace (*cp))
183             ++cp;
184
185           if (*cp == '\0')
186             use_netid = 1;
187
188           /* For now, just drop out of the loop.  */
189           break;
190         }
191
192       free (line);
193
194       fclose (fp);
195     }
196   init = 1;
197 }
198
199
200 static int
201 get_uid (const char *user, uid_t *uidp)
202 {
203   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
204   char *buf = (char *) alloca (buflen);
205
206   while (1)
207     {
208       struct passwd result;
209       struct passwd *resp;
210
211       int r = getpwnam_r (user, &result, buf, buflen, &resp);
212       if (r == 0 && resp != NULL)
213         {
214           *uidp = resp->pw_uid;
215           return 0;
216         }
217
218       if (r != ERANGE)
219         break;
220
221       extend_alloca (buf, buflen, 2 * buflen);
222     }
223
224   return 1;
225 }
226
227
228 static enum nss_status
229 initgroups_netid (uid_t uid, gid_t group, long int *start, long int *size,
230                   gid_t **groupsp, long int limit, int *errnop,
231                   const char *domainname)
232 {
233   /* Prepare the key.  The form is "unix.UID@DOMAIN" with the UID and
234      DOMAIN field filled in appropriately.  */
235   char key[sizeof ("unix.@") + sizeof (uid_t) * 3 + strlen (domainname)];
236   ssize_t keylen = snprintf (key, sizeof (key), "unix.%lu@%s",
237                              (unsigned long int) uid, domainname);
238
239   enum nss_status retval;
240   char *result;
241   int reslen;
242   retval = yperr2nss (yp_match (domainname, "netid.byname", key, keylen,
243                                 &result, &reslen));
244   if (retval != NSS_STATUS_SUCCESS)
245     return retval;
246
247   /* Parse the result: following the colon is a comma separated list of
248      group IDs.  */
249   char *cp = strchr (result, ':');
250   if (cp == NULL)
251     {
252     errout:
253       free (result);
254       return NSS_STATUS_NOTFOUND;
255     }
256   /* Skip the colon.  */
257   ++cp;
258
259   gid_t *groups = *groupsp;
260   while (*cp != '\0')
261     {
262       char *endp;
263       unsigned long int gid = strtoul (cp, &endp, 0);
264       if (cp == endp)
265         goto errout;
266       if (*endp == ',')
267         ++endp;
268       else if (*endp != '\0')
269         goto errout;
270       cp = endp;
271
272       if (gid == group)
273         /* We do not need this group again.  */
274         continue;
275
276       /* Insert this group.  */
277       if (*start == *size)
278         {
279           /* Need a bigger buffer.  */
280           gid_t *newgroups;
281           long int newsize;
282
283           if (limit > 0 && *size == limit)
284             /* We reached the maximum.  */
285             break;
286
287           if (limit <= 0)
288             newsize = 2 * *size;
289           else
290             newsize = MIN (limit, 2 * *size);
291
292           newgroups = realloc (groups, newsize * sizeof (*groups));
293           if (newgroups == NULL)
294             goto errout;
295           *groupsp = groups = newgroups;
296           *size = newsize;
297         }
298
299       groups[*start] = gid;
300       *start += 1;
301     }
302
303   free (result);
304
305   return NSS_STATUS_SUCCESS;
306 }
307
308
309 enum nss_status
310 _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
311                          long int *size, gid_t **groupsp, long int limit,
312                          int *errnop)
313 {
314   /* We always need the domain name.  */
315   char *domainname;
316   if (yp_get_default_domain (&domainname))
317     return NSS_STATUS_UNAVAIL;
318
319   /* Check whether we are supposed to use the netid.byname map.  */
320   if (!init)
321     check_default_nss ();
322
323   if (use_netid)
324     {
325       /* We need the user ID.  */
326       uid_t uid;
327
328       if (get_uid (user, &uid) == 0
329           && initgroups_netid (uid, group, start, size, groupsp, limit,
330                                errnop, domainname) == NSS_STATUS_SUCCESS)
331         return NSS_STATUS_SUCCESS;
332     }
333
334   struct group grpbuf, *g;
335   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
336   char *tmpbuf;
337   enum nss_status status;
338   intern_t intern = { NULL, NULL };
339   gid_t *groups = *groupsp;
340
341   status = internal_setgrent (domainname, &intern);
342   if (status != NSS_STATUS_SUCCESS)
343     return status;
344
345   tmpbuf = __alloca (buflen);
346
347   do
348     {
349       while ((status =
350               internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop,
351                                    &intern)) == NSS_STATUS_TRYAGAIN
352              && *errnop == ERANGE)
353         tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
354
355       if (status != NSS_STATUS_SUCCESS)
356         goto done;
357
358
359       g = &grpbuf;
360       if (g->gr_gid != group)
361         {
362           char **m;
363
364           for (m = g->gr_mem; *m != NULL; ++m)
365             if (strcmp (*m, user) == 0)
366               {
367                 /* Matches user.  Insert this group.  */
368                 if (*start == *size)
369                   {
370                     /* Need a bigger buffer.  */
371                     gid_t *newgroups;
372                     long int newsize;
373
374                     if (limit > 0 && *size == limit)
375                       /* We reached the maximum.  */
376                       goto done;
377
378                     if (limit <= 0)
379                       newsize = 2 * *size;
380                     else
381                       newsize = MIN (limit, 2 * *size);
382
383                     newgroups = realloc (groups, newsize * sizeof (*groups));
384                     if (newgroups == NULL)
385                       goto done;
386                     *groupsp = groups = newgroups;
387                     *size = newsize;
388                   }
389
390                 groups[*start] = g->gr_gid;
391                 *start += 1;
392
393                 break;
394               }
395         }
396     }
397   while (status == NSS_STATUS_SUCCESS);
398
399 done:
400   while (intern.start != NULL)
401     {
402       intern.next = intern.start;
403       intern.start = intern.start->next;
404       free (intern.next);
405     }
406
407   return NSS_STATUS_SUCCESS;
408 }