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$
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];
121156997Scperciva  char *username;
122156997Scperciva  uid_t ruid;
12322347Spst  struct passwd *pp;
12422347Spst
12522347Spst  memset(seed, 0, sizeof(seed));
12622347Spst
127156997Scperciva  ruid = getuid();
128156997Scperciva  username = getlogin();
129156997Scperciva  pp = getpwnam(username);
130156997Scperciva  if (username == NULL || pp == NULL || pp->pw_uid != ruid)
131156997Scperciva    pp = getpwuid(ruid);
132156997Scperciva  if (pp == NULL) {
13322347Spst    fprintf(stderr, "Who are you?");
13422347Spst    return 1;
13522347Spst  }
13622347Spst
13729964Sache  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
13822347Spst    switch (i) {
13922347Spst    case 'v':
14022347Spst      opieversion();
14122347Spst    case 'f':
14222347Spst#if INSECURE_OVERRIDE
14359118Skris      force = OPIEPASSWD_FORCE;
14422347Spst#else /* INSECURE_OVERRIDE */
14522347Spst      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
14622347Spst#endif /* INSECURE_OVERRIDE */
14722347Spst      break;
14822347Spst    case 'c':
14922347Spst      mode = MODE_CONSOLE;
15022347Spst      break;
15122347Spst    case 'd':
15222347Spst      mode = MODE_DISABLE;
15322347Spst      break;
15422347Spst    case 'n':
15522347Spst      i = atoi(optarg);
15622347Spst      if (!(i > 0 && i < 10000)) {
15722347Spst	printf("Sequence numbers must be > 0 and < 10000\n");
15822347Spst	finish(NULL);
15922347Spst      }
16022347Spst      n = i;
16122347Spst      break;
16222347Spst    case 's':
16322347Spst      i = strlen(optarg);
16422347Spst      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
16522347Spst	printf("Seeds must be between %d and %d characters long.\n",
16622347Spst	       OPIE_SEED_MIN, OPIE_SEED_MAX);
16722347Spst	finish(NULL);
16822347Spst      }
16992914Smarkm      opiestrncpy(seed, optarg, sizeof(seed));
17022347Spst      break;
17122347Spst    default:
17222347Spst      usage(argv[0]);
17322347Spst    }
17422347Spst  }
17522347Spst
17622347Spst  if (argc - optind >= 1) {
17722347Spst    if (strcmp(argv[optind], pp->pw_name)) {
17822347Spst      if (getuid()) {
17922347Spst	printf("Only root can change others' passwords.\n");
18022347Spst	exit(1);
18122347Spst      }
18222347Spst      if ((pp = getpwnam(argv[optind])) == NULL) {
18322347Spst	printf("%s: user unknown.\n", argv[optind]);
18422347Spst	exit(1);
18522347Spst      }
18622347Spst    }
18722347Spst  }
18822347Spst
18922347Spst  opielock(pp->pw_name);
19022347Spst  rval = opielookup(&opie, pp->pw_name);
19122347Spst
19222347Spst  switch (rval) {
19322347Spst  case 0:
19422347Spst    printf("Updating %s:\n", pp->pw_name);
19522347Spst    break;
19622347Spst  case 1:
19722347Spst    printf("Adding %s:\n", pp->pw_name);
19822347Spst    break;
19922347Spst  case 2:
20022347Spst    fprintf(stderr, "Error: Can't update key database.\n");
20159118Skris    finish(NULL);
20222347Spst  default:
20322347Spst    fprintf(stderr, "Error reading key database\n");
20459118Skris    finish(NULL);
20522347Spst  }
20622347Spst
20722347Spst  if (seed[0]) {
20822347Spst    i = strlen(seed);
20922347Spst    if (i > OPIE_SEED_MAX) {
21022347Spst      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
21122347Spst      finish(NULL);
21222347Spst    }
21322347Spst    if (i < OPIE_SEED_MIN) {
21422347Spst      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
21522347Spst      finish(NULL);
21622347Spst    }
21722347Spst  } else {
21822347Spst    if (!rval)
21922347Spst      strcpy(seed, opie.opie_seed);
22022347Spst
22122347Spst    if (opienewseed(seed) < 0) {
22222347Spst      fprintf(stderr, "Error updating seed.\n");
22322347Spst      finish(NULL);
22422347Spst    }
22522347Spst  }
22622347Spst
22722347Spst  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
22822347Spst    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
22922347Spst    finish(NULL);
23022347Spst  }
23122347Spst
23222347Spst  switch(mode) {
23322347Spst  case MODE_DEFAULT:
23422347Spst    {
23522347Spst      char tmp[OPIE_RESPONSE_MAX + 2];
23622347Spst
23722347Spst      printf("You need the response from an OTP generator.\n");
23822347Spst#if DEBUG
23922347Spst      if (!rval) {
24022347Spst#else /* DEBUG */
24122347Spst      if (!rval && getuid()) {
24222347Spst#endif /* DEBUG */
24322347Spst	char oseed[OPIE_SEED_MAX + 1];
24422347Spst	int on;
24522347Spst
24622347Spst	if (opiechallenge(&opie, pp->pw_name, tmp)) {
24722347Spst	  fprintf(stderr, "Error issuing challenge.\n");
24822347Spst	  finish(NULL);
24922347Spst	}
25022347Spst	on = opiegetsequence(&opie);
25122347Spst	{
25222347Spst	  char *c;
25322347Spst	  if (c = strrchr(tmp, ' '))
25492914Smarkm	    opiestrncpy(oseed, c + 1, sizeof(oseed));
25522347Spst	  else {
25622347Spst#if DEBUG
25722347Spst	    fprintf(stderr, "opiepasswd: bogus challenge\n");
25822347Spst#endif /* DEBUG */
25922347Spst	    finish(NULL);
26022347Spst	  }
26122347Spst	}
26222347Spst	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
26322347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1))
26422347Spst	  tmp[0] = 0;
26522347Spst	i = opieverify(&opie, tmp);
26622347Spst	if (!tmp[0]) {
26722347Spst	  fprintf(stderr, "Error reading response.\n");
26822347Spst	  finish(NULL);
26922347Spst	}
27022347Spst	if (i) {
27122347Spst	  fprintf(stderr, "Error verifying response.\n");
27222347Spst#if DEBUG
27322347Spst	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
27422347Spst#endif /* DEBUG */
27522347Spst	  finish(NULL);
27622347Spst	}
27722347Spst	{
27822347Spst	  char nseed[OPIE_SEED_MAX + 1];
27922347Spst	  int nn;
28022347Spst
28122347Spst	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
28222347Spst	    fprintf(stderr, "Error verifying database.\n");
28322347Spst	    finish(NULL);
28422347Spst	  }
28522347Spst
28622347Spst	  nn = opiegetsequence(&opie);
28722347Spst	  {
28822347Spst	    char *c;
28922347Spst	    if (c = strrchr(tmp, ' '))
29092914Smarkm	      opiestrncpy(nseed, c + 1, sizeof(nseed));
29122347Spst	    else {
29222347Spst#if DEBUG
29322347Spst	      fprintf(stderr, "opiepasswd: bogus challenge\n");
29422347Spst#endif /* DEBUG */
29522347Spst	      finish(NULL);
29622347Spst	    }
29722347Spst	  }
29822347Spst
29922347Spst	  opieverify(&opie, "");
30022347Spst	  nn++;
30122347Spst
30222347Spst	  if ((nn != on) || strcmp(oseed, nseed))
30322347Spst	    finish(pp->pw_name);
30422347Spst	}
30522347Spst      }
30622347Spst      printf("New secret pass phrase:");
30722347Spst      for (i = 0;; i++) {
30822347Spst	if (i > 2)
30922347Spst	  finish(NULL);
31022347Spst	printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
31122347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
31222347Spst	  fprintf(stderr, "Error reading response.\n");
31322347Spst	  finish(NULL);
31422347Spst	}
31522347Spst	if (tmp[0] == '?') {
31622347Spst	  printf("Enter the response from your OTP calculator: \n");
31722347Spst	  continue;
31822347Spst	}
31922347Spst	if (tmp[0] == '\0') {
32022347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
32122347Spst	  finish(NULL);
32222347Spst	}
32322347Spst
32459118Skris	if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
32522347Spst	  finish(pp->pw_name);
32622347Spst
32722347Spst	if (rval < 0) {
32822347Spst	  fprintf(stderr, "Error updating key database.\n");
32922347Spst	  finish(NULL);
33022347Spst	}
33122347Spst	printf("\tThat is not a valid OTP response.\n");
33222347Spst      }
33322347Spst    }
33422347Spst    break;
33522347Spst  case MODE_CONSOLE:
33622347Spst    {
33722347Spst      char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
33822347Spst      /* Get user's secret password */
33922347Spst      fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
34022347Spst      fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
34122347Spst      fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
34259118Skris      if (opieinsecure() && !force) {
34322347Spst	fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
34422347Spst	if (force)
34522347Spst          fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
34622347Spst        else
34722347Spst          finish(NULL);
34822347Spst      };
34922347Spst      printf("Using %s to compute responses.\n", algnames[MDX]);
35022347Spst      if (!rval && getuid()) {
35122347Spst	printf("Enter old secret pass phrase: ");
35222347Spst	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
35322347Spst	  fprintf(stderr, "Error reading secret pass phrase!\n");
35422347Spst	  finish(NULL);
35522347Spst	}
35622347Spst	if (!passwd[0]) {
35722347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
35822347Spst	  finish(NULL);
35922347Spst	}
36022347Spst	{
36192914Smarkm	  struct opie_otpkey key;
36222347Spst	  char tbuf[OPIE_RESPONSE_MAX + 1];
36322347Spst
36492914Smarkm	  if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) {
36522347Spst	    fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
36622347Spst	    finish(NULL);
36722347Spst	  }
36822347Spst	  memset(passwd, 0, sizeof(passwd));
36922347Spst	  i = opie.opie_n - 1;
37022347Spst	  while (i-- != 0)
37192914Smarkm	    opiehash(&key, MDX);
37292914Smarkm	  opiebtoe(tbuf, &key);
37322347Spst	  if (opieverify(&opie, tbuf)) {
37422347Spst	    fprintf(stderr, "Sorry.\n");
37522347Spst	    finish(NULL);
37622347Spst	  }
37722347Spst	}
37822347Spst      }
37922347Spst      for (i = 0;; i++) {
38022347Spst	if (i > 2)
38122347Spst	  finish(NULL);
38222347Spst	printf("Enter new secret pass phrase: ");
38322347Spst	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
38422347Spst	  fprintf(stderr, "Error reading secret pass phrase.\n");
38522347Spst	  finish(NULL);
38622347Spst	}
38722347Spst	if (!passwd[0] || feof(stdin)) {
38822347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
38922347Spst	  finish(NULL);
39022347Spst	}
39122347Spst	if (opiepasscheck(passwd)) {
39222347Spst	  memset(passwd, 0, sizeof(passwd));
39322347Spst	  fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
39422347Spst	  continue;
39522347Spst	}
39622347Spst	printf("Again new secret pass phrase: ");
39722347Spst	if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
39822347Spst	  fprintf(stderr, "Error reading secret pass phrase.\n");
39922347Spst	  finish(NULL);
40022347Spst	}
40122347Spst	if (feof(stdin)) {
40222347Spst	  fprintf(stderr, "Secret pass phrase unchanged.\n");
40322347Spst	  finish(NULL);
40422347Spst	}
40522347Spst	if (!passwd[0] || !strcmp(passwd, passwd2))
40622347Spst	  break;
40722347Spst	fprintf(stderr, "Sorry, no match.\n");
40822347Spst      }
40922347Spst      memset(passwd2, 0, sizeof(passwd2));
41059118Skris      if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
41122347Spst	fprintf(stderr, "Error updating key database.\n");
41222347Spst	finish(NULL);
41322347Spst      }
41422347Spst      finish(pp->pw_name);
41522347Spst    }
41622347Spst  case MODE_DISABLE:
41722347Spst    {
41822347Spst      char tmp[4];
41922347Spst      int i;
42022347Spst
42122347Spst      for (i = 0;; i++) {
42222347Spst	if (i > 2)
42322347Spst	  finish(NULL);
42422347Spst
42522347Spst	printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
42622347Spst	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
42722347Spst	  fprintf(stderr, "Error reading entry.\n");
42822347Spst	  finish(NULL);
42922347Spst	}
43022347Spst	if (!strcmp(tmp, "no"))
43122347Spst	  finish(NULL);
43222347Spst	if (!strcmp(tmp, "yes")) {
43322347Spst	  if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
43422347Spst	    fprintf(stderr, "Error updating key database.\n");
43522347Spst	    finish(NULL);
43622347Spst	  }
43722347Spst	  finish(pp->pw_name);
43822347Spst	}
43922347Spst      }
44022347Spst    }
44122347Spst  }
44222347Spst}
443