122347Spst/* generator.c: The opiegenerator() library function. 222347Spst 329967Sache%%% portions-copyright-cmetz-96 492914SmarkmPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights 522347SpstReserved. The Inner Net License Version 2 applies to these portions of 622347Spstthe software. 722347SpstYou should have received a copy of the license with this software. If 822347Spstyou didn't get a copy, you may request one from <license@inner.net>. 922347Spst 1022347Spst History: 1122347Spst 1292914Smarkm Modified by cmetz for OPIE 2.4. Added opieauto code based on 1392914Smarkm previously released test code. Renamed buffer to challenge. 1492914Smarkm Use struct opie_otpkey for keys. 1559121Skris Modified by cmetz for OPIE 2.32. If secret=NULL, always return 1659121Skris as if opieauto returned "get the secret". Renamed 1759121Skris _opieparsechallenge() to __opieparsechallenge(). Check 1859121Skris challenge for extended response support and don't send 1959121Skris an init-hex response if extended response support isn't 2059121Skris indicated in the challenge. 2129967Sache Modified by cmetz for OPIE 2.31. Renamed "init" to "init-hex". 2259121Skris Removed active attack protection support. Fixed fairly 2359121Skris bug in how init response was computed (i.e., dead wrong). 2422347Spst Modified by cmetz for OPIE 2.3. Use _opieparsechallenge(). ifdef 2559121Skris around string.h. Output hex responses by default, output 2659121Skris OTP re-init extended responses (same secret) if sequence 2759121Skris number falls below 10. 2822347Spst Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al. 2959121Skris Bug fixes. 3022347Spst Created at NRL for OPIE 2.2. 3159121Skris 3259121Skris$FreeBSD$ 3322347Spst*/ 3422347Spst 3522347Spst#include "opie_cfg.h" 3622347Spst#if HAVE_STRING_H 3722347Spst#include <string.h> 3822347Spst#endif /* HAVE_STRING_H */ 3992914Smarkm#if OPIEAUTO 4092914Smarkm#include <errno.h> 4192914Smarkm#if HAVE_STDLIB_H 4292914Smarkm#include <stdlib.h> 4392914Smarkm#endif /* HAVE_STDLIB_H */ 4492914Smarkm#include <sys/stat.h> 4592914Smarkm 4692914Smarkm#include <sys/socket.h> 4792914Smarkm#include <sys/un.h> 4892914Smarkm#endif /* OPIEAUTO */ 4992914Smarkm#if DEBUG 5092914Smarkm#include <syslog.h> 5192914Smarkm#endif /* DEBUG */ 5222347Spst#include "opie.h" 5322347Spst 5422347Spststatic char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" }; 5522347Spst 5692914Smarkm#if OPIEAUTO 5792914Smarkm#ifndef max 5892914Smarkm#define max(x, y) (((x) > (y)) ? (x) : (y)) 5992914Smarkm#endif /* max */ 6092914Smarkm 6192914Smarkmstatic int opieauto_connect FUNCTION_NOARGS 6222347Spst{ 6392914Smarkm int s; 6492914Smarkm struct sockaddr_un sun; 6592914Smarkm char buffer[1024]; 6692914Smarkm char *c, *c2 ="/.opieauto"; 6792914Smarkm uid_t myuid = getuid(), myeuid = geteuid(); 6892914Smarkm 6992914Smarkm if (!myuid || !myeuid || (myuid != myeuid)) { 7092914Smarkm#if DEBUG 7192914Smarkm syslog(LOG_DEBUG, "opieauto_connect: superuser and/or setuid not allowed"); 7292914Smarkm#endif /* DEBUG */ 7392914Smarkm return -1; 7492914Smarkm }; 7592914Smarkm 7692914Smarkm memset(&sun, 0, sizeof(struct sockaddr_un)); 7792914Smarkm sun.sun_family = AF_UNIX; 7892914Smarkm 7992914Smarkm if (!(c = getenv("HOME"))) { 8092914Smarkm#if DEBUG 8192914Smarkm syslog(LOG_DEBUG, "opieauto_connect: no HOME variable?"); 8292914Smarkm#endif /* DEBUG */ 8392914Smarkm return -1; 8492914Smarkm }; 8592914Smarkm 8692914Smarkm if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1)) { 8792914Smarkm#if DEBUG 8892914Smarkm syslog(LOG_DEBUG, "opieauto_connect: HOME is too long: %s", c); 8992914Smarkm#endif /* DEBUG */ 9092914Smarkm return -1; 9192914Smarkm }; 9292914Smarkm 9392914Smarkm strcpy(sun.sun_path, c); 9492914Smarkm strcat(sun.sun_path, c2); 9592914Smarkm 9692914Smarkm if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { 9792914Smarkm#if DEBUG 9892914Smarkm syslog(LOG_DEBUG, "opieauto_connect: socket: %s(%d)", strerror(errno), errno); 9992914Smarkm#endif /* DEBUG */ 10092914Smarkm return -1; 10192914Smarkm }; 10292914Smarkm 10392914Smarkm { 10492914Smarkm struct stat st; 10592914Smarkm 10692914Smarkm if (stat(sun.sun_path, &st) < 0) { 10792914Smarkm#if DEBUG 10892914Smarkm syslog(LOG_DEBUG, "opieauto_connect: stat: %s(%d)\n", strerror(errno), errno); 10992914Smarkm#endif /* DEBUG */ 11092914Smarkm goto ret; 11192914Smarkm }; 11292914Smarkm 11392914Smarkm if (connect(s, (struct sockaddr *)&sun, sizeof(struct sockaddr_un))) { 11492914Smarkm#if DEBUG 11592914Smarkm syslog(LOG_DEBUG, "opieauto_connect: connect: %s(%d)\n", strerror(errno), errno); 11692914Smarkm#endif /* DEBUG */ 11792914Smarkm goto ret; 11892914Smarkm }; 11992914Smarkm 12092914Smarkm if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600)) { 12192914Smarkm#if DEBUG 12292914Smarkm syslog(LOG_DEBUG, "opieauto_connect: something's fishy about the socket\n"); 12392914Smarkm#endif /* DEBUG */ 12492914Smarkm goto ret; 12592914Smarkm }; 12692914Smarkm }; 12792914Smarkm 12892914Smarkm return s; 12992914Smarkm 13092914Smarkmret: 13192914Smarkm close(s); 13292914Smarkm return -1; 13392914Smarkm}; 13492914Smarkm#endif /* OPIEAUTO */ 13592914Smarkm 13692914Smarkmint opiegenerator FUNCTION((challenge, secret, response), char *challenge AND char *secret AND char *response) 13792914Smarkm{ 13822347Spst int algorithm; 13922347Spst int sequence; 14022347Spst char *seed; 14192914Smarkm struct opie_otpkey key; 14222347Spst int i; 14359121Skris int exts; 14492914Smarkm#if OPIEAUTO 14592914Smarkm int s; 14692914Smarkm int window; 14792914Smarkm char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1]; 14892914Smarkm char *c; 14992914Smarkm#endif /* OPIEAUTO */ 15022347Spst 15192914Smarkm if (!(challenge = strstr(challenge, "otp-"))) 15222347Spst return 1; 15322347Spst 15492914Smarkm challenge += 4; 15522347Spst 15692914Smarkm if (__opieparsechallenge(challenge, &algorithm, &sequence, &seed, &exts)) 15722347Spst return 1; 15822347Spst 15922347Spst if ((sequence < 2) || (sequence > 9999)) 16022347Spst return 1; 16122347Spst 16292914Smarkm if (*secret) { 16392914Smarkm if (opiepasscheck(secret)) 16492914Smarkm return -2; 16559121Skris 16692914Smarkm if (i = opiekeycrunch(algorithm, &key, seed, secret)) 16792914Smarkm return i; 16822347Spst 16992914Smarkm if (sequence <= OPIE_SEQUENCE_RESTRICT) { 17092914Smarkm if (!(exts & 1)) 17192914Smarkm return 1; 17222347Spst 17392914Smarkm { 17492914Smarkm char newseed[OPIE_SEED_MAX + 1]; 17592914Smarkm struct opie_otpkey newkey; 17692914Smarkm char *c; 17792914Smarkm char buf[OPIE_SEED_MAX + 48 + 1]; 17829967Sache 17992914Smarkm while (sequence-- != 0) 18092914Smarkm opiehash(&key, algorithm); 18192914Smarkm 18292914Smarkm if (opienewseed(strcpy(newseed, seed)) < 0) 18392914Smarkm return -1; 18492914Smarkm 18592914Smarkm if (opiekeycrunch(algorithm, &newkey, newseed, secret)) 18692914Smarkm return -1; 18792914Smarkm 18892914Smarkm for (i = 0; i < 499; i++) 18992914Smarkm opiehash(&newkey, algorithm); 19092914Smarkm 19192914Smarkm strcpy(response, "init-hex:"); 19292914Smarkm strcat(response, opiebtoh(buf, &key)); 19392914Smarkm if (snprintf(buf, sizeof(buf), ":%s 499 %s:", algids[algorithm], 19492914Smarkm newseed) >= sizeof(buf)) { 19592914Smarkm#ifdef DEBUG 19692914Smarkm syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at init-hex"); 19792914Smarkm#endif /* DEBUG */ 19892914Smarkm return -1; 19992914Smarkm } 20092914Smarkm strcat(response, buf); 20192914Smarkm strcat(response, opiebtoh(buf, &newkey)); 20292914Smarkm }; 20392914Smarkm }; 20492914Smarkm }; 20592914Smarkm 20692914Smarkm#if OPIEAUTO 20792914Smarkm if ((s = opieauto_connect()) >= 0) { 20892914Smarkm if ((i = read(s, cmd, sizeof(cmd)-1)) < 0) { 20992914Smarkm#if DEBUG 21092914Smarkm syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 21192914Smarkm#endif /* DEBUG */ 21292914Smarkm close(s); 21392914Smarkm s = -1; 21492914Smarkm goto l0; 21592914Smarkm }; 21692914Smarkm cmd[i] = 0; 21792914Smarkm if ((cmd[0] != 'C') || (cmd[1] != '+') || (cmd[2] != ' ')) { 21892914Smarkm#if DEBUG 21992914Smarkm syslog(LOG_DEBUG, "opiegenerator: got invalid/failing C+ response: %s\n", cmd); 22092914Smarkm#endif /* DEBUG */ 22192914Smarkm close(s); 22292914Smarkm s = -1; 22392914Smarkm goto l0; 22492914Smarkm }; 22592914Smarkm 22692914Smarkm window = strtoul(&cmd[3], &c, 10); 22792914Smarkm if (!window || (window >= (OPIE_SEQUENCE_MAX - OPIE_SEQUENCE_RESTRICT)) || !isspace(*c)) { 22892914Smarkm#if DEBUG 22992914Smarkm syslog(LOG_DEBUG, "opiegenerator: got bogus option response: %s\n", cmd); 23092914Smarkm#endif /* DEBUG */ 23192914Smarkm close(s); 23292914Smarkm s = -1; 23392914Smarkm goto l0; 23492914Smarkm }; 23592914Smarkm }; 23692914Smarkm 23792914Smarkml0: 23892914Smarkm if (*secret) { 23992914Smarkm int j; 24092914Smarkm 24192914Smarkm if (s < 0) { 24292914Smarkm j = 0; 24392914Smarkm goto l1; 24492914Smarkm }; 24592914Smarkm 24692914Smarkm j = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT); 24792914Smarkm 24892914Smarkm for (i = j; i > 0; i--) 24992914Smarkm opiehash(&key, algorithm); 25092914Smarkm 25159121Skris { 25292914Smarkm char buf[16+1]; 25322347Spst 25492914Smarkm opiebtoa8(buf, &key); 25529967Sache 25692914Smarkm if (snprintf(cmd, sizeof(cmd), "S= %d %d %s %s\n", algorithm, sequence, 25792914Smarkm seed, buf) >= sizeof(cmd)) { 25892914Smarkm#if DEBUG 25992914Smarkm syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at S=\n"); 26092914Smarkm#endif /* DEBUG */ 26192914Smarkm goto l1; 26292914Smarkm } 26392914Smarkm } 26422347Spst 26592914Smarkm if (write(s, cmd, i = strlen(cmd)) != i) { 26692914Smarkm#if DEBUG 26792914Smarkm syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno); 26892914Smarkm#endif /* DEBUG */ 26992914Smarkm goto l1; 27092914Smarkm }; 27122347Spst 27292914Smarkm if ((i = read(s, cmd, sizeof(cmd))) < 0) { 27392914Smarkm#if DEBUG 27492914Smarkm syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 27592914Smarkm#endif /* DEBUG */ 27692914Smarkm }; 27792914Smarkm close(s); 27822347Spst 27992914Smarkm cmd[i] = 0; 28092914Smarkm i = strlen(seed); 28192914Smarkm 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')) { 28292914Smarkm#if DEBUG 28392914Smarkm syslog(LOG_DEBUG, "opiegenerator: got invalid/failing S+ response: %s\n", cmd); 28492914Smarkm#endif /* DEBUG */ 28559121Skris }; 28692914Smarkm 28792914Smarkml1: 28892914Smarkm for (i = sequence - j; i > 0; i--) 28992914Smarkm opiehash(&key, algorithm); 29092914Smarkm 29192914Smarkm opiebtoh(response, &key); 29222347Spst } else { 29392914Smarkm if (s < 0) 29492914Smarkm goto l2; 29592914Smarkm 29692914Smarkm if ((snprintf(cmd, sizeof(cmd), "s= %d %d %s\n", algorithm, sequence, 29792914Smarkm seed) >= sizeof(cmd))) { 29892914Smarkm#if DEBUG 29992914Smarkm syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at s=\n"); 30092914Smarkm#endif /* DEBUG */ 30192914Smarkm goto l2; 30292914Smarkm } 30392914Smarkm 30492914Smarkm if (write(s, cmd, i = strlen(cmd)) != i) { 30592914Smarkm#if DEBUG 30692914Smarkm syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno); 30792914Smarkm#endif /* DEBUG */ 30892914Smarkm goto l2; 30992914Smarkm }; 31092914Smarkm 31192914Smarkm if ((i = read(s, cmd, sizeof(cmd))) < 0) { 31292914Smarkm#if DEBUG 31392914Smarkm syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 31492914Smarkm#endif /* DEBUG */ 31592914Smarkm goto l2; 31692914Smarkm }; 31792914Smarkm close(s); 31892914Smarkm 31992914Smarkm i = strlen(seed); 32092914Smarkm 32192914Smarkm if ((cmd[0] != 's') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i)) { 32292914Smarkm#if DEBUG 32392914Smarkm if (c) 32492914Smarkm *c = 0; 32592914Smarkm else 32692914Smarkm cmd[3] = 0; 32792914Smarkm 32892914Smarkm syslog(LOG_DEBUG, "opiegenerator: got bogus/invalid s response: %s\n", cmd); 32992914Smarkm#endif /* DEBUG */ 33092914Smarkm goto l2; 33192914Smarkm }; 33292914Smarkm 33392914Smarkm c += i; 33492914Smarkm 33592914Smarkm if (cmd[1] == '-') { 33692914Smarkm#if DEBUG 33792914Smarkm if (*c != '\n') { 33892914Smarkm *c = 0; 33992914Smarkm syslog(LOG_DEBUG, "opiegenerator: got invalid s- response: %s\n", cmd); 34092914Smarkm }; 34192914Smarkm#endif /* DEBUG */ 34292914Smarkm goto l2; 34392914Smarkm }; 34492914Smarkm 34592914Smarkm if (cmd[1] != '+') { 34692914Smarkm#if DEBUG 34792914Smarkm *c = 0; 34892914Smarkm syslog(LOG_DEBUG, "opiegenerator: got invalid s response: %s\n", cmd); 34992914Smarkm#endif /* DEBUG */ 35092914Smarkm goto l2; 35192914Smarkm }; 35292914Smarkm 35392914Smarkm { 35492914Smarkm char *c2; 35592914Smarkm 35692914Smarkm if (!(c2 = strchr(++c, '\n'))) { 35792914Smarkm#if DEBUG 35892914Smarkm *c = 0; 35992914Smarkm syslog(LOG_DEBUG, "opiegenerator: got invalid s+ response: %s\n", cmd); 36092914Smarkm#endif /* DEBUG */ 36192914Smarkm goto l2; 36292914Smarkm }; 36392914Smarkm 36492914Smarkm *c2++ = 0; 36592914Smarkm }; 36692914Smarkm 36792914Smarkm if (!opieatob8(&key, c)) 36892914Smarkm goto l2; 36992914Smarkm 37092914Smarkm opiebtoh(response, &key); 37192914Smarkm }; 37292914Smarkm 37392914Smarkm if (s >= 0) 37492914Smarkm close(s); 37592914Smarkm#else /* OPIEAUTO */ 37692914Smarkm if (*secret) { 37722347Spst while (sequence-- != 0) 37892914Smarkm opiehash(&key, algorithm); 37929967Sache 38092914Smarkm opiebtoh(response, &key); 38192914Smarkm } else 38292914Smarkm return -2; 38392914Smarkm#endif /* OPIEAUTO */ 38422347Spst 38522347Spst return 0; 38692914Smarkm 38792914Smarkm#if OPIEAUTO 38892914Smarkml2: 38992914Smarkm#if DEBUG 39092914Smarkm syslog(LOG_DEBUG, "opiegenerator: no opieauto response available.\n"); 39192914Smarkm#endif /* DEBUG */ 39292914Smarkm if (s >= 0) 39392914Smarkm close(s); 39492914Smarkm 39592914Smarkm return -2; 39692914Smarkm#endif /* OPIEAUTO */ 39792914Smarkm}; 398