(check_default_nss): Recognize comment lines.
[kopensolaris-gnu/glibc.git] / nis / nss_nis / nis-initgroups.c
index cf666a4..e3ba0cd 100644 (file)
@@ -1,26 +1,30 @@
-/* Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1998-2000, 2002, 2003, 2004 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
 
    The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
    The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
 
-#include <nss.h>
-#include <grp.h>
+#include <alloca.h>
 #include <ctype.h>
 #include <errno.h>
+#include <grp.h>
+#include <nss.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdio_ext.h>
 #include <string.h>
 #include <unistd.h>
 #include <rpcsvc/yp.h>
@@ -37,8 +41,8 @@
 
 struct response_t
 {
-  char *val;
   struct response_t *next;
+  char val[0];
 };
 
 struct intern_t
@@ -59,41 +63,30 @@ saveit (int instatus, char *inkey, int inkeylen, char *inval,
 
   if (inkey && inkeylen > 0 && inval && invallen > 0)
     {
+      struct response_t *newp = malloc (sizeof (struct response_t)
+                                       + invallen + 1);
+      if (newp == NULL)
+       return YP_FALSE; /* We have no error code for out of memory */
+
       if (intern->start == NULL)
-        {
-          intern->start = malloc (sizeof (struct response_t));
-         if (intern->start == NULL)
-           return YP_FALSE;
-          intern->next = intern->start;
-        }
+       intern->start = newp;
       else
-        {
-          intern->next->next = malloc (sizeof (struct response_t));
-         if (intern->next->next == NULL)
-           return YP_FALSE;
-          intern->next = intern->next->next;
-        }
-      intern->next->next = NULL;
-      intern->next->val = malloc (invallen + 1);
-      if (intern->next->val == NULL)
-       return YP_FALSE;
-      strncpy (intern->next->val, inval, invallen);
-      intern->next->val[invallen] = '\0';
+       intern->next->next = newp;
+      intern->next = newp;
+
+      newp->next = NULL;
+      *((char *) mempcpy (newp->val, inval, invallen)) = '\0';
     }
 
   return 0;
 }
 
 static enum nss_status
-internal_setgrent (intern_t *intern)
+internal_setgrent (char *domainname, intern_t *intern)
 {
-  char *domainname;
   struct ypall_callback ypcb;
   enum nss_status status;
 
-  if (yp_get_default_domain (&domainname))
-    return NSS_STATUS_UNAVAIL;
-
   intern->start = NULL;
 
   ypcb.foreach = saveit;
@@ -113,16 +106,14 @@ internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
   char *p;
 
   if (intern->start == NULL)
-    internal_setgrent (intern);
+    return NSS_STATUS_NOTFOUND;
 
   /* Get the next entry until we found a correct one. */
   do
     {
       if (intern->next == NULL)
-       {
-         *errnop = ENOENT;
-         return NSS_STATUS_NOTFOUND;
-       }
+       return NSS_STATUS_NOTFOUND;
+
       p = strncpy (buffer, intern->next->val, buflen);
       while (isspace (*p))
         ++p;
@@ -137,11 +128,213 @@ internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
   return NSS_STATUS_SUCCESS;
 }
 
+
+static int init;
+static int use_netid;
+
+
+static const char default_nss[] = "/etc/default/nss";
+
+static void
+check_default_nss (void)
+{
+  FILE *fp = fopen (default_nss, "rc");
+  if (fp != NULL)
+    {
+      char *line = NULL;
+      size_t linelen = 0;
+
+      __fsetlocking (fp, FSETLOCKING_BYCALLER);
+
+      while (!feof_unlocked (fp))
+       {
+         ssize_t n = getline (&line, &linelen, fp);
+         if (n <= 0)
+           break;
+
+         /* There currently is only one variable we expect, so
+            simplify the parsing.  Recognize only
+
+              NETID_AUTHORITATIVE = TRUE
+
+            with arbitrary white spaces.  */
+         char *cp = line;
+         while (isspace (*cp))
+           ++cp;
+
+         /* Recognize comment lines.  */
+         if (*cp == '#')
+           continue;
+
+         static const char netid_authoritative[] = "NETID_AUTHORITATIVE";
+         if (strncmp (cp, netid_authoritative,
+                      sizeof (netid_authoritative) - 1) != 0)
+           continue;
+
+         cp += sizeof (netid_authoritative) - 1;
+         while (isspace (*cp))
+           ++cp;
+         if (*cp++ != '=')
+           continue;
+         while (isspace (*cp))
+           ++cp;
+
+         if (strncmp (cp, "TRUE", 4) != 0)
+           continue;
+         cp +=4;
+
+         while (isspace (*cp))
+           ++cp;
+
+         if (*cp == '\0')
+           use_netid = 1;
+
+         /* For now, just drop out of the loop.  */
+         break;
+       }
+
+      free (line);
+
+      fclose (fp);
+    }
+  init = 1;
+}
+
+
+static int
+get_uid (const char *user, uid_t *uidp)
+{
+  size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
+  char *buf = (char *) alloca (buflen);
+
+  while (1)
+    {
+      struct passwd result;
+      struct passwd *resp;
+
+      int r = getpwnam_r (user, &result, buf, buflen, &resp);
+      if (r == 0 && resp != NULL)
+       {
+         *uidp = resp->pw_uid;
+         return 0;
+       }
+
+      if (r != ERANGE)
+       break;
+
+      extend_alloca (buf, buflen, 2 * buflen);
+    }
+
+  return 1;
+}
+
+
+static enum nss_status
+initgroups_netid (uid_t uid, gid_t group, long int *start, long int *size,
+                 gid_t **groupsp, long int limit, int *errnop,
+                 const char *domainname)
+{
+  /* Prepare the key.  The form is "unix.UID@DOMAIN" with the UID and
+     DOMAIN field filled in appropriately.  */
+  char key[sizeof ("unix.@") + sizeof (uid_t) * 3 + strlen (domainname)];
+  ssize_t keylen = snprintf (key, sizeof (key), "unix.%lu@%s",
+                            (unsigned long int) uid, domainname);
+
+  enum nss_status retval;
+  char *result;
+  int reslen;
+  retval = yperr2nss (yp_match (domainname, "netid.byname", key, keylen,
+                               &result, &reslen));
+  if (retval != NSS_STATUS_SUCCESS)
+    return retval;
+
+  /* Parse the result: following the colon is a comma separated list of
+     group IDs.  */
+  char *cp = strchr (result, ':');
+  if (cp == NULL)
+    {
+    errout:
+      free (result);
+      return NSS_STATUS_NOTFOUND;
+    }
+  /* Skip the colon.  */
+  ++cp;
+
+  gid_t *groups = *groupsp;
+  while (*cp != '\0')
+    {
+      char *endp;
+      unsigned long int gid = strtoul (cp, &endp, 0);
+      if (cp == endp)
+       goto errout;
+      if (*endp == ',')
+       ++endp;
+      else if (*endp != '\0')
+       goto errout;
+      cp = endp;
+
+      if (gid == group)
+       /* We do not need this group again.  */
+       continue;
+
+      /* Insert this group.  */
+      if (*start == *size)
+       {
+         /* Need a bigger buffer.  */
+         gid_t *newgroups;
+         long int newsize;
+
+         if (limit > 0 && *size == limit)
+           /* We reached the maximum.  */
+           break;
+
+         if (limit <= 0)
+           newsize = 2 * *size;
+         else
+           newsize = MIN (limit, 2 * *size);
+
+         newgroups = realloc (groups, newsize * sizeof (*groups));
+         if (newgroups == NULL)
+           goto errout;
+         *groupsp = groups = newgroups;
+         *size = newsize;
+       }
+
+      groups[*start] = gid;
+      *start += 1;
+    }
+
+  free (result);
+
+  return NSS_STATUS_SUCCESS;
+}
+
+
 enum nss_status
 _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
                         long int *size, gid_t **groupsp, long int limit,
                         int *errnop)
 {
+  /* We always need the domain name.  */
+  char *domainname;
+  if (yp_get_default_domain (&domainname))
+    return NSS_STATUS_UNAVAIL;
+
+  /* Check whether we are supposed to use the netid.byname map.  */
+  if (!init)
+    check_default_nss ();
+
+  if (use_netid)
+    {
+      /* We need the user ID.  */
+      uid_t uid;
+
+      if (get_uid (user, &uid) == 0
+         && initgroups_netid (uid, group, start, size, groupsp, limit,
+                              errnop, domainname) == NSS_STATUS_SUCCESS)
+       return NSS_STATUS_SUCCESS;
+    }
+
   struct group grpbuf, *g;
   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
   char *tmpbuf;
@@ -149,7 +342,7 @@ _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
   intern_t intern = { NULL, NULL };
   gid_t *groups = *groupsp;
 
-  status = internal_setgrent (&intern);
+  status = internal_setgrent (domainname, &intern);
   if (status != NSS_STATUS_SUCCESS)
     return status;
 
@@ -161,10 +354,7 @@ _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
              internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop,
                                   &intern)) == NSS_STATUS_TRYAGAIN
              && *errnop == ERANGE)
-        {
-          buflen *= 2;
-          tmpbuf = __alloca (buflen);
-        }
+       tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
 
       if (status != NSS_STATUS_SUCCESS)
        goto done;
@@ -213,8 +403,6 @@ _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
 done:
   while (intern.start != NULL)
     {
-      if (intern.start->val != NULL)
-        free (intern.start->val);
       intern.next = intern.start;
       intern.start = intern.start->next;
       free (intern.next);