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