1/* opiekey.c: Stand-alone program for computing responses to OTP challenges.
2
3 Takes a sequence number and seed (presumably from an OPIE challenge)
4 as command line arguments, prompts for the user's secret pass phrase,
5 and outputs a response.
6
7%%% portions-copyright-cmetz-96
8Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
9Reserved. The Inner Net License Version 2 applies to these portions of
10the software.
11You should have received a copy of the license with this software. If
12you didn't get a copy, you may request one from <license@inner.net>.
13
14Portions of this software are Copyright 1995 by Randall Atkinson and Dan
15McDonald, All Rights Reserved. All Rights under this copyright are assigned
16to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
17License Agreement applies to this software.
18
19	History:
20
21	Modified by cmetz for OPIE 2.4. Use struct opie_key for key blocks.
22	Modified by cmetz for OPIE 2.31. Renamed "init" and RESPONSE_INIT
23	        to "init-hex" and RESPONSE_INIT_HEX. Removed active attack
24		protection support.
25	Modified by cmetz for OPIE 2.3. OPIE_PASS_MAX changed to
26		OPIE_SECRET_MAX. Added extended responses, which created
27		lots of changes. Eliminated extra variable. Added -x and
28		-t to help. Added -f flag. Added SHA support.
29	Modified by cmetz for OPIE 2.22. Print newline after seed too long
30	        message. Check for minimum seed length. Correct a grammar
31		error.
32        Modified at NRL for OPIE 2.2. Check opiereadpass() return.
33                Change opiereadpass() calls to add echo arg. Use FUNCTION
34                definition et al. Check seed length here, too. Added back
35		hex output. Reworked final output function.
36	Modified at NRL for OPIE 2.0.
37	Written at Bellcore for the S/Key Version 1 software distribution
38		(skey.c).
39
40$FreeBSD$
41
42*/
43#include "opie_cfg.h"
44
45#include <stdio.h>
46#include <string.h>
47#include <stdlib.h>
48
49#include "opie.h"
50
51#ifdef	__MSDOS__
52#include <dos.h>
53#endif
54
55#if HAVE_FCNTL_H
56#include <fcntl.h>
57#endif /* HAVE_FCNTL_H */
58
59extern char *optarg;
60extern int optind, opterr;
61
62int aflag = 0;
63
64char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
65char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
66
67/******** Begin real source code ***************/
68
69static VOIDRET usage FUNCTION((s), char *s)
70{
71  fprintf(stderr, "usage: %s [-v] [-h] [-f] [-x] [-t type] [-4 | -5 | -s] [-a] [-n count] sequence_number seed\n", s);
72  exit(1);
73}
74
75#define RESPONSE_STANDARD  0
76#define RESPONSE_WORD      1
77#define RESPONSE_HEX       2
78#define RESPONSE_INIT_HEX  3
79#define RESPONSE_INIT_WORD 4
80#define RESPONSE_UNKNOWN   5
81
82struct _rtrans {
83  int type;
84  char *name;
85};
86
87static struct _rtrans rtrans[] = {
88  { RESPONSE_WORD, "word" },
89  { RESPONSE_HEX, "hex" },
90  { RESPONSE_INIT_HEX, "init-hex" },
91  { RESPONSE_INIT_WORD, "init-word" },
92  { RESPONSE_STANDARD, "" },
93  { RESPONSE_STANDARD, "standard" },
94  { RESPONSE_STANDARD, "otp" },
95  { RESPONSE_UNKNOWN, NULL }
96};
97
98static void getsecret FUNCTION((secret, promptextra, retype), char *secret AND char *promptextra AND int flags)
99{
100  fprintf(stderr, "Enter %ssecret pass phrase: ", promptextra);
101  if (!opiereadpass(secret, OPIE_SECRET_MAX, 0)) {
102    fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra);
103    exit(1);
104  }
105  if (secret[0] && (flags & 1)) {
106    char verify[OPIE_SECRET_MAX + 1];
107
108    fprintf(stderr, "Again %ssecret pass phrase: ", promptextra);
109    if (!opiereadpass(verify, OPIE_SECRET_MAX, 0)) {
110      fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra);
111      memset(verify, 0, sizeof(verify));
112      memset(secret, 0, OPIE_SECRET_MAX + 1);
113      exit(1);
114    }
115    if (verify[0] && strcmp(verify, secret)) {
116      fprintf(stderr, "They don't match. Try again.\n");
117      memset(verify, 0, sizeof(verify));
118      memset(secret, 0, OPIE_SECRET_MAX + 1);
119      exit(1);
120    }
121    memset(verify, 0, sizeof(verify));
122  }
123  if (!(flags & 2) && !aflag && opiepasscheck(secret)) {
124    memset(secret, 0, OPIE_SECRET_MAX + 1);
125    fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
126    exit(1);
127  };
128}
129
130int main FUNCTION((argc, argv), int argc AND char *argv[])
131{
132  /* variable declarations */
133  unsigned algorithm = MDX;	/* default algorithm per Makefile's MDX
134				   symbol */
135  int keynum = 0;
136  int i;
137  int count = 1;
138  char secret[OPIE_SECRET_MAX + 1], newsecret[OPIE_SECRET_MAX + 1];
139  struct opie_otpkey key, newkey;
140  char *seed, newseed[OPIE_SEED_MAX + 1];
141  char response[OPIE_RESPONSE_MAX + 1];
142  char *slash;
143  int hex = 0;
144  int type = RESPONSE_STANDARD;
145  int force = 0;
146
147  if (slash = strchr(argv[0], '/'))
148    slash++;
149  else
150    slash = argv[0];
151
152  if (!strcmp(slash, "key") || strstr(slash, "md4"))
153    algorithm = 4;
154
155  if (strstr(slash, "md5"))
156    algorithm = 5;
157
158  if (strstr(slash, "sha"))
159    algorithm = 3;
160
161  while ((i = getopt(argc, argv, "fhvn:x45at:s")) != EOF) {
162    switch (i) {
163    case 'v':
164      opieversion();
165
166    case 'n':
167      count = atoi(optarg);
168      break;
169
170    case 'x':
171      hex = 1;
172      break;
173
174    case 'f':
175#if INSECURE_OVERRIDE
176      force = 1;
177#else /* INSECURE_OVERRIDE */
178      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
179#endif /* INSECURE_OVERRIDE */
180      break;
181
182    case '4':
183      /* use MD4 algorithm */
184      algorithm = 4;
185      break;
186
187    case '5':
188      /* use MD5 algorithm */
189      algorithm = 5;
190      break;
191
192    case 'a':
193      aflag = 1;
194      break;
195
196    case 't':
197      {
198	struct _rtrans *r;
199	for (r = rtrans; r->name && strcmp(r->name, optarg); r++);
200	if (!r->name) {
201	  fprintf(stderr, "%s: %s: unknown response type.\n", argv[0], optarg);
202	  exit(1);
203	}
204	type = r->type;
205      }
206      break;
207
208    case 's':
209      algorithm = 3;
210      break;
211
212    default:
213      usage(argv[0]);
214    }
215  }
216
217  if ((argc - optind) < 2)
218    usage(argv[0]);
219
220  fprintf(stderr, "Using the %s algorithm to compute response.\n", algnames[algorithm]);
221
222  /* get sequence number, which is next-to-last parameter */
223  keynum = atoi(argv[optind]);
224  if (keynum < 1) {
225    fprintf(stderr, "Sequence number %s is not positive.\n", argv[optind]);
226    exit(1);
227  }
228  /* get seed string, which is last parameter */
229  seed = argv[optind + 1];
230  {
231    i = strlen(seed);
232
233    if (i > OPIE_SEED_MAX) {
234      fprintf(stderr, "Seeds must be less than %d characters long.\n", OPIE_SEED_MAX);
235      exit(1);
236    }
237    if (i < OPIE_SEED_MIN) {
238      fprintf(stderr, "Seeds must be greater than %d characters long.\n", OPIE_SEED_MIN);
239      exit(1);
240    }
241  }
242
243  fprintf(stderr, "Reminder: Don't use opiekey from telnet or dial-in sessions.\n");
244
245  if (opieinsecure()) {
246    fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
247#if INSECURE_OVERRIDE
248    if (force)
249      fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
250    else
251#endif /* INSECURE_OVERRIDE */
252      exit(1);
253  }
254
255  if ((type == RESPONSE_INIT_HEX) || (type == RESPONSE_INIT_WORD)) {
256#if RETYPE
257    getsecret(secret, "old ", 1);
258#else /* RETYPE */
259    getsecret(secret, "old ", 0);
260#endif /* RETYPE */
261    getsecret(newsecret, "new ", 1);
262    if (!newsecret[0])
263      strcpy(newsecret, secret);
264
265    if (opienewseed(strcpy(newseed, seed)) < 0) {
266      fprintf(stderr, "Error updating seed.\n");
267      goto error;
268    }
269
270    if (opiekeycrunch(algorithm, &newkey, newseed, newsecret)) {
271      fprintf(stderr, "%s: key crunch failed (1)\n", argv[0]);
272      goto error;
273    }
274
275    for (i = 0; i < 499; i++)
276      opiehash(&newkey, algorithm);
277  } else
278#if RETYPE
279    getsecret(secret, "", 1);
280#else /* RETYPE */
281    getsecret(secret, "", 0);
282#endif /* RETYPE */
283
284  /* Crunch seed and secret password into starting key normally */
285  if (opiekeycrunch(algorithm, &key, seed, secret)) {
286    fprintf(stderr, "%s: key crunch failed\n", argv[0]);
287    goto error;
288  }
289
290  for (i = 0; i <= (keynum - count); i++)
291    opiehash(&key, algorithm);
292
293  {
294    char buf[OPIE_SEED_MAX + 48 + 1];
295    char *c;
296
297    for (; i <= keynum; i++) {
298      if (count > 1)
299	printf("%d: %s", i, (type == RESPONSE_STANDARD) ? "" : "\n");
300
301      switch(type) {
302      case RESPONSE_STANDARD:
303	if (hex)
304	  opiebtoh(response, &key);
305	else
306	  opiebtoe(response, &key);
307	break;
308      case RESPONSE_WORD:
309	strcpy(response, "word:");
310	strcat(response, opiebtoe(buf, &key));
311	break;
312      case RESPONSE_HEX:
313	strcpy(response, "hex:");
314	strcat(response, opiebtoh(buf, &key));
315	break;
316      case RESPONSE_INIT_HEX:
317      case RESPONSE_INIT_WORD:
318	if (type == RESPONSE_INIT_HEX) {
319	  strcpy(response, "init-hex:");
320	  strcat(response, opiebtoh(buf, &key));
321	  sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed);
322	  strcat(response, buf);
323	  strcat(response, opiebtoh(buf, &newkey));
324	} else {
325	  strcpy(response, "init-word:");
326	  strcat(response, opiebtoe(buf, &key));
327	  sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed);
328	  strcat(response, buf);
329	  strcat(response, opiebtoe(buf, &newkey));
330	}
331	break;
332      }
333      puts(response);
334      opiehash(&key, algorithm);
335    }
336  }
337
338  memset(secret, 0, sizeof(secret));
339  memset(newsecret, 0, sizeof(newsecret));
340  return 0;
341
342error:
343  memset(secret, 0, sizeof(secret));
344  memset(newsecret, 0, sizeof(newsecret));
345  return 1;
346}
347