122347Spst/* generator.c: The opiegenerator() library function.
222347Spst
329967Sache%%% 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
1022347Spst        History:
1122347Spst
1292914Smarkm	Modified by cmetz for OPIE 2.4. Added opieauto code based on
1392914Smarkm	        previously released test code. Renamed buffer to challenge.
1492914Smarkm		Use struct opie_otpkey for keys.
1559121Skris	Modified by cmetz for OPIE 2.32. If secret=NULL, always return
1659121Skris		as if opieauto returned "get the secret". Renamed
1759121Skris		_opieparsechallenge() to __opieparsechallenge(). Check
1859121Skris		challenge for extended response support and don't send
1959121Skris		an init-hex response if extended response support isn't
2059121Skris		indicated in the challenge.
2129967Sache	Modified by cmetz for OPIE 2.31. Renamed "init" to "init-hex".
2259121Skris		Removed active attack protection support. Fixed fairly
2359121Skris		bug in how init response was computed (i.e., dead wrong).
2422347Spst	Modified by cmetz for OPIE 2.3. Use _opieparsechallenge(). ifdef
2559121Skris		around string.h. Output hex responses by default, output
2659121Skris		OTP re-init extended responses (same secret) if sequence
2759121Skris		number falls below 10.
2822347Spst	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
2959121Skris		Bug fixes.
3022347Spst	Created at NRL for OPIE 2.2.
3159121Skris
3259121Skris$FreeBSD$
3322347Spst*/
3422347Spst
3522347Spst#include "opie_cfg.h"
3622347Spst#if HAVE_STRING_H
3722347Spst#include <string.h>
3822347Spst#endif /* HAVE_STRING_H */
3992914Smarkm#if OPIEAUTO
4092914Smarkm#include <errno.h>
4192914Smarkm#if HAVE_STDLIB_H
4292914Smarkm#include <stdlib.h>
4392914Smarkm#endif /* HAVE_STDLIB_H */
4492914Smarkm#include <sys/stat.h>
4592914Smarkm
4692914Smarkm#include <sys/socket.h>
4792914Smarkm#include <sys/un.h>
4892914Smarkm#endif /* OPIEAUTO */
4992914Smarkm#if DEBUG
5092914Smarkm#include <syslog.h>
5192914Smarkm#endif /* DEBUG */
5222347Spst#include "opie.h"
5322347Spst
5422347Spststatic char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
5522347Spst
5692914Smarkm#if OPIEAUTO
5792914Smarkm#ifndef max
5892914Smarkm#define max(x, y) (((x) > (y)) ? (x) : (y))
5992914Smarkm#endif /* max */
6092914Smarkm
6192914Smarkmstatic int opieauto_connect FUNCTION_NOARGS
6222347Spst{
6392914Smarkm  int s;
6492914Smarkm  struct sockaddr_un sun;
6592914Smarkm  char buffer[1024];
6692914Smarkm  char *c, *c2 ="/.opieauto";
6792914Smarkm  uid_t myuid = getuid(), myeuid = geteuid();
6892914Smarkm
6992914Smarkm  if (!myuid || !myeuid || (myuid != myeuid)) {
7092914Smarkm#if DEBUG
7192914Smarkm    syslog(LOG_DEBUG, "opieauto_connect: superuser and/or setuid not allowed");
7292914Smarkm#endif /* DEBUG */
7392914Smarkm    return -1;
7492914Smarkm  };
7592914Smarkm
7692914Smarkm  memset(&sun, 0, sizeof(struct sockaddr_un));
7792914Smarkm  sun.sun_family = AF_UNIX;
7892914Smarkm
7992914Smarkm  if (!(c = getenv("HOME"))) {
8092914Smarkm#if DEBUG
8192914Smarkm    syslog(LOG_DEBUG, "opieauto_connect: no HOME variable?");
8292914Smarkm#endif /* DEBUG */
8392914Smarkm    return -1;
8492914Smarkm  };
8592914Smarkm
8692914Smarkm  if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1)) {
8792914Smarkm#if DEBUG
8892914Smarkm    syslog(LOG_DEBUG, "opieauto_connect: HOME is too long: %s", c);
8992914Smarkm#endif /* DEBUG */
9092914Smarkm    return -1;
9192914Smarkm  };
9292914Smarkm
9392914Smarkm  strcpy(sun.sun_path, c);
9492914Smarkm  strcat(sun.sun_path, c2);
9592914Smarkm
9692914Smarkm  if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
9792914Smarkm#if DEBUG
9892914Smarkm    syslog(LOG_DEBUG, "opieauto_connect: socket: %s(%d)", strerror(errno), errno);
9992914Smarkm#endif /* DEBUG */
10092914Smarkm    return -1;
10192914Smarkm  };
10292914Smarkm
10392914Smarkm  {
10492914Smarkm    struct stat st;
10592914Smarkm
10692914Smarkm    if (stat(sun.sun_path, &st) < 0) {
10792914Smarkm#if DEBUG
10892914Smarkm      syslog(LOG_DEBUG, "opieauto_connect: stat: %s(%d)\n", strerror(errno), errno);
10992914Smarkm#endif /* DEBUG */
11092914Smarkm      goto ret;
11192914Smarkm    };
11292914Smarkm
11392914Smarkm    if (connect(s, (struct sockaddr *)&sun, sizeof(struct sockaddr_un))) {
11492914Smarkm#if DEBUG
11592914Smarkm      syslog(LOG_DEBUG, "opieauto_connect: connect: %s(%d)\n", strerror(errno), errno);
11692914Smarkm#endif /* DEBUG */
11792914Smarkm      goto ret;
11892914Smarkm    };
11992914Smarkm
12092914Smarkm    if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600)) {
12192914Smarkm#if DEBUG
12292914Smarkm      syslog(LOG_DEBUG, "opieauto_connect: something's fishy about the socket\n");
12392914Smarkm#endif /* DEBUG */
12492914Smarkm      goto ret;
12592914Smarkm    };
12692914Smarkm  };
12792914Smarkm
12892914Smarkm  return s;
12992914Smarkm
13092914Smarkmret:
13192914Smarkm  close(s);
13292914Smarkm  return -1;
13392914Smarkm};
13492914Smarkm#endif /* OPIEAUTO */
13592914Smarkm
13692914Smarkmint opiegenerator FUNCTION((challenge, secret, response), char *challenge AND char *secret AND char *response)
13792914Smarkm{
13822347Spst  int algorithm;
13922347Spst  int sequence;
14022347Spst  char *seed;
14192914Smarkm  struct opie_otpkey key;
14222347Spst  int i;
14359121Skris  int exts;
14492914Smarkm#if OPIEAUTO
14592914Smarkm  int s;
14692914Smarkm  int window;
14792914Smarkm  char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1];
14892914Smarkm  char *c;
14992914Smarkm#endif /* OPIEAUTO */
15022347Spst
15192914Smarkm  if (!(challenge = strstr(challenge, "otp-")))
15222347Spst    return 1;
15322347Spst
15492914Smarkm  challenge += 4;
15522347Spst
15692914Smarkm  if (__opieparsechallenge(challenge, &algorithm, &sequence, &seed, &exts))
15722347Spst    return 1;
15822347Spst
15922347Spst  if ((sequence < 2) || (sequence > 9999))
16022347Spst    return 1;
16122347Spst
16292914Smarkm  if (*secret) {
16392914Smarkm    if (opiepasscheck(secret))
16492914Smarkm      return -2;
16559121Skris
16692914Smarkm    if (i = opiekeycrunch(algorithm, &key, seed, secret))
16792914Smarkm      return i;
16822347Spst
16992914Smarkm    if (sequence <= OPIE_SEQUENCE_RESTRICT) {
17092914Smarkm      if (!(exts & 1))
17192914Smarkm	return 1;
17222347Spst
17392914Smarkm      {
17492914Smarkm	char newseed[OPIE_SEED_MAX + 1];
17592914Smarkm	struct opie_otpkey newkey;
17692914Smarkm	char *c;
17792914Smarkm	char buf[OPIE_SEED_MAX + 48 + 1];
17829967Sache
17992914Smarkm	while (sequence-- != 0)
18092914Smarkm	  opiehash(&key, algorithm);
18192914Smarkm
18292914Smarkm	if (opienewseed(strcpy(newseed, seed)) < 0)
18392914Smarkm	  return -1;
18492914Smarkm
18592914Smarkm	if (opiekeycrunch(algorithm, &newkey, newseed, secret))
18692914Smarkm	  return -1;
18792914Smarkm
18892914Smarkm	for (i = 0; i < 499; i++)
18992914Smarkm	  opiehash(&newkey, algorithm);
19092914Smarkm
19192914Smarkm	strcpy(response, "init-hex:");
19292914Smarkm	strcat(response, opiebtoh(buf, &key));
19392914Smarkm	if (snprintf(buf, sizeof(buf), ":%s 499 %s:", algids[algorithm],
19492914Smarkm	    newseed) >= sizeof(buf)) {
19592914Smarkm#ifdef DEBUG
19692914Smarkm	  syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at init-hex");
19792914Smarkm#endif /* DEBUG */
19892914Smarkm	  return -1;
19992914Smarkm	}
20092914Smarkm	strcat(response, buf);
20192914Smarkm	strcat(response, opiebtoh(buf, &newkey));
20292914Smarkm      };
20392914Smarkm    };
20492914Smarkm  };
20592914Smarkm
20692914Smarkm#if OPIEAUTO
20792914Smarkm  if ((s = opieauto_connect()) >= 0) {
20892914Smarkm    if ((i = read(s, cmd, sizeof(cmd)-1)) < 0) {
20992914Smarkm#if DEBUG
21092914Smarkm      syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
21192914Smarkm#endif /* DEBUG */
21292914Smarkm      close(s);
21392914Smarkm      s = -1;
21492914Smarkm      goto l0;
21592914Smarkm    };
21692914Smarkm    cmd[i] = 0;
21792914Smarkm    if ((cmd[0] != 'C') || (cmd[1] != '+') || (cmd[2] != ' ')) {
21892914Smarkm#if DEBUG
21992914Smarkm      syslog(LOG_DEBUG, "opiegenerator: got invalid/failing C+ response: %s\n", cmd);
22092914Smarkm#endif /* DEBUG */
22192914Smarkm      close(s);
22292914Smarkm      s = -1;
22392914Smarkm      goto l0;
22492914Smarkm    };
22592914Smarkm
22692914Smarkm    window = strtoul(&cmd[3], &c, 10);
22792914Smarkm    if (!window || (window >= (OPIE_SEQUENCE_MAX - OPIE_SEQUENCE_RESTRICT)) || !isspace(*c)) {
22892914Smarkm#if DEBUG
22992914Smarkm      syslog(LOG_DEBUG, "opiegenerator: got bogus option response: %s\n", cmd);
23092914Smarkm#endif /* DEBUG */
23192914Smarkm      close(s);
23292914Smarkm      s = -1;
23392914Smarkm      goto l0;
23492914Smarkm    };
23592914Smarkm  };
23692914Smarkm
23792914Smarkml0:
23892914Smarkm  if (*secret) {
23992914Smarkm    int j;
24092914Smarkm
24192914Smarkm    if (s < 0) {
24292914Smarkm      j = 0;
24392914Smarkm      goto l1;
24492914Smarkm    };
24592914Smarkm
24692914Smarkm    j = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT);
24792914Smarkm
24892914Smarkm    for (i = j; i > 0; i--)
24992914Smarkm      opiehash(&key, algorithm);
25092914Smarkm
25159121Skris    {
25292914Smarkm      char buf[16+1];
25322347Spst
25492914Smarkm      opiebtoa8(buf, &key);
25529967Sache
25692914Smarkm      if (snprintf(cmd, sizeof(cmd), "S= %d %d %s %s\n", algorithm, sequence,
25792914Smarkm          seed, buf) >= sizeof(cmd)) {
25892914Smarkm#if DEBUG
25992914Smarkm        syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at S=\n");
26092914Smarkm#endif /* DEBUG */
26192914Smarkm	goto l1;
26292914Smarkm      }
26392914Smarkm    }
26422347Spst
26592914Smarkm    if (write(s, cmd, i = strlen(cmd)) != i) {
26692914Smarkm#if DEBUG
26792914Smarkm      syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno);
26892914Smarkm#endif /* DEBUG */
26992914Smarkm      goto l1;
27092914Smarkm    };
27122347Spst
27292914Smarkm    if ((i = read(s, cmd, sizeof(cmd))) < 0) {
27392914Smarkm#if DEBUG
27492914Smarkm      syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
27592914Smarkm#endif /* DEBUG */
27692914Smarkm    };
27792914Smarkm    close(s);
27822347Spst
27992914Smarkm    cmd[i] = 0;
28092914Smarkm    i = strlen(seed);
28192914Smarkm    if ((cmd[0] != 'S') || (cmd[1] != '+') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i) || (*(c + i) != '\n')) {
28292914Smarkm#if DEBUG
28392914Smarkm      syslog(LOG_DEBUG, "opiegenerator: got invalid/failing S+ response: %s\n", cmd);
28492914Smarkm#endif /* DEBUG */
28559121Skris    };
28692914Smarkm
28792914Smarkml1:
28892914Smarkm    for (i = sequence - j; i > 0; i--)
28992914Smarkm      opiehash(&key, algorithm);
29092914Smarkm
29192914Smarkm    opiebtoh(response, &key);
29222347Spst  } else {
29392914Smarkm    if (s < 0)
29492914Smarkm      goto l2;
29592914Smarkm
29692914Smarkm    if ((snprintf(cmd, sizeof(cmd), "s= %d %d %s\n", algorithm, sequence,
29792914Smarkm        seed) >= sizeof(cmd))) {
29892914Smarkm#if DEBUG
29992914Smarkm      syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at s=\n");
30092914Smarkm#endif /* DEBUG */
30192914Smarkm      goto l2;
30292914Smarkm    }
30392914Smarkm
30492914Smarkm    if (write(s, cmd, i = strlen(cmd)) != i) {
30592914Smarkm#if DEBUG
30692914Smarkm      syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno);
30792914Smarkm#endif /* DEBUG */
30892914Smarkm      goto l2;
30992914Smarkm    };
31092914Smarkm
31192914Smarkm    if ((i = read(s, cmd, sizeof(cmd))) < 0) {
31292914Smarkm#if DEBUG
31392914Smarkm      syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno);
31492914Smarkm#endif /* DEBUG */
31592914Smarkm      goto l2;
31692914Smarkm    };
31792914Smarkm    close(s);
31892914Smarkm
31992914Smarkm    i = strlen(seed);
32092914Smarkm
32192914Smarkm    if ((cmd[0] != 's') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i)) {
32292914Smarkm#if DEBUG
32392914Smarkm      if (c)
32492914Smarkm	*c = 0;
32592914Smarkm      else
32692914Smarkm	cmd[3] = 0;
32792914Smarkm
32892914Smarkm      syslog(LOG_DEBUG, "opiegenerator: got bogus/invalid s response: %s\n", cmd);
32992914Smarkm#endif /* DEBUG */
33092914Smarkm      goto l2;
33192914Smarkm    };
33292914Smarkm
33392914Smarkm    c += i;
33492914Smarkm
33592914Smarkm    if (cmd[1] == '-') {
33692914Smarkm#if DEBUG
33792914Smarkm      if (*c != '\n') {
33892914Smarkm	*c = 0;
33992914Smarkm	syslog(LOG_DEBUG, "opiegenerator: got invalid s- response: %s\n", cmd);
34092914Smarkm      };
34192914Smarkm#endif /* DEBUG */
34292914Smarkm      goto l2;
34392914Smarkm    };
34492914Smarkm
34592914Smarkm    if (cmd[1] != '+') {
34692914Smarkm#if DEBUG
34792914Smarkm      *c = 0;
34892914Smarkm      syslog(LOG_DEBUG, "opiegenerator: got invalid s response: %s\n", cmd);
34992914Smarkm#endif /* DEBUG */
35092914Smarkm      goto l2;
35192914Smarkm    };
35292914Smarkm
35392914Smarkm    {
35492914Smarkm      char *c2;
35592914Smarkm
35692914Smarkm      if (!(c2 = strchr(++c, '\n'))) {
35792914Smarkm#if DEBUG
35892914Smarkm	*c = 0;
35992914Smarkm	syslog(LOG_DEBUG, "opiegenerator: got invalid s+ response: %s\n", cmd);
36092914Smarkm#endif /* DEBUG */
36192914Smarkm	goto l2;
36292914Smarkm      };
36392914Smarkm
36492914Smarkm      *c2++ = 0;
36592914Smarkm    };
36692914Smarkm
36792914Smarkm    if (!opieatob8(&key, c))
36892914Smarkm      goto l2;
36992914Smarkm
37092914Smarkm    opiebtoh(response, &key);
37192914Smarkm  };
37292914Smarkm
37392914Smarkm  if (s >= 0)
37492914Smarkm    close(s);
37592914Smarkm#else /* OPIEAUTO */
37692914Smarkm  if (*secret) {
37722347Spst    while (sequence-- != 0)
37892914Smarkm      opiehash(&key, algorithm);
37929967Sache
38092914Smarkm    opiebtoh(response, &key);
38192914Smarkm  } else
38292914Smarkm    return -2;
38392914Smarkm#endif /* OPIEAUTO */
38422347Spst
38522347Spst  return 0;
38692914Smarkm
38792914Smarkm#if OPIEAUTO
38892914Smarkml2:
38992914Smarkm#if DEBUG
39092914Smarkm  syslog(LOG_DEBUG, "opiegenerator: no opieauto response available.\n");
39192914Smarkm#endif /* DEBUG */
39292914Smarkm  if (s >= 0)
39392914Smarkm    close(s);
39492914Smarkm
39592914Smarkm  return -2;
39692914Smarkm#endif /* OPIEAUTO */
39792914Smarkm};
398