From: michael@mercury.cs.mun.ca (Mike Rendell)
Subject: fileutils-3.4: chgrp problems on old bsd systems, +fix
Organization: GNUs Not Usenet
Distribution: gnu
Date: Fri, 11 Dec 1992 11:09:55 GMT

Machine: vax
OS: (old) 4.3bsd from Mt. Xinu, Ultrix 2.2, others

The GNU chgrp doesn't work on older versions of bsd because only root can
use chown(2) (there is a comment to this effect about sequents in the README
file).  I've added (ifdef'd) code to the GNU chgrp to deal with such systems
when it is installed setuid root.  The code checks that the user is in the
named group, makes sure the user owns all files before changing the group,
and checks directory permissions before recursing into them.  The diffs for
configure(.in) add a test to see if the system has such a broken chown(2)
(the test is done in a readable/executable subdirectory, conftest.dir, so it
will work even if root is running configure).  The diffs also add a
GETGROUPS_T test to the configure scripts since the modified chgrp uses
getgroups().  Note that the Makefile doesn't install chgrp as setuid
root - this needs to be done by hand.


*** src/chgrp.c.orig	Fri Jul 17 17:18:38 1992
--- src/chgrp.c	Fri Dec 11 14:41:06 1992
***************
*** 24,32 ****
  #include <getopt.h>
  #include "system.h"
  
! #ifndef _POSIX_VERSION
  struct group *getgrnam ();
! #endif
  
  #ifdef _POSIX_SOURCE
  #define endgrent()
--- 24,48 ----
  #include <getopt.h>
  #include "system.h"
  
! #ifdef _POSIX_VERSION
! #include <limits.h>
! #if !defined(NGROUPS_MAX) || NGROUPS_MAX < 1
! #define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
! #endif /* !NGROUPS_MAX */
! 
! #else /* not _POSIX_VERSION */
  struct group *getgrnam ();
! uid_t getuid ();
! gid_t getgid ();
! #include <sys/param.h>
! #if !defined(NGROUPS_MAX) && defined(NGROUPS)
! #define NGROUPS_MAX NGROUPS
! #endif /* not NGROUPS_MAX and NGROUPS */
! #endif /* not _POSIX_VERSION */
! 
! #ifndef GETGROUPS_T
! # define GETGROUPS_T gid_t
! #endif /* !GETGROUPS_T */
  
  #ifdef _POSIX_SOURCE
  #define endgrent()
***************
*** 63,68 ****
--- 79,90 ----
  /* The name of the group to which ownership of the files is being given. */
  char *groupname;
  
+ #ifdef HAVE_BROKEN_CHOWN
+ /* Uid/gid of the user running the program. */
+ uid_t	my_uid;
+ gid_t	my_gid;
+ #endif /* HAVE_BROKEN_CHOWN */
+ 
  struct option long_options[] =
  {
    {"recursive", 0, 0, 'R'},
***************
*** 113,118 ****
--- 135,166 ----
  
    parse_group (argv[optind++], &group);
  
+ #ifdef HAVE_BROKEN_CHOWN
+   my_uid = getuid ();
+   my_gid = getgid ();
+   /* Check that (non-root) user is in group GROUP */
+   if (my_uid)
+     {
+ # ifdef NGROUPS_MAX
+       GETGROUPS_T *groups;
+       int i, n;
+ 
+       groups = (GETGROUPS_T *) xmalloc (NGROUPS_MAX * sizeof(GETGROUPS_T));
+       if ((n = getgroups (NGROUPS_MAX, groups)) < 0)
+ 	error (1, errno, "cannot get supplemental group list");
+       for (i = 0; i < n; i++)
+ 	if (groups[i] == group)
+ 	  break;
+       if (i == n)
+ 	error (1, 0, "you are not in group %s", groupname);
+       free (groups);
+ # else /* not NGROUPS_MAX */
+       if (my_gid != group)
+ 	error (1, 0, "you are not in group %s", groupname);
+ # endif /* not NGROUPS_MAX */
+     }
+ #endif /* HAVE_BROKEN_CHOWN */
+ 
    for (; optind < argc; ++optind)
      errors |= change_file_group (argv[optind], group);
  
***************
*** 165,178 ****
  
    if (group != file_stats.st_gid)
      {
!       if (verbose)
! 	describe_change (file, 1);
!       if (chown (file, file_stats.st_uid, group))
  	{
  	  if (force_silent == 0)
! 	    error (0, errno, "%s", file);
  	  errors = 1;
  	}
      }
    else if (verbose && changes_only == 0)
      describe_change (file, 0);
--- 213,237 ----
  
    if (group != file_stats.st_gid)
      {
! #ifdef HAVE_BROKEN_CHOWN
!       if (my_uid && file_stats.st_uid != my_uid)
  	{
  	  if (force_silent == 0)
! 	    error (0, 0, "you are not the owner of %s", file);
  	  errors = 1;
  	}
+       else
+ #endif /* HAVE_BROKEN_CHOWN */
+ 	{
+ 	  if (verbose)
+ 	    describe_change (file, 1);
+ 	  if (chown (file, file_stats.st_uid, group))
+ 	    {
+ 	      if (force_silent == 0)
+ 		error (0, errno, "%s", file);
+ 	      errors = 1;
+ 	    }
+ 	}
      }
    else if (verbose && changes_only == 0)
      describe_change (file, 0);
***************
*** 199,204 ****
--- 258,274 ----
    unsigned filelength;		/* Length of each pathname to process. */
    unsigned pathlength;		/* Bytes allocated for `path'. */
    int errors = 0;
+ 
+ #ifdef HAVE_BROKEN_CHOWN
+   /* If only X_OK were used, chgrp's errors could be used to list the
+      contents of unreadable directories.  So use R_OK as well... */
+   if (my_uid && access (dir, R_OK|X_OK))
+     {
+       if (force_silent == 0)
+ 	error (0, errno, "%s", dir);
+       return 1;
+     }
+ #endif /* HAVE_BROKEN_CHOWN */
  
    errno = 0;
    name_space = savedir (dir, statp->st_size);


*** configure.orig	Thu Oct 29 18:14:52 1992
--- configure	Fri Dec 11 13:52:57 1992
***************
*** 525,530 ****
--- 525,582 ----
  fi
  rm -f conftest*
  
+ echo checking getgroups group id type
+ echo "#include <sys/types.h>
+ #define NGID 256
+ main()
+ {
+   gid_t gidset[NGID];
+   int i, n;
+   union { gid_t gval; long lval; }  val;
+ 
+   val.lval = -1;
+   for (i = 0; i < NGID; i++)
+     gidset[i] = val.gval;
+   n = getgroups(sizeof(gidset) / (sizeof(int) > sizeof(gid_t)
+ 	                          ? sizeof(int) : sizeof(gid_t)) - 1, gidset);
+   if (n > 0 && gidset[n] != val.gval)
+     exit(0);
+   exit(1);
+ }" > conftest.c
+ eval $compile
+ if test -s conftest && ./conftest 2>/dev/null ; then
+     DEFS="$DEFS -DGETGROUPS_T=int"
+ fi
+ rm -f conftest*
+ 
+ echo checking for broken chown
+ echo "main()
+ {
+   int fd;
+   char *file = "\"conftest.data\"";
+ 
+   unlink (file);
+   if ((fd = creat (file, 0644)) < 0)
+     exit (1);
+   close (fd);
+   if (!getuid ())
+     {
+       if (chown (file, 1, getgid ()) < 0)
+ 	exit (1);
+       setuid (1);
+     }
+   if (chown (file, getuid (), getgid ()) < 0)
+     exit (0);
+   exit (1);
+ }" > conftest.c
+ eval $compile
+ [ -d conftest.dir ] || mkdir conftest.dir
+ chmod 755 conftest.dir
+ if test -s conftest && (cd ./conftest.dir; ../conftest) 2>/dev/null ; then
+     DEFS="$DEFS -DHAVE_BROKEN_CHOWN"
+ fi
+ rm -rf conftest*
+ 
  echo checking integer size
  cat <<EOF > conftest.c
  main() { exit(!(sizeof(long) > sizeof(int))); }
***************
*** 715,720 ****
--- 767,775 ----
      LIBS="$SAVELIBS" ALLOCA=alloca.o
    fi
  fi
+ 
+ echo checking for BSD
+ ( test -f /vmunix || test -f /sdmach || test -f /../../mach ) && DEFS="$DEFS -DBSD42=1"
  
  echo checking for st_blocks in struct stat
  echo "#include <sys/types.h>


*** configure.in.orig	Thu Oct 29 16:27:10 1992
--- configure.in	Fri Dec 11 13:56:32 1992
***************
*** 90,95 ****
--- 90,144 ----
  fi
  
  AC_UID_T
+ echo checking getgroups group id type
+ echo "#include <sys/types.h>
+ #define NGID 256
+ main()
+ {
+   gid_t gidset[NGID];
+   int i, n;
+   union { gid_t gval; long lval; }  val;
+ 
+   val.lval = -1;
+   for (i = 0; i < NGID; i++)
+     gidset[i] = val.gval;
+   n = getgroups (sizeof(gidset) / (sizeof(int) > sizeof(gid_t)
+ 	                          ? sizeof(int) : sizeof(gid_t)) - 1, gidset);
+   if (n > 0 && gidset[n] != val.gval)
+     exit (0);
+   exit (1);
+ }" > conftest.c
+ eval $compile
+ if test -s conftest && ./conftest 2>/dev/null ; then
+     DEFS="$DEFS -DGETGROUPS_T=int"
+ fi
+ rm -f conftest*
+ echo checking for broken chown
+ echo "main()
+ {
+   int fd;
+   char *file = "\"conftest.data\"";
+ 
+   unlink (file);
+   if ((fd = creat (file, 0644)) < 0)
+     exit (1);
+   close (fd);
+   if (!getuid ())
+     {
+       if (chown (file, 1, getgid ()) < 0)
+ 	exit (1);
+       setuid (1);
+     }
+   if (chown (file, getuid (), getgid ()) < 0)
+ 	  exit (0);
+   exit (1);
+ }" > conftest.c
+ [ -d conftest.dir ] || mkdir conftest.dir
+ chmod 755 conftest.dir
+ if test -s conftest && (cd ./conftest.dir; ../conftest) 2>/dev/null ; then
+     DEFS="$DEFS -DHAVE_BROKEN_CHOWN"
+ fi
+ rm -rf conftest*
  AC_INT_16_BITS
  AC_STDC_HEADERS
  AC_UNISTD_H
***************
*** 117,122 ****
--- 166,173 ----
  
  AC_VPRINTF
  AC_ALLOCA
+ echo checking for BSD
+ ( test -f /vmunix || test -f /sdmach || test -f /../../mach ) && AC_DEFINE(BSD4_2)
  AC_ST_BLOCKS
  AC_UTIME_NULL
  AC_XENIX_DIR

--
Michael Rendell - Dept. of Comp. Sci., Memorial University of Newfoundland

Newsgroups: gnu.utils.bug