1116742Ssam/* generator.c: The opiegenerator() library function. 2116904Ssam 3139530Ssam%%% portions-copyright-cmetz-96 4116742SsamPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights 5116742SsamReserved. The Inner Net License Version 2 applies to these portions of 6116742Ssamthe software. 7116742SsamYou should have received a copy of the license with this software. If 8116742Ssamyou didn't get a copy, you may request one from <license@inner.net>. 9116742Ssam 10116904Ssam History: 11116904Ssam 12116904Ssam Modified by cmetz for OPIE 2.4. Added opieauto code based on 13116904Ssam previously released test code. Renamed buffer to challenge. 14116904Ssam Use struct opie_otpkey for keys. 15116904Ssam Modified by cmetz for OPIE 2.32. If secret=NULL, always return 16116742Ssam as if opieauto returned "get the secret". Renamed 17116742Ssam _opieparsechallenge() to __opieparsechallenge(). Check 18116742Ssam challenge for extended response support and don't send 19116742Ssam an init-hex response if extended response support isn't 20116742Ssam indicated in the challenge. 21116904Ssam Modified by cmetz for OPIE 2.31. Renamed "init" to "init-hex". 22116904Ssam Removed active attack protection support. Fixed fairly 23116904Ssam bug in how init response was computed (i.e., dead wrong). 24116904Ssam Modified by cmetz for OPIE 2.3. Use _opieparsechallenge(). ifdef 25116904Ssam around string.h. Output hex responses by default, output 26116904Ssam OTP re-init extended responses (same secret) if sequence 27116904Ssam number falls below 10. 28116904Ssam Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al. 29116904Ssam Bug fixes. 30116904Ssam Created at NRL for OPIE 2.2. 31116742Ssam 32116742Ssam$FreeBSD$ 33116742Ssam*/ 34116742Ssam 35116742Ssam#include "opie_cfg.h" 36116742Ssam#if HAVE_STRING_H 37116742Ssam#include <string.h> 38116742Ssam#endif /* HAVE_STRING_H */ 39116742Ssam#if OPIEAUTO 40116742Ssam#include <errno.h> 41116742Ssam#if HAVE_STDLIB_H 42116742Ssam#include <stdlib.h> 43138568Ssam#endif /* HAVE_STDLIB_H */ 44116742Ssam#include <sys/stat.h> 45138568Ssam 46116742Ssam#include <sys/socket.h> 47116742Ssam#include <sys/un.h> 48116742Ssam#endif /* OPIEAUTO */ 49116742Ssam#if DEBUG 50138568Ssam#include <syslog.h> 51116742Ssam#endif /* DEBUG */ 52116742Ssam#include "opie.h" 53116742Ssam 54138568Ssamstatic char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" }; 55138568Ssam 56138568Ssam#if OPIEAUTO 57116742Ssam#ifndef max 58116742Ssam#define max(x, y) (((x) > (y)) ? (x) : (y)) 59116742Ssam#endif /* max */ 60116742Ssam 61116742Ssamstatic int opieauto_connect FUNCTION_NOARGS 62116742Ssam{ 63116742Ssam int s; 64116742Ssam struct sockaddr_un sun; 65116742Ssam char buffer[1024]; 66138568Ssam char *c, *c2 ="/.opieauto"; 67138568Ssam uid_t myuid = getuid(), myeuid = geteuid(); 68138568Ssam 69138568Ssam if (!myuid || !myeuid || (myuid != myeuid)) { 70138568Ssam#if DEBUG 71138568Ssam syslog(LOG_DEBUG, "opieauto_connect: superuser and/or setuid not allowed"); 72167283Ssam#endif /* DEBUG */ 73167283Ssam return -1; 74167283Ssam }; 75167283Ssam 76167283Ssam memset(&sun, 0, sizeof(struct sockaddr_un)); 77167283Ssam sun.sun_family = AF_UNIX; 78167283Ssam 79167283Ssam if (!(c = getenv("HOME"))) { 80167283Ssam#if DEBUG 81167283Ssam syslog(LOG_DEBUG, "opieauto_connect: no HOME variable?"); 82117811Ssam#endif /* DEBUG */ 83117811Ssam return -1; 84117811Ssam }; 85117811Ssam 86117811Ssam if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1)) { 87117811Ssam#if DEBUG 88117811Ssam syslog(LOG_DEBUG, "opieauto_connect: HOME is too long: %s", c); 89138568Ssam#endif /* DEBUG */ 90138568Ssam return -1; 91138568Ssam }; 92138568Ssam 93138568Ssam strcpy(sun.sun_path, c); 94138568Ssam strcat(sun.sun_path, c2); 95138568Ssam 96116742Ssam if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { 97117811Ssam#if DEBUG 98117811Ssam syslog(LOG_DEBUG, "opieauto_connect: socket: %s(%d)", strerror(errno), errno); 99116742Ssam#endif /* DEBUG */ 100138568Ssam return -1; 101116742Ssam }; 102138568Ssam 103116742Ssam { 104138568Ssam struct stat st; 105138568Ssam 106116742Ssam if (stat(sun.sun_path, &st) < 0) { 107116742Ssam#if DEBUG 108148291Ssam syslog(LOG_DEBUG, "opieauto_connect: stat: %s(%d)\n", strerror(errno), errno); 109148290Ssam#endif /* DEBUG */ 110153349Ssam goto ret; 111154736Ssam }; 112153346Ssam 113127648Ssam if (connect(s, (struct sockaddr *)&sun, sizeof(struct sockaddr_un))) { 114138568Ssam#if DEBUG 115116742Ssam syslog(LOG_DEBUG, "opieauto_connect: connect: %s(%d)\n", strerror(errno), errno); 116138568Ssam#endif /* DEBUG */ 117138568Ssam goto ret; 118138568Ssam }; 119121816Sbrooks 120116742Ssam if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600)) { 121117811Ssam#if DEBUG 122117811Ssam syslog(LOG_DEBUG, "opieauto_connect: something's fishy about the socket\n"); 123117811Ssam#endif /* DEBUG */ 124116742Ssam goto ret; 125116742Ssam }; 126116742Ssam }; 127160690Ssam 128116742Ssam return s; 129116742Ssam 130116742Ssamret: 131138568Ssam close(s); 132116742Ssam return -1; 133116742Ssam}; 134138568Ssam#endif /* OPIEAUTO */ 135138568Ssam 136138568Ssamint opiegenerator FUNCTION((challenge, secret, response), char *challenge AND char *secret AND char *response) 137138568Ssam{ 138138568Ssam int algorithm; 139138568Ssam int sequence; 140138568Ssam char *seed; 141138568Ssam struct opie_otpkey key; 142165894Ssam int i; 143116742Ssam int exts; 144138568Ssam#if OPIEAUTO 145138568Ssam int s; 146138568Ssam int window; 147138568Ssam char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1]; 148138568Ssam char *c; 149138568Ssam#endif /* OPIEAUTO */ 150116742Ssam 151116742Ssam if (!(challenge = strstr(challenge, "otp-"))) 152138568Ssam return 1; 153138568Ssam 154138568Ssam challenge += 4; 155138568Ssam 156138568Ssam if (__opieparsechallenge(challenge, &algorithm, &sequence, &seed, &exts)) 157138568Ssam return 1; 158138568Ssam 159138568Ssam if ((sequence < 2) || (sequence > 9999)) 160138568Ssam return 1; 161138568Ssam 162138568Ssam if (*secret) { 163138568Ssam if (opiepasscheck(secret)) 164138568Ssam return -2; 165138568Ssam 166138568Ssam if (i = opiekeycrunch(algorithm, &key, seed, secret)) 167138568Ssam return i; 168138568Ssam 169138568Ssam if (sequence <= OPIE_SEQUENCE_RESTRICT) { 170138568Ssam if (!(exts & 1)) 171138568Ssam return 1; 172138568Ssam 173138568Ssam { 174138568Ssam char newseed[OPIE_SEED_MAX + 1]; 175138568Ssam struct opie_otpkey newkey; 176138568Ssam char *c; 177138568Ssam char buf[OPIE_SEED_MAX + 48 + 1]; 178138568Ssam 179138568Ssam while (sequence-- != 0) 180138568Ssam opiehash(&key, algorithm); 181138568Ssam 182138568Ssam if (opienewseed(strcpy(newseed, seed)) < 0) 183138568Ssam return -1; 184138568Ssam 185138568Ssam if (opiekeycrunch(algorithm, &newkey, newseed, secret)) 186138568Ssam return -1; 187138568Ssam 188138568Ssam for (i = 0; i < 499; i++) 189138568Ssam opiehash(&newkey, algorithm); 190138568Ssam 191138568Ssam strcpy(response, "init-hex:"); 192138568Ssam strcat(response, opiebtoh(buf, &key)); 193138568Ssam if (snprintf(buf, sizeof(buf), ":%s 499 %s:", algids[algorithm], 194138568Ssam newseed) >= sizeof(buf)) { 195138568Ssam#ifdef DEBUG 196138568Ssam syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at init-hex"); 197138568Ssam#endif /* DEBUG */ 198116742Ssam return -1; 199138568Ssam } 200138568Ssam strcat(response, buf); 201116742Ssam strcat(response, opiebtoh(buf, &newkey)); 202138568Ssam }; 203138568Ssam }; 204138568Ssam }; 205138568Ssam 206138568Ssam#if OPIEAUTO 207138568Ssam if ((s = opieauto_connect()) >= 0) { 208138568Ssam if ((i = read(s, cmd, sizeof(cmd)-1)) < 0) { 209138568Ssam#if DEBUG 210138568Ssam syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 211138568Ssam#endif /* DEBUG */ 212138568Ssam close(s); 213138568Ssam s = -1; 214138568Ssam goto l0; 215138568Ssam }; 216138568Ssam cmd[i] = 0; 217138568Ssam if ((cmd[0] != 'C') || (cmd[1] != '+') || (cmd[2] != ' ')) { 218138568Ssam#if DEBUG 219138568Ssam syslog(LOG_DEBUG, "opiegenerator: got invalid/failing C+ response: %s\n", cmd); 220138568Ssam#endif /* DEBUG */ 221138568Ssam close(s); 222138568Ssam s = -1; 223138568Ssam goto l0; 224138568Ssam }; 225138568Ssam 226138568Ssam window = strtoul(&cmd[3], &c, 10); 227138568Ssam if (!window || (window >= (OPIE_SEQUENCE_MAX - OPIE_SEQUENCE_RESTRICT)) || !isspace(*c)) { 228138568Ssam#if DEBUG 229138568Ssam syslog(LOG_DEBUG, "opiegenerator: got bogus option response: %s\n", cmd); 230138568Ssam#endif /* DEBUG */ 231138568Ssam close(s); 232138568Ssam s = -1; 233138568Ssam goto l0; 234138568Ssam }; 235138568Ssam }; 236138568Ssam 237138568Ssaml0: 238138568Ssam if (*secret) { 239138568Ssam int j; 240138568Ssam 241138568Ssam if (s < 0) { 242138568Ssam j = 0; 243138568Ssam goto l1; 244138568Ssam }; 245138568Ssam 246138568Ssam j = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT); 247138568Ssam 248138568Ssam for (i = j; i > 0; i--) 249116742Ssam opiehash(&key, algorithm); 250116742Ssam 251116742Ssam { 252116742Ssam char buf[16+1]; 253116742Ssam 254116742Ssam opiebtoa8(buf, &key); 255116742Ssam 256116742Ssam if (snprintf(cmd, sizeof(cmd), "S= %d %d %s %s\n", algorithm, sequence, 257116742Ssam seed, buf) >= sizeof(cmd)) { 258116742Ssam#if DEBUG 259116742Ssam syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at S=\n"); 260116742Ssam#endif /* DEBUG */ 261116742Ssam goto l1; 262116742Ssam } 263116742Ssam } 264116742Ssam 265116742Ssam if (write(s, cmd, i = strlen(cmd)) != i) { 266116742Ssam#if DEBUG 267116742Ssam syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno); 268116742Ssam#endif /* DEBUG */ 269116742Ssam goto l1; 270116742Ssam }; 271138568Ssam 272116742Ssam if ((i = read(s, cmd, sizeof(cmd))) < 0) { 273138568Ssam#if DEBUG 274116742Ssam syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 275116742Ssam#endif /* DEBUG */ 276138568Ssam }; 277116742Ssam close(s); 278116742Ssam 279116742Ssam cmd[i] = 0; 280116742Ssam i = strlen(seed); 281116742Ssam if ((cmd[0] != 'S') || (cmd[1] != '+') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i) || (*(c + i) != '\n')) { 282116742Ssam#if DEBUG 283116742Ssam syslog(LOG_DEBUG, "opiegenerator: got invalid/failing S+ response: %s\n", cmd); 284116742Ssam#endif /* DEBUG */ 285116742Ssam }; 286116742Ssam 287116742Ssaml1: 288116742Ssam for (i = sequence - j; i > 0; i--) 289116742Ssam opiehash(&key, algorithm); 290116742Ssam 291116742Ssam opiebtoh(response, &key); 292116742Ssam } else { 293116742Ssam if (s < 0) 294138568Ssam goto l2; 295116742Ssam 296116742Ssam if ((snprintf(cmd, sizeof(cmd), "s= %d %d %s\n", algorithm, sequence, 297116742Ssam seed) >= sizeof(cmd))) { 298116742Ssam#if DEBUG 299116742Ssam syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at s=\n"); 300116742Ssam#endif /* DEBUG */ 301116742Ssam goto l2; 302116742Ssam } 303116742Ssam 304116742Ssam if (write(s, cmd, i = strlen(cmd)) != i) { 305116742Ssam#if DEBUG 306116742Ssam syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno); 307116742Ssam#endif /* DEBUG */ 308116742Ssam goto l2; 309116742Ssam }; 310116742Ssam 311116742Ssam if ((i = read(s, cmd, sizeof(cmd))) < 0) { 312116742Ssam#if DEBUG 313138568Ssam syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 314138568Ssam#endif /* DEBUG */ 315138568Ssam goto l2; 316138568Ssam }; 317138568Ssam close(s); 318138568Ssam 319138568Ssam i = strlen(seed); 320116742Ssam 321116742Ssam if ((cmd[0] != 's') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i)) { 322116742Ssam#if DEBUG 323116742Ssam if (c) 324116742Ssam *c = 0; 325116742Ssam else 326116742Ssam cmd[3] = 0; 327116742Ssam 328116742Ssam syslog(LOG_DEBUG, "opiegenerator: got bogus/invalid s response: %s\n", cmd); 329116742Ssam#endif /* DEBUG */ 330116742Ssam goto l2; 331116742Ssam }; 332116742Ssam 333116742Ssam c += i; 334116742Ssam 335165887Ssam if (cmd[1] == '-') { 336165887Ssam#if DEBUG 337165887Ssam if (*c != '\n') { 338165887Ssam *c = 0; 339165887Ssam syslog(LOG_DEBUG, "opiegenerator: got invalid s- response: %s\n", cmd); 340165887Ssam }; 341165887Ssam#endif /* DEBUG */ 342165887Ssam goto l2; 343165887Ssam }; 344165887Ssam 345165887Ssam if (cmd[1] != '+') { 346116742Ssam#if DEBUG 347148299Ssam *c = 0; 348116742Ssam syslog(LOG_DEBUG, "opiegenerator: got invalid s response: %s\n", cmd); 349116742Ssam#endif /* DEBUG */ 350148299Ssam goto l2; 351165887Ssam }; 352138568Ssam 353165569Ssam { 354165569Ssam char *c2; 355116742Ssam 356116742Ssam if (!(c2 = strchr(++c, '\n'))) { 357138568Ssam#if DEBUG 358138568Ssam *c = 0; 359138568Ssam syslog(LOG_DEBUG, "opiegenerator: got invalid s+ response: %s\n", cmd); 360138568Ssam#endif /* DEBUG */ 361148290Ssam goto l2; 362148290Ssam }; 363138568Ssam 364116742Ssam *c2++ = 0; 365138568Ssam }; 366165569Ssam 367116742Ssam if (!opieatob8(&key, c)) 368120482Ssam goto l2; 369116742Ssam 370116742Ssam opiebtoh(response, &key); 371116742Ssam }; 372116742Ssam 373116742Ssam if (s >= 0) 374116742Ssam close(s); 375116742Ssam#else /* OPIEAUTO */ 376116742Ssam if (*secret) { 377116742Ssam while (sequence-- != 0) 378116742Ssam opiehash(&key, algorithm); 379116742Ssam 380116742Ssam opiebtoh(response, &key); 381116742Ssam } else 382116742Ssam return -2; 383116742Ssam#endif /* OPIEAUTO */ 384116742Ssam 385138568Ssam return 0; 386116742Ssam 387138568Ssam#if OPIEAUTO 388138568Ssaml2: 389116742Ssam#if DEBUG 390165887Ssam syslog(LOG_DEBUG, "opiegenerator: no opieauto response available.\n"); 391165887Ssam#endif /* DEBUG */ 392165887Ssam if (s >= 0) 393165887Ssam close(s); 394116742Ssam 395165887Ssam return -2; 396120482Ssam#endif /* OPIEAUTO */ 397120482Ssam}; 398120482Ssam