/*
 * 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 "kauth.h"

RCSID("$Id: kauth.c,v 1.60 1996/07/02 09:48:31 bg Exp $");

char *prog;
static char inst[100];
static char name[100];
static char realm[REALM_SZ + 1];
static char srvtab[MaxPathLen + 1];
static int lifetime = DEFAULT_TKT_LIFE;
static char remote_tktfile[MaxPathLen + 1];
static char remoteuser[100];
static char *cell = 0;

static void
usage()
{
  fprintf(stderr,
	  "Usage: %s [-n <name>] [-r remoteuser] [-t remote ticketfile]"
	  "[ -l lifetime (in minutes) ] [ -h hosts... ]"
	  "[ -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);
    fprintf(stderr, "%s: Can't exec program ``%s'' %s\n",
	    prog, argv[0], strerror(errno));
    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();
    if (k_hasafs())
      k_unlog();	 
    break;
  }
}

static RETSIGTYPE
renew(int sig)
{
  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_get_err_text(code));
  else if (k_hasafs())
    {
      if ((code = k_afsklog(cell, NULL)) != 0 && code != KDC_PR_UNKNOWN) {
        fprintf(stderr, "%s: %s\n", prog, krb_get_err_text(code));
      }
    }

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

static int
zrefresh()
{
  switch (fork()) {
  case -1:
      fprintf(stderr, "Warning %s: Failed to fork zrefresh\n", prog);
      return -1;
  case 0:
      /* Child */
      execlp("zrefresh", "zrefresh", 0);
      execl(BINDIR "/zrefresh", "zrefresh", 0);
#if 0
      fprintf(stderr, "Warning %s: Failed to exec zrefresh\n", prog);
#endif
      exit(1);
  default:
      /* Parent */
      break;
  }
  return 0;
}

static void
upcase(char *t)
{
    for (; *t; t++)
        *t = toupper(*t);
}

static int
key_to_key(char *user, char *instance, char *realm, void *arg,
	   des_cblock *key)
{
  memcpy(key, arg, sizeof(des_cblock));
  return 0;
}

int
main(argc, argv)
     int argc;
     char **argv;
{
  int code, more_args;
  int ret;
  int c;
  char *file;
  int pflag = 0;
  char passwd[100];
  des_cblock key;
  char **host;
  int nhost;
  char tf[MaxPathLen];

  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));
  *remoteuser = '\0';
  nhost = 0;
  
  while ((c = getopt(argc, argv, "r:t:f:hl:n:c:")) != EOF)
    switch (c) {
    case 'f':
      strncpy(srvtab, optarg, sizeof(srvtab));
      break;
    case 't':
      strncpy(remote_tktfile, optarg, sizeof(remote_tktfile));
      break;
    case 'r':
      strncpy(remoteuser, optarg, sizeof(remoteuser));
      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_get_err_text(code));
	  usage();
	}
      upcase(realm);
      pflag = 1;
      break;
    case 'c':
      cell = optarg;
      break;
    case 'h':
      host = argv + optind;
      for(nhost = 0; optind < argc && *argv[optind] != '-'; ++optind)
	++nhost;
      break;
    case '?':
      usage();
      break;
    default:
      fprintf(stderr, "Unknown option, try -?\n");
      exit(1);
      break;
    }
  
  /* Look for kerberos name */
  if (!pflag && optind < argc &&
      kname_parse(name, inst, realm, argv[optind]) == 0)
    {
      ++optind;	 
      upcase(realm);
    }

  if (name[0] == '\0' &&
      krb_get_default_principal (name, inst, realm) < 0)
    {
      fprintf (stderr, "%s: Could not get default principal\n",
	       argv[0]);
      return 1;
    }
  
  if (*remoteuser == '\0')
    strcpy (remoteuser, name);

  more_args = argc - optind;

  if (realm[0] == 0)
    if (krb_get_lrealm(realm, 1) != KSUCCESS)
      strcpy(realm, KRB_REALM);

  if (more_args) {
    int f;

    do{
      sprintf(tf, TKT_ROOT "%d_%d", (int)getuid(), (int)(getpid()*time(0)));
      f = open(tf, O_CREAT|O_EXCL|O_RDWR);
    }while(f < 0);
    close(f);
    unlink(tf);
    setenv("KRBTKFILE", tf, 1);
    krb_set_tkt_string (tf);
  }
    
  if (srvtab[0])
    {
      signal(SIGALRM, renew);


      code = read_service_key (name, inst, realm, 0, srvtab, (char *)&key);
      if (code == KSUCCESS)
	code = krb_get_in_tkt(name, inst, realm, "krbtgt", realm, lifetime,
			      key_to_key, NULL, key);
      alarm(krb_life_to_time(0, lifetime)/2 - 60);
    }
  else
    {
      char prompt[128];
      sprintf(prompt,
	      "%s%s%s@%s's Password: ",
	      name, inst[0] ? "." : "", inst, realm);
      if (des_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0))
	*passwd = '\0';
      des_string_to_key (passwd, &key);
      code = krb_get_in_tkt (name, inst, realm, "krbtgt", realm, lifetime,
			     key_to_key, NULL, key);
      if(code == INTK_BADPW)
	{
	  afs_string_to_key (passwd, realm, &key);
	  code = krb_get_in_tkt (name, inst, realm, "krbtgt", realm, lifetime,
				 key_to_key, NULL, key);
	}
      memset(passwd, 0, sizeof(passwd));
    }
  if (code)
    {
      memset (key, 0, sizeof(key));
      fprintf(stderr, "%s\n", krb_get_err_text(code));
      exit(1);
    }

  if (k_hasafs())
    {
      if (more_args)
        k_setpag();
      if ((code = k_afsklog(cell, NULL)) != 0 && code != KDC_PR_UNKNOWN)
        fprintf(stderr, "%s: %s\n", prog, krb_get_err_text(code));
    }

  for(ret = 0; nhost-- > 0; host++)
    ret += rkinit(name, inst, realm, lifetime, remoteuser,
		  remote_tktfile, &key, *host);

  if (ret)
    return ret;

  if (more_args)
    doexec(more_args, &argv[optind]);
  else
    zrefresh();

  return 0;
}
