Subject: bin/11047: newgrp is missing
To: None <tech-userlevel@netbsd.org>
From: Tim Bandy <bandy@timn8r.org>
List: tech-userlevel
Date: 04/25/2002 14:46:21
Greetings,
I've found myself with a little extra time on my hands.  I'm hoping
that I can be of service to the netbsd community by fixing prs here
and there.  Now, my coding skills are a bit rusty, so please feel free
to provide whatever pointers you can to improve my skills :)
Hopefully they're not too far under par.

I've taken a crack at writing a newgrp program.  It's included below.
Please review it, and if you find it useful, it's yours.  Of course,
documentation is as of yet nonexistant.  Copyrights and id tags will
need fixing.

Thanks to bjh for the documentation pointers.

Password validation is still lame.  Error messages are hopefully
informative, but they may need improvement.  Also, this does, afaik,
require suid root privileges, so please check that it does not
introduce any new exploits.  I think that the environment and open
filedescriptors may need to be checked before the exec, but I'm not
sure how to do this correctly.

Cheers.

--
Tim Bandy (bandy@timn8r.org)

Excellent day for putting Slinkies on an escalator.
		-- fortune




#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1992, 1993\n\
	The Regents of the University of California.  All rights reserved.\n");
#endif /* not lint */

#ifndef lint
#if 0
static char sccsid[] = "";
#else
__RCSID("$Id$");
#endif
#endif /* not lint */

#include <sys/types.h>
#include <grp.h>
#include <pwd.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void usage __P((void));
int main __P((int, char *[]));

int
main(argc, argv)
	int argc;
	char *argv[];
{
  int groupid;
  int idx;
  char loginflag = 0;
  char validgrp = 0;
  char validmember = 0;
  char validpass = 0;
  char numeric = 0;

  struct group *ngrp;
  struct passwd *pw;
  char *shell;
  char *name;
  char *tmp;
  char *grouparg;
  char *passwd;

  if(argc == 2) {
    grouparg = argv[1];
  }
  else if(argc == 3) {
    if(strncmp(argv[1], "-l", 3)) {
      usage();
      exit(1);
    }
    else {
      loginflag = 1;
      grouparg = argv[2];
    }
  }
  else {
    usage();
    exit(1);
  }

    /* first check the group by name */
  ngrp = getgrnam(grouparg);
  if(ngrp == NULL) {
    fprintf(stderr, "Invalid group name %s: %s\n", grouparg, strerror(errno));

    /* Check if our argument is numeric */
    tmp = grouparg;
    numeric = 1;
    for(;numeric && (tmp != NULL) && (*tmp != NULL);tmp++) {
      
      /*
	printf("testing %c %x\n", *tmp, *tmp);
	printf("tmp = %p\n", tmp);
      */

      if(! isdigit(*tmp)) {
	tmp = NULL;
	numeric = 0;
      }
    }

    if(numeric) {
      groupid = atol(grouparg);
      if(groupid >= 0) {
	ngrp = getgrgid(atol(grouparg));
	if(ngrp == NULL) {
	  fprintf(stderr, "Invalid group id %d: %s\n",
		  atol(grouparg), strerror(errno));
	}
	else {
	  validgrp = 1;
	}
      }
      else {
	printf("Argument %s invalid group id\n", grouparg);
      }
    }
    else {
      printf("Argument %s not a number\n", grouparg);
    }
  }
  else {
    validgrp = 1;
  }

  if(validgrp) {

    /* Get passwd entry */
    pw = getpwuid(getuid());
    if(pw == NULL) {
      fprintf(stderr, "Error getting passwd entry for uid %d: %s\n",
	      getuid(), strerror(errno));
      exit(1);
    }

    /* Check membership */
    for(idx = 0; (validmember == 0) && (ngrp->gr_mem[idx] != NULL); idx++) {
      if(! strncmp((char *)pw->pw_name,
		   (char *)ngrp->gr_mem[idx],
		   strlen(pw->pw_name))) {
	validmember = 1;
      }
    }

    if(validmember == 0) {
      fprintf(stderr, "You do not belong to group %s\n", ngrp->gr_name);
      exit(1);
    }

    /* check for a passwd */
    if(strlen(ngrp->gr_passwd)) {
      passwd = getpass("Group password: ");

      if(passwd == NULL) {
	fprintf(stderr, "Error reading passwd: %s\n", strerror(errno));
	exit(1);
      }

      if(! strncmp(ngrp->gr_passwd,
		   crypt(passwd, ngrp->gr_passwd),
		   strlen(ngrp->gr_passwd))) {
	validpass = 1;
      }

      bzero(passwd, strlen(passwd));
    }
    else {
      validpass = 1; /* no passwd on the group */
    }

    if(validpass == 0) {
      fprintf(stderr, "Password validation failed\n");
      exit(1);
    }

    if(setgid(ngrp->gr_gid)) {
    /*  if(setegid(ngrp->gr_gid)) { */
      fprintf(stderr, "failed to set gid to %d: %s\n",
	      ngrp->gr_gid, strerror(errno));
      exit(1);
    }

    if(setuid(getuid())) {
      fprintf(stderr, "failed to set id to %d: %s\n", getuid());
      exit(1);
    }

    shell = getenv("SHELL");
    if(shell == NULL) {
      fprintf(stderr, "Valid SHELL variable not found in environment.\n");
      exit(1);
    }

    if((strlen(shell) > PATH_MAX) || (strlen(shell) > _POSIX_PATH_MAX)) {
      fprintf(stderr, "Invalid SHELL: SHELL too long\n");
      exit(1);
    }

    if((name = strrchr(shell, '/')) == NULL)
      name = shell;
    else
      ++name;

    if(loginflag) {
      tmp = malloc(strlen(name) + 2);
      if(tmp == NULL) {
	fprintf(stderr, "Malloc failed: %s\n", strerror(errno));
	exit(1);
      }
      sprintf(tmp, "-%s", name);
      name = tmp;
    }

    /*
      clean environment?
      close fds?
    */

    execl(shell, name, NULL);
    fprintf(stderr, "exec failed: errno=%d %s\n", errno, strerror(errno));
  }

  exit(0);
}

void
usage()
{
	extern char *__progname;
	(void)fprintf(stderr, "Usage: %s [-l] group\n", __progname);
	exit(1);
}