1/* opieauto.c: The opieauto program.
2
3%%% copyright-cmetz-96
4This software is Copyright 1996-2001 by Craig Metz, All Rights Reserved.
5The Inner Net License Version 3 applies to this software.
6You should have received a copy of the license with this software. If
7you didn't get a copy, you may request one from <license@inner.net>.
8
9        History:
10
11	Created by cmetz for OPIE 2.4 based on previously released
12	        test code. Use opiestrncpy().
13*/
14
15#include "opie_cfg.h"
16#include <sys/types.h>
17#include <sys/socket.h>
18#include <sys/un.h>
19#if HAVE_SYS_TIME_H
20#include <sys/time.h>
21#endif /* HAVE_SYS_TIME_H */
22#include <stdio.h>
23#include <errno.h>
24#if HAVE_STRING_H
25#include <string.h>
26#endif /* HAVE_STRING_H */
27#include <getopt.h>
28#if HAVE_STDLIB_H
29#include <stdlib.h>
30#endif /* HAVE_STDLIB_H */
31#if HAVE_UNISTD_H
32#include <unistd.h>
33#endif /* HAVE_UNISTD_H */
34#include <sys/stat.h>
35
36#include "opie.h"
37
38#ifndef max
39#define max(x, y) (((x) > (y)) ? (x) : (y))
40#endif /* max */
41
42int window = 10;
43char *myname = NULL;
44
45uid_t myuid = 0;
46
47#define MAXCLIENTS 2
48int parents, s[MAXCLIENTS + 1];
49
50char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1];
51
52struct cachedotp {
53  struct cachedotp *next;
54  int algorithm, base, current;
55  struct opie_otpkey basekey;
56  char seed[OPIE_SEED_MAX+1];
57};
58
59struct cachedotp *head = NULL;
60
61char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
62
63void baile(x) {
64  fprintf(stderr, "%s: %s: %s(%d)\n", myname, x, strerror(errno), errno);
65  exit(1);
66}
67
68void bail(x) {
69  fprintf(stderr, "%s: %s\n", myname, x);
70  exit(1);
71}
72
73void zerocache(void)
74{
75  struct cachedotp *c = head, *c2;
76
77  while(c) {
78    c2 = c->next;
79    memset(c, 0, sizeof(struct cachedotp));
80    c = c2;
81  };
82};
83
84int doreq(int fd)
85{
86  int algorithm, sequence, i;
87  char *seed = NULL, *response = NULL;
88
89  if (((cmd[0] != 'S') && (cmd[0] != 's')) || (cmd[1] != '=') || (cmd[2] != ' ')) {
90#if DEBUG
91    fprintf(stderr, "%s: got bogus command: %s\n", myname, cmd);
92#endif /* DEBUG */
93    goto error;
94  };
95
96  {
97  char *c;
98
99  if (((algorithm = strtoul(&cmd[3], &c, 10)) < 3) || (algorithm > 5) || (*c != ' ')) {
100#if DEBUG
101    fprintf(stderr, "%s: got bogus algorithm: %s\n", myname, cmd);
102#endif /* DEBUG */
103    goto error;
104  };
105
106  if (((sequence = strtoul(c + 1, &c, 10)) <= OPIE_SEQUENCE_RESTRICT) || (sequence > OPIE_SEQUENCE_MAX)) {
107#if DEBUG
108    fprintf(stderr, "%s: got bogus sequence: %s\n", myname, cmd);
109#endif /* DEBUG */
110    goto error;
111  };
112
113  if (cmd[0] == 'S') {
114    if (!(c = strchr(seed = c + 1, ' '))) {
115#if DEBUG
116      fprintf(stderr, "%s: got bogus seed: %s\n", myname, cmd);
117#endif /* DEBUG */
118      goto error;
119    };
120
121    *c = 0;
122
123    if (!(c = strchr(response = c + 1, '\n'))) {
124#if DEBUG
125      fprintf(stderr, "%s: got bogus response: %s\n", myname, cmd);
126#endif /* DEBUG */
127      goto error;
128    };
129
130    *c = 0;
131  } else {
132    if (!(c = strchr(seed = c + 1, '\n'))) {
133#if DEBUG
134      fprintf(stderr, "%s: got bogus seed: %s\n", myname, cmd);
135#endif /* DEBUG */
136      goto error;
137    };
138
139    *c = 0;
140  };
141  };
142
143#if DEBUG
144  fprintf(stderr, "got cmd=%c, algorithm=%d sequence=%d seed=+%s+ response=+%s+ on fd %d\n", cmd[0], algorithm, sequence, seed, response, fd);
145#endif /* DEBUG */
146
147  seed = strdup(seed);
148
149  if (sequence < 10) {
150#if DEBUG
151    fprintf(stderr, "sequence < 10; can't do it\n");
152#endif /* DEBUG */
153    sprintf(cmd, "%c- %d %d %s\n", cmd[0], algorithm, sequence, seed);
154  };
155
156  {
157  struct cachedotp **c;
158
159  for (c = &head; *c && (strcmp((*c)->seed, seed) || ((*c)->algorithm != algorithm)); c = &((*c)->next));
160  if (!(*c)) {
161    if (cmd[0] == 's') {
162#if DEBUG
163      fprintf(stderr, "(seed, algorithm) not found for s command\n");
164#endif /* DEBUG */
165      sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, seed);
166      goto out;
167    }
168
169    if (!(*c = malloc(sizeof(struct cachedotp))))
170      baile("malloc");
171    memset(*c, 0, sizeof(struct cachedotp));
172
173    (*c)->algorithm = algorithm;
174    opiestrncpy((*c)->seed, seed, OPIE_SEED_MAX);
175  };
176
177  if (cmd[0] == 'S') {
178    (*c)->base = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT);
179    (*c)->current = sequence;
180
181    if (!opieatob8(&(*c)->basekey, response))
182      goto error;
183
184    sprintf(cmd, "S+ %d %d %s\n", algorithm, sequence, (*c)->seed);
185  } else {
186    if (sequence != ((*c)->current - 1)) {
187#if DEBUG
188      fprintf(stderr, "out of sequence: sequence=%d, base=%d, current=%d\n", sequence, (*c)->base, (*c)->current);
189#endif /* DEBUG */
190      sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, (*c)->seed);
191      goto out;
192    };
193
194    if (sequence < (*c)->base) {
195#if DEBUG
196      fprintf(stderr, "attempt to generate below base: sequence=%d, base=%d, current=%d\n", sequence, (*c)->base, (*c)->current);
197#endif /* DEBUG */
198      sprintf(cmd, "s- %d %d %s\n", algorithm, sequence, (*c)->seed);
199      goto out;
200    };
201
202    (*c)->current = sequence;
203    i = sequence - (*c)->base;
204    {
205      struct opie_otpkey key;
206      char buffer[16+1];
207
208      key = (*c)->basekey;
209      while(i--)
210	opiehash(&key, algorithm);
211
212      opiebtoa8(buffer, &key);
213      sprintf(cmd, "s+ %d %d %s %s\n", algorithm, sequence, (*c)->seed, buffer);
214    };
215  };
216
217  printf("%c otp-%s %d %s (%d/%d)\n", cmd[0], algids[algorithm], sequence, (*c)->seed, sequence - (*c)->base, window);
218  fflush(stdout);
219
220  if (sequence == (*c)->base) {
221    struct cachedotp *c2 = *c;
222    *c = (*c)->next;
223    memset(c2, 0, sizeof(struct cachedotp));
224    free(c2);
225  };
226  };
227
228out:
229  write(fd, cmd, i = strlen(cmd));
230  free(seed);
231  return 0;
232
233error:
234  fprintf(stderr, "Invalid command on fd %d\n", fd);
235  if (seed)
236    free(seed);
237  return -1;
238}
239
240static void usage()
241{
242  fprintf(stderr, "usage: %s [-v] [-h] [-q] [-n <number of OTPs>]\n", myname);
243  exit(1);
244}
245
246int main(int argc, char **argv)
247{
248  int i;
249  struct stat st;
250  char *sockpath;
251
252  if (myname = strrchr(argv[0], '/'))
253    myname++;
254  else
255    myname = argv[0];
256
257  while((i = getopt(argc, argv, "w:hv")) != EOF) {
258    switch(i) {
259      case 'v':
260	opieversion();
261
262      case 'w':
263	if (!(window = atoi(optarg))) {
264	  fprintf(stderr, "%s: invalid number of OTPs: %s\n", myname, optarg);
265	  exit(1);
266	};
267	break;
268
269      default:
270	usage();
271    }
272  };
273
274  {
275    uid_t myeuid;
276
277    if (!(myuid = getuid()) || !(myeuid = geteuid()) || (myuid != myeuid))
278      bail("this program must not be run with superuser priveleges or setuid.");
279  };
280
281  if (atexit(zerocache) < 0)
282    baile("atexit");
283
284  {
285    struct sockaddr_un sun;
286
287    memset(&sun, 0, sizeof(struct sockaddr_un));
288    sun.sun_family = AF_UNIX;
289
290    {
291    char *c;
292    char *c2 = "/.opieauto";
293
294    if (!(c = getenv("HOME")))
295      bail("getenv(HOME) failed -- no HOME variable?");
296
297    if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1))
298      bail("your HOME is too long");
299
300    strcpy(sun.sun_path, c);
301    strcat(sun.sun_path, c2);
302    sockpath = strdup(sun.sun_path);
303    };
304
305    if ((parents = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
306      baile("socket");
307
308    if (unlink(sockpath) && (errno != ENOENT))
309      baile("unlink");
310
311    if (umask(0177) < 0)
312      baile("umask");
313
314    if (bind(parents, (struct sockaddr *)&sun, sizeof(struct sockaddr_un)))
315      baile("bind");
316
317    if (stat(sockpath, &st) < 0)
318      baile("stat");
319
320    if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600))
321      bail("socket permissions and/or ownership were not correctly created.");
322
323    if (listen(parents, 1) < 0)
324      baile("listen");
325  };
326
327  {
328    fd_set fds, rfds, efds;
329    int maxfd = parents;
330    int i, j;
331
332    FD_ZERO(&fds);
333    FD_SET(parents, &fds);
334
335    while(1) {
336      memcpy(&rfds, &fds, sizeof(fd_set));
337
338      if (select(maxfd + 1, &rfds, NULL, NULL, NULL) < 0)
339	baile("select");
340
341      for (i = 0; s[i]; i++) {
342	if (!FD_ISSET(s[i], &rfds))
343	  continue;
344
345	if (((j = read(s[i], cmd, sizeof(cmd)-1)) <= 0) || ((cmd[j] = 0) || doreq(s[i]))) {
346	  close(s[i]);
347	  FD_CLR(s[i], &fds);
348
349	  if (s[i] == maxfd)
350	    maxfd--;
351
352	  for (j = i; s[j]; s[j] = s[j + 1], j++);
353	  FD_SET(parents, &fds);
354	  i--;
355	  continue;
356	};
357      };
358
359      if (FD_ISSET(parents, &rfds)) {
360	for (i = 0; s[i]; i++)
361	  if (i > MAXCLIENTS)
362	    bail("this message never printed");
363
364	if (stat(sockpath, &st) < 0)
365	  baile("stat");
366
367	if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600))
368	  bail("socket permissions and/or ownership has been messed with.");
369
370	if ((s[i] = accept(parents, NULL, 0)) < 0)
371	  baile("accept");
372
373	FD_SET(s[i], &fds);
374	if (s[i] > maxfd)
375	  maxfd = s[i];
376
377	sprintf(cmd, "C+ %d\n", window);
378	if (write(s[i], cmd, j = strlen(cmd)) != j)
379	  baile("write");
380
381	if (++i == MAXCLIENTS)
382	  FD_CLR(parents, &fds);
383      }
384    }
385  }
386}
387