/*
 * Little program that reads an srvtab or password and
 * creates a suitable ticketfile and associated AFS tokens.
 *
 * If an optional command is given the command is executed in a
 * new PAG and when the command exits the tickets are destroyed.
 */

#include "config.h"

RCSID("$Header: /afs/nada.kth.se/src/packages/kth-krb/SourceRepository/krb4/appl/kauth/kauth.c,v 1.32 1995/10/27 10:55:31 bg Exp $");

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>

#include <pwd.h>
#include <unistd.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif /* HAVE_SYS_RESOURCE_H */
#include <sys/wait.h>

#include <krb.h>
#include <kafs.h>

extern char *optarg;
extern int optind, opterr;
extern char *getenv();

char *prog;
char inst[100];
char name[100];
char realm[REALM_SZ + 1];
char srvtab[MaxPathLen + 1];
int lifetime = DEFAULT_TKT_LIFE;

static void
usage()
{
  fprintf(stderr,
	  "Usage: %s -n <name> [ -l lifetime (in minutes) ] [ -f srvtab ] [ -c AFS cell name ] [ command ... ]\n",
	  prog);
  fprintf(stderr, "\nA fully qualified name can be given user[.instance][@realm]\nRealm is converted to uppercase!\n");
  exit(1);
}

static void
doexec(argc, argv)
     int argc;
     char **argv;
{
  int status, ret;

  switch (fork()) {
  case -1:
    perror(prog);
    break;
  case 0:
    /* in child */
    execvp(argv[0], argv);
    perror(prog);
    exit(1);
    break;
  default:
    /* in parent */
    do {
      ret = wait(&status);
    } while (ret > 0 && !WIFEXITED(status) || ret < 0 && errno == EINTR);
    if (ret < 0)
      perror("wait");
    dest_tkt();
    break;
  }
}

static void
renew()
{
  int code;

  signal(SIGALRM, renew);

  code = krb_get_svc_in_tkt(name, inst, realm,
			    "krbtgt", realm, lifetime, srvtab);
  if (code)
    fprintf(stderr, "%s: %s\n", prog, krb_err_txt[code]);
  else if (k_hasafs())
    {
      if ((code = k_afsklog(0, realm)) != 0) {
        fprintf(stderr, "%s: %s\n", prog, krb_err_txt[code]);
      }
    }

  alarm(krb_life_to_time(0, lifetime)/2 - 60);
  return;
}

int
main(argc, argv)
     int argc;
     char **argv;
{
  int code, more_args;
  int c;
  char *cell = 0;
  char *file;

  if ((file =  getenv("KRBTKFILE")) == 0)
    file = TKT_FILE;  

  prog = argv[0];
  memset(inst, 0, sizeof(inst));
  memset(name, 0, sizeof(name));
  memset(realm, 0, sizeof(realm));
  memset(srvtab, 0, sizeof(srvtab));
  
  /* Look for kerberos name */
  if (argc > 1 &&
      argv[1][0] != '-' &&
      kname_parse(name, inst, realm, argv[1]) == 0)
    {
      char *t;
      argc--, argv++;
      for (t = realm; *t; t++)
	if ('a' <= *t && *t <= 'z')
	  *t = *t - 'a' + 'A';
    }

  while ((c = getopt(argc, argv, "f:hl:n:c:")) != EOF)
    switch (c) {
    case 'f':
      strncpy(srvtab, optarg, sizeof(srvtab));
      break;
    case 'l':
      lifetime = atoi(optarg);
      if (lifetime == -1)
	lifetime = 255;
      else if (lifetime < 5)
	lifetime = 1;
      else
	lifetime = krb_time_to_life(0, lifetime*60);
      if (lifetime > 255)
	lifetime = 255;
      break;
    case 'n':
      if ((code = kname_parse(name, inst, realm, optarg)) != 0)
	{
	  fprintf(stderr, "%s\n", krb_err_txt[code]);
	  usage();
	}
      break;
    case 'c':
      cell = optarg;
      break;
    case 'h':
    case '?':
      usage();
      break;
    default:
      fprintf(stderr, "Unknown option, try -h\n");
      exit(1);
      break;
    }
  
  /* No kerberos name, look for old ticket file. */
  if (name[0] == 0 && tf_init(file, R_TKT_FIL) == KSUCCESS)
    {
      (void) tf_close();
      if ((code = krb_get_tf_realm(file, realm)) == KSUCCESS &&
	  (code = tf_init(file, R_TKT_FIL)) == KSUCCESS &&
	  (code = tf_get_pname(name)) == KSUCCESS &&
	  (code = tf_get_pinst(inst)) == KSUCCESS)
	(void) tf_close();	/* alles gut */
      else
	{
	  fprintf(stderr,
		  "failed to read %s: %s. Ignoring %s and continuing\n",
		  file, krb_err_txt[code], file);
	  name[0] = 0;
	}
    }
  /* Still no kerberos name, try username instead. */
  if (name[0] == 0)
    {
      struct passwd *pw = getpwuid(getuid());
      if (pw == 0)
	{
	  fprintf(stderr, "name must be non null\n");
	  usage();
	}
      else
	strncpy(name, pw->pw_name, sizeof(name));
      if (strcmp(name, "root") == 0) /* Running as root? */
	{
	  strcpy(inst, name);
	  strcpy(name, getenv("USER"));
	}
    }
  
  more_args = argc - optind;

  if (realm[0] == 0)
    if (krb_get_lrealm(realm, 1) != KSUCCESS)
      strcpy(realm, "NO.DEFAULT.REALM");
  
  if (srvtab[0])
    {
      signal(SIGALRM, renew);
      alarm(krb_life_to_time(0, lifetime)/2 - 60);
      code = krb_get_svc_in_tkt(name, inst, realm,
				"krbtgt", realm, lifetime, srvtab);
    }
  else
    {
      printf("User's %s%s%s@%s ", name, inst[0] ? "." : "", inst, realm);
      fflush(stdout);
      code = krb_get_pw_in_tkt(name, inst, realm,
			       "krbtgt", realm, lifetime, 0);
    }
  if (code)
    {
      fprintf(stderr, "%s\n", krb_err_txt[code]);
      exit(1);
    }

  if (k_hasafs())
    {
      if (more_args)
        k_setpag();
      if ((code = k_afsklog(cell, realm)) != 0)
        fprintf(stderr, "%s: %s\n", prog, krb_err_txt[code]);
    }

  if (more_args)
    doexec(more_args, &argv[optind]);
#ifdef ZEPHYR
  else
    zrenew();
#endif

  exit(0);
}

#ifdef ZEPHYR
#include <zephyr/zephyr.h>

static int
zrenew()
{
  Code_t code;
  int wgport;

  char *display = getenv("DISPLAY");
  if (display != 0 && *display)
    {
      char *t = strchr(display, ':');
      if (t != 0)
	if (   strcmp(t, ":0.0") == 0
	    || strcmp(t, ":0.1") == 0)
	  t[2] = '\0';		/* Chop of .0 */
    }

  if ((wgport = ZGetWGPort()) != -1)
    if ((code = ZInitialize() == ZERR_NONE))
      if ((code = ZSubscribeTo(0, 0, (u_short)wgport)) == ZERR_NONE)
	{
	  char buf[100], *t;
	  t = ZGetVariable("exposure");

	  strncpy(buf, t, sizeof(buf));
	  for (t = buf; *t; t++)
	    if (islower(*t))
	      *t = toupper(*t);

	  if ((code = ZSetLocation(buf)) == ZERR_NONE)
	    return 0;
	}
  return -1;
}
#endif
