387 lines
8.5 KiB
C
387 lines
8.5 KiB
C
/* opieauto.c: The opieauto program.
|
|
|
|
%%% copyright-cmetz-96
|
|
This software is Copyright 1996-2001 by Craig Metz, All Rights Reserved.
|
|
The Inner Net License Version 3 applies to this software.
|
|
You should have received a copy of the license with this software. If
|
|
you didn't get a copy, you may request one from <license@inner.net>.
|
|
|
|
History:
|
|
|
|
Created by cmetz for OPIE 2.4 based on previously released
|
|
test code. Use opiestrncpy().
|
|
*/
|
|
|
|
#include "opie_cfg.h"
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#if HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif /* HAVE_SYS_TIME_H */
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif /* HAVE_STRING_H */
|
|
#include <getopt.h>
|
|
#if HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif /* HAVE_STDLIB_H */
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif /* HAVE_UNISTD_H */
|
|
#include <sys/stat.h>
|
|
|
|
#include "opie.h"
|
|
|
|
#ifndef max
|
|
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
|
#endif /* max */
|
|
|
|
int window = 10;
|
|
char *myname = NULL;
|
|
|
|
uid_t myuid = 0;
|
|
|
|
#define MAXCLIENTS 2
|
|
int parents, s[MAXCLIENTS + 1];
|
|
|
|
char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1];
|
|
|
|
struct cachedotp {
|
|
struct cachedotp *next;
|
|
int algorithm, base, current;
|
|
struct opie_otpkey basekey;
|
|
char seed[OPIE_SEED_MAX+1];
|
|
};
|
|
|
|
struct cachedotp *head = NULL;
|
|
|
|
char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
|
|
|
|
void baile(x) {
|
|
fprintf(stderr, "%s: %s: %s(%d)\n", myname, x, strerror(errno), errno);
|
|
exit(1);
|
|
}
|
|
|
|
void bail(x) {
|
|
fprintf(stderr, "%s: %s\n", myname, x);
|
|
exit(1);
|
|
}
|
|
|
|
void zerocache(void)
|
|
{
|
|
struct cachedotp *c = head, *c2;
|
|
|
|
while(c) {
|
|
c2 = c->next;
|
|
memset(c, 0, sizeof(struct cachedotp));
|
|
c = c2;
|
|
};
|
|
};
|
|
|
|
int doreq(int fd)
|
|
{
|
|
int algorithm, sequence, i;
|
|
char *seed = NULL, *response = NULL;
|
|
|
|
if (((cmd[0] != 'S') && (cmd[0] != 's')) || (cmd[1] != '=') || (cmd[2] != ' ')) {
|
|
#if DEBUG
|
|
fprintf(stderr, "%s: got bogus command: %s\n", myname, cmd);
|
|
#endif /* DEBUG */
|
|
goto error;
|
|
};
|
|
|
|
{
|
|
char *c;
|
|
|
|
if (((algorithm = strtoul(&cmd[3], &c, 10)) < 3) || (algorithm > 5) || (*c != ' ')) {
|
|
#if DEBUG
|
|
fprintf(stderr, "%s: got bogus algorithm: %s\n", myname, cmd);
|
|
#endif /* DEBUG */
|
|
goto error;
|
|
};
|
|
|
|
if (((sequence = strtoul(c + 1, &c, 10)) <= OPIE_SEQUENCE_RESTRICT) || (sequence > OPIE_SEQUENCE_MAX)) {
|
|
#if DEBUG
|
|
fprintf(stderr, "%s: got bogus sequence: %s\n", myname, cmd);
|
|
#endif /* DEBUG */
|
|
goto error;
|
|
};
|
|
|
|
if (cmd[0] == 'S') {
|
|
if (!(c = strchr(seed = c + 1, ' '))) {
|
|
#if DEBUG
|
|
fprintf(stderr, "%s: got bogus seed: %s\n", myname, cmd);
|
|
#endif /* DEBUG */
|
|
goto error;
|
|
};
|
|
|
|
*c = 0;
|
|
|
|
if (!(c = strchr(response = c + 1, '\n'))) {
|
|
#if DEBUG
|
|
fprintf(stderr, "%s: got bogus response: %s\n", myname, cmd);
|
|
#endif /* DEBUG */
|
|
goto error;
|
|
};
|
|
|
|
*c = 0;
|
|
} else {
|
|
if (!(c = strchr(seed = c + 1, '\n'))) {
|
|
#if DEBUG
|
|
fprintf(stderr, "%s: got bogus seed: %s\n", myname, cmd);
|
|
#endif /* DEBUG */
|
|
goto error;
|
|
};
|
|
|
|
*c = 0;
|
|
};
|
|
};
|
|
|
|
#if DEBUG
|
|
fprintf(stderr, "got cmd=%c, algorithm=%d sequence=%d seed=+%s+ response=+%s+ on fd %d\n", cmd[0], algorithm, sequence, seed, response, fd);
|
|
#endif /* DEBUG */
|
|
|
|
seed = strdup(seed);
|
|
|
|
if (sequence < 10) {
|
|
#if DEBUG
|
|
fprintf(stderr, "sequence < 10; can't do it\n");
|
|
#endif /* DEBUG */
|
|
sprintf(cmd, "%c- %d %d %s\n", cmd[0], algorithm, sequence, seed);
|
|
};
|
|
|
|
{
|
|
struct cachedotp **c;
|
|
|
|
for (c = &head; *c && (strcmp((*c)->seed, seed) || ((*c)->algorithm != algorithm)); c = &((*c)->next));
|
|
if (!(*c)) {
|
|
if (cmd[0] == 's') {
|
|
#if DEBUG
|
|
fprintf(stderr, "(seed, algorithm) not found for s command\n");
|
|
#endif /* DEBUG */
|
|
sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, seed);
|
|
goto out;
|
|
}
|
|
|
|
if (!(*c = malloc(sizeof(struct cachedotp))))
|
|
baile("malloc");
|
|
memset(*c, 0, sizeof(struct cachedotp));
|
|
|
|
(*c)->algorithm = algorithm;
|
|
opiestrncpy((*c)->seed, seed, OPIE_SEED_MAX);
|
|
};
|
|
|
|
if (cmd[0] == 'S') {
|
|
(*c)->base = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT);
|
|
(*c)->current = sequence;
|
|
|
|
if (!opieatob8(&(*c)->basekey, response))
|
|
goto error;
|
|
|
|
sprintf(cmd, "S+ %d %d %s\n", algorithm, sequence, (*c)->seed);
|
|
} else {
|
|
if (sequence != ((*c)->current - 1)) {
|
|
#if DEBUG
|
|
fprintf(stderr, "out of sequence: sequence=%d, base=%d, current=%d\n", sequence, (*c)->base, (*c)->current);
|
|
#endif /* DEBUG */
|
|
sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, (*c)->seed);
|
|
goto out;
|
|
};
|
|
|
|
if (sequence < (*c)->base) {
|
|
#if DEBUG
|
|
fprintf(stderr, "attempt to generate below base: sequence=%d, base=%d, current=%d\n", sequence, (*c)->base, (*c)->current);
|
|
#endif /* DEBUG */
|
|
sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, (*c)->seed);
|
|
goto out;
|
|
};
|
|
|
|
(*c)->current = sequence;
|
|
i = sequence - (*c)->base;
|
|
{
|
|
struct opie_otpkey key;
|
|
char buffer[16+1];
|
|
|
|
key = (*c)->basekey;
|
|
while(i--)
|
|
opiehash(&key, algorithm);
|
|
|
|
opiebtoa8(buffer, &key);
|
|
sprintf(cmd, "s+ %d %d %s %s\n", algorithm, sequence, (*c)->seed, buffer);
|
|
};
|
|
};
|
|
|
|
printf("%c otp-%s %d %s (%d/%d)\n", cmd[0], algids[algorithm], sequence, (*c)->seed, sequence - (*c)->base, window);
|
|
fflush(stdout);
|
|
|
|
if (sequence == (*c)->base) {
|
|
struct cachedotp *c2 = *c;
|
|
*c = (*c)->next;
|
|
memset(c2, 0, sizeof(struct cachedotp));
|
|
free(c2);
|
|
};
|
|
};
|
|
|
|
out:
|
|
write(fd, cmd, i = strlen(cmd));
|
|
free(seed);
|
|
return 0;
|
|
|
|
error:
|
|
fprintf(stderr, "Invalid command on fd %d\n", fd);
|
|
if (seed)
|
|
free(seed);
|
|
return -1;
|
|
}
|
|
|
|
static void usage()
|
|
{
|
|
fprintf(stderr, "usage: %s [-v] [-h] [-q] [-n <number of OTPs>]\n", myname);
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i;
|
|
struct stat st;
|
|
char *sockpath;
|
|
|
|
if (myname = strrchr(argv[0], '/'))
|
|
myname++;
|
|
else
|
|
myname = argv[0];
|
|
|
|
while((i = getopt(argc, argv, "w:hv")) != EOF) {
|
|
switch(i) {
|
|
case 'v':
|
|
opieversion();
|
|
|
|
case 'w':
|
|
if (!(window = atoi(optarg))) {
|
|
fprintf(stderr, "%s: invalid number of OTPs: %s\n", myname, optarg);
|
|
exit(1);
|
|
};
|
|
break;
|
|
|
|
default:
|
|
usage();
|
|
}
|
|
};
|
|
|
|
{
|
|
uid_t myeuid;
|
|
|
|
if (!(myuid = getuid()) || !(myeuid = geteuid()) || (myuid != myeuid))
|
|
bail("this program must not be run with superuser priveleges or setuid.");
|
|
};
|
|
|
|
if (atexit(zerocache) < 0)
|
|
baile("atexit");
|
|
|
|
{
|
|
struct sockaddr_un sun;
|
|
|
|
memset(&sun, 0, sizeof(struct sockaddr_un));
|
|
sun.sun_family = AF_UNIX;
|
|
|
|
{
|
|
char *c;
|
|
char *c2 = "/.opieauto";
|
|
|
|
if (!(c = getenv("HOME")))
|
|
bail("getenv(HOME) failed -- no HOME variable?");
|
|
|
|
if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1))
|
|
bail("your HOME is too long");
|
|
|
|
strcpy(sun.sun_path, c);
|
|
strcat(sun.sun_path, c2);
|
|
sockpath = strdup(sun.sun_path);
|
|
};
|
|
|
|
if ((parents = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
|
|
baile("socket");
|
|
|
|
if (unlink(sockpath) && (errno != ENOENT))
|
|
baile("unlink");
|
|
|
|
if (umask(0177) < 0)
|
|
baile("umask");
|
|
|
|
if (bind(parents, (struct sockaddr *)&sun, sizeof(struct sockaddr_un)))
|
|
baile("bind");
|
|
|
|
if (stat(sockpath, &st) < 0)
|
|
baile("stat");
|
|
|
|
if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600))
|
|
bail("socket permissions and/or ownership were not correctly created.");
|
|
|
|
if (listen(parents, 1) < 0)
|
|
baile("listen");
|
|
};
|
|
|
|
{
|
|
fd_set fds, rfds, efds;
|
|
int maxfd = parents;
|
|
int i, j;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(parents, &fds);
|
|
|
|
while(1) {
|
|
memcpy(&rfds, &fds, sizeof(fd_set));
|
|
|
|
if (select(maxfd + 1, &rfds, NULL, NULL, NULL) < 0)
|
|
baile("select");
|
|
|
|
for (i = 0; s[i]; i++) {
|
|
if (!FD_ISSET(s[i], &rfds))
|
|
continue;
|
|
|
|
if (((j = read(s[i], cmd, sizeof(cmd)-1)) <= 0) || ((cmd[j] = 0) || doreq(s[i]))) {
|
|
close(s[i]);
|
|
FD_CLR(s[i], &fds);
|
|
|
|
if (s[i] == maxfd)
|
|
maxfd--;
|
|
|
|
for (j = i; s[j]; s[j] = s[j + 1], j++);
|
|
FD_SET(parents, &fds);
|
|
i--;
|
|
continue;
|
|
};
|
|
};
|
|
|
|
if (FD_ISSET(parents, &rfds)) {
|
|
for (i = 0; s[i]; i++)
|
|
if (i > MAXCLIENTS)
|
|
bail("this message never printed");
|
|
|
|
if (stat(sockpath, &st) < 0)
|
|
baile("stat");
|
|
|
|
if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600))
|
|
bail("socket permissions and/or ownership has been messed with.");
|
|
|
|
if ((s[i] = accept(parents, NULL, 0)) < 0)
|
|
baile("accept");
|
|
|
|
FD_SET(s[i], &fds);
|
|
if (s[i] > maxfd)
|
|
maxfd = s[i];
|
|
|
|
sprintf(cmd, "C+ %d\n", window);
|
|
if (write(s[i], cmd, j = strlen(cmd)) != j)
|
|
baile("write");
|
|
|
|
if (++i == MAXCLIENTS)
|
|
FD_CLR(parents, &fds);
|
|
}
|
|
}
|
|
}
|
|
}
|