opiepasswd.c revision 59118
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).
4422347Spst*/
4522347Spst#include "opie_cfg.h"
4622347Spst
4722347Spst#if HAVE_PWD_H
4822347Spst#include <pwd.h>
4922347Spst#endif /* HAVE_PWD_H */
5022347Spst#include <stdio.h>
5122347Spst#if HAVE_STRING_H
5222347Spst#include <string.h>
5322347Spst#endif /* HAVE_STRING_H */
5422347Spst#include <stdio.h>
5522347Spst#include <sys/types.h>
5622347Spst#if HAVE_UNISTD_H
5722347Spst#include <unistd.h>
5822347Spst#endif /* HAVE_UNISTD_H */
5922347Spst#if HAVE_STDLIB_H
6022347Spst#include <stdlib.h>
6122347Spst#endif /* HAVE_STDLIB_H */
6222347Spst
6322347Spst#include "opie.h"
6422347Spst
6522347Spst#define MODE_DEFAULT 0
6622347Spst#define MODE_CONSOLE 1
6722347Spst#define MODE_DISABLE 2
6822347Spst
6922347Spstextern int optind;
7022347Spstextern char *optarg;
7122347Spst
7222347Spstchar *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
7322347Spstchar *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
7422347Spst
7522347Spststatic VOIDRET usage FUNCTION((myname), char *myname)
7622347Spst{
7722347Spst  fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
7822347Spst  exit(1);
7922347Spst}
8022347Spst
8122347Spststatic VOIDRET finish FUNCTION((name), char *name)
8222347Spst{
8322347Spst  struct opie opie;
8422347Spst  char buf[OPIE_RESPONSE_MAX + 1];
8522347Spst
8622347Spst  if (name) {
8722347Spst    if (opiechallenge(&opie, name, buf)) {
8822347Spst      fprintf(stderr, "Error verifying database.\n");
8922347Spst      finish(NULL);
9022347Spst    }
9122347Spst    printf("\nID %s ", opie.opie_principal);
9222347Spst    if (opie.opie_val && (opie.opie_val[0] == '*')) {
9322347Spst      printf("is disabled.\n");
9422347Spst      finish(NULL);
9522347Spst    }
9622347Spst    printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
9722347Spst    {
9822347Spst      char key[8];
9922347Spst      if (!opieatob8(key, opie.opie_val)) {
10022347Spst	fprintf(stderr, "Error verifying key -- possible database corruption.\n");
10122347Spst	finish(NULL);
10222347Spst      }
10322347Spst      printf("%s\n", opiebtoe(buf, key));
10422347Spst    }
10522347Spst  }
10622347Spst
10722347Spst  while(!opieunlock());
10822347Spst  exit(name ? 0 : 1);
10922347Spst}
11022347Spst
11122347Spstint main FUNCTION((argc, argv), int argc AND char *argv[])
11222347Spst{
11322347Spst  struct opie opie;
11422347Spst  int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
11559118Skris  char seed[OPIE_SEED_MAX+1];
11622347Spst  struct passwd *pp;
11722347Spst
11822347Spst  memset(seed, 0, sizeof(seed));
11922347Spst
12022347Spst  if (!(pp = getpwuid(getuid()))) {
12122347Spst    fprintf(stderr, "Who are you?");
12222347Spst    return 1;
12322347Spst  }
12422347Spst
12529964Sache  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
12622347Spst    switch (i) {
12722347Spst    case 'v':
12822347Spst      opieversion();
12922347Spst    case 'f':
13022347Spst#if INSECURE_OVERRIDE
13159118Skris      force = OPIEPASSWD_FORCE;
13222347Spst#else /* INSECURE_OVERRIDE */
13322347Spst      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
13422347Spst#endif /* INSECURE_OVERRIDE */
13522347Spst      break;
13622347Spst    case 'c':
13722347Spst      mode = MODE_CONSOLE;
13822347Spst      break;
13922347Spst    case 'd':
14022347Spst      mode = MODE_DISABLE;
14122347Spst      break;
14222347Spst    case 'n':
14322347Spst      i = atoi(optarg);
14422347Spst      if (!(i > 0 && i < 10000)) {
14522347Spst	printf("Sequence numbers must be > 0 and < 10000\n");
14622347Spst	finish(NULL);
14722347Spst      }
14822347Spst      n = i;
14922347Spst      break;
15022347Spst    case 's':
15122347Spst      i = strlen(optarg);
15222347Spst      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
15322347Spst	printf("Seeds must be between %d and %d characters long.\n",
15422347Spst	       OPIE_SEED_MIN, OPIE_SEED_MAX);
15522347Spst	finish(NULL);
15622347Spst      }
15722347Spst      strncpy(seed, optarg, sizeof(seed));
15822347Spst      seed[sizeof(seed) - 1] = 0;
15922347Spst      break;
16022347Spst    default:
16122347Spst      usage(argv[0]);
16222347Spst    }
16322347Spst  }
16422347Spst
16522347Spst  if (argc - optind >= 1) {
16622347Spst    if (strcmp(argv[optind], pp->pw_name)) {
16722347Spst      if (getuid()) {
16822347Spst	printf("Only root can change others' passwords.\n");
16922347Spst	exit(1);
17022347Spst      }
17122347Spst      if ((pp = getpwnam(argv[optind])) == NULL) {
17222347Spst	printf("%s: user unknown.\n", argv[optind]);
17322347Spst	exit(1);
17422347Spst      }
17522347Spst    }
17622347Spst  }
17722347Spst
17822347Spst  opielock(pp->pw_name);
17922347Spst  rval = opielookup(&opie, pp->pw_name);
18022347Spst
18122347Spst  switch (rval) {
18222347Spst  case 0:
18322347Spst    printf("Updating %s:\n", pp->pw_name);
18422347Spst    break;
18522347Spst  case 1:
18622347Spst    printf("Adding %s:\n", pp->pw_name);
18722347Spst    break;
18822347Spst  case 2:
18922347Spst    fprintf(stderr, "Error: Can't update key database.\n");
19059118Skris    finish(NULL);
19122347Spst  default:
19222347Spst    fprintf(stderr, "Error reading key database\n");
19359118Skris    finish(NULL);
19422347Spst  }
19522347Spst
19622347Spst  if (seed[0]) {
19722347Spst    i = strlen(seed);
19822347Spst    if (i > OPIE_SEED_MAX) {
19922347Spst      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
20022347Spst      finish(NULL);
20122347Spst    }
20222347Spst    if (i < OPIE_SEED_MIN) {
20322347Spst      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
20422347Spst      finish(NULL);
20522347Spst    }
20622347Spst  } else {
20722347Spst    if (!rval)
20822347Spst      strcpy(seed, opie.opie_seed);
20922347Spst
21022347Spst    if (opienewseed(seed) < 0) {
21122347Spst      fprintf(stderr, "Error updating seed.\n");
21222347Spst      finish(NULL);
21322347Spst    }
21422347Spst  }
21522347Spst
21622347Spst  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
21722347Spst    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
21822347Spst    finish(NULL);
21922347Spst  }
22022347Spst
22122347Spst  switch(mode) {
22222347Spst  case MODE_DEFAULT:
22322347Spst    {
22422347Spst      char tmp[OPIE_RESPONSE_MAX + 2];
22522347Spst
22622347Spst      printf("You need the response from an OTP generator.\n");
22722347Spst#if DEBUG
22822347Spst      if (!rval) {
22922347Spst#else /* DEBUG */
23022347Spst      if (!rval && getuid()) {
23122347Spst#endif /* DEBUG */
23222347Spst	char oseed[OPIE_SEED_MAX + 1];
23322347Spst	int on;
23422347Spst
23522347Spst	if (opiechallenge(&opie, pp->pw_name, tmp)) {
23622347Spst	  fprintf(stderr, "Error issuing challenge.\n");
23722347Spst	  finish(NULL);
23822347Spst	}
23922347Spst	on = opiegetsequence(&opie);
24022347Spst	{
24122347Spst	  char *c;
24222347Spst	  if (c = strrchr(tmp, ' '))
24322347Spst	    strncpy(oseed, c + 1, sizeof(oseed));
24422347Spst	  else {
24522347Spst#if DEBUG
24622347Spst	    fprintf(stderr, "opiepasswd: bogus challenge\n");
24722347Spst#endif /* DEBUG */
24822347Spst	    finish(NULL);
24922347Spst	  }
25022347Spst	}
25122347Spst	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
25222347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1))
25322347Spst	  tmp[0] = 0;
25422347Spst	i = opieverify(&opie, tmp);
25522347Spst	if (!tmp[0]) {
25622347Spst	  fprintf(stderr, "Error reading response.\n");
25722347Spst	  finish(NULL);
25822347Spst	}
25922347Spst	if (i) {
26022347Spst	  fprintf(stderr, "Error verifying response.\n");
26122347Spst#if DEBUG
26222347Spst	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
26322347Spst#endif /* DEBUG */
26422347Spst	  finish(NULL);
26522347Spst	}
26622347Spst	{
26722347Spst	  char nseed[OPIE_SEED_MAX + 1];
26822347Spst	  int nn;
26922347Spst
27022347Spst	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
27122347Spst	    fprintf(stderr, "Error verifying database.\n");
27222347Spst	    finish(NULL);
27322347Spst	  }
27422347Spst
27522347Spst	  nn = opiegetsequence(&opie);
27622347Spst	  {
27722347Spst	    char *c;
27822347Spst	    if (c = strrchr(tmp, ' '))
27922347Spst	      strncpy(nseed, c + 1, sizeof(nseed));
28022347Spst	    else {
28122347Spst#if DEBUG
28222347Spst	      fprintf(stderr, "opiepasswd: bogus challenge\n");
28322347Spst#endif /* DEBUG */
28422347Spst	      finish(NULL);
28522347Spst	    }
28622347Spst	  }
28722347Spst
28822347Spst	  opieverify(&opie, "");
28922347Spst	  nn++;
29022347Spst
29122347Spst	  if ((nn != on) || strcmp(oseed, nseed))
29222347Spst	    finish(pp->pw_name);
29322347Spst	}
29422347Spst      }
29522347Spst      printf("New secret pass phrase:");
29622347Spst      for (i = 0;; i++) {
29722347Spst	if (i > 2)
29822347Spst	  finish(NULL);
29922347Spst	printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
30022347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
30122347Spst	  fprintf(stderr, "Error reading response.\n");
30222347Spst	  finish(NULL);
30322347Spst	}
30422347Spst	if (tmp[0] == '?') {
30522347Spst	  printf("Enter the response from your OTP calculator: \n");
30622347Spst	  continue;
30722347Spst	}
30822347Spst	if (tmp[0] == '\0') {
30922347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
31022347Spst	  finish(NULL);
31122347Spst	}
31222347Spst
31359118Skris	if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
31422347Spst	  finish(pp->pw_name);
31522347Spst
31622347Spst	if (rval < 0) {
31722347Spst	  fprintf(stderr, "Error updating key database.\n");
31822347Spst	  finish(NULL);
31922347Spst	}
32022347Spst	printf("\tThat is not a valid OTP response.\n");
32122347Spst      }
32222347Spst    }
32322347Spst    break;
32422347Spst  case MODE_CONSOLE:
32522347Spst    {
32622347Spst      char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
32722347Spst      /* Get user's secret password */
32822347Spst      fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
32922347Spst      fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
33022347Spst      fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
33159118Skris      if (opieinsecure() && !force) {
33222347Spst	fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
33322347Spst	if (force)
33422347Spst          fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
33522347Spst        else
33622347Spst          finish(NULL);
33722347Spst      };
33822347Spst      printf("Using %s to compute responses.\n", algnames[MDX]);
33922347Spst      if (!rval && getuid()) {
34022347Spst	printf("Enter old secret pass phrase: ");
34122347Spst	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
34222347Spst	  fprintf(stderr, "Error reading secret pass phrase!\n");
34322347Spst	  finish(NULL);
34422347Spst	}
34522347Spst	if (!passwd[0]) {
34622347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
34722347Spst	  finish(NULL);
34822347Spst	}
34922347Spst	{
35022347Spst	  char key[8];
35122347Spst	  char tbuf[OPIE_RESPONSE_MAX + 1];
35222347Spst
35322347Spst	  if (opiekeycrunch(MDX, key, opie.opie_seed, passwd) != 0) {
35422347Spst	    fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
35522347Spst	    finish(NULL);
35622347Spst	  }
35722347Spst	  memset(passwd, 0, sizeof(passwd));
35822347Spst	  i = opie.opie_n - 1;
35922347Spst	  while (i-- != 0)
36022347Spst	    opiehash(key, MDX);
36122347Spst	  opiebtoe(tbuf, key);
36222347Spst	  if (opieverify(&opie, tbuf)) {
36322347Spst	    fprintf(stderr, "Sorry.\n");
36422347Spst	    finish(NULL);
36522347Spst	  }
36622347Spst	}
36722347Spst      }
36822347Spst      for (i = 0;; i++) {
36922347Spst	if (i > 2)
37022347Spst	  finish(NULL);
37122347Spst	printf("Enter new secret pass phrase: ");
37222347Spst	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
37322347Spst	  fprintf(stderr, "Error reading secret pass phrase.\n");
37422347Spst	  finish(NULL);
37522347Spst	}
37622347Spst	if (!passwd[0] || feof(stdin)) {
37722347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
37822347Spst	  finish(NULL);
37922347Spst	}
38022347Spst	if (opiepasscheck(passwd)) {
38122347Spst	  memset(passwd, 0, sizeof(passwd));
38222347Spst	  fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
38322347Spst	  continue;
38422347Spst	}
38522347Spst	printf("Again new secret pass phrase: ");
38622347Spst	if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
38722347Spst	  fprintf(stderr, "Error reading secret pass phrase.\n");
38822347Spst	  finish(NULL);
38922347Spst	}
39022347Spst	if (feof(stdin)) {
39122347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
39222347Spst	  finish(NULL);
39322347Spst	}
39422347Spst	if (!passwd[0] || !strcmp(passwd, passwd2))
39522347Spst	  break;
39622347Spst	fprintf(stderr, "Sorry, no match.\n");
39722347Spst      }
39822347Spst      memset(passwd2, 0, sizeof(passwd2));
39959118Skris      if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
40022347Spst	fprintf(stderr, "Error updating key database.\n");
40122347Spst	finish(NULL);
40222347Spst      }
40322347Spst      finish(pp->pw_name);
40422347Spst    }
40522347Spst  case MODE_DISABLE:
40622347Spst    {
40722347Spst      char tmp[4];
40822347Spst      int i;
40922347Spst
41022347Spst      for (i = 0;; i++) {
41122347Spst	if (i > 2)
41222347Spst	  finish(NULL);
41322347Spst
41422347Spst	printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
41522347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
41622347Spst	  fprintf(stderr, "Error reading entry.\n");
41722347Spst	  finish(NULL);
41822347Spst	}
41922347Spst	if (!strcmp(tmp, "no"))
42022347Spst	  finish(NULL);
42122347Spst	if (!strcmp(tmp, "yes")) {
42222347Spst	  if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
42322347Spst	    fprintf(stderr, "Error updating key database.\n");
42422347Spst	    finish(NULL);
42522347Spst	  }
42622347Spst	  finish(pp->pw_name);
42722347Spst	}
42822347Spst      }
42922347Spst    }
43022347Spst  }
43122347Spst}
432