122347Spst/* opiekey.c: Stand-alone program for computing responses to OTP challenges.
222347Spst
322347Spst Takes a sequence number and seed (presumably from an OPIE challenge)
422347Spst as command line arguments, prompts for the user's secret pass phrase,
522347Spst and outputs a response.
622347Spst
729964Sache%%% portions-copyright-cmetz-96
892914SmarkmPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights
922347SpstReserved. The Inner Net License Version 2 applies to these portions of
1022347Spstthe software.
1122347SpstYou should have received a copy of the license with this software. If
1222347Spstyou didn't get a copy, you may request one from <license@inner.net>.
1322347Spst
1422347SpstPortions of this software are Copyright 1995 by Randall Atkinson and Dan
1522347SpstMcDonald, All Rights Reserved. All Rights under this copyright are assigned
1622347Spstto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
1722347SpstLicense Agreement applies to this software.
1822347Spst
1922347Spst	History:
2022347Spst
2192914Smarkm	Modified by cmetz for OPIE 2.4. Use struct opie_key for key blocks.
2229964Sache	Modified by cmetz for OPIE 2.31. Renamed "init" and RESPONSE_INIT
2329964Sache	        to "init-hex" and RESPONSE_INIT_HEX. Removed active attack
2429964Sache		protection support.
2522347Spst	Modified by cmetz for OPIE 2.3. OPIE_PASS_MAX changed to
2622347Spst		OPIE_SECRET_MAX. Added extended responses, which created
2722347Spst		lots of changes. Eliminated extra variable. Added -x and
2822347Spst		-t to help. Added -f flag. Added SHA support.
2922347Spst	Modified by cmetz for OPIE 2.22. Print newline after seed too long
3022347Spst	        message. Check for minimum seed length. Correct a grammar
3122347Spst		error.
3222347Spst        Modified at NRL for OPIE 2.2. Check opiereadpass() return.
3322347Spst                Change opiereadpass() calls to add echo arg. Use FUNCTION
3422347Spst                definition et al. Check seed length here, too. Added back
3522347Spst		hex output. Reworked final output function.
3622347Spst	Modified at NRL for OPIE 2.0.
3722347Spst	Written at Bellcore for the S/Key Version 1 software distribution
3822347Spst		(skey.c).
3981596Sache
4081596Sache$FreeBSD$
4181596Sache
4222347Spst*/
4322347Spst#include "opie_cfg.h"
4422347Spst
4522347Spst#include <stdio.h>
4622347Spst#include <string.h>
4722347Spst#include <stdlib.h>
48257264Ssbruno#include <unistd.h>
4922347Spst
5022347Spst#include "opie.h"
5122347Spst
5222347Spst#ifdef	__MSDOS__
5322347Spst#include <dos.h>
5422347Spst#endif
5522347Spst
5622347Spst#if HAVE_FCNTL_H
5722347Spst#include <fcntl.h>
5822347Spst#endif /* HAVE_FCNTL_H */
5922347Spst
6022347Spstextern char *optarg;
6122347Spstextern int optind, opterr;
6222347Spst
6322347Spstint aflag = 0;
6422347Spst
6522347Spstchar *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
6622347Spstchar *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
6722347Spst
6822347Spst/******** Begin real source code ***************/
6922347Spst
7022347Spststatic VOIDRET usage FUNCTION((s), char *s)
7122347Spst{
7222347Spst  fprintf(stderr, "usage: %s [-v] [-h] [-f] [-x] [-t type] [-4 | -5 | -s] [-a] [-n count] sequence_number seed\n", s);
7322347Spst  exit(1);
7422347Spst}
7522347Spst
7622347Spst#define RESPONSE_STANDARD  0
7722347Spst#define RESPONSE_WORD      1
7822347Spst#define RESPONSE_HEX       2
7929964Sache#define RESPONSE_INIT_HEX  3
8022347Spst#define RESPONSE_INIT_WORD 4
8122347Spst#define RESPONSE_UNKNOWN   5
8222347Spst
8322347Spststruct _rtrans {
8422347Spst  int type;
8522347Spst  char *name;
8622347Spst};
8722347Spst
8822347Spststatic struct _rtrans rtrans[] = {
8922347Spst  { RESPONSE_WORD, "word" },
9022347Spst  { RESPONSE_HEX, "hex" },
9129964Sache  { RESPONSE_INIT_HEX, "init-hex" },
9222347Spst  { RESPONSE_INIT_WORD, "init-word" },
9322347Spst  { RESPONSE_STANDARD, "" },
9422347Spst  { RESPONSE_STANDARD, "standard" },
9522347Spst  { RESPONSE_STANDARD, "otp" },
9622347Spst  { RESPONSE_UNKNOWN, NULL }
9722347Spst};
9822347Spst
9922347Spststatic void getsecret FUNCTION((secret, promptextra, retype), char *secret AND char *promptextra AND int flags)
10022347Spst{
10122347Spst  fprintf(stderr, "Enter %ssecret pass phrase: ", promptextra);
10222347Spst  if (!opiereadpass(secret, OPIE_SECRET_MAX, 0)) {
10322347Spst    fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra);
10422347Spst    exit(1);
10522347Spst  }
10622347Spst  if (secret[0] && (flags & 1)) {
10722347Spst    char verify[OPIE_SECRET_MAX + 1];
10822347Spst
10922347Spst    fprintf(stderr, "Again %ssecret pass phrase: ", promptextra);
11022347Spst    if (!opiereadpass(verify, OPIE_SECRET_MAX, 0)) {
11122347Spst      fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra);
11222347Spst      memset(verify, 0, sizeof(verify));
113246873Sdim      memset(secret, 0, OPIE_SECRET_MAX + 1);
11422347Spst      exit(1);
11522347Spst    }
11622347Spst    if (verify[0] && strcmp(verify, secret)) {
11722347Spst      fprintf(stderr, "They don't match. Try again.\n");
11822347Spst      memset(verify, 0, sizeof(verify));
119246873Sdim      memset(secret, 0, OPIE_SECRET_MAX + 1);
12022347Spst      exit(1);
12122347Spst    }
12222347Spst    memset(verify, 0, sizeof(verify));
12322347Spst  }
12489135Sjoerg  if (!(flags & 2) && !aflag && opiepasscheck(secret)) {
125246873Sdim    memset(secret, 0, OPIE_SECRET_MAX + 1);
12622347Spst    fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
12722347Spst    exit(1);
12822347Spst  };
12922347Spst}
13022347Spst
13122347Spstint main FUNCTION((argc, argv), int argc AND char *argv[])
13222347Spst{
13322347Spst  /* variable declarations */
13422347Spst  unsigned algorithm = MDX;	/* default algorithm per Makefile's MDX
13522347Spst				   symbol */
13622347Spst  int keynum = 0;
13722347Spst  int i;
13822347Spst  int count = 1;
13922347Spst  char secret[OPIE_SECRET_MAX + 1], newsecret[OPIE_SECRET_MAX + 1];
14092914Smarkm  struct opie_otpkey key, newkey;
14122347Spst  char *seed, newseed[OPIE_SEED_MAX + 1];
14222347Spst  char response[OPIE_RESPONSE_MAX + 1];
14322347Spst  char *slash;
14422347Spst  int hex = 0;
14522347Spst  int type = RESPONSE_STANDARD;
14681596Sache  int force = 0;
14722347Spst
148269811Sache  if (slash = strrchr(argv[0], '/'))
14922347Spst    slash++;
15022347Spst  else
15122347Spst    slash = argv[0];
15222347Spst
15322347Spst  if (!strcmp(slash, "key") || strstr(slash, "md4"))
15422347Spst    algorithm = 4;
15522347Spst
15622347Spst  if (strstr(slash, "md5"))
15722347Spst    algorithm = 5;
15822347Spst
15922347Spst  if (strstr(slash, "sha"))
16022347Spst    algorithm = 3;
16122347Spst
16222347Spst  while ((i = getopt(argc, argv, "fhvn:x45at:s")) != EOF) {
16322347Spst    switch (i) {
16422347Spst    case 'v':
16522347Spst      opieversion();
16622347Spst
16722347Spst    case 'n':
16822347Spst      count = atoi(optarg);
16922347Spst      break;
17022347Spst
17122347Spst    case 'x':
17222347Spst      hex = 1;
17322347Spst      break;
17422347Spst
17522347Spst    case 'f':
17622347Spst#if INSECURE_OVERRIDE
17722347Spst      force = 1;
17822347Spst#else /* INSECURE_OVERRIDE */
17922347Spst      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
18022347Spst#endif /* INSECURE_OVERRIDE */
18122347Spst      break;
18222347Spst
18322347Spst    case '4':
18422347Spst      /* use MD4 algorithm */
18522347Spst      algorithm = 4;
18622347Spst      break;
18722347Spst
18822347Spst    case '5':
18922347Spst      /* use MD5 algorithm */
19022347Spst      algorithm = 5;
19122347Spst      break;
19222347Spst
19322347Spst    case 'a':
19422347Spst      aflag = 1;
19522347Spst      break;
19622347Spst
19722347Spst    case 't':
19822347Spst      {
19922347Spst	struct _rtrans *r;
20022347Spst	for (r = rtrans; r->name && strcmp(r->name, optarg); r++);
20122347Spst	if (!r->name) {
20222347Spst	  fprintf(stderr, "%s: %s: unknown response type.\n", argv[0], optarg);
20322347Spst	  exit(1);
20422347Spst	}
20522347Spst	type = r->type;
20622347Spst      }
20722347Spst      break;
20822347Spst
20922347Spst    case 's':
21022347Spst      algorithm = 3;
21122347Spst      break;
21222347Spst
21322347Spst    default:
21422347Spst      usage(argv[0]);
21522347Spst    }
21622347Spst  }
21722347Spst
21822347Spst  if ((argc - optind) < 2)
21922347Spst    usage(argv[0]);
22022347Spst
22122347Spst  fprintf(stderr, "Using the %s algorithm to compute response.\n", algnames[algorithm]);
22222347Spst
22322347Spst  /* get sequence number, which is next-to-last parameter */
22422347Spst  keynum = atoi(argv[optind]);
22522347Spst  if (keynum < 1) {
22622347Spst    fprintf(stderr, "Sequence number %s is not positive.\n", argv[optind]);
22722347Spst    exit(1);
22822347Spst  }
22922347Spst  /* get seed string, which is last parameter */
23022347Spst  seed = argv[optind + 1];
23122347Spst  {
23222347Spst    i = strlen(seed);
23322347Spst
23422347Spst    if (i > OPIE_SEED_MAX) {
23522347Spst      fprintf(stderr, "Seeds must be less than %d characters long.\n", OPIE_SEED_MAX);
23622347Spst      exit(1);
23722347Spst    }
23822347Spst    if (i < OPIE_SEED_MIN) {
23922347Spst      fprintf(stderr, "Seeds must be greater than %d characters long.\n", OPIE_SEED_MIN);
24022347Spst      exit(1);
24122347Spst    }
24222347Spst  }
24322347Spst
24422347Spst  fprintf(stderr, "Reminder: Don't use opiekey from telnet or dial-in sessions.\n");
24522347Spst
24622347Spst  if (opieinsecure()) {
24722347Spst    fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
24822347Spst#if INSECURE_OVERRIDE
24922347Spst    if (force)
25022347Spst      fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
25122347Spst    else
25222347Spst#endif /* INSECURE_OVERRIDE */
25322347Spst      exit(1);
25422347Spst  }
25522347Spst
25629964Sache  if ((type == RESPONSE_INIT_HEX) || (type == RESPONSE_INIT_WORD)) {
25722347Spst#if RETYPE
25822347Spst    getsecret(secret, "old ", 1);
25922347Spst#else /* RETYPE */
26022347Spst    getsecret(secret, "old ", 0);
26122347Spst#endif /* RETYPE */
26222347Spst    getsecret(newsecret, "new ", 1);
26322347Spst    if (!newsecret[0])
26422347Spst      strcpy(newsecret, secret);
26522347Spst
26622347Spst    if (opienewseed(strcpy(newseed, seed)) < 0) {
26722347Spst      fprintf(stderr, "Error updating seed.\n");
26822347Spst      goto error;
26922347Spst    }
27022347Spst
27192914Smarkm    if (opiekeycrunch(algorithm, &newkey, newseed, newsecret)) {
27222347Spst      fprintf(stderr, "%s: key crunch failed (1)\n", argv[0]);
27322347Spst      goto error;
27422347Spst    }
27522347Spst
27622347Spst    for (i = 0; i < 499; i++)
27792914Smarkm      opiehash(&newkey, algorithm);
27822347Spst  } else
27922347Spst#if RETYPE
28022347Spst    getsecret(secret, "", 1);
28122347Spst#else /* RETYPE */
28222347Spst    getsecret(secret, "", 0);
28322347Spst#endif /* RETYPE */
28422347Spst
28522347Spst  /* Crunch seed and secret password into starting key normally */
28692914Smarkm  if (opiekeycrunch(algorithm, &key, seed, secret)) {
28722347Spst    fprintf(stderr, "%s: key crunch failed\n", argv[0]);
28822347Spst    goto error;
28922347Spst  }
29022347Spst
29122347Spst  for (i = 0; i <= (keynum - count); i++)
29292914Smarkm    opiehash(&key, algorithm);
29322347Spst
29422347Spst  {
29522347Spst    char buf[OPIE_SEED_MAX + 48 + 1];
29622347Spst    char *c;
29722347Spst
29822347Spst    for (; i <= keynum; i++) {
29922347Spst      if (count > 1)
30022347Spst	printf("%d: %s", i, (type == RESPONSE_STANDARD) ? "" : "\n");
30122347Spst
30222347Spst      switch(type) {
30322347Spst      case RESPONSE_STANDARD:
30422347Spst	if (hex)
30592914Smarkm	  opiebtoh(response, &key);
30622347Spst	else
30792914Smarkm	  opiebtoe(response, &key);
30822347Spst	break;
30922347Spst      case RESPONSE_WORD:
31022347Spst	strcpy(response, "word:");
31192914Smarkm	strcat(response, opiebtoe(buf, &key));
31222347Spst	break;
31322347Spst      case RESPONSE_HEX:
31422347Spst	strcpy(response, "hex:");
31592914Smarkm	strcat(response, opiebtoh(buf, &key));
31622347Spst	break;
31729964Sache      case RESPONSE_INIT_HEX:
31822347Spst      case RESPONSE_INIT_WORD:
31929964Sache	if (type == RESPONSE_INIT_HEX) {
32059118Skris	  strcpy(response, "init-hex:");
32192914Smarkm	  strcat(response, opiebtoh(buf, &key));
32222347Spst	  sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed);
32322347Spst	  strcat(response, buf);
32492914Smarkm	  strcat(response, opiebtoh(buf, &newkey));
32522347Spst	} else {
32622347Spst	  strcpy(response, "init-word:");
32792914Smarkm	  strcat(response, opiebtoe(buf, &key));
32822347Spst	  sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed);
32922347Spst	  strcat(response, buf);
33092914Smarkm	  strcat(response, opiebtoe(buf, &newkey));
33122347Spst	}
33222347Spst	break;
33322347Spst      }
33422347Spst      puts(response);
33592914Smarkm      opiehash(&key, algorithm);
33622347Spst    }
33722347Spst  }
33822347Spst
33922347Spst  memset(secret, 0, sizeof(secret));
34022347Spst  memset(newsecret, 0, sizeof(newsecret));
34122347Spst  return 0;
34222347Spst
34322347Spsterror:
34422347Spst  memset(secret, 0, sizeof(secret));
34522347Spst  memset(newsecret, 0, sizeof(newsecret));
34622347Spst  return 1;
34722347Spst}
348