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