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