opiepasswd.c revision 92914
1/* opiepasswd.c: Add/change an OTP password in the key database.
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11McDonald, All Rights Reserved. All Rights under this copyright are assigned
12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13License Agreement applies to this software.
14
15	History:
16
17	Modified by cmetz for OPIE 2.4. Use struct opie_key for key blocks.
18		Use opiestrncpy().
19	Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of
20		hard coding the length. Unlock user on failed lookup.
21	Modified by cmetz for OPIE 2.3. Got of some variables and made some
22		local to where they're used. Split out the finishing code. Use
23		opielookup() instead of opiechallenge() to find user. Three
24		strikes on prompts. Use opiepasswd()'s new calling
25		convention. Changed OPIE_PASS_{MAX,MIN} to
26		OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning
27		below us. Got rid of unneeded headers. Use new opieatob8()
28		return value convention. Added -f flag. Added SHA support.
29	Modified by cmetz for OPIE 2.22. Finally got rid of the lock
30	        filename kluge by implementing refcounts for locks.
31		Use opiepasswd() to update key file. Error if we can't
32		write to the key file. Check for minimum seed length.
33        Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to
34                opiestripcrlf. Check opiereadpass() return value.
35                Minor optimization. Change calls to opiereadpass() to
36                use echo arg. Use opiereadpass() where we can.
37                Make everything static. Ifdef around some headers.
38                Changed use of gethostname() to uname(). Got rid of
39                the need for buf[]. Properly check return value of
40                opieatob8. Check seed length. Always generate proper-
41                length seeds.
42	Modified at NRL for OPIE 2.1. Minor autoconf changes.
43        Modified heavily at NRL for OPIE 2.0.
44	Written at Bellcore for the S/Key Version 1 software distribution
45		(skeyinit.c).
46
47 $FreeBSD: head/contrib/opie/opiepasswd.c 92914 2002-03-21 23:42:52Z markm $
48*/
49#include "opie_cfg.h"
50
51#if HAVE_PWD_H
52#include <pwd.h>
53#endif /* HAVE_PWD_H */
54#include <stdio.h>
55#if HAVE_STRING_H
56#include <string.h>
57#endif /* HAVE_STRING_H */
58#include <stdio.h>
59#include <sys/types.h>
60#if HAVE_UNISTD_H
61#include <unistd.h>
62#endif /* HAVE_UNISTD_H */
63#if HAVE_STDLIB_H
64#include <stdlib.h>
65#endif /* HAVE_STDLIB_H */
66
67#include "opie.h"
68
69#define MODE_DEFAULT 0
70#define MODE_CONSOLE 1
71#define MODE_DISABLE 2
72
73extern int optind;
74extern char *optarg;
75
76char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
77char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
78
79static VOIDRET usage FUNCTION((myname), char *myname)
80{
81  fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
82  exit(1);
83}
84
85static VOIDRET finish FUNCTION((name), char *name)
86{
87  struct opie opie;
88  char buf[OPIE_RESPONSE_MAX + 1];
89
90  if (name) {
91    if (opiechallenge(&opie, name, buf)) {
92      fprintf(stderr, "Error verifying database.\n");
93      finish(NULL);
94    }
95    printf("\nID %s ", opie.opie_principal);
96    if (opie.opie_val && (opie.opie_val[0] == '*')) {
97      printf("is disabled.\n");
98      finish(NULL);
99    }
100    printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
101    {
102      struct opie_otpkey key;
103
104      if (!opieatob8(&key, opie.opie_val)) {
105	fprintf(stderr, "Error verifying key -- possible database corruption.\n");
106	finish(NULL);
107      }
108      printf("%s\n", opiebtoe(buf, &key));
109    }
110  }
111
112  while(!opieunlock());
113  exit(name ? 0 : 1);
114}
115
116int main FUNCTION((argc, argv), int argc AND char *argv[])
117{
118  struct opie opie;
119  int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
120  char seed[OPIE_SEED_MAX+1];
121  struct passwd *pp;
122
123  memset(seed, 0, sizeof(seed));
124
125  if (!(pp = getpwnam(getlogin()))) {
126    fprintf(stderr, "Who are you?");
127    return 1;
128  }
129
130  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
131    switch (i) {
132    case 'v':
133      opieversion();
134    case 'f':
135#if INSECURE_OVERRIDE
136      force = OPIEPASSWD_FORCE;
137#else /* INSECURE_OVERRIDE */
138      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
139#endif /* INSECURE_OVERRIDE */
140      break;
141    case 'c':
142      mode = MODE_CONSOLE;
143      break;
144    case 'd':
145      mode = MODE_DISABLE;
146      break;
147    case 'n':
148      i = atoi(optarg);
149      if (!(i > 0 && i < 10000)) {
150	printf("Sequence numbers must be > 0 and < 10000\n");
151	finish(NULL);
152      }
153      n = i;
154      break;
155    case 's':
156      i = strlen(optarg);
157      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
158	printf("Seeds must be between %d and %d characters long.\n",
159	       OPIE_SEED_MIN, OPIE_SEED_MAX);
160	finish(NULL);
161      }
162      opiestrncpy(seed, optarg, sizeof(seed));
163      break;
164    default:
165      usage(argv[0]);
166    }
167  }
168
169  if (argc - optind >= 1) {
170    if (strcmp(argv[optind], pp->pw_name)) {
171      if (getuid()) {
172	printf("Only root can change others' passwords.\n");
173	exit(1);
174      }
175      if ((pp = getpwnam(argv[optind])) == NULL) {
176	printf("%s: user unknown.\n", argv[optind]);
177	exit(1);
178      }
179    }
180  }
181
182  opielock(pp->pw_name);
183  rval = opielookup(&opie, pp->pw_name);
184
185  switch (rval) {
186  case 0:
187    printf("Updating %s:\n", pp->pw_name);
188    break;
189  case 1:
190    printf("Adding %s:\n", pp->pw_name);
191    break;
192  case 2:
193    fprintf(stderr, "Error: Can't update key database.\n");
194    finish(NULL);
195  default:
196    fprintf(stderr, "Error reading key database\n");
197    finish(NULL);
198  }
199
200  if (seed[0]) {
201    i = strlen(seed);
202    if (i > OPIE_SEED_MAX) {
203      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
204      finish(NULL);
205    }
206    if (i < OPIE_SEED_MIN) {
207      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
208      finish(NULL);
209    }
210  } else {
211    if (!rval)
212      strcpy(seed, opie.opie_seed);
213
214    if (opienewseed(seed) < 0) {
215      fprintf(stderr, "Error updating seed.\n");
216      finish(NULL);
217    }
218  }
219
220  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
221    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
222    finish(NULL);
223  }
224
225  switch(mode) {
226  case MODE_DEFAULT:
227    {
228      char tmp[OPIE_RESPONSE_MAX + 2];
229
230      printf("You need the response from an OTP generator.\n");
231#if DEBUG
232      if (!rval) {
233#else /* DEBUG */
234      if (!rval && getuid()) {
235#endif /* DEBUG */
236	char oseed[OPIE_SEED_MAX + 1];
237	int on;
238
239	if (opiechallenge(&opie, pp->pw_name, tmp)) {
240	  fprintf(stderr, "Error issuing challenge.\n");
241	  finish(NULL);
242	}
243	on = opiegetsequence(&opie);
244	{
245	  char *c;
246	  if (c = strrchr(tmp, ' '))
247	    opiestrncpy(oseed, c + 1, sizeof(oseed));
248	  else {
249#if DEBUG
250	    fprintf(stderr, "opiepasswd: bogus challenge\n");
251#endif /* DEBUG */
252	    finish(NULL);
253	  }
254	}
255	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
256	if (!opiereadpass(tmp, sizeof(tmp), 1))
257	  tmp[0] = 0;
258	i = opieverify(&opie, tmp);
259	if (!tmp[0]) {
260	  fprintf(stderr, "Error reading response.\n");
261	  finish(NULL);
262	}
263	if (i) {
264	  fprintf(stderr, "Error verifying response.\n");
265#if DEBUG
266	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
267#endif /* DEBUG */
268	  finish(NULL);
269	}
270	{
271	  char nseed[OPIE_SEED_MAX + 1];
272	  int nn;
273
274	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
275	    fprintf(stderr, "Error verifying database.\n");
276	    finish(NULL);
277	  }
278
279	  nn = opiegetsequence(&opie);
280	  {
281	    char *c;
282	    if (c = strrchr(tmp, ' '))
283	      opiestrncpy(nseed, c + 1, sizeof(nseed));
284	    else {
285#if DEBUG
286	      fprintf(stderr, "opiepasswd: bogus challenge\n");
287#endif /* DEBUG */
288	      finish(NULL);
289	    }
290	  }
291
292	  opieverify(&opie, "");
293	  nn++;
294
295	  if ((nn != on) || strcmp(oseed, nseed))
296	    finish(pp->pw_name);
297	}
298      }
299      printf("New secret pass phrase:");
300      for (i = 0;; i++) {
301	if (i > 2)
302	  finish(NULL);
303	printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
304	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
305	  fprintf(stderr, "Error reading response.\n");
306	  finish(NULL);
307	}
308	if (tmp[0] == '?') {
309	  printf("Enter the response from your OTP calculator: \n");
310	  continue;
311	}
312	if (tmp[0] == '\0') {
313	  fprintf(stderr, "Secret pass phrase unchanged.\n");
314	  finish(NULL);
315	}
316
317	if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
318	  finish(pp->pw_name);
319
320	if (rval < 0) {
321	  fprintf(stderr, "Error updating key database.\n");
322	  finish(NULL);
323	}
324	printf("\tThat is not a valid OTP response.\n");
325      }
326    }
327    break;
328  case MODE_CONSOLE:
329    {
330      char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
331      /* Get user's secret password */
332      fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
333      fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
334      fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
335      if (opieinsecure() && !force) {
336	fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
337	if (force)
338          fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
339        else
340          finish(NULL);
341      };
342      printf("Using %s to compute responses.\n", algnames[MDX]);
343      if (!rval && getuid()) {
344	printf("Enter old secret pass phrase: ");
345	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
346	  fprintf(stderr, "Error reading secret pass phrase!\n");
347	  finish(NULL);
348	}
349	if (!passwd[0]) {
350	  fprintf(stderr, "Secret pass phrase unchanged.\n");
351	  finish(NULL);
352	}
353	{
354	  struct opie_otpkey key;
355	  char tbuf[OPIE_RESPONSE_MAX + 1];
356
357	  if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) {
358	    fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
359	    finish(NULL);
360	  }
361	  memset(passwd, 0, sizeof(passwd));
362	  i = opie.opie_n - 1;
363	  while (i-- != 0)
364	    opiehash(&key, MDX);
365	  opiebtoe(tbuf, &key);
366	  if (opieverify(&opie, tbuf)) {
367	    fprintf(stderr, "Sorry.\n");
368	    finish(NULL);
369	  }
370	}
371      }
372      for (i = 0;; i++) {
373	if (i > 2)
374	  finish(NULL);
375	printf("Enter new secret pass phrase: ");
376	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
377	  fprintf(stderr, "Error reading secret pass phrase.\n");
378	  finish(NULL);
379	}
380	if (!passwd[0] || feof(stdin)) {
381	  fprintf(stderr, "Secret pass phrase unchanged.\n");
382	  finish(NULL);
383	}
384	if (opiepasscheck(passwd)) {
385	  memset(passwd, 0, sizeof(passwd));
386	  fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
387	  continue;
388	}
389	printf("Again new secret pass phrase: ");
390	if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
391	  fprintf(stderr, "Error reading secret pass phrase.\n");
392	  finish(NULL);
393	}
394	if (feof(stdin)) {
395	  fprintf(stderr, "Secret pass phrase unchanged.\n");
396	  finish(NULL);
397	}
398	if (!passwd[0] || !strcmp(passwd, passwd2))
399	  break;
400	fprintf(stderr, "Sorry, no match.\n");
401      }
402      memset(passwd2, 0, sizeof(passwd2));
403      if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
404	fprintf(stderr, "Error updating key database.\n");
405	finish(NULL);
406      }
407      finish(pp->pw_name);
408    }
409  case MODE_DISABLE:
410    {
411      char tmp[4];
412      int i;
413
414      for (i = 0;; i++) {
415	if (i > 2)
416	  finish(NULL);
417
418	printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
419	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
420	  fprintf(stderr, "Error reading entry.\n");
421	  finish(NULL);
422	}
423	if (!strcmp(tmp, "no"))
424	  finish(NULL);
425	if (!strcmp(tmp, "yes")) {
426	  if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
427	    fprintf(stderr, "Error updating key database.\n");
428	    finish(NULL);
429	  }
430	  finish(pp->pw_name);
431	}
432      }
433    }
434  }
435}
436