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