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