Subject: standards/25401: chown() not posix conformant wrt egid
To: None <gnats-bugs@gnats.NetBSD.org>
From: Mark Davies <mark@mcs.vuw.ac.nz>
List: netbsd-bugs
Date: 04/30/2004 16:59:20
>Number:         25401
>Category:       standards
>Synopsis:       chown() not posix conformant wrt egid
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    standards-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Apr 30 05:00:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Mark Davies
>Release:        NetBSD 2.0C
>Organization:
Dept. of Comp. Sci., Victoria Uni. of Wellington, New Zealand.
>Environment:
	
	
System: NetBSD city-art.mcs.vuw.ac.nz 2.0C NetBSD 2.0C (MCS_WORKSTATION) #1: Mon Apr 19 13:58:42 NZST 2004 mark@delld6h3.mcs.vuw.ac.nz:/mnt/SAVE/build.obj/mnt/src/src/sys/arch/i386/compile/MCS_WORKSTATION i386
Architecture: i386
Machine: i386
>Description:
POSIX.1-2003 says

	Changing the group ID is permitted to a process with an effective user
	ID equal to the user ID of the file, but without appropriate
	privileges, if and only if owner is equal to the file's user ID or
	(uid_t)-1 and group is equal either to the calling process' effective
	group ID or to one of its supplementary group IDs.

NetBSD fails to do the "group is equal to the calling process' effective
group ID" case. ufs_chown() does:

        if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid ||
            (gid != ip->i_gid && !groupmember((gid_t)gid, cred))) &&
            ((error = suser(cred, &p->p_acflag)) != 0))
                return (error);

but when a process is setgid the effective gid is not in the groups list.

	
>How-To-Repeat:
	Compile the following program (call it "foo")

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

main(int argc, char *argv[])
{
  int gid = getegid();
  int ret = 0;
  int verbose = 0;
  int i = 1;

  if (strcmp(argv[i], "-v") == 0) {
    verbose = 1;
    i++;
  }
  for (; i < argc; i++) {
    if (chown(argv[i], -1, gid) < 0) {
      if (verbose)
        fprintf(stderr,"Changing group of %s: %s\n",argv[i],strerror(errno));
      ret = 1;
    }
  }

  exit(ret);
}

Make the program setgid some group you are not a member of.  Create a new file
(call it "bar"), then run "foo -v bar" and note that the group of bar doesn't
change (file does change when run on Solaris and Tru64).

	
>Fix:
	change the  "!groupmember((gid_t)gid, cred)" above to
	"!(groupmember((gid_t)gid, cred) || gid == cred->cr_gid)" ?
	

cheers
mark
>Release-Note:
>Audit-Trail:
>Unformatted: