opiepasswd.c revision 92914
122347Spst/* opiepasswd.c: Add/change an OTP password in the key database.
222347Spst
329964Sache%%% portions-copyright-cmetz-96
492914SmarkmPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights
522347SpstReserved. The Inner Net License Version 2 applies to these portions of
622347Spstthe software.
722347SpstYou should have received a copy of the license with this software. If
822347Spstyou didn't get a copy, you may request one from <license@inner.net>.
922347Spst
1022347SpstPortions of this software are Copyright 1995 by Randall Atkinson and Dan
1122347SpstMcDonald, All Rights Reserved. All Rights under this copyright are assigned
1222347Spstto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
1322347SpstLicense Agreement applies to this software.
1422347Spst
1522347Spst	History:
1622347Spst
1792914Smarkm	Modified by cmetz for OPIE 2.4. Use struct opie_key for key blocks.
1892914Smarkm		Use opiestrncpy().
1959118Skris	Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of
2059118Skris		hard coding the length. Unlock user on failed lookup.
2122347Spst	Modified by cmetz for OPIE 2.3. Got of some variables and made some
2222347Spst		local to where they're used. Split out the finishing code. Use
2322347Spst		opielookup() instead of opiechallenge() to find user. Three
2422347Spst		strikes on prompts. Use opiepasswd()'s new calling
2522347Spst		convention. Changed OPIE_PASS_{MAX,MIN} to
2622347Spst		OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning
2722347Spst		below us. Got rid of unneeded headers. Use new opieatob8()
2822347Spst		return value convention. Added -f flag. Added SHA support.
2922347Spst	Modified by cmetz for OPIE 2.22. Finally got rid of the lock
3022347Spst	        filename kluge by implementing refcounts for locks.
3122347Spst		Use opiepasswd() to update key file. Error if we can't
3222347Spst		write to the key file. Check for minimum seed length.
3322347Spst        Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to
3422347Spst                opiestripcrlf. Check opiereadpass() return value.
3522347Spst                Minor optimization. Change calls to opiereadpass() to
3622347Spst                use echo arg. Use opiereadpass() where we can.
3722347Spst                Make everything static. Ifdef around some headers.
3822347Spst                Changed use of gethostname() to uname(). Got rid of
3922347Spst                the need for buf[]. Properly check return value of
4022347Spst                opieatob8. Check seed length. Always generate proper-
4122347Spst                length seeds.
4222347Spst	Modified at NRL for OPIE 2.1. Minor autoconf changes.
4322347Spst        Modified heavily at NRL for OPIE 2.0.
4422347Spst	Written at Bellcore for the S/Key Version 1 software distribution
4522347Spst		(skeyinit.c).
4679710Smarkm
4779710Smarkm $FreeBSD: head/contrib/opie/opiepasswd.c 92914 2002-03-21 23:42:52Z markm $
4822347Spst*/
4922347Spst#include "opie_cfg.h"
5022347Spst
5122347Spst#if HAVE_PWD_H
5222347Spst#include <pwd.h>
5322347Spst#endif /* HAVE_PWD_H */
5422347Spst#include <stdio.h>
5522347Spst#if HAVE_STRING_H
5622347Spst#include <string.h>
5722347Spst#endif /* HAVE_STRING_H */
5822347Spst#include <stdio.h>
5922347Spst#include <sys/types.h>
6022347Spst#if HAVE_UNISTD_H
6122347Spst#include <unistd.h>
6222347Spst#endif /* HAVE_UNISTD_H */
6322347Spst#if HAVE_STDLIB_H
6422347Spst#include <stdlib.h>
6522347Spst#endif /* HAVE_STDLIB_H */
6622347Spst
6722347Spst#include "opie.h"
6822347Spst
6922347Spst#define MODE_DEFAULT 0
7022347Spst#define MODE_CONSOLE 1
7122347Spst#define MODE_DISABLE 2
7222347Spst
7322347Spstextern int optind;
7422347Spstextern char *optarg;
7522347Spst
7622347Spstchar *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
7722347Spstchar *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
7822347Spst
7922347Spststatic VOIDRET usage FUNCTION((myname), char *myname)
8022347Spst{
8122347Spst  fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
8222347Spst  exit(1);
8322347Spst}
8422347Spst
8522347Spststatic VOIDRET finish FUNCTION((name), char *name)
8622347Spst{
8722347Spst  struct opie opie;
8822347Spst  char buf[OPIE_RESPONSE_MAX + 1];
8922347Spst
9022347Spst  if (name) {
9122347Spst    if (opiechallenge(&opie, name, buf)) {
9222347Spst      fprintf(stderr, "Error verifying database.\n");
9322347Spst      finish(NULL);
9422347Spst    }
9522347Spst    printf("\nID %s ", opie.opie_principal);
9622347Spst    if (opie.opie_val && (opie.opie_val[0] == '*')) {
9722347Spst      printf("is disabled.\n");
9822347Spst      finish(NULL);
9922347Spst    }
10022347Spst    printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
10122347Spst    {
10292914Smarkm      struct opie_otpkey key;
10392914Smarkm
10492914Smarkm      if (!opieatob8(&key, opie.opie_val)) {
10522347Spst	fprintf(stderr, "Error verifying key -- possible database corruption.\n");
10622347Spst	finish(NULL);
10722347Spst      }
10892914Smarkm      printf("%s\n", opiebtoe(buf, &key));
10922347Spst    }
11022347Spst  }
11122347Spst
11222347Spst  while(!opieunlock());
11322347Spst  exit(name ? 0 : 1);
11422347Spst}
11522347Spst
11622347Spstint main FUNCTION((argc, argv), int argc AND char *argv[])
11722347Spst{
11822347Spst  struct opie opie;
11922347Spst  int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
12059118Skris  char seed[OPIE_SEED_MAX+1];
12122347Spst  struct passwd *pp;
12222347Spst
12322347Spst  memset(seed, 0, sizeof(seed));
12422347Spst
12579710Smarkm  if (!(pp = getpwnam(getlogin()))) {
12622347Spst    fprintf(stderr, "Who are you?");
12722347Spst    return 1;
12822347Spst  }
12922347Spst
13029964Sache  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
13122347Spst    switch (i) {
13222347Spst    case 'v':
13322347Spst      opieversion();
13422347Spst    case 'f':
13522347Spst#if INSECURE_OVERRIDE
13659118Skris      force = OPIEPASSWD_FORCE;
13722347Spst#else /* INSECURE_OVERRIDE */
13822347Spst      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
13922347Spst#endif /* INSECURE_OVERRIDE */
14022347Spst      break;
14122347Spst    case 'c':
14222347Spst      mode = MODE_CONSOLE;
14322347Spst      break;
14422347Spst    case 'd':
14522347Spst      mode = MODE_DISABLE;
14622347Spst      break;
14722347Spst    case 'n':
14822347Spst      i = atoi(optarg);
14922347Spst      if (!(i > 0 && i < 10000)) {
15022347Spst	printf("Sequence numbers must be > 0 and < 10000\n");
15122347Spst	finish(NULL);
15222347Spst      }
15322347Spst      n = i;
15422347Spst      break;
15522347Spst    case 's':
15622347Spst      i = strlen(optarg);
15722347Spst      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
15822347Spst	printf("Seeds must be between %d and %d characters long.\n",
15922347Spst	       OPIE_SEED_MIN, OPIE_SEED_MAX);
16022347Spst	finish(NULL);
16122347Spst      }
16292914Smarkm      opiestrncpy(seed, optarg, sizeof(seed));
16322347Spst      break;
16422347Spst    default:
16522347Spst      usage(argv[0]);
16622347Spst    }
16722347Spst  }
16822347Spst
16922347Spst  if (argc - optind >= 1) {
17022347Spst    if (strcmp(argv[optind], pp->pw_name)) {
17122347Spst      if (getuid()) {
17222347Spst	printf("Only root can change others' passwords.\n");
17322347Spst	exit(1);
17422347Spst      }
17522347Spst      if ((pp = getpwnam(argv[optind])) == NULL) {
17622347Spst	printf("%s: user unknown.\n", argv[optind]);
17722347Spst	exit(1);
17822347Spst      }
17922347Spst    }
18022347Spst  }
18122347Spst
18222347Spst  opielock(pp->pw_name);
18322347Spst  rval = opielookup(&opie, pp->pw_name);
18422347Spst
18522347Spst  switch (rval) {
18622347Spst  case 0:
18722347Spst    printf("Updating %s:\n", pp->pw_name);
18822347Spst    break;
18922347Spst  case 1:
19022347Spst    printf("Adding %s:\n", pp->pw_name);
19122347Spst    break;
19222347Spst  case 2:
19322347Spst    fprintf(stderr, "Error: Can't update key database.\n");
19459118Skris    finish(NULL);
19522347Spst  default:
19622347Spst    fprintf(stderr, "Error reading key database\n");
19759118Skris    finish(NULL);
19822347Spst  }
19922347Spst
20022347Spst  if (seed[0]) {
20122347Spst    i = strlen(seed);
20222347Spst    if (i > OPIE_SEED_MAX) {
20322347Spst      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
20422347Spst      finish(NULL);
20522347Spst    }
20622347Spst    if (i < OPIE_SEED_MIN) {
20722347Spst      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
20822347Spst      finish(NULL);
20922347Spst    }
21022347Spst  } else {
21122347Spst    if (!rval)
21222347Spst      strcpy(seed, opie.opie_seed);
21322347Spst
21422347Spst    if (opienewseed(seed) < 0) {
21522347Spst      fprintf(stderr, "Error updating seed.\n");
21622347Spst      finish(NULL);
21722347Spst    }
21822347Spst  }
21922347Spst
22022347Spst  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
22122347Spst    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
22222347Spst    finish(NULL);
22322347Spst  }
22422347Spst
22522347Spst  switch(mode) {
22622347Spst  case MODE_DEFAULT:
22722347Spst    {
22822347Spst      char tmp[OPIE_RESPONSE_MAX + 2];
22922347Spst
23022347Spst      printf("You need the response from an OTP generator.\n");
23122347Spst#if DEBUG
23222347Spst      if (!rval) {
23322347Spst#else /* DEBUG */
23422347Spst      if (!rval && getuid()) {
23522347Spst#endif /* DEBUG */
23622347Spst	char oseed[OPIE_SEED_MAX + 1];
23722347Spst	int on;
23822347Spst
23922347Spst	if (opiechallenge(&opie, pp->pw_name, tmp)) {
24022347Spst	  fprintf(stderr, "Error issuing challenge.\n");
24122347Spst	  finish(NULL);
24222347Spst	}
24322347Spst	on = opiegetsequence(&opie);
24422347Spst	{
24522347Spst	  char *c;
24622347Spst	  if (c = strrchr(tmp, ' '))
24792914Smarkm	    opiestrncpy(oseed, c + 1, sizeof(oseed));
24822347Spst	  else {
24922347Spst#if DEBUG
25022347Spst	    fprintf(stderr, "opiepasswd: bogus challenge\n");
25122347Spst#endif /* DEBUG */
25222347Spst	    finish(NULL);
25322347Spst	  }
25422347Spst	}
25522347Spst	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
25622347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1))
25722347Spst	  tmp[0] = 0;
25822347Spst	i = opieverify(&opie, tmp);
25922347Spst	if (!tmp[0]) {
26022347Spst	  fprintf(stderr, "Error reading response.\n");
26122347Spst	  finish(NULL);
26222347Spst	}
26322347Spst	if (i) {
26422347Spst	  fprintf(stderr, "Error verifying response.\n");
26522347Spst#if DEBUG
26622347Spst	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
26722347Spst#endif /* DEBUG */
26822347Spst	  finish(NULL);
26922347Spst	}
27022347Spst	{
27122347Spst	  char nseed[OPIE_SEED_MAX + 1];
27222347Spst	  int nn;
27322347Spst
27422347Spst	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
27522347Spst	    fprintf(stderr, "Error verifying database.\n");
27622347Spst	    finish(NULL);
27722347Spst	  }
27822347Spst
27922347Spst	  nn = opiegetsequence(&opie);
28022347Spst	  {
28122347Spst	    char *c;
28222347Spst	    if (c = strrchr(tmp, ' '))
28392914Smarkm	      opiestrncpy(nseed, c + 1, sizeof(nseed));
28422347Spst	    else {
28522347Spst#if DEBUG
28622347Spst	      fprintf(stderr, "opiepasswd: bogus challenge\n");
28722347Spst#endif /* DEBUG */
28822347Spst	      finish(NULL);
28922347Spst	    }
29022347Spst	  }
29122347Spst
29222347Spst	  opieverify(&opie, "");
29322347Spst	  nn++;
29422347Spst
29522347Spst	  if ((nn != on) || strcmp(oseed, nseed))
29622347Spst	    finish(pp->pw_name);
29722347Spst	}
29822347Spst      }
29922347Spst      printf("New secret pass phrase:");
30022347Spst      for (i = 0;; i++) {
30122347Spst	if (i > 2)
30222347Spst	  finish(NULL);
30322347Spst	printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
30422347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
30522347Spst	  fprintf(stderr, "Error reading response.\n");
30622347Spst	  finish(NULL);
30722347Spst	}
30822347Spst	if (tmp[0] == '?') {
30922347Spst	  printf("Enter the response from your OTP calculator: \n");
31022347Spst	  continue;
31122347Spst	}
31222347Spst	if (tmp[0] == '\0') {
31322347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
31422347Spst	  finish(NULL);
31522347Spst	}
31622347Spst
31759118Skris	if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
31822347Spst	  finish(pp->pw_name);
31922347Spst
32022347Spst	if (rval < 0) {
32122347Spst	  fprintf(stderr, "Error updating key database.\n");
32222347Spst	  finish(NULL);
32322347Spst	}
32422347Spst	printf("\tThat is not a valid OTP response.\n");
32522347Spst      }
32622347Spst    }
32722347Spst    break;
32822347Spst  case MODE_CONSOLE:
32922347Spst    {
33022347Spst      char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
33122347Spst      /* Get user's secret password */
33222347Spst      fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
33322347Spst      fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
33422347Spst      fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
33559118Skris      if (opieinsecure() && !force) {
33622347Spst	fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
33722347Spst	if (force)
33822347Spst          fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
33922347Spst        else
34022347Spst          finish(NULL);
34122347Spst      };
34222347Spst      printf("Using %s to compute responses.\n", algnames[MDX]);
34322347Spst      if (!rval && getuid()) {
34422347Spst	printf("Enter old secret pass phrase: ");
34522347Spst	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
34622347Spst	  fprintf(stderr, "Error reading secret pass phrase!\n");
34722347Spst	  finish(NULL);
34822347Spst	}
34922347Spst	if (!passwd[0]) {
35022347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
35122347Spst	  finish(NULL);
35222347Spst	}
35322347Spst	{
35492914Smarkm	  struct opie_otpkey key;
35522347Spst	  char tbuf[OPIE_RESPONSE_MAX + 1];
35622347Spst
35792914Smarkm	  if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) {
35822347Spst	    fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
35922347Spst	    finish(NULL);
36022347Spst	  }
36122347Spst	  memset(passwd, 0, sizeof(passwd));
36222347Spst	  i = opie.opie_n - 1;
36322347Spst	  while (i-- != 0)
36492914Smarkm	    opiehash(&key, MDX);
36592914Smarkm	  opiebtoe(tbuf, &key);
36622347Spst	  if (opieverify(&opie, tbuf)) {
36722347Spst	    fprintf(stderr, "Sorry.\n");
36822347Spst	    finish(NULL);
36922347Spst	  }
37022347Spst	}
37122347Spst      }
37222347Spst      for (i = 0;; i++) {
37322347Spst	if (i > 2)
37422347Spst	  finish(NULL);
37522347Spst	printf("Enter new secret pass phrase: ");
37622347Spst	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
37722347Spst	  fprintf(stderr, "Error reading secret pass phrase.\n");
37822347Spst	  finish(NULL);
37922347Spst	}
38022347Spst	if (!passwd[0] || feof(stdin)) {
38122347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
38222347Spst	  finish(NULL);
38322347Spst	}
38422347Spst	if (opiepasscheck(passwd)) {
38522347Spst	  memset(passwd, 0, sizeof(passwd));
38622347Spst	  fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
38722347Spst	  continue;
38822347Spst	}
38922347Spst	printf("Again new secret pass phrase: ");
39022347Spst	if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
39122347Spst	  fprintf(stderr, "Error reading secret pass phrase.\n");
39222347Spst	  finish(NULL);
39322347Spst	}
39422347Spst	if (feof(stdin)) {
39522347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
39622347Spst	  finish(NULL);
39722347Spst	}
39822347Spst	if (!passwd[0] || !strcmp(passwd, passwd2))
39922347Spst	  break;
40022347Spst	fprintf(stderr, "Sorry, no match.\n");
40122347Spst      }
40222347Spst      memset(passwd2, 0, sizeof(passwd2));
40359118Skris      if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
40422347Spst	fprintf(stderr, "Error updating key database.\n");
40522347Spst	finish(NULL);
40622347Spst      }
40722347Spst      finish(pp->pw_name);
40822347Spst    }
40922347Spst  case MODE_DISABLE:
41022347Spst    {
41122347Spst      char tmp[4];
41222347Spst      int i;
41322347Spst
41422347Spst      for (i = 0;; i++) {
41522347Spst	if (i > 2)
41622347Spst	  finish(NULL);
41722347Spst
41822347Spst	printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
41922347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
42022347Spst	  fprintf(stderr, "Error reading entry.\n");
42122347Spst	  finish(NULL);
42222347Spst	}
42322347Spst	if (!strcmp(tmp, "no"))
42422347Spst	  finish(NULL);
42522347Spst	if (!strcmp(tmp, "yes")) {
42622347Spst	  if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
42722347Spst	    fprintf(stderr, "Error updating key database.\n");
42822347Spst	    finish(NULL);
42922347Spst	  }
43022347Spst	  finish(pp->pw_name);
43122347Spst	}
43222347Spst      }
43322347Spst    }
43422347Spst  }
43522347Spst}
436