192906Smarkm/* opieauto.c: The opieauto program.
292906Smarkm
392906Smarkm%%% copyright-cmetz-96
492906SmarkmThis software is Copyright 1996-2001 by Craig Metz, All Rights Reserved.
592906SmarkmThe Inner Net License Version 3 applies to this software.
692906SmarkmYou should have received a copy of the license with this software. If
792906Smarkmyou didn't get a copy, you may request one from <license@inner.net>.
892906Smarkm
992906Smarkm        History:
1092906Smarkm
1192906Smarkm	Created by cmetz for OPIE 2.4 based on previously released
1292906Smarkm	        test code. Use opiestrncpy().
1392906Smarkm*/
1492906Smarkm
1592906Smarkm#include "opie_cfg.h"
1692906Smarkm#include <sys/types.h>
1792906Smarkm#include <sys/socket.h>
1892906Smarkm#include <sys/un.h>
1992906Smarkm#if HAVE_SYS_TIME_H
2092906Smarkm#include <sys/time.h>
2192906Smarkm#endif /* HAVE_SYS_TIME_H */
2292906Smarkm#include <stdio.h>
2392906Smarkm#include <errno.h>
2492906Smarkm#if HAVE_STRING_H
2592906Smarkm#include <string.h>
2692906Smarkm#endif /* HAVE_STRING_H */
2792906Smarkm#include <getopt.h>
2892906Smarkm#if HAVE_STDLIB_H
2992906Smarkm#include <stdlib.h>
3092906Smarkm#endif /* HAVE_STDLIB_H */
3192906Smarkm#if HAVE_UNISTD_H
3292906Smarkm#include <unistd.h>
3392906Smarkm#endif /* HAVE_UNISTD_H */
3492906Smarkm#include <sys/stat.h>
3592906Smarkm
3692906Smarkm#include "opie.h"
3792906Smarkm
3892906Smarkm#ifndef max
3992906Smarkm#define max(x, y) (((x) > (y)) ? (x) : (y))
4092906Smarkm#endif /* max */
4192906Smarkm
4292906Smarkmint window = 10;
4392906Smarkmchar *myname = NULL;
4492906Smarkm
4592906Smarkmuid_t myuid = 0;
4692906Smarkm
4792906Smarkm#define MAXCLIENTS 2
4892906Smarkmint parents, s[MAXCLIENTS + 1];
4992906Smarkm
5092906Smarkmchar cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1];
5192906Smarkm
5292906Smarkmstruct cachedotp {
5392906Smarkm  struct cachedotp *next;
5492906Smarkm  int algorithm, base, current;
5592906Smarkm  struct opie_otpkey basekey;
5692906Smarkm  char seed[OPIE_SEED_MAX+1];
5792906Smarkm};
5892906Smarkm
5992906Smarkmstruct cachedotp *head = NULL;
6092906Smarkm
6192906Smarkmchar *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
6292906Smarkm
6392906Smarkmvoid baile(x) {
6492906Smarkm  fprintf(stderr, "%s: %s: %s(%d)\n", myname, x, strerror(errno), errno);
6592906Smarkm  exit(1);
6692906Smarkm}
6792906Smarkm
6892906Smarkmvoid bail(x) {
6992906Smarkm  fprintf(stderr, "%s: %s\n", myname, x);
7092906Smarkm  exit(1);
7192906Smarkm}
7292906Smarkm
7392906Smarkmvoid zerocache(void)
7492906Smarkm{
7592906Smarkm  struct cachedotp *c = head, *c2;
7692906Smarkm
7792906Smarkm  while(c) {
7892906Smarkm    c2 = c->next;
7992906Smarkm    memset(c, 0, sizeof(struct cachedotp));
8092906Smarkm    c = c2;
8192906Smarkm  };
8292906Smarkm};
8392906Smarkm
8492906Smarkmint doreq(int fd)
8592906Smarkm{
8692906Smarkm  int algorithm, sequence, i;
8792906Smarkm  char *seed = NULL, *response = NULL;
8892906Smarkm
8992906Smarkm  if (((cmd[0] != 'S') && (cmd[0] != 's')) || (cmd[1] != '=') || (cmd[2] != ' ')) {
9092906Smarkm#if DEBUG
9192906Smarkm    fprintf(stderr, "%s: got bogus command: %s\n", myname, cmd);
9292906Smarkm#endif /* DEBUG */
9392906Smarkm    goto error;
9492906Smarkm  };
9592906Smarkm
9692906Smarkm  {
9792906Smarkm  char *c;
9892906Smarkm
9992906Smarkm  if (((algorithm = strtoul(&cmd[3], &c, 10)) < 3) || (algorithm > 5) || (*c != ' ')) {
10092906Smarkm#if DEBUG
10192906Smarkm    fprintf(stderr, "%s: got bogus algorithm: %s\n", myname, cmd);
10292906Smarkm#endif /* DEBUG */
10392906Smarkm    goto error;
10492906Smarkm  };
10592906Smarkm
10692906Smarkm  if (((sequence = strtoul(c + 1, &c, 10)) <= OPIE_SEQUENCE_RESTRICT) || (sequence > OPIE_SEQUENCE_MAX)) {
10792906Smarkm#if DEBUG
10892906Smarkm    fprintf(stderr, "%s: got bogus sequence: %s\n", myname, cmd);
10992906Smarkm#endif /* DEBUG */
11092906Smarkm    goto error;
11192906Smarkm  };
11292906Smarkm
11392906Smarkm  if (cmd[0] == 'S') {
11492906Smarkm    if (!(c = strchr(seed = c + 1, ' '))) {
11592906Smarkm#if DEBUG
11692906Smarkm      fprintf(stderr, "%s: got bogus seed: %s\n", myname, cmd);
11792906Smarkm#endif /* DEBUG */
11892906Smarkm      goto error;
11992906Smarkm    };
12092906Smarkm
12192906Smarkm    *c = 0;
12292906Smarkm
12392906Smarkm    if (!(c = strchr(response = c + 1, '\n'))) {
12492906Smarkm#if DEBUG
12592906Smarkm      fprintf(stderr, "%s: got bogus response: %s\n", myname, cmd);
12692906Smarkm#endif /* DEBUG */
12792906Smarkm      goto error;
12892906Smarkm    };
12992906Smarkm
13092906Smarkm    *c = 0;
13192906Smarkm  } else {
13292906Smarkm    if (!(c = strchr(seed = c + 1, '\n'))) {
13392906Smarkm#if DEBUG
13492906Smarkm      fprintf(stderr, "%s: got bogus seed: %s\n", myname, cmd);
13592906Smarkm#endif /* DEBUG */
13692906Smarkm      goto error;
13792906Smarkm    };
13892906Smarkm
13992906Smarkm    *c = 0;
14092906Smarkm  };
14192906Smarkm  };
14292906Smarkm
14392906Smarkm#if DEBUG
14492906Smarkm  fprintf(stderr, "got cmd=%c, algorithm=%d sequence=%d seed=+%s+ response=+%s+ on fd %d\n", cmd[0], algorithm, sequence, seed, response, fd);
14592906Smarkm#endif /* DEBUG */
14692906Smarkm
14792906Smarkm  seed = strdup(seed);
14892906Smarkm
14992906Smarkm  if (sequence < 10) {
15092906Smarkm#if DEBUG
15192906Smarkm    fprintf(stderr, "sequence < 10; can't do it\n");
15292906Smarkm#endif /* DEBUG */
15392906Smarkm    sprintf(cmd, "%c- %d %d %s\n", cmd[0], algorithm, sequence, seed);
15492906Smarkm  };
15592906Smarkm
15692906Smarkm  {
15792906Smarkm  struct cachedotp **c;
15892906Smarkm
15992906Smarkm  for (c = &head; *c && (strcmp((*c)->seed, seed) || ((*c)->algorithm != algorithm)); c = &((*c)->next));
16092906Smarkm  if (!(*c)) {
16192906Smarkm    if (cmd[0] == 's') {
16292906Smarkm#if DEBUG
16392906Smarkm      fprintf(stderr, "(seed, algorithm) not found for s command\n");
16492906Smarkm#endif /* DEBUG */
16592906Smarkm      sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, seed);
16692906Smarkm      goto out;
16792906Smarkm    }
16892906Smarkm
16992906Smarkm    if (!(*c = malloc(sizeof(struct cachedotp))))
17092906Smarkm      baile("malloc");
17192906Smarkm    memset(*c, 0, sizeof(struct cachedotp));
17292906Smarkm
17392906Smarkm    (*c)->algorithm = algorithm;
17492906Smarkm    opiestrncpy((*c)->seed, seed, OPIE_SEED_MAX);
17592906Smarkm  };
17692906Smarkm
17792906Smarkm  if (cmd[0] == 'S') {
17892906Smarkm    (*c)->base = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT);
17992906Smarkm    (*c)->current = sequence;
18092906Smarkm
18192906Smarkm    if (!opieatob8(&(*c)->basekey, response))
18292906Smarkm      goto error;
18392906Smarkm
18492906Smarkm    sprintf(cmd, "S+ %d %d %s\n", algorithm, sequence, (*c)->seed);
18592906Smarkm  } else {
18692906Smarkm    if (sequence != ((*c)->current - 1)) {
18792906Smarkm#if DEBUG
18892906Smarkm      fprintf(stderr, "out of sequence: sequence=%d, base=%d, current=%d\n", sequence, (*c)->base, (*c)->current);
18992906Smarkm#endif /* DEBUG */
19092906Smarkm      sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, (*c)->seed);
19192906Smarkm      goto out;
19292906Smarkm    };
19392906Smarkm
19492906Smarkm    if (sequence < (*c)->base) {
19592906Smarkm#if DEBUG
19692906Smarkm      fprintf(stderr, "attempt to generate below base: sequence=%d, base=%d, current=%d\n", sequence, (*c)->base, (*c)->current);
19792906Smarkm#endif /* DEBUG */
19892906Smarkm      sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, (*c)->seed);
19992906Smarkm      goto out;
20092906Smarkm    };
20192906Smarkm
20292906Smarkm    (*c)->current = sequence;
20392906Smarkm    i = sequence - (*c)->base;
20492906Smarkm    {
20592906Smarkm      struct opie_otpkey key;
20692906Smarkm      char buffer[16+1];
20792906Smarkm
20892906Smarkm      key = (*c)->basekey;
20992906Smarkm      while(i--)
21092906Smarkm	opiehash(&key, algorithm);
21192906Smarkm
21292906Smarkm      opiebtoa8(buffer, &key);
21392906Smarkm      sprintf(cmd, "s+ %d %d %s %s\n", algorithm, sequence, (*c)->seed, buffer);
21492906Smarkm    };
21592906Smarkm  };
21692906Smarkm
21792906Smarkm  printf("%c otp-%s %d %s (%d/%d)\n", cmd[0], algids[algorithm], sequence, (*c)->seed, sequence - (*c)->base, window);
21892906Smarkm  fflush(stdout);
21992906Smarkm
22092906Smarkm  if (sequence == (*c)->base) {
22192906Smarkm    struct cachedotp *c2 = *c;
22292906Smarkm    *c = (*c)->next;
22392906Smarkm    memset(c2, 0, sizeof(struct cachedotp));
22492906Smarkm    free(c2);
22592906Smarkm  };
22692906Smarkm  };
22792906Smarkm
22892906Smarkmout:
22992906Smarkm  write(fd, cmd, i = strlen(cmd));
23092906Smarkm  free(seed);
23192906Smarkm  return 0;
23292906Smarkm
23392906Smarkmerror:
23492906Smarkm  fprintf(stderr, "Invalid command on fd %d\n", fd);
23592906Smarkm  if (seed)
23692906Smarkm    free(seed);
23792906Smarkm  return -1;
23892906Smarkm}
23992906Smarkm
24092906Smarkmstatic void usage()
24192906Smarkm{
24292906Smarkm  fprintf(stderr, "usage: %s [-v] [-h] [-q] [-n <number of OTPs>]\n", myname);
24392906Smarkm  exit(1);
24492906Smarkm}
24592906Smarkm
24692906Smarkmint main(int argc, char **argv)
24792906Smarkm{
24892906Smarkm  int i;
24992906Smarkm  struct stat st;
25092906Smarkm  char *sockpath;
25192906Smarkm
25292906Smarkm  if (myname = strrchr(argv[0], '/'))
25392906Smarkm    myname++;
25492906Smarkm  else
25592906Smarkm    myname = argv[0];
25692906Smarkm
25792906Smarkm  while((i = getopt(argc, argv, "w:hv")) != EOF) {
25892906Smarkm    switch(i) {
25992906Smarkm      case 'v':
26092906Smarkm	opieversion();
26192906Smarkm
26292906Smarkm      case 'w':
26392906Smarkm	if (!(window = atoi(optarg))) {
26492906Smarkm	  fprintf(stderr, "%s: invalid number of OTPs: %s\n", myname, optarg);
26592906Smarkm	  exit(1);
26692906Smarkm	};
26792906Smarkm	break;
26892906Smarkm
26992906Smarkm      default:
27092906Smarkm	usage();
27192906Smarkm    }
27292906Smarkm  };
27392906Smarkm
27492906Smarkm  {
27592906Smarkm    uid_t myeuid;
27692906Smarkm
27792906Smarkm    if (!(myuid = getuid()) || !(myeuid = geteuid()) || (myuid != myeuid))
27892906Smarkm      bail("this program must not be run with superuser priveleges or setuid.");
27992906Smarkm  };
28092906Smarkm
28192906Smarkm  if (atexit(zerocache) < 0)
28292906Smarkm    baile("atexit");
28392906Smarkm
28492906Smarkm  {
28592906Smarkm    struct sockaddr_un sun;
28692906Smarkm
28792906Smarkm    memset(&sun, 0, sizeof(struct sockaddr_un));
28892906Smarkm    sun.sun_family = AF_UNIX;
28992906Smarkm
29092906Smarkm    {
29192906Smarkm    char *c;
29292906Smarkm    char *c2 = "/.opieauto";
29392906Smarkm
29492906Smarkm    if (!(c = getenv("HOME")))
29592906Smarkm      bail("getenv(HOME) failed -- no HOME variable?");
29692906Smarkm
29792906Smarkm    if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1))
29892906Smarkm      bail("your HOME is too long");
29992906Smarkm
30092906Smarkm    strcpy(sun.sun_path, c);
30192906Smarkm    strcat(sun.sun_path, c2);
30292906Smarkm    sockpath = strdup(sun.sun_path);
30392906Smarkm    };
30492906Smarkm
30592906Smarkm    if ((parents = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
30692906Smarkm      baile("socket");
30792906Smarkm
30892906Smarkm    if (unlink(sockpath) && (errno != ENOENT))
30992906Smarkm      baile("unlink");
31092906Smarkm
31192906Smarkm    if (umask(0177) < 0)
31292906Smarkm      baile("umask");
31392906Smarkm
31492906Smarkm    if (bind(parents, (struct sockaddr *)&sun, sizeof(struct sockaddr_un)))
31592906Smarkm      baile("bind");
31692906Smarkm
31792906Smarkm    if (stat(sockpath, &st) < 0)
31892906Smarkm      baile("stat");
31992906Smarkm
32092906Smarkm    if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600))
32192906Smarkm      bail("socket permissions and/or ownership were not correctly created.");
32292906Smarkm
32392906Smarkm    if (listen(parents, 1) < 0)
32492906Smarkm      baile("listen");
32592906Smarkm  };
32692906Smarkm
32792906Smarkm  {
32892906Smarkm    fd_set fds, rfds, efds;
32992906Smarkm    int maxfd = parents;
33092906Smarkm    int i, j;
33192906Smarkm
33292906Smarkm    FD_ZERO(&fds);
33392906Smarkm    FD_SET(parents, &fds);
33492906Smarkm
33592906Smarkm    while(1) {
33692906Smarkm      memcpy(&rfds, &fds, sizeof(fd_set));
33792906Smarkm
33892906Smarkm      if (select(maxfd + 1, &rfds, NULL, NULL, NULL) < 0)
33992906Smarkm	baile("select");
34092906Smarkm
34192906Smarkm      for (i = 0; s[i]; i++) {
34292906Smarkm	if (!FD_ISSET(s[i], &rfds))
34392906Smarkm	  continue;
34492906Smarkm
34592906Smarkm	if (((j = read(s[i], cmd, sizeof(cmd)-1)) <= 0) || ((cmd[j] = 0) || doreq(s[i]))) {
34692906Smarkm	  close(s[i]);
34792906Smarkm	  FD_CLR(s[i], &fds);
34892906Smarkm
34992906Smarkm	  if (s[i] == maxfd)
35092906Smarkm	    maxfd--;
35192906Smarkm
35292906Smarkm	  for (j = i; s[j]; s[j] = s[j + 1], j++);
35392906Smarkm	  FD_SET(parents, &fds);
35492906Smarkm	  i--;
35592906Smarkm	  continue;
35692906Smarkm	};
35792906Smarkm      };
35892906Smarkm
35992906Smarkm      if (FD_ISSET(parents, &rfds)) {
36092906Smarkm	for (i = 0; s[i]; i++)
36192906Smarkm	  if (i > MAXCLIENTS)
36292906Smarkm	    bail("this message never printed");
36392906Smarkm
36492906Smarkm	if (stat(sockpath, &st) < 0)
36592906Smarkm	  baile("stat");
36692906Smarkm
36792906Smarkm	if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600))
36892906Smarkm	  bail("socket permissions and/or ownership has been messed with.");
36992906Smarkm
37092906Smarkm	if ((s[i] = accept(parents, NULL, 0)) < 0)
37192906Smarkm	  baile("accept");
37292906Smarkm
37392906Smarkm	FD_SET(s[i], &fds);
37492906Smarkm	if (s[i] > maxfd)
37592906Smarkm	  maxfd = s[i];
37692906Smarkm
37792906Smarkm	sprintf(cmd, "C+ %d\n", window);
37892906Smarkm	if (write(s[i], cmd, j = strlen(cmd)) != j)
37992906Smarkm	  baile("write");
38092906Smarkm
38192906Smarkm	if (++i == MAXCLIENTS)
38292906Smarkm	  FD_CLR(parents, &fds);
38392906Smarkm      }
38492906Smarkm    }
38592906Smarkm  }
38692906Smarkm}
387