opiepasswd.c revision 59118
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#include "opie_cfg.h"
46
47#if HAVE_PWD_H
48#include <pwd.h>
49#endif /* HAVE_PWD_H */
50#include <stdio.h>
51#if HAVE_STRING_H
52#include <string.h>
53#endif /* HAVE_STRING_H */
54#include <stdio.h>
55#include <sys/types.h>
56#if HAVE_UNISTD_H
57#include <unistd.h>
58#endif /* HAVE_UNISTD_H */
59#if HAVE_STDLIB_H
60#include <stdlib.h>
61#endif /* HAVE_STDLIB_H */
62
63#include "opie.h"
64
65#define MODE_DEFAULT 0
66#define MODE_CONSOLE 1
67#define MODE_DISABLE 2
68
69extern int optind;
70extern char *optarg;
71
72char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
73char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
74
75static VOIDRET usage FUNCTION((myname), char *myname)
76{
77  fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
78  exit(1);
79}
80
81static VOIDRET finish FUNCTION((name), char *name)
82{
83  struct opie opie;
84  char buf[OPIE_RESPONSE_MAX + 1];
85
86  if (name) {
87    if (opiechallenge(&opie, name, buf)) {
88      fprintf(stderr, "Error verifying database.\n");
89      finish(NULL);
90    }
91    printf("\nID %s ", opie.opie_principal);
92    if (opie.opie_val && (opie.opie_val[0] == '*')) {
93      printf("is disabled.\n");
94      finish(NULL);
95    }
96    printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
97    {
98      char key[8];
99      if (!opieatob8(key, opie.opie_val)) {
100	fprintf(stderr, "Error verifying key -- possible database corruption.\n");
101	finish(NULL);
102      }
103      printf("%s\n", opiebtoe(buf, key));
104    }
105  }
106
107  while(!opieunlock());
108  exit(name ? 0 : 1);
109}
110
111int main FUNCTION((argc, argv), int argc AND char *argv[])
112{
113  struct opie opie;
114  int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
115  char seed[OPIE_SEED_MAX+1];
116  struct passwd *pp;
117
118  memset(seed, 0, sizeof(seed));
119
120  if (!(pp = getpwuid(getuid()))) {
121    fprintf(stderr, "Who are you?");
122    return 1;
123  }
124
125  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
126    switch (i) {
127    case 'v':
128      opieversion();
129    case 'f':
130#if INSECURE_OVERRIDE
131      force = OPIEPASSWD_FORCE;
132#else /* INSECURE_OVERRIDE */
133      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
134#endif /* INSECURE_OVERRIDE */
135      break;
136    case 'c':
137      mode = MODE_CONSOLE;
138      break;
139    case 'd':
140      mode = MODE_DISABLE;
141      break;
142    case 'n':
143      i = atoi(optarg);
144      if (!(i > 0 && i < 10000)) {
145	printf("Sequence numbers must be > 0 and < 10000\n");
146	finish(NULL);
147      }
148      n = i;
149      break;
150    case 's':
151      i = strlen(optarg);
152      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
153	printf("Seeds must be between %d and %d characters long.\n",
154	       OPIE_SEED_MIN, OPIE_SEED_MAX);
155	finish(NULL);
156      }
157      strncpy(seed, optarg, sizeof(seed));
158      seed[sizeof(seed) - 1] = 0;
159      break;
160    default:
161      usage(argv[0]);
162    }
163  }
164
165  if (argc - optind >= 1) {
166    if (strcmp(argv[optind], pp->pw_name)) {
167      if (getuid()) {
168	printf("Only root can change others' passwords.\n");
169	exit(1);
170      }
171      if ((pp = getpwnam(argv[optind])) == NULL) {
172	printf("%s: user unknown.\n", argv[optind]);
173	exit(1);
174      }
175    }
176  }
177
178  opielock(pp->pw_name);
179  rval = opielookup(&opie, pp->pw_name);
180
181  switch (rval) {
182  case 0:
183    printf("Updating %s:\n", pp->pw_name);
184    break;
185  case 1:
186    printf("Adding %s:\n", pp->pw_name);
187    break;
188  case 2:
189    fprintf(stderr, "Error: Can't update key database.\n");
190    finish(NULL);
191  default:
192    fprintf(stderr, "Error reading key database\n");
193    finish(NULL);
194  }
195
196  if (seed[0]) {
197    i = strlen(seed);
198    if (i > OPIE_SEED_MAX) {
199      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
200      finish(NULL);
201    }
202    if (i < OPIE_SEED_MIN) {
203      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
204      finish(NULL);
205    }
206  } else {
207    if (!rval)
208      strcpy(seed, opie.opie_seed);
209
210    if (opienewseed(seed) < 0) {
211      fprintf(stderr, "Error updating seed.\n");
212      finish(NULL);
213    }
214  }
215
216  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
217    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
218    finish(NULL);
219  }
220
221  switch(mode) {
222  case MODE_DEFAULT:
223    {
224      char tmp[OPIE_RESPONSE_MAX + 2];
225
226      printf("You need the response from an OTP generator.\n");
227#if DEBUG
228      if (!rval) {
229#else /* DEBUG */
230      if (!rval && getuid()) {
231#endif /* DEBUG */
232	char oseed[OPIE_SEED_MAX + 1];
233	int on;
234
235	if (opiechallenge(&opie, pp->pw_name, tmp)) {
236	  fprintf(stderr, "Error issuing challenge.\n");
237	  finish(NULL);
238	}
239	on = opiegetsequence(&opie);
240	{
241	  char *c;
242	  if (c = strrchr(tmp, ' '))
243	    strncpy(oseed, c + 1, sizeof(oseed));
244	  else {
245#if DEBUG
246	    fprintf(stderr, "opiepasswd: bogus challenge\n");
247#endif /* DEBUG */
248	    finish(NULL);
249	  }
250	}
251	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
252	if (!opiereadpass(tmp, sizeof(tmp), 1))
253	  tmp[0] = 0;
254	i = opieverify(&opie, tmp);
255	if (!tmp[0]) {
256	  fprintf(stderr, "Error reading response.\n");
257	  finish(NULL);
258	}
259	if (i) {
260	  fprintf(stderr, "Error verifying response.\n");
261#if DEBUG
262	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
263#endif /* DEBUG */
264	  finish(NULL);
265	}
266	{
267	  char nseed[OPIE_SEED_MAX + 1];
268	  int nn;
269
270	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
271	    fprintf(stderr, "Error verifying database.\n");
272	    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