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