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>
4822347Spst
4922347Spst#include "opie.h"
5022347Spst
5122347Spst#ifdef	__MSDOS__
5222347Spst#include <dos.h>
5322347Spst#endif
5422347Spst
5522347Spst#if HAVE_FCNTL_H
5622347Spst#include <fcntl.h>
5722347Spst#endif /* HAVE_FCNTL_H */
5822347Spst
5922347Spstextern char *optarg;
6022347Spstextern int optind, opterr;
6122347Spst
6222347Spstint aflag = 0;
6322347Spst
6422347Spstchar *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
6522347Spstchar *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
6622347Spst
6722347Spst/******** Begin real source code ***************/
6822347Spst
6922347Spststatic VOIDRET usage FUNCTION((s), char *s)
7022347Spst{
7122347Spst  fprintf(stderr, "usage: %s [-v] [-h] [-f] [-x] [-t type] [-4 | -5 | -s] [-a] [-n count] sequence_number seed\n", s);
7222347Spst  exit(1);
7322347Spst}
7422347Spst
7522347Spst#define RESPONSE_STANDARD  0
7622347Spst#define RESPONSE_WORD      1
7722347Spst#define RESPONSE_HEX       2
7829964Sache#define RESPONSE_INIT_HEX  3
7922347Spst#define RESPONSE_INIT_WORD 4
8022347Spst#define RESPONSE_UNKNOWN   5
8122347Spst
8222347Spststruct _rtrans {
8322347Spst  int type;
8422347Spst  char *name;
8522347Spst};
8622347Spst
8722347Spststatic struct _rtrans rtrans[] = {
8822347Spst  { RESPONSE_WORD, "word" },
8922347Spst  { RESPONSE_HEX, "hex" },
9029964Sache  { RESPONSE_INIT_HEX, "init-hex" },
9122347Spst  { RESPONSE_INIT_WORD, "init-word" },
9222347Spst  { RESPONSE_STANDARD, "" },
9322347Spst  { RESPONSE_STANDARD, "standard" },
9422347Spst  { RESPONSE_STANDARD, "otp" },
9522347Spst  { RESPONSE_UNKNOWN, NULL }
9622347Spst};
9722347Spst
9822347Spststatic void getsecret FUNCTION((secret, promptextra, retype), char *secret AND char *promptextra AND int flags)
9922347Spst{
10022347Spst  fprintf(stderr, "Enter %ssecret pass phrase: ", promptextra);
10122347Spst  if (!opiereadpass(secret, OPIE_SECRET_MAX, 0)) {
10222347Spst    fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra);
10322347Spst    exit(1);
10422347Spst  }
10522347Spst  if (secret[0] && (flags & 1)) {
10622347Spst    char verify[OPIE_SECRET_MAX + 1];
10722347Spst
10822347Spst    fprintf(stderr, "Again %ssecret pass phrase: ", promptextra);
10922347Spst    if (!opiereadpass(verify, OPIE_SECRET_MAX, 0)) {
11022347Spst      fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra);
11122347Spst      memset(verify, 0, sizeof(verify));
112246980Sdim      memset(secret, 0, OPIE_SECRET_MAX + 1);
11322347Spst      exit(1);
11422347Spst    }
11522347Spst    if (verify[0] && strcmp(verify, secret)) {
11622347Spst      fprintf(stderr, "They don't match. Try again.\n");
11722347Spst      memset(verify, 0, sizeof(verify));
118246980Sdim      memset(secret, 0, OPIE_SECRET_MAX + 1);
11922347Spst      exit(1);
12022347Spst    }
12122347Spst    memset(verify, 0, sizeof(verify));
12222347Spst  }
12389135Sjoerg  if (!(flags & 2) && !aflag && opiepasscheck(secret)) {
124246980Sdim    memset(secret, 0, OPIE_SECRET_MAX + 1);
12522347Spst    fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
12622347Spst    exit(1);
12722347Spst  };
12822347Spst}
12922347Spst
13022347Spstint main FUNCTION((argc, argv), int argc AND char *argv[])
13122347Spst{
13222347Spst  /* variable declarations */
13322347Spst  unsigned algorithm = MDX;	/* default algorithm per Makefile's MDX
13422347Spst				   symbol */
13522347Spst  int keynum = 0;
13622347Spst  int i;
13722347Spst  int count = 1;
13822347Spst  char secret[OPIE_SECRET_MAX + 1], newsecret[OPIE_SECRET_MAX + 1];
13992914Smarkm  struct opie_otpkey key, newkey;
14022347Spst  char *seed, newseed[OPIE_SEED_MAX + 1];
14122347Spst  char response[OPIE_RESPONSE_MAX + 1];
14222347Spst  char *slash;
14322347Spst  int hex = 0;
14422347Spst  int type = RESPONSE_STANDARD;
14581596Sache  int force = 0;
14622347Spst
14722347Spst  if (slash = strchr(argv[0], '/'))
14822347Spst    slash++;
14922347Spst  else
15022347Spst    slash = argv[0];
15122347Spst
15222347Spst  if (!strcmp(slash, "key") || strstr(slash, "md4"))
15322347Spst    algorithm = 4;
15422347Spst
15522347Spst  if (strstr(slash, "md5"))
15622347Spst    algorithm = 5;
15722347Spst
15822347Spst  if (strstr(slash, "sha"))
15922347Spst    algorithm = 3;
16022347Spst
16122347Spst  while ((i = getopt(argc, argv, "fhvn:x45at:s")) != EOF) {
16222347Spst    switch (i) {
16322347Spst    case 'v':
16422347Spst      opieversion();
16522347Spst
16622347Spst    case 'n':
16722347Spst      count = atoi(optarg);
16822347Spst      break;
16922347Spst
17022347Spst    case 'x':
17122347Spst      hex = 1;
17222347Spst      break;
17322347Spst
17422347Spst    case 'f':
17522347Spst#if INSECURE_OVERRIDE
17622347Spst      force = 1;
17722347Spst#else /* INSECURE_OVERRIDE */
17822347Spst      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
17922347Spst#endif /* INSECURE_OVERRIDE */
18022347Spst      break;
18122347Spst
18222347Spst    case '4':
18322347Spst      /* use MD4 algorithm */
18422347Spst      algorithm = 4;
18522347Spst      break;
18622347Spst
18722347Spst    case '5':
18822347Spst      /* use MD5 algorithm */
18922347Spst      algorithm = 5;
19022347Spst      break;
19122347Spst
19222347Spst    case 'a':
19322347Spst      aflag = 1;
19422347Spst      break;
19522347Spst
19622347Spst    case 't':
19722347Spst      {
19822347Spst	struct _rtrans *r;
19922347Spst	for (r = rtrans; r->name && strcmp(r->name, optarg); r++);
20022347Spst	if (!r->name) {
20122347Spst	  fprintf(stderr, "%s: %s: unknown response type.\n", argv[0], optarg);
20222347Spst	  exit(1);
20322347Spst	}
20422347Spst	type = r->type;
20522347Spst      }
20622347Spst      break;
20722347Spst
20822347Spst    case 's':
20922347Spst      algorithm = 3;
21022347Spst      break;
21122347Spst
21222347Spst    default:
21322347Spst      usage(argv[0]);
21422347Spst    }
21522347Spst  }
21622347Spst
21722347Spst  if ((argc - optind) < 2)
21822347Spst    usage(argv[0]);
21922347Spst
22022347Spst  fprintf(stderr, "Using the %s algorithm to compute response.\n", algnames[algorithm]);
22122347Spst
22222347Spst  /* get sequence number, which is next-to-last parameter */
22322347Spst  keynum = atoi(argv[optind]);
22422347Spst  if (keynum < 1) {
22522347Spst    fprintf(stderr, "Sequence number %s is not positive.\n", argv[optind]);
22622347Spst    exit(1);
22722347Spst  }
22822347Spst  /* get seed string, which is last parameter */
22922347Spst  seed = argv[optind + 1];
23022347Spst  {
23122347Spst    i = strlen(seed);
23222347Spst
23322347Spst    if (i > OPIE_SEED_MAX) {
23422347Spst      fprintf(stderr, "Seeds must be less than %d characters long.\n", OPIE_SEED_MAX);
23522347Spst      exit(1);
23622347Spst    }
23722347Spst    if (i < OPIE_SEED_MIN) {
23822347Spst      fprintf(stderr, "Seeds must be greater than %d characters long.\n", OPIE_SEED_MIN);
23922347Spst      exit(1);
24022347Spst    }
24122347Spst  }
24222347Spst
24322347Spst  fprintf(stderr, "Reminder: Don't use opiekey from telnet or dial-in sessions.\n");
24422347Spst
24522347Spst  if (opieinsecure()) {
24622347Spst    fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
24722347Spst#if INSECURE_OVERRIDE
24822347Spst    if (force)
24922347Spst      fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
25022347Spst    else
25122347Spst#endif /* INSECURE_OVERRIDE */
25222347Spst      exit(1);
25322347Spst  }
25422347Spst
25529964Sache  if ((type == RESPONSE_INIT_HEX) || (type == RESPONSE_INIT_WORD)) {
25622347Spst#if RETYPE
25722347Spst    getsecret(secret, "old ", 1);
25822347Spst#else /* RETYPE */
25922347Spst    getsecret(secret, "old ", 0);
26022347Spst#endif /* RETYPE */
26122347Spst    getsecret(newsecret, "new ", 1);
26222347Spst    if (!newsecret[0])
26322347Spst      strcpy(newsecret, secret);
26422347Spst
26522347Spst    if (opienewseed(strcpy(newseed, seed)) < 0) {
26622347Spst      fprintf(stderr, "Error updating seed.\n");
26722347Spst      goto error;
26822347Spst    }
26922347Spst
27092914Smarkm    if (opiekeycrunch(algorithm, &newkey, newseed, newsecret)) {
27122347Spst      fprintf(stderr, "%s: key crunch failed (1)\n", argv[0]);
27222347Spst      goto error;
27322347Spst    }
27422347Spst
27522347Spst    for (i = 0; i < 499; i++)
27692914Smarkm      opiehash(&newkey, algorithm);
27722347Spst  } else
27822347Spst#if RETYPE
27922347Spst    getsecret(secret, "", 1);
28022347Spst#else /* RETYPE */
28122347Spst    getsecret(secret, "", 0);
28222347Spst#endif /* RETYPE */
28322347Spst
28422347Spst  /* Crunch seed and secret password into starting key normally */
28592914Smarkm  if (opiekeycrunch(algorithm, &key, seed, secret)) {
28622347Spst    fprintf(stderr, "%s: key crunch failed\n", argv[0]);
28722347Spst    goto error;
28822347Spst  }
28922347Spst
29022347Spst  for (i = 0; i <= (keynum - count); i++)
29192914Smarkm    opiehash(&key, algorithm);
29222347Spst
29322347Spst  {
29422347Spst    char buf[OPIE_SEED_MAX + 48 + 1];
29522347Spst    char *c;
29622347Spst
29722347Spst    for (; i <= keynum; i++) {
29822347Spst      if (count > 1)
29922347Spst	printf("%d: %s", i, (type == RESPONSE_STANDARD) ? "" : "\n");
30022347Spst
30122347Spst      switch(type) {
30222347Spst      case RESPONSE_STANDARD:
30322347Spst	if (hex)
30492914Smarkm	  opiebtoh(response, &key);
30522347Spst	else
30692914Smarkm	  opiebtoe(response, &key);
30722347Spst	break;
30822347Spst      case RESPONSE_WORD:
30922347Spst	strcpy(response, "word:");
31092914Smarkm	strcat(response, opiebtoe(buf, &key));
31122347Spst	break;
31222347Spst      case RESPONSE_HEX:
31322347Spst	strcpy(response, "hex:");
31492914Smarkm	strcat(response, opiebtoh(buf, &key));
31522347Spst	break;
31629964Sache      case RESPONSE_INIT_HEX:
31722347Spst      case RESPONSE_INIT_WORD:
31829964Sache	if (type == RESPONSE_INIT_HEX) {
31959118Skris	  strcpy(response, "init-hex:");
32092914Smarkm	  strcat(response, opiebtoh(buf, &key));
32122347Spst	  sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed);
32222347Spst	  strcat(response, buf);
32392914Smarkm	  strcat(response, opiebtoh(buf, &newkey));
32422347Spst	} else {
32522347Spst	  strcpy(response, "init-word:");
32692914Smarkm	  strcat(response, opiebtoe(buf, &key));
32722347Spst	  sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed);
32822347Spst	  strcat(response, buf);
32992914Smarkm	  strcat(response, opiebtoe(buf, &newkey));
33022347Spst	}
33122347Spst	break;
33222347Spst      }
33322347Spst      puts(response);
33492914Smarkm      opiehash(&key, algorithm);
33522347Spst    }
33622347Spst  }
33722347Spst
33822347Spst  memset(secret, 0, sizeof(secret));
33922347Spst  memset(newsecret, 0, sizeof(newsecret));
34022347Spst  return 0;
34122347Spst
34222347Spsterror:
34322347Spst  memset(secret, 0, sizeof(secret));
34422347Spst  memset(newsecret, 0, sizeof(newsecret));
34522347Spst  return 1;
34622347Spst}
347