Merge branch 'vendor/OPENSSL'
[dragonfly.git] / contrib / opie / opieauto.c
1 /* opieauto.c: The opieauto program.
2
3 %%% copyright-cmetz-96
4 This software is Copyright 1996-2001 by Craig Metz, All Rights Reserved.
5 The Inner Net License Version 3 applies to this software.
6 You should have received a copy of the license with this software. If
7 you 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
42 int window = 10;
43 char *myname = NULL;
44
45 uid_t myuid = 0;
46
47 #define MAXCLIENTS 2
48 int parents, s[MAXCLIENTS + 1];
49
50 char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1];
51
52 struct cachedotp {
53   struct cachedotp *next;
54   int algorithm, base, current;
55   struct opie_otpkey basekey;
56   char seed[OPIE_SEED_MAX+1];
57 };
58
59 struct cachedotp *head = NULL;
60
61 char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
62
63 void baile(x) {
64   fprintf(stderr, "%s: %s: %s(%d)\n", myname, x, strerror(errno), errno);
65   exit(1);
66 }
67
68 void bail(x) {
69   fprintf(stderr, "%s: %s\n", myname, x);
70   exit(1);
71 }
72
73 void 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
84 int 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
228 out:
229   write(fd, cmd, i = strlen(cmd));
230   free(seed);
231   return 0;
232
233 error:
234   fprintf(stderr, "Invalid command on fd %d\n", fd);
235   if (seed)
236     free(seed);
237   return -1;
238 }
239
240 static void usage()
241 {
242   fprintf(stderr, "usage: %s [-v] [-h] [-q] [-n <number of OTPs>]\n", myname);
243   exit(1);
244 }
245
246 int 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 }