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: releng/11.0/contrib/opie/opiepasswd.c 156997 2006-03-22 16:00:42Z cperciva $
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  char *username;
122  uid_t ruid;
123  struct passwd *pp;
124
125  memset(seed, 0, sizeof(seed));
126
127  ruid = getuid();
128  username = getlogin();
129  pp = getpwnam(username);
130  if (username == NULL || pp == NULL || pp->pw_uid != ruid)
131    pp = getpwuid(ruid);
132  if (pp == NULL) {
133    fprintf(stderr, "Who are you?");
134    return 1;
135  }
136
137  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
138    switch (i) {
139    case 'v':
140      opieversion();
141    case 'f':
142#if INSECURE_OVERRIDE
143      force = OPIEPASSWD_FORCE;
144#else /* INSECURE_OVERRIDE */
145      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
146#endif /* INSECURE_OVERRIDE */
147      break;
148    case 'c':
149      mode = MODE_CONSOLE;
150      break;
151    case 'd':
152      mode = MODE_DISABLE;
153      break;
154    case 'n':
155      i = atoi(optarg);
156      if (!(i > 0 && i < 10000)) {
157	printf("Sequence numbers must be > 0 and < 10000\n");
158	finish(NULL);
159      }
160      n = i;
161      break;
162    case 's':
163      i = strlen(optarg);
164      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
165	printf("Seeds must be between %d and %d characters long.\n",
166	       OPIE_SEED_MIN, OPIE_SEED_MAX);
167	finish(NULL);
168      }
169      opiestrncpy(seed, optarg, sizeof(seed));
170      break;
171    default:
172      usage(argv[0]);
173    }
174  }
175
176  if (argc - optind >= 1) {
177    if (strcmp(argv[optind], pp->pw_name)) {
178      if (getuid()) {
179	printf("Only root can change others' passwords.\n");
180	exit(1);
181      }
182      if ((pp = getpwnam(argv[optind])) == NULL) {
183	printf("%s: user unknown.\n", argv[optind]);
184	exit(1);
185      }
186    }
187  }
188
189  opielock(pp->pw_name);
190  rval = opielookup(&opie, pp->pw_name);
191
192  switch (rval) {
193  case 0:
194    printf("Updating %s:\n", pp->pw_name);
195    break;
196  case 1:
197    printf("Adding %s:\n", pp->pw_name);
198    break;
199  case 2:
200    fprintf(stderr, "Error: Can't update key database.\n");
201    finish(NULL);
202  default:
203    fprintf(stderr, "Error reading key database\n");
204    finish(NULL);
205  }
206
207  if (seed[0]) {
208    i = strlen(seed);
209    if (i > OPIE_SEED_MAX) {
210      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
211      finish(NULL);
212    }
213    if (i < OPIE_SEED_MIN) {
214      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
215      finish(NULL);
216    }
217  } else {
218    if (!rval)
219      strcpy(seed, opie.opie_seed);
220
221    if (opienewseed(seed) < 0) {
222      fprintf(stderr, "Error updating seed.\n");
223      finish(NULL);
224    }
225  }
226
227  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
228    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
229    finish(NULL);
230  }
231
232  switch(mode) {
233  case MODE_DEFAULT:
234    {
235      char tmp[OPIE_RESPONSE_MAX + 2];
236
237      printf("You need the response from an OTP generator.\n");
238#if DEBUG
239      if (!rval) {
240#else /* DEBUG */
241      if (!rval && getuid()) {
242#endif /* DEBUG */
243	char oseed[OPIE_SEED_MAX + 1];
244	int on;
245
246	if (opiechallenge(&opie, pp->pw_name, tmp)) {
247	  fprintf(stderr, "Error issuing challenge.\n");
248	  finish(NULL);
249	}
250	on = opiegetsequence(&opie);
251	{
252	  char *c;
253	  if (c = strrchr(tmp, ' '))
254	    opiestrncpy(oseed, c + 1, sizeof(oseed));
255	  else {
256#if DEBUG
257	    fprintf(stderr, "opiepasswd: bogus challenge\n");
258#endif /* DEBUG */
259	    finish(NULL);
260	  }
261	}
262	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
263	if (!opiereadpass(tmp, sizeof(tmp), 1))
264	  tmp[0] = 0;
265	i = opieverify(&opie, tmp);
266	if (!tmp[0]) {
267	  fprintf(stderr, "Error reading response.\n");
268	  finish(NULL);
269	}
270	if (i) {
271	  fprintf(stderr, "Error verifying response.\n");
272#if DEBUG
273	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
274#endif /* DEBUG */
275	  finish(NULL);
276	}
277	{
278	  char nseed[OPIE_SEED_MAX + 1];
279	  int nn;
280
281	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
282	    fprintf(stderr, "Error verifying database.\n");
283	    finish(NULL);
284	  }
285
286	  nn = opiegetsequence(&opie);
287	  {
288	    char *c;
289	    if (c = strrchr(tmp, ' '))
290	      opiestrncpy(nseed, c + 1, sizeof(nseed));
291	    else {
292#if DEBUG
293	      fprintf(stderr, "opiepasswd: bogus challenge\n");
294#endif /* DEBUG */
295	      finish(NULL);
296	    }
297	  }
298
299	  opieverify(&opie, "");
300	  nn++;
301
302	  if ((nn != on) || strcmp(oseed, nseed))
303	    finish(pp->pw_name);
304	}
305      }
306      printf("New secret pass phrase:");
307      for (i = 0;; i++) {
308	if (i > 2)
309	  finish(NULL);
310	printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
311	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
312	  fprintf(stderr, "Error reading response.\n");
313	  finish(NULL);
314	}
315	if (tmp[0] == '?') {
316	  printf("Enter the response from your OTP calculator: \n");
317	  continue;
318	}
319	if (tmp[0] == '\0') {
320	  fprintf(stderr, "Secret pass phrase unchanged.\n");
321	  finish(NULL);
322	}
323
324	if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
325	  finish(pp->pw_name);
326
327	if (rval < 0) {
328	  fprintf(stderr, "Error updating key database.\n");
329	  finish(NULL);
330	}
331	printf("\tThat is not a valid OTP response.\n");
332      }
333    }
334    break;
335  case MODE_CONSOLE:
336    {
337      char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
338      /* Get user's secret password */
339      fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
340      fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
341      fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
342      if (opieinsecure() && !force) {
343	fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
344	if (force)
345          fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
346        else
347          finish(NULL);
348      };
349      printf("Using %s to compute responses.\n", algnames[MDX]);
350      if (!rval && getuid()) {
351	printf("Enter old secret pass phrase: ");
352	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
353	  fprintf(stderr, "Error reading secret pass phrase!\n");
354	  finish(NULL);
355	}
356	if (!passwd[0]) {
357	  fprintf(stderr, "Secret pass phrase unchanged.\n");
358	  finish(NULL);
359	}
360	{
361	  struct opie_otpkey key;
362	  char tbuf[OPIE_RESPONSE_MAX + 1];
363
364	  if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) {
365	    fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
366	    finish(NULL);
367	  }
368	  memset(passwd, 0, sizeof(passwd));
369	  i = opie.opie_n - 1;
370	  while (i-- != 0)
371	    opiehash(&key, MDX);
372	  opiebtoe(tbuf, &key);
373	  if (opieverify(&opie, tbuf)) {
374	    fprintf(stderr, "Sorry.\n");
375	    finish(NULL);
376	  }
377	}
378      }
379      for (i = 0;; i++) {
380	if (i > 2)
381	  finish(NULL);
382	printf("Enter new secret pass phrase: ");
383	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
384	  fprintf(stderr, "Error reading secret pass phrase.\n");
385	  finish(NULL);
386	}
387	if (!passwd[0] || feof(stdin)) {
388	  fprintf(stderr, "Secret pass phrase unchanged.\n");
389	  finish(NULL);
390	}
391	if (opiepasscheck(passwd)) {
392	  memset(passwd, 0, sizeof(passwd));
393	  fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
394	  continue;
395	}
396	printf("Again new secret pass phrase: ");
397	if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
398	  fprintf(stderr, "Error reading secret pass phrase.\n");
399	  finish(NULL);
400	}
401	if (feof(stdin)) {
402	  fprintf(stderr, "Secret pass phrase unchanged.\n");
403	  finish(NULL);
404	}
405	if (!passwd[0] || !strcmp(passwd, passwd2))
406	  break;
407	fprintf(stderr, "Sorry, no match.\n");
408      }
409      memset(passwd2, 0, sizeof(passwd2));
410      if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
411	fprintf(stderr, "Error updating key database.\n");
412	finish(NULL);
413      }
414      finish(pp->pw_name);
415    }
416  case MODE_DISABLE:
417    {
418      char tmp[4];
419      int i;
420
421      for (i = 0;; i++) {
422	if (i > 2)
423	  finish(NULL);
424
425	printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
426	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
427	  fprintf(stderr, "Error reading entry.\n");
428	  finish(NULL);
429	}
430	if (!strcmp(tmp, "no"))
431	  finish(NULL);
432	if (!strcmp(tmp, "yes")) {
433	  if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
434	    fprintf(stderr, "Error updating key database.\n");
435	    finish(NULL);
436	  }
437	  finish(pp->pw_name);
438	}
439      }
440    }
441  }
442}
443