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