/* S/KEY v1.1b (skeyinit.c)
 *
 * Authors:
 *          Neil M. Haller <nmh@thumper.bellcore.com>
 *          Philip R. Karn <karn@chicago.qualcomm.com>
 *          John S. Walden <jsw@thumper.bellcore.com>
 *          Scott Chasin <chasin@crimelab.com>
 *
 * S/KEY initialization and seed update
 */

#include <stdio.h>
#include <string.h>
#include <pwd.h>
#ifdef linux
#undef SHADOW
#include <shadow.h>
#endif
#include <sys/resource.h>
#include <time.h>

#ifdef __svr4__
#include <sys/systeminfo.h>
#include <unistd.h>
#include <shadow.h>
#include "sysv_shadow.h"
#endif /* __svr4__ */


#include "skey.h"

extern int optind;
extern char *optarg;

char *readpass (), *malloc (), *getpass (), *crypt ();

int skeylookup __ARGS ((struct skey * mp, char *name));

#define NAMELEN 2


main (argc, argv)
  int argc;
  char *argv[];
{
  int rval, n, nn, i, defaultsetup, l;
  time_t now;
  char seed[18], tmp[80], key[8], defaultseed[17], passwd[256], passwd2[256],
       tbuf[27], buf[60], lastc, me[80], user [8], *salt, *p, *pw; 
  struct skey skey;
  struct passwd *pp;
  struct spwd   *sp;
  struct tm *tm;


  time (&now);
  tm = localtime (&now);
  strftime (tbuf, sizeof (tbuf), "%M%j", tm);

  if ((gethostname (defaultseed, sizeof (defaultseed))) < 0)
      exit (-1);

  strcpy (&defaultseed[NAMELEN], tbuf);

  pp = getpwuid (getuid ());
  strcpy (me, pp->pw_name);

  if ((pp = getpwnam (me)) == NULL) {
     fprintf(stderr, "Who are you?\n");
     exit(1);
  }
 
  defaultsetup = 1;

  if (argc > 1)
  {
    if (strcmp ("-s", argv[1]) == 0)
      defaultsetup = 0;
    else
      pp = getpwnam (argv[1]);

    if (argc > 2)
      pp = getpwnam (argv[2]);

  }

  if (pp == NULL)
  {
    printf ("User unknown\n");
    exit (1);
  }

  if (strcmp (pp->pw_name, me) != 0)
  {
    if (getuid () != 0)
    {
      /* Only root can change other's passwds */
      printf ("Permission denied.\n");
      exit (1);
    }
  }

#ifdef SHADOW
  if ((sp = getspnam (pp->pw_name)) == NULL) {
     fprintf(stderr, "Who are you?\n");
     exit(1);
  }
#endif /* SHADOW */

#ifdef SHADOW
  salt = sp->sp_pwdp;
#else
  salt = pp->pw_passwd;
#endif
#ifdef PRIO_PROCESS
  setpriority (PRIO_PROCESS, 0, -4);
#endif

  if (getuid () != 0) {
#ifdef PRIO_PROCESS
     setpriority (PRIO_PROCESS, 0, -4);
#endif

     pw = getpass ("Password:");
     p  = crypt (pw, salt);
#ifdef PRIO_PROCESS
     setpriority(PRIO_PROCESS, 0, 0);
#endif

#ifdef SHADOW
     if (pp && strcmp(p, sp->sp_pwdp)) {
#else
     if (pp && strcmp(p, pp->pw_passwd)) {
#endif
        printf ("Password incorrect.\n");
        exit (-1);
     }
  }

  rval = skeylookup (&skey, pp->pw_name);
  switch (rval)
  {
  case -1:
    perror ("Error opening database: ");
    exit (1);
  case 0:
    printf ("[Updating %s]\n", pp->pw_name);
    printf ("Old key: %s\n", skey.seed);

    /* lets be nice if they have a skey.seed that ends in 0-8 just add one */
    l = strlen (skey.seed);
    if (l > 0)
    {
      lastc = skey.seed[l - 1];
      if (isdigit (lastc) && lastc != '9')
      {
	strcpy (defaultseed, skey.seed);
	defaultseed[l - 1] = lastc + 1;
      }
      if (isdigit (lastc) && lastc == '9' && l < 16)
      {
	strcpy (defaultseed, skey.seed);
	defaultseed[l - 1] = '0';
	defaultseed[l] = '0';
	defaultseed[l + 1] = '\0';
      }
    }
    break;
  case 1:
    printf ("[Adding %s]\n", pp->pw_name);
    break;
  }
  n = 99;

  if (!defaultsetup)
  {
    printf ("You need the 6 english words generated from the \"key\" command.\n");
    for (i = 0 ;; i++)
    {
      if (i >= 2)
	exit (1);
      printf ("Enter sequence count from 1 to 10000: ");
      fgets (tmp, sizeof (tmp), stdin);
      n = atoi (tmp);
      if (n > 0 && n < 10000)
	break;		/* Valid range */
      printf ("\n Error: Count must be > 0 and < 10000\n");
    }
  }

  if (!defaultsetup)
  {
    printf ("Enter new key [default %s]: ", defaultseed);
    fflush (stdout);
    fgets (seed, sizeof (seed), stdin);
    rip (seed);
    if (strlen (seed) > 16)
    {
      printf ("Notice: Seed truncated to 16 characters.\n");
      seed[16] = '\0';
    }

    if (seed[0] == '\0')
      strcpy (seed, defaultseed);

    for (i = 0 ;; i++)
    {
      if (i >= 2)
	exit (1);

      printf ("s/key %d %s\ns/key access password: ", n, seed);
      fgets (tmp, sizeof (tmp), stdin);
      rip (tmp);
      backspace (tmp);

      if (tmp[0] == '?')
      {
	printf ("Enter 6 English words from secure S/Key calculation.\n");
	continue;
      }

      if (tmp[0] == '\0')
      {
	exit (1);
      }
      if (etob (key, tmp) == 1 || atob8 (key, tmp) == 0)
	break;			/* Valid format */
      printf ("Invalid format - try again with 6 English words.\n");
    }
  }
  else
  {
    /* Get user's secret password */
    for (i = 0 ;; i++)
    {

      if (i >= 2)
	exit (1);

      printf ("Enter secret password: ");
      readpass (passwd, sizeof (passwd));

      if (passwd[0] == '\0')
	exit (1);

      printf ("Again secret password: ");
      readpass (passwd2, sizeof (passwd));

      if (passwd2[0] == '\0')
	exit (1);

      if (strlen (passwd) < 4 && strlen (passwd2) < 4)
      {
	fprintf (stderr, "Error: Your password must be longer.\n\r");
	exit (1);
      }

      if (strcmp (passwd, passwd2) == 0)
	break;

      printf ("Error: Passwords dont match.\n");
    }
    strcpy (seed, defaultseed);

    /* Crunch seed and password into starting key */
    if (keycrunch (key, seed, passwd) != 0)
    {
      fprintf (stderr, "%s: key crunch failed.\n", argv[0]);
      exit (2);
    }
    nn = n;
    while (nn-- != 0)
      f (key);
  }
  time (&now);
  tm = localtime (&now);
  strftime (tbuf, sizeof (tbuf), " %b %d,%Y %T", tm);

  skey.val = malloc (16 + 1);

  btoa8 (skey.val, key);

  fprintf (skey.keyfile, "%s %04d %-16s %s %-21s\n", pp->pw_name, n,
	   seed, skey.val, tbuf);
  fclose (skey.keyfile);
  printf ("\nID %s s/key is %d %s\n", pp->pw_name, n, seed);
  printf ("Next login password: %s\n", btoe (buf, key));
#ifdef HEXIN
  printf ("%s\n", put8 (buf, key));
#endif

  exit (1);
}

#ifdef __svr4__
int gethostname (name, len)
char *name;
int len;
{ 
   int  namelen = 128;
 
   if (sysinfo (SI_HOSTNAME, name, len) <0)  {
        perror("hostname");
        return -1;
   }
   return 0;
}
#endif
