Patch: netgrp can't handle splitted NIS groups

Christian Mudra <[email protected]> Fri, 8 Jul 2005 11:54:05 +0200
Newsgroups gmane.linux.pld.shadow.general
Message-ID <[email protected]>
Hi,

today I found out, that newgrp doesn't handle splitted NIS groups correctly.
I've checked several implementations of newgrp (shadow, util-linux and
opensolaris), but didn't have success finding a 'good' one. It's interesting,
that only the newgroup of AIX (I tested AIX-5.1) takes care of splitted NIS
groups and handles them correctly.

Background: If a line in the NIS map 'group' will be larger than about
1018 bytes (some bytes less than 2^10), under some architectures/OS the
resulting NIS map will be corrupt. In large environments it's sometimes
needed to have a couple of people (let's say 300++) in one NIS group.
Because the group entry in the NIS map would become way too large,
it must be split into several groups.

Given the users

   user1, user2, user3, user4

user1:*:1001:50000:....
user2:*:1002:50000:....
user3:*:1003:50000:....
user4:*:1004:50000:....

in the groups

users:*:50000
largegrp:*:59999:user1,user2
largegrp_1:*:59999:user3,user4
...

The command

  newgrp largegrp

given by user3 fails.

The 'groups' command handles that case for the users 'user3' and 'user4'
correctly, e.g. it shows, that the user 'user3' is in the groups

  users largegrp

(not in 'largegrp_1').

Reading the code in newgrp.c: The requested group (largegrp) is checked
for existence, and then the list of group members of 'largegrp' is searched
for "user3". This of course will fail, because in the struct grp->gr_mem
only the users "user1" and "user2" are listed.
Because of that, a group password is asked for.

Attached are two patches, but there are still some TO-DO's.

The first patch (newgrp_handle_split_groups) extends the functionality
of src/newgrp.c, that, if the requested group is given, all groups of
the same GID are tested for membership of the requesting user.
This patch requires configure to write a switch
  USE_SPLITTEDGROUPS
to config.h, which should be added.

The second patch (use_have_lastlog_h) patches libmisc/log.c, as the
struct lastlog is not known on HPUX (more of this topis further below).
This patch is not complete in the meaning, that 
1) only log.c is changed but not all the other files which also
   include <lastlog.h>,
2) all files with occurances with a call to dolastlog() should be
   fixed too, because of the prototype change.


I also compiled the command 'newgrp' on several platforms:

*) On IRIX(-6.5.24) the following fix is needed:
lib/Makefile: DEFS=-DLOGINDEFS=\"/etc/default/login\"

*) On Linux (RedHat8 on x86, SuSE9 on x86_64), everything was fine.

*) SunOS-5.7 up to 5.9:
during 'configure':
checking location of faillog/lastlog/wtmp... ./configure[23124]: test: argument expected
./configure[23124]: test: argument expected
./configure[23124]: test: argument expected
./configure[23124]: test: argument expected
/etc

As on IRIX, lib/Makefile needed a change:
lib/Makefile: DEFS=-DLOGINDEFS=\"/etc/default/login\"

*) HPUX was the hardest platform. For all tested HPUX platforms (I used
HPUX-11.00-PARISC2 and HPUX-11.22-ia64), configure fails like on SunOS:

checking location of faillog/lastlog/wtmp... ./configure[23124]: test: Specify a parameter with this command.
./configure[23124]: test: Specify a parameter with this command.
./configure[23124]: test: Specify a parameter with this command.
./configure[23124]: test: Specify a parameter with this command.
/etc

All HPUX platforms, either the common /etc/login.defs or /etc/default/login
is only available as /etc/default/security or simply not existant.
So, the shadow package file
  lib/getdefs.c
should _NOT_ stop (exit 1) when the LOGIN_DEFS file could not be read.

Also, HPUX does not have a <lastlog.h>. configure sets a
/* #undef HAVE_LASTLOG_H */
in all config.h on all tested platforms. Beside the fact, that either the
test in configure is not working (as Linux and IRIX offer a <lastlog.h>),
no code actually uses that flag. At least on HPUX, it should be used.
In that case,
  src/login.c
should be patched, too, to use the dolastlog() call in libmisc/log.c only
if HAVE_LASTLOG_H is set.

**) HPUX-11.00 on PA-RISC2:
configure needs to look for getspnam() in the library -lsec,
and newgrp must be linked against libsec

**) HPUX-11.11 on PA-RISC2:
configure aborts the xgettext test:
checking for xgettext... ./configure[24480]: 24051 Abort(coredump)
no
... but I didn't dig deeper into that.


Regards,

  Christian Mudra.

-- 
Christian Mudra                     science + computing ag
IT-Services                         Ingolstaedter Str. 22
BMW-Infrastruktur                   80807 Muenchen, Germany
phone   +49 (0)89 356386-865        [email protected]
fax     +49 (0)89 356386-737
newgrp_handle_split_groups.patch (text/plain, 2.2 KB)
diff -uNr src/newgrp.c.orig src/newgrp.c
--- src/newgrp.c.orig	2005-06-20 12:17:08.000000000 +0200
+++ src/newgrp.c	2005-07-08 11:43:06.000000000 +0200
@@ -40,6 +40,8 @@
 #include "getdef.h"
 extern char **environ;
 
+#define USE_SPLITTEDGROUPS
+
 #ifdef HAVE_SETGROUPS
 static int ngroups;
 static GETGROUPS_T *grouplist;
@@ -63,6 +65,37 @@
 		fprintf (stderr, _("Usage: sg group [[-c] command]\n"));
 }
 
+#ifdef USE_SPLITTEDGROUPS
+/*
+ * find_matching_group - search all groups of a given group id for
+ *                       membership of a given username
+ */
+
+static struct group* find_matching_group(const char *name, gid_t gid) {
+        struct group *gr;
+        char **look; 
+        int notfound=1;
+
+        setgrent();
+        while ((gr = getgrent()) != NULL) {
+                if (gr->gr_gid != gid) {
+                        continue;
+                }
+                /*
+                 * A group with matching GID was found.
+                 * Test for membership of 'name'.
+                 */
+                look = gr->gr_mem;
+                while (*look && (notfound = strcmp(*look++, name)))
+                        ;
+                if (!notfound)
+                        break;
+        }
+        endgrent();
+        return gr;
+}   
+#endif
+
 /*
  * newgrp - change the invokers current real and effective group id
  */
@@ -289,6 +322,24 @@
 		fprintf (stderr, _("unknown group: %s\n"), group);
 		goto failure;
 	}
+#ifdef USE_SPLITTEDGROUPS
+        /*
+         * For splitted groups (due to limitations of NIS), check all 
+         * groups of the same GID like the requested group for
+         * membership of the current user.
+         */
+        grp = find_matching_group(name, grp->gr_gid);
+        if (!grp) {
+                /*
+                 * No matching group found. As we already know that
+                 * the group exists, this happens only in the case
+                 * of a requested group where the user is not member.
+                 *
+                 * Re-read the group entry for further processing.
+                 */
+                grp = getgrnam(group);
+        }
+#endif
 #ifdef SHADOWGRP
 	if ((sgrp = getsgnam (group))) {
 		grp->gr_passwd = sgrp->sg_passwd;
use_have_lastlog_h.patch (text/plain, 730 B)
diff -uNr libmisc/log.c.orig libmisc/log.c 
--- libmisc/log.c.orig	2005-06-14 22:27:35.000000000 +0200
+++ libmisc/log.c	2005-07-08 11:50:21.000000000 +0200
@@ -36,7 +36,10 @@
 #include <fcntl.h>
 #include <time.h>
 #include "defines.h"
+#if HAVE_LASTLOG_H
 #include <lastlog.h>
+#endif
+
 /* 
  * dolastlog - create lastlog entry
  *
@@ -44,6 +47,7 @@
  *	UID is extracted from the global (struct passwd) entry and the
  *	TTY information is gotten from the (struct utmp).
  */
+#if HAVE_LASTLOG_H
 void
 dolastlog (struct lastlog *ll, const struct passwd *pw, const char *line,
 	   const char *host)
@@ -94,3 +98,9 @@
 		write (fd, (char *) &newlog, sizeof newlog);
 	close (fd);
 }
+
+#else
+
+void dolastlog () { }
+
+#endif