commit 71170aa0a956c59d8bad0cf6f5ed31d78c90e332
Author: Ulrich Drepper <drepper@redhat.com>
Date:   Wed Apr 7 07:37:39 2010 -0700

    Implement new mode for NIS passwd.adjunct.byname table.
    
    The passwd.adjunct.byname table will not be used to fill in password fields
    in the passwd.byname replies.  Instead it is used to synthesize the
    shadow.byname table, should it be missing.  This is a useful mode in some
    installations involving Solaris.

2010-04-07  Ulrich Drepper  <drepper@redhat.com>

	[BZ #11134]
	* nis/libnsl.h (NSS_FLAG_ADJUNCT_AS_SHADOW): Define.
	* nis/nss: Document new ADJUNCT_AS_SHADOW variable.
	* nis/nss-default.c: Handle ADJUNCT_AS_SHADOW variable.
	* nis/nss_nis/nis-pwd.c (internal_nis_endpwent): Minor cleanups.
	(internal_nis_getpwent_r): Don't fill in password from adjunct table
	if NSS_FLAG_ADJUNCT_AS_SHADOW is set.
	(_nss_nis_getpwnam_r): Likewise.
	(_nss_nis_getpwuid_r): Likewise.
	* nis/nss_nis/nis-spwd.c (ent_adjunct_used): Nee global variable.
	(_nss_nis_setspent): Also reset ent_adjunct_used.
	(internal_nis_getspent_r): If new_start is set and shadow.byname table
	does not exist and NSS_FLAG_ADJUNCT_AS_SHADOW is set, try to get
	passwd.adjunct.byname table.  If new_start is not set get next entry
	from the initially used table.  Synthesize shadow.byname table if
	necessary by adding two empty fields.
	(_nss_nis_getspnam_r): If shadow.byname table does not exist and
	NSS_FLAG_ADJUNCT_AS_SHADOW is set, try to get passwd.adjunct.byname
	table and synthesize shadow.byname table.

Index: glibc-2.11.1/nis/libnsl.h
===================================================================
--- glibc-2.11.1.orig/nis/libnsl.h
+++ glibc-2.11.1/nis/libnsl.h
@@ -22,6 +22,7 @@
 #define NSS_FLAG_SERVICES_AUTHORITATIVE	2
 #define NSS_FLAG_SETENT_BATCH_READ	4
 #define NSS_FLAG_SPLIT_GROUPS		8
+#define NSS_FLAG_ADJUNCT_AS_SHADOW	16
 
 
 /* Get current set of default flags.  */
Index: glibc-2.11.1/nis/nss
===================================================================
--- glibc-2.11.1.orig/nis/nss
+++ glibc-2.11.1/nis/nss
@@ -1,7 +1,7 @@
 # /etc/default/nss
 # This file can theoretically contain a bunch of customization variables
 # for Name Service Switch in the GNU C library.  For now there are only
-# four variables:
+# five variables:
 #
 # NETID_AUTHORITATIVE
 #   If set to TRUE, the initgroups() function will accept the information
@@ -34,3 +34,12 @@
 #   with same gid, a practice used to overcome entry length limitations.
 #   The downside is a certain performance degradation.
 #SPLIT_GROUPS=TRUE
+#
+# ADJUNCT_AS_SHADOW
+#  If set to TRUE, the passwd routines in the NIS NSS module will not
+#  use the passwd.adjunct.byname tables to fill in the password data
+#  in the passwd structure.  This is a security problem if the NIS
+#  server cannot be trusted to send the passwd.adjuct table only to
+#  privileged clients.  Instead the passwd.adjunct.byname table is
+#  used to synthesize the shadow.byname table if it does not exist.
+#ADJUNCT_AS_SHADOW=TRUE
Index: glibc-2.11.1/nis/nss-default.c
===================================================================
--- glibc-2.11.1.orig/nis/nss-default.c
+++ glibc-2.11.1/nis/nss-default.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 2001, 2004, 2006, 2007 Free Software Foundation, Inc.
+/* Copyright (C) 1996,2001,2004,2006,2007,2010 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -49,6 +49,7 @@ static const struct
     { STRNLEN ("SERVICES_AUTHORITATIVE"), NSS_FLAG_SERVICES_AUTHORITATIVE },
     { STRNLEN ("SETENT_BATCH_READ"), NSS_FLAG_SETENT_BATCH_READ },
     { STRNLEN ("SPLIT_GROUPS"), NSS_FLAG_SPLIT_GROUPS },
+    { STRNLEN ("ADJUNCT_AS_SHADOW"), NSS_FLAG_ADJUNCT_AS_SHADOW },
   };
 #define nvars (sizeof (vars) / sizeof (vars[0]))
 
Index: glibc-2.11.1/nis/nss_nis/nis-pwd.c
===================================================================
--- glibc-2.11.1.orig/nis/nss_nis/nis-pwd.c
+++ glibc-2.11.1/nis/nss_nis/nis-pwd.c
@@ -39,7 +39,7 @@
 /* Protect global state against multiple changers */
 __libc_lock_define_initialized (static, lock)
 
-static bool_t new_start = 1;
+static bool new_start = true;
 static char *oldkey;
 static int oldkeylen;
 static intern_t intern;
@@ -108,13 +108,10 @@ _nis_saveit (int instatus, char *inkey,
 static void
 internal_nis_endpwent (void)
 {
-  new_start = 1;
-  if (oldkey != NULL)
-    {
-      free (oldkey);
-      oldkey = NULL;
-      oldkeylen = 0;
-    }
+  new_start = true;
+  free (oldkey);
+  oldkey = NULL;
+  oldkeylen = 0;
 
   struct response_t *curr = intern.start;
 
@@ -264,18 +261,21 @@ internal_nis_getpwent_r (struct passwd *
 	}
 
       /* Check for adjunct style secret passwords.  They can be
-	 recognized by a password starting with "##".  */
+	 recognized by a password starting with "##".  We do not use
+	 it if the passwd.adjunct.byname table is supposed to be used
+	 as a shadow.byname replacement.  */
       char *p = strchr (result, ':');
       size_t namelen;
       char *result2;
       int len2;
-      if (p != NULL	/* This better should be true in all cases.  */
+      if ((_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW) == 0
+	  && p != NULL	/* This better should be true in all cases.  */
 	  && p[1] == '#' && p[2] == '#'
 	  && (namelen = p - result,
 	      yp_match (domain, "passwd.adjunct.byname", result, namelen,
 			&result2, &len2)) == YPERR_SUCCESS)
 	{
-	  /* We found a passwd.adjunct entry.  Merge encrypted
+	  /* We found a passwd.adjunct.byname entry.  Merge encrypted
 	     password therein into original result.  */
 	  char *encrypted = strchr (result2, ':');
 	  char *endp;
@@ -325,7 +325,7 @@ internal_nis_getpwent_r (struct passwd *
 	}
 
       while (isspace (*p))
-        ++p;
+	++p;
       if (!batch_read)
 	free (result);
 
@@ -346,7 +346,7 @@ internal_nis_getpwent_r (struct passwd *
 	  free (oldkey);
 	  oldkey = outkey;
 	  oldkeylen = keylen;
-	  new_start = 0;
+	  new_start = false;
 	}
     }
   while (parse_res < 1);
@@ -399,16 +399,19 @@ _nss_nis_getpwnam_r (const char *name, s
     }
 
   /* Check for adjunct style secret passwords.  They can be recognized
-     by a password starting with "##".  */
+     by a password starting with "##". We do not use it if the
+     passwd.adjunct.byname table is supposed to be used as a shadow.byname
+     replacement.  */
   char *result2;
   int len2;
   char *p = strchr (result, ':');
-  if (p != NULL	/* This better should be true in all cases.  */
+  if ((_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW) == 0
+      && p != NULL	/* This better should be true in all cases.  */
       && p[1] == '#' && p[2] == '#'
       && yp_match (domain, "passwd.adjunct.byname", name, namelen,
 		   &result2, &len2) == YPERR_SUCCESS)
     {
-      /* We found a passwd.adjunct entry.  Merge encrypted password
+      /* We found a passwd.adjunct.byname entry.  Merge encrypted password
 	 therein into original result.  */
       char *encrypted = strchr (result2, ':');
       char *endp;
@@ -465,7 +468,7 @@ _nss_nis_getpwnam_r (const char *name, s
   if (__builtin_expect (parse_res < 1, 0))
     {
       if (parse_res == -1)
-        return NSS_STATUS_TRYAGAIN;
+	return NSS_STATUS_TRYAGAIN;
       else
 	return NSS_STATUS_NOTFOUND;
     }
@@ -498,18 +501,21 @@ _nss_nis_getpwuid_r (uid_t uid, struct p
     }
 
   /* Check for adjunct style secret passwords.  They can be recognized
-     by a password starting with "##".  */
+     by a password starting with "##".  We do not use it if the
+     passwd.adjunct.byname table is supposed to be used as a shadow.byname
+     replacement.  */
   char *result2;
   int len2;
   size_t namelen;
   char *p = strchr (result, ':');
-  if (p != NULL	/* This better should be true in all cases.  */
+  if ((_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW) == 0
+      && p != NULL	/* This better should be true in all cases.  */
       && p[1] == '#' && p[2] == '#'
       && (namelen = p - result,
 	  yp_match (domain, "passwd.adjunct.byname", result, namelen,
 		    &result2, &len2)) == YPERR_SUCCESS)
     {
-      /* We found a passwd.adjunct entry.  Merge encrypted password
+      /* We found a passwd.adjunct.byname entry.  Merge encrypted password
 	 therein into original result.  */
       char *encrypted = strchr (result2, ':');
       char *endp;
@@ -567,7 +573,7 @@ _nss_nis_getpwuid_r (uid_t uid, struct p
   if (__builtin_expect (parse_res < 1, 0))
     {
       if (parse_res == -1)
-        return NSS_STATUS_TRYAGAIN;
+	return NSS_STATUS_TRYAGAIN;
      else
        return NSS_STATUS_NOTFOUND;
     }
Index: glibc-2.11.1/nis/nss_nis/nis-spwd.c
===================================================================
--- glibc-2.11.1.orig/nis/nss_nis/nis-spwd.c
+++ glibc-2.11.1/nis/nss_nis/nis-spwd.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996-1998,2001,2002,2003,2006 Free Software Foundation, Inc.
+/* Copyright (C) 1996-1998,2001-2003,2006,2010 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
 
@@ -31,6 +31,7 @@
 #include <rpcsvc/ypclnt.h>
 
 #include "nss-nis.h"
+#include <libnsl.h>
 
 /* Get the declaration of the parser function.  */
 #define ENTNAME spent
@@ -41,7 +42,8 @@
 /* Protect global state against multiple changers */
 __libc_lock_define_initialized (static, lock)
 
-static bool_t new_start = 1;
+static bool new_start = true;
+static bool ent_adjunct_used;
 static char *oldkey;
 static int oldkeylen;
 
@@ -50,7 +52,8 @@ _nss_nis_setspent (int stayopen)
 {
   __libc_lock_lock (lock);
 
-  new_start = 1;
+  new_start = true;
+  ent_adjunct_used = false;
   free (oldkey);
   oldkey = NULL;
   oldkeylen = 0;
@@ -83,32 +86,50 @@ internal_nis_getspent_r (struct spwd *sp
       int yperr;
 
       if (new_start)
-        yperr = yp_first (domain, "shadow.byname", &outkey, &keylen, &result,
-			  &len);
+	{
+	  yperr = yp_first (domain, "shadow.byname", &outkey, &keylen, &result,
+			    &len);
+	  if (__builtin_expect (yperr == YPERR_MAP, 0)
+	      && (_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW))
+	    {
+	      free (result);
+	      yperr = yp_first (domain, "passwd.adjunct.byname", &outkey,
+				&keylen, &result, &len);
+	      ent_adjunct_used = true;
+	    }
+	}
       else
-        yperr = yp_next (domain, "shadow.byname", oldkey, oldkeylen, &outkey,
-			 &keylen, &result, &len);
+	yperr = yp_next (domain, (ent_adjunct_used
+				  ? "passwd.adjunct.byname" : "shadow.byname"),
+			 oldkey, oldkeylen, &outkey, &keylen, &result, &len);
 
       if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
-        {
+	{
 	  enum nss_status retval = yperr2nss (yperr);
 
 	  if (retval == NSS_STATUS_TRYAGAIN)
 	    *errnop = errno;
-          return retval;
-        }
+	  return retval;
+	}
 
-      if (__builtin_expect ((size_t) (len + 1) > buflen, 0))
-        {
-          free (result);
+      if (__builtin_expect ((size_t) (len + (ent_adjunct_used ? 3 : 1))
+			    > buflen, 0))
+	{
+	  free (result);
 	  *errnop = ERANGE;
-          return NSS_STATUS_TRYAGAIN;
-        }
+	  return NSS_STATUS_TRYAGAIN;
+	}
 
       char *p = strncpy (buffer, result, len);
-      buffer[len] = '\0';
+      if (ent_adjunct_used)
+	/* This is an ugly trick.  The format of passwd.adjunct.byname almost
+	   matches the shadow.byname format except that the last two fields
+	   are missing.  Synthesize them by marking them empty.  */
+	strcpy (&buffer[len], "::");
+      else
+	buffer[len] = '\0';
       while (isspace (*p))
-        ++p;
+	++p;
       free (result);
 
       parse_res = _nss_files_parse_spent (p, sp, (void *) buffer, buflen,
@@ -123,7 +144,7 @@ internal_nis_getspent_r (struct spwd *sp
       free (oldkey);
       oldkey = outkey;
       oldkeylen = keylen;
-      new_start = 0;
+      new_start = false;
     }
   while (!parse_res);
 
@@ -154,15 +175,25 @@ _nss_nis_getspnam_r (const char *name, s
       *errnop = EINVAL;
       return NSS_STATUS_UNAVAIL;
     }
+  const size_t name_len = strlen (name);
 
   char *domain;
   if (__builtin_expect (yp_get_default_domain (&domain), 0))
     return NSS_STATUS_UNAVAIL;
 
+  bool adjunct_used = false;
   char *result;
   int len;
-  int yperr = yp_match (domain, "shadow.byname", name, strlen (name), &result,
+  int yperr = yp_match (domain, "shadow.byname", name, name_len, &result,
 			&len);
+  if (__builtin_expect (yperr == YPERR_MAP, 0)
+      && (_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW))
+    {
+      free (result);
+      yperr = yp_match (domain, "passwd.adjunct.byname", name, name_len,
+			&result, &len);
+      adjunct_used = true;
+    }
 
   if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
     {
@@ -173,7 +204,7 @@ _nss_nis_getspnam_r (const char *name, s
       return retval;
     }
 
-  if (__builtin_expect ((size_t) (len + 1) > buflen, 0))
+  if (__builtin_expect ((size_t) (len + (adjunct_used ? 3 : 1)) > buflen, 0))
     {
       free (result);
       *errnop = ERANGE;
@@ -181,7 +212,13 @@ _nss_nis_getspnam_r (const char *name, s
     }
 
   char *p = strncpy (buffer, result, len);
-  buffer[len] = '\0';
+  if (__builtin_expect (adjunct_used, false))
+    /* This is an ugly trick.  The format of passwd.adjunct.byname almost
+       matches the shadow.byname format except that the last two fields
+       are missing.  Synthesize them by marking them empty.  */
+    strcpy (&buffer[len], "::");
+  else
+    buffer[len] = '\0';
   while (isspace (*p))
     ++p;
   free (result);
