kern/69825: 1st group supplied to setgroups() does not take effect in file access

Xiaolin Zang czang at panasas.com
Fri Jul 30 12:50:44 PDT 2004


>Number:         69825
>Category:       kern
>Synopsis:       1st group supplied to setgroups() does not take effect in file access
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jul 30 19:50:21 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Xiaolin Zang
>Release:        4.6.2
>Organization:
Panasas, Inc
>Environment:
FreeBSD rack-bsd14 4.6.2-RELEASE FreeBSD 4.6.2-RELEASE #0: Wed Aug 14 21:23:26 GMT 2002     murray at builder.freebsdmall.com:/usr/src/sys/compile/GENERIC  i386

>Description:
      Demonstrated with the following program:


#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

/*
 * This program takes three arguments: The file to try (use full path of
 * the file system), the limit of groups on your machine (16 or 32) and
 * the slot in the group vector you want gid 8000 (file's owning group)
 * to be set.
 *
 * Run the program as root.  It creates a file owned by uid 7000 and gid 8000.
 * Then it changes the process's euid, egid and group vector so that the
 * process has no write permission to the fie.  Then it sets gid 8000 into
 * the specified slot in the group vector and see if the file becomes
 * writable.
 */
main(
  int                argc,
  char             **argv)
{
  char *path = argv[1];
  int ngroups = atoi(argv[2]);
  int slot = atoi(argv[3]);
  FILE *fh;
  gid_t gids[32], get_gids[32];
  int i, real_ngroups;

  printf("file %s, ngroups %d, slot %d\n", path, ngroups, slot);

  for (i = 0; i < 32; i++) {
    gids[i] = 8100 + i;
    get_gids[i] = 0;
  }

  /*
   * create a file and set owner/group to 7000/8000. 
   * group has write permission.
   */
  unlink(path);
  fh = fopen(path, "w+");
  fwrite("abcd", 1, 5, fh);
  fclose(fh);
  chmod(path, 0664);
  chown(path, 7000, 8000);

  /*
   * Set the credentials to make sure the requestor is not the owner, nor
   * the owning group, and expect the open to fail.
   */
  seteuid(0);
  setgroups(ngroups, gids);
  setegid(8001);
  seteuid(7001);

  fh = fopen(path, "w+");
  if (fh) {
    printf("open for write should fail but did not!\n");
  }

  /* 
   * Try slot 15 againt an nfs server on freebsd from any client
   * you'll get permission error.  
   *
   * Try slot 0 on a freebsd machine using any file system you'll get
   * permission error.
   */
  gids[slot] = 8000; 
  seteuid(0);

  setgroups(ngroups, gids);
  setegid(8001);
  seteuid(7001);

  real_ngroups = getgroups(ngroups, get_gids);
  printf("group vector size %d, groups ", real_ngroups);
  for (i = 0; i < real_ngroups; i++) {
    printf("%d, ", get_gids[i]);
  }
  printf("\n");

  fh = fopen(path, "w+");
  if (!fh) {
    printf("setting primary group into group vector slot %d did not work!\n",
           slot);
  }
}

On a freebsd machine:

rack-bsd14 > a.out /usr0/czang/try 16 0
file /usr0/czang/try, ngroups 16, slot 0
group vector size 16, groups 8001, 8101, 8102, 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8110, 8111, 8112, 8113, 8114, 8115, 
setting primary group into group vector slot 0 did not work!

On a solaris machine:

icarus > a.out /usr0/czang/try 16 0
file /usr0/czang/try, ngroups 16, slot 0
group vector size 16, groups 8000, 8101, 8102, 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8110, 8111, 8112, 8113, 8114, 8115, 

On a linux machine:

perf-1 > a.out /usr0/czang/try 16 0
file /usr0/czang/try, ngroups 16, slot 0
group vector size 16, groups 8000, 8101, 8102, 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8110, 8111, 8112, 8113, 8114, 8115, 

Freebsd has this practice of using cr_groups[0] for egid.  I
personally don't see why this is necessary.  But let's just say this
is OK.  Then at least two things should be fixed: 1) In the document
say the effective size of the group vector is 15 instead of 16 and 2)
Do not drop the first group (slot 0) when handling a setgroups call
(drop the ones at the high end of the gid array supplied).

I think the best fix is not to use cr_groups[0] for egid. The
assumption that cr_groups[0] is egid is in freebsd's nfs client and
server, too.

>How-To-Repeat:
      See the test program in the "full descrition" part.
>Fix:
      
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list