/* test-uid.c --- playing with setuid.
 * xscreensaver, Copyright (c) 1998, 2005 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

static void
print(void)
{
  int uid = getuid();
  int gid = getgid();
  int euid = geteuid();
  int egid = getegid();
  struct passwd *p = 0;
  struct group *g = 0;
  gid_t groups[1024];
  int n, size;

  p = getpwuid (uid);
  g = getgrgid (gid);
  fprintf(stderr, "real user/group: %ld/%ld (%s/%s)\n", (long) uid, (long) gid,
	  (p && p->pw_name ? p->pw_name : "???"),
	  (g && g->gr_name ? g->gr_name : "???"));

  p = getpwuid (euid);
  g = getgrgid (egid);
  fprintf(stderr, "eff. user/group: %ld/%ld (%s/%s)\n", (long)euid, (long)egid,
	  (p && p->pw_name ? p->pw_name : "???"),
	  (g && g->gr_name ? g->gr_name : "???"));

  size = sizeof(groups) / sizeof(gid_t);
  n = getgroups(size - 1, groups);
  if (n < 0)
    perror("getgroups failed");
  else
    {
      int i;
      fprintf (stderr, "eff. group list: [");
      for (i = 0; i < n; i++)
        {
          g = getgrgid (groups[i]);
          fprintf(stderr, "%s%s=%ld", (i == 0 ? "" : ", "),
                  (g->gr_name ? g->gr_name : "???"),
                  (long) groups[i]);
        }
      fprintf (stderr, "]\n");
    }
}

int
main (int argc, char **argv)
{
  int i;
  struct passwd *p = 0;
  struct group *g = 0;

  if (argc <= 1)
    {
      fprintf(stderr,
	      "usage: %s [ user/group ... ]\n"
	      "\tEach argument may be a user name, or user/group.\n"
	      "\tThis program will attempt to setuid/setgid to each\n"
	      "\tin turn, and report the results.  The user and group\n"
	      "\tnames may be strings, or numeric.\n",
	      argv[0]);
      exit(1);
    }

  print();
  for (i = 1; i < argc; i++)
    {
      char *user = argv[i];
      char *group = strchr(user, '/');
      if (!group)
	group = strchr(user, '.');
      if (group)
	*group++ = 0;

      if (group && *group)
	{
	  long gid = 0;
	  int was_numeric = 0;

	  g = 0;
	  if (*group == '-' || (*group >= '0' && *group <= '9'))
	    if (1 == sscanf(group, "%ld", &gid))
	      {
		g = getgrgid (gid);
		was_numeric = 1;
	      }

	  if (!g)
	    g = getgrnam(group);

	  if (g)
	    {
	      gid = g->gr_gid;
	      group = g->gr_name;
	    }
	  else
	    {
	      if (was_numeric)
		{
		  fprintf(stderr, "no group numbered %s\n", group);
		  group = "";
		}
	      else
		{
		  fprintf(stderr, "no group named %s\n", group);
		  goto NOGROUP;
		}
	    }

	  fprintf(stderr, "setgroups(1, [%ld]) \"%s\"", gid, group);
          {
            gid_t g2 = gid;
            if (setgroups(1, &g2) == 0)
              fprintf(stderr, " succeeded\n");
            else
              perror(" failed");
          }

	  fprintf(stderr, "setgid(%ld) \"%s\"", gid, group);
	  if (setgid(gid) == 0)
	    fprintf(stderr, " succeeded\n");
	  else
	    perror(" failed");

	NOGROUP: ;
	}

      if (user && *user)
	{
	  long uid = 0;
	  int was_numeric = 0;

	  p = 0;
	  if (*user == '-' || (*user >= '0' && *user <= '9'))
	    if (1 == sscanf(user, "%ld", &uid))
	      {
		p = getpwuid (uid);
		was_numeric = 1;
	      }

	  if (!p)
	    p = getpwnam(user);

	  if (p)
	    {
	      uid = p->pw_uid;
	      user = p->pw_name;
	    }
	  else
	    {
	      if (was_numeric)
		{
		  fprintf(stderr, "no user numbered \"%s\"\n", user);
		  user = "";
		}
	      else
		{
		  fprintf(stderr, "no user named %s\n", user);
		  goto NOUSER;
		}
	    }

	  fprintf(stderr, "setuid(%ld) \"%s\"", uid, user);
	  if (setuid(uid) == 0)
	    fprintf(stderr, " succeeded\n");
	  else
	    perror(" failed");
	NOUSER: ;
	}
      print();
    }

  fprintf(stderr,
	  "running \"whoami\" and \"groups\" in a sub-process reports:\n");
  fflush(stdout);
  fflush(stderr);
  system ("/bin/sh -c 'echo \"`whoami` / `groups`\"'");

  fflush(stdout);
  fflush(stderr);
  exit(0);
}
