opiepasswd.c revision 59118
1228753Smm/* opiepasswd.c: Add/change an OTP password in the key database.
2228753Smm
3228753Smm%%% portions-copyright-cmetz-96
4228753SmmPortions of this software are Copyright 1996-1998 by Craig Metz, All Rights
5228753SmmReserved. The Inner Net License Version 2 applies to these portions of
6228753Smmthe software.
7228753SmmYou should have received a copy of the license with this software. If
8228753Smmyou didn't get a copy, you may request one from <license@inner.net>.
9228753Smm
10228753SmmPortions of this software are Copyright 1995 by Randall Atkinson and Dan
11228753SmmMcDonald, All Rights Reserved. All Rights under this copyright are assigned
12228753Smmto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13228753SmmLicense Agreement applies to this software.
14228753Smm
15228753Smm	History:
16228753Smm
17228753Smm	Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of
18228753Smm		hard coding the length. Unlock user on failed lookup.
19228753Smm	Modified by cmetz for OPIE 2.3. Got of some variables and made some
20228753Smm		local to where they're used. Split out the finishing code. Use
21228753Smm		opielookup() instead of opiechallenge() to find user. Three
22228753Smm		strikes on prompts. Use opiepasswd()'s new calling
23228753Smm		convention. Changed OPIE_PASS_{MAX,MIN} to
24228753Smm		OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning
25228753Smm		below us. Got rid of unneeded headers. Use new opieatob8()
26228753Smm		return value convention. Added -f flag. Added SHA support.
27229592Smm	Modified by cmetz for OPIE 2.22. Finally got rid of the lock
28228753Smm	        filename kluge by implementing refcounts for locks.
29228753Smm		Use opiepasswd() to update key file. Error if we can't
30228753Smm		write to the key file. Check for minimum seed length.
31228753Smm        Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to
32228753Smm                opiestripcrlf. Check opiereadpass() return value.
33228753Smm                Minor optimization. Change calls to opiereadpass() to
34228753Smm                use echo arg. Use opiereadpass() where we can.
35228753Smm                Make everything static. Ifdef around some headers.
36228753Smm                Changed use of gethostname() to uname(). Got rid of
37228753Smm                the need for buf[]. Properly check return value of
38228753Smm                opieatob8. Check seed length. Always generate proper-
39228753Smm                length seeds.
40228753Smm	Modified at NRL for OPIE 2.1. Minor autoconf changes.
41228753Smm        Modified heavily at NRL for OPIE 2.0.
42228753Smm	Written at Bellcore for the S/Key Version 1 software distribution
43228753Smm		(skeyinit.c).
44228753Smm*/
45228753Smm#include "opie_cfg.h"
46228753Smm
47228753Smm#if HAVE_PWD_H
48228753Smm#include <pwd.h>
49228753Smm#endif /* HAVE_PWD_H */
50228753Smm#include <stdio.h>
51228753Smm#if HAVE_STRING_H
52228753Smm#include <string.h>
53228753Smm#endif /* HAVE_STRING_H */
54228753Smm#include <stdio.h>
55228753Smm#include <sys/types.h>
56228753Smm#if HAVE_UNISTD_H
57228753Smm#include <unistd.h>
58228753Smm#endif /* HAVE_UNISTD_H */
59228753Smm#if HAVE_STDLIB_H
60228753Smm#include <stdlib.h>
61228753Smm#endif /* HAVE_STDLIB_H */
62228753Smm
63228753Smm#include "opie.h"
64228753Smm
65228753Smm#define MODE_DEFAULT 0
66228753Smm#define MODE_CONSOLE 1
67228753Smm#define MODE_DISABLE 2
68228753Smm
69228753Smmextern int optind;
70228753Smmextern char *optarg;
71228753Smm
72228753Smmchar *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
73228753Smmchar *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
74228753Smm
75228753Smmstatic VOIDRET usage FUNCTION((myname), char *myname)
76228753Smm{
77228753Smm  fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
78228753Smm  exit(1);
79228753Smm}
80228753Smm
81228753Smmstatic VOIDRET finish FUNCTION((name), char *name)
82228753Smm{
83228753Smm  struct opie opie;
84228753Smm  char buf[OPIE_RESPONSE_MAX + 1];
85228753Smm
86228753Smm  if (name) {
87228753Smm    if (opiechallenge(&opie, name, buf)) {
88228753Smm      fprintf(stderr, "Error verifying database.\n");
89228753Smm      finish(NULL);
90228753Smm    }
91228753Smm    printf("\nID %s ", opie.opie_principal);
92228753Smm    if (opie.opie_val && (opie.opie_val[0] == '*')) {
93228753Smm      printf("is disabled.\n");
94228753Smm      finish(NULL);
95228753Smm    }
96228753Smm    printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
97228753Smm    {
98228753Smm      char key[8];
99228753Smm      if (!opieatob8(key, opie.opie_val)) {
100228753Smm	fprintf(stderr, "Error verifying key -- possible database corruption.\n");
101228753Smm	finish(NULL);
102228753Smm      }
103228753Smm      printf("%s\n", opiebtoe(buf, key));
104228753Smm    }
105228753Smm  }
106228753Smm
107228753Smm  while(!opieunlock());
108228753Smm  exit(name ? 0 : 1);
109228753Smm}
110228753Smm
111228753Smmint main FUNCTION((argc, argv), int argc AND char *argv[])
112228753Smm{
113228753Smm  struct opie opie;
114228753Smm  int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
115228753Smm  char seed[OPIE_SEED_MAX+1];
116228753Smm  struct passwd *pp;
117228753Smm
118228753Smm  memset(seed, 0, sizeof(seed));
119228753Smm
120228753Smm  if (!(pp = getpwuid(getuid()))) {
121228753Smm    fprintf(stderr, "Who are you?");
122228753Smm    return 1;
123228753Smm  }
124228753Smm
125228753Smm  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
126228753Smm    switch (i) {
127228753Smm    case 'v':
128228753Smm      opieversion();
129228753Smm    case 'f':
130228753Smm#if INSECURE_OVERRIDE
131228753Smm      force = OPIEPASSWD_FORCE;
132228753Smm#else /* INSECURE_OVERRIDE */
133228753Smm      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
134228753Smm#endif /* INSECURE_OVERRIDE */
135228753Smm      break;
136228753Smm    case 'c':
137228753Smm      mode = MODE_CONSOLE;
138228753Smm      break;
139228753Smm    case 'd':
140228753Smm      mode = MODE_DISABLE;
141228753Smm      break;
142228753Smm    case 'n':
143228753Smm      i = atoi(optarg);
144228753Smm      if (!(i > 0 && i < 10000)) {
145228753Smm	printf("Sequence numbers must be > 0 and < 10000\n");
146228753Smm	finish(NULL);
147228753Smm      }
148228753Smm      n = i;
149228753Smm      break;
150228753Smm    case 's':
151228753Smm      i = strlen(optarg);
152228753Smm      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
153228753Smm	printf("Seeds must be between %d and %d characters long.\n",
154228753Smm	       OPIE_SEED_MIN, OPIE_SEED_MAX);
155228753Smm	finish(NULL);
156228753Smm      }
157228753Smm      strncpy(seed, optarg, sizeof(seed));
158228753Smm      seed[sizeof(seed) - 1] = 0;
159228753Smm      break;
160228753Smm    default:
161228753Smm      usage(argv[0]);
162228753Smm    }
163228753Smm  }
164228753Smm
165228753Smm  if (argc - optind >= 1) {
166228753Smm    if (strcmp(argv[optind], pp->pw_name)) {
167228753Smm      if (getuid()) {
168228753Smm	printf("Only root can change others' passwords.\n");
169228753Smm	exit(1);
170228753Smm      }
171228753Smm      if ((pp = getpwnam(argv[optind])) == NULL) {
172228753Smm	printf("%s: user unknown.\n", argv[optind]);
173228753Smm	exit(1);
174228753Smm      }
175228753Smm    }
176228753Smm  }
177228753Smm
178228753Smm  opielock(pp->pw_name);
179228753Smm  rval = opielookup(&opie, pp->pw_name);
180228753Smm
181228753Smm  switch (rval) {
182228753Smm  case 0:
183228753Smm    printf("Updating %s:\n", pp->pw_name);
184228753Smm    break;
185228753Smm  case 1:
186228753Smm    printf("Adding %s:\n", pp->pw_name);
187228753Smm    break;
188228753Smm  case 2:
189228753Smm    fprintf(stderr, "Error: Can't update key database.\n");
190228753Smm    finish(NULL);
191228753Smm  default:
192228753Smm    fprintf(stderr, "Error reading key database\n");
193228753Smm    finish(NULL);
194228753Smm  }
195228753Smm
196228753Smm  if (seed[0]) {
197228753Smm    i = strlen(seed);
198228753Smm    if (i > OPIE_SEED_MAX) {
199228753Smm      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
200228753Smm      finish(NULL);
201228753Smm    }
202228753Smm    if (i < OPIE_SEED_MIN) {
203228753Smm      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
204228753Smm      finish(NULL);
205228753Smm    }
206228753Smm  } else {
207228753Smm    if (!rval)
208228753Smm      strcpy(seed, opie.opie_seed);
209228753Smm
210228753Smm    if (opienewseed(seed) < 0) {
211228753Smm      fprintf(stderr, "Error updating seed.\n");
212228753Smm      finish(NULL);
213228753Smm    }
214228753Smm  }
215228753Smm
216228753Smm  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
217228753Smm    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
218228753Smm    finish(NULL);
219228753Smm  }
220228753Smm
221228753Smm  switch(mode) {
222228753Smm  case MODE_DEFAULT:
223228753Smm    {
224228753Smm      char tmp[OPIE_RESPONSE_MAX + 2];
225228753Smm
226228753Smm      printf("You need the response from an OTP generator.\n");
227228753Smm#if DEBUG
228228753Smm      if (!rval) {
229228753Smm#else /* DEBUG */
230228753Smm      if (!rval && getuid()) {
231228753Smm#endif /* DEBUG */
232228753Smm	char oseed[OPIE_SEED_MAX + 1];
233228753Smm	int on;
234228753Smm
235228753Smm	if (opiechallenge(&opie, pp->pw_name, tmp)) {
236228753Smm	  fprintf(stderr, "Error issuing challenge.\n");
237228753Smm	  finish(NULL);
238228753Smm	}
239228753Smm	on = opiegetsequence(&opie);
240228753Smm	{
241228753Smm	  char *c;
242228753Smm	  if (c = strrchr(tmp, ' '))
243228753Smm	    strncpy(oseed, c + 1, sizeof(oseed));
244228753Smm	  else {
245228753Smm#if DEBUG
246228753Smm	    fprintf(stderr, "opiepasswd: bogus challenge\n");
247228753Smm#endif /* DEBUG */
248228753Smm	    finish(NULL);
249228753Smm	  }
250228753Smm	}
251228753Smm	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
252228753Smm	if (!opiereadpass(tmp, sizeof(tmp), 1))
253228753Smm	  tmp[0] = 0;
254228753Smm	i = opieverify(&opie, tmp);
255228753Smm	if (!tmp[0]) {
256228753Smm	  fprintf(stderr, "Error reading response.\n");
257228753Smm	  finish(NULL);
258228753Smm	}
259228753Smm	if (i) {
260228753Smm	  fprintf(stderr, "Error verifying response.\n");
261228753Smm#if DEBUG
262228753Smm	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
263228753Smm#endif /* DEBUG */
264228753Smm	  finish(NULL);
265228753Smm	}
266228753Smm	{
267228753Smm	  char nseed[OPIE_SEED_MAX + 1];
268228753Smm	  int nn;
269228753Smm
270228753Smm	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
271228753Smm	    fprintf(stderr, "Error verifying database.\n");
272228753Smm	    finish(NULL);
273	  }
274
275	  nn = opiegetsequence(&opie);
276	  {
277	    char *c;
278	    if (c = strrchr(tmp, ' '))
279	      strncpy(nseed, c + 1, sizeof(nseed));
280	    else {
281#if DEBUG
282	      fprintf(stderr, "opiepasswd: bogus challenge\n");
283#endif /* DEBUG */
284	      finish(NULL);
285	    }
286	  }
287
288	  opieverify(&opie, "");
289	  nn++;
290
291	  if ((nn != on) || strcmp(oseed, nseed))
292	    finish(pp->pw_name);
293	}
294      }
295      printf("New secret pass phrase:");
296      for (i = 0;; i++) {
297	if (i > 2)
298	  finish(NULL);
299	printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
300	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
301	  fprintf(stderr, "Error reading response.\n");
302	  finish(NULL);
303	}
304	if (tmp[0] == '?') {
305	  printf("Enter the response from your OTP calculator: \n");
306	  continue;
307	}
308	if (tmp[0] == '\0') {
309	  fprintf(stderr, "Secret pass phrase unchanged.\n");
310	  finish(NULL);
311	}
312
313	if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
314	  finish(pp->pw_name);
315
316	if (rval < 0) {
317	  fprintf(stderr, "Error updating key database.\n");
318	  finish(NULL);
319	}
320	printf("\tThat is not a valid OTP response.\n");
321      }
322    }
323    break;
324  case MODE_CONSOLE:
325    {
326      char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
327      /* Get user's secret password */
328      fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
329      fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
330      fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
331      if (opieinsecure() && !force) {
332	fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
333	if (force)
334          fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
335        else
336          finish(NULL);
337      };
338      printf("Using %s to compute responses.\n", algnames[MDX]);
339      if (!rval && getuid()) {
340	printf("Enter old secret pass phrase: ");
341	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
342	  fprintf(stderr, "Error reading secret pass phrase!\n");
343	  finish(NULL);
344	}
345	if (!passwd[0]) {
346	  fprintf(stderr, "Secret pass phrase unchanged.\n");
347	  finish(NULL);
348	}
349	{
350	  char key[8];
351	  char tbuf[OPIE_RESPONSE_MAX + 1];
352
353	  if (opiekeycrunch(MDX, key, opie.opie_seed, passwd) != 0) {
354	    fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
355	    finish(NULL);
356	  }
357	  memset(passwd, 0, sizeof(passwd));
358	  i = opie.opie_n - 1;
359	  while (i-- != 0)
360	    opiehash(key, MDX);
361	  opiebtoe(tbuf, key);
362	  if (opieverify(&opie, tbuf)) {
363	    fprintf(stderr, "Sorry.\n");
364	    finish(NULL);
365	  }
366	}
367      }
368      for (i = 0;; i++) {
369	if (i > 2)
370	  finish(NULL);
371	printf("Enter new secret pass phrase: ");
372	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
373	  fprintf(stderr, "Error reading secret pass phrase.\n");
374	  finish(NULL);
375	}
376	if (!passwd[0] || feof(stdin)) {
377	  fprintf(stderr, "Secret pass phrase unchanged.\n");
378	  finish(NULL);
379	}
380	if (opiepasscheck(passwd)) {
381	  memset(passwd, 0, sizeof(passwd));
382	  fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
383	  continue;
384	}
385	printf("Again new secret pass phrase: ");
386	if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
387	  fprintf(stderr, "Error reading secret pass phrase.\n");
388	  finish(NULL);
389	}
390	if (feof(stdin)) {
391	  fprintf(stderr, "Secret pass phrase unchanged.\n");
392	  finish(NULL);
393	}
394	if (!passwd[0] || !strcmp(passwd, passwd2))
395	  break;
396	fprintf(stderr, "Sorry, no match.\n");
397      }
398      memset(passwd2, 0, sizeof(passwd2));
399      if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
400	fprintf(stderr, "Error updating key database.\n");
401	finish(NULL);
402      }
403      finish(pp->pw_name);
404    }
405  case MODE_DISABLE:
406    {
407      char tmp[4];
408      int i;
409
410      for (i = 0;; i++) {
411	if (i > 2)
412	  finish(NULL);
413
414	printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
415	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
416	  fprintf(stderr, "Error reading entry.\n");
417	  finish(NULL);
418	}
419	if (!strcmp(tmp, "no"))
420	  finish(NULL);
421	if (!strcmp(tmp, "yes")) {
422	  if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
423	    fprintf(stderr, "Error updating key database.\n");
424	    finish(NULL);
425	  }
426	  finish(pp->pw_name);
427	}
428      }
429    }
430  }
431}
432