1/* generator.c: The opiegenerator() library function. 2 3%%% portions-copyright-cmetz-96 4Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights 5Reserved. The Inner Net License Version 2 applies to these portions of 6the software. 7You should have received a copy of the license with this software. If 8you didn't get a copy, you may request one from <license@inner.net>. 9 10 History: 11 12 Modified by cmetz for OPIE 2.4. Added opieauto code based on 13 previously released test code. Renamed buffer to challenge. 14 Use struct opie_otpkey for keys. 15 Modified by cmetz for OPIE 2.32. If secret=NULL, always return 16 as if opieauto returned "get the secret". Renamed 17 _opieparsechallenge() to __opieparsechallenge(). Check 18 challenge for extended response support and don't send 19 an init-hex response if extended response support isn't 20 indicated in the challenge. 21 Modified by cmetz for OPIE 2.31. Renamed "init" to "init-hex". 22 Removed active attack protection support. Fixed fairly 23 bug in how init response was computed (i.e., dead wrong). 24 Modified by cmetz for OPIE 2.3. Use _opieparsechallenge(). ifdef 25 around string.h. Output hex responses by default, output 26 OTP re-init extended responses (same secret) if sequence 27 number falls below 10. 28 Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al. 29 Bug fixes. 30 Created at NRL for OPIE 2.2. 31 32$FreeBSD$ 33*/ 34 35#include "opie_cfg.h" 36#if HAVE_STRING_H 37#include <string.h> 38#endif /* HAVE_STRING_H */ 39#if OPIEAUTO 40#include <errno.h> 41#if HAVE_STDLIB_H 42#include <stdlib.h> 43#endif /* HAVE_STDLIB_H */ 44#include <sys/stat.h> 45 46#include <sys/socket.h> 47#include <sys/un.h> 48#endif /* OPIEAUTO */ 49#if DEBUG 50#include <syslog.h> 51#endif /* DEBUG */ 52#include "opie.h" 53 54static char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" }; 55 56#if OPIEAUTO 57#ifndef max 58#define max(x, y) (((x) > (y)) ? (x) : (y)) 59#endif /* max */ 60 61static int opieauto_connect FUNCTION_NOARGS 62{ 63 int s; 64 struct sockaddr_un sun; 65 char buffer[1024]; 66 char *c, *c2 ="/.opieauto"; 67 uid_t myuid = getuid(), myeuid = geteuid(); 68 69 if (!myuid || !myeuid || (myuid != myeuid)) { 70#if DEBUG 71 syslog(LOG_DEBUG, "opieauto_connect: superuser and/or setuid not allowed"); 72#endif /* DEBUG */ 73 return -1; 74 }; 75 76 memset(&sun, 0, sizeof(struct sockaddr_un)); 77 sun.sun_family = AF_UNIX; 78 79 if (!(c = getenv("HOME"))) { 80#if DEBUG 81 syslog(LOG_DEBUG, "opieauto_connect: no HOME variable?"); 82#endif /* DEBUG */ 83 return -1; 84 }; 85 86 if (strlen(c) > (sizeof(sun.sun_path) - strlen(c2) - 1)) { 87#if DEBUG 88 syslog(LOG_DEBUG, "opieauto_connect: HOME is too long: %s", c); 89#endif /* DEBUG */ 90 return -1; 91 }; 92 93 strcpy(sun.sun_path, c); 94 strcat(sun.sun_path, c2); 95 96 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { 97#if DEBUG 98 syslog(LOG_DEBUG, "opieauto_connect: socket: %s(%d)", strerror(errno), errno); 99#endif /* DEBUG */ 100 return -1; 101 }; 102 103 { 104 struct stat st; 105 106 if (stat(sun.sun_path, &st) < 0) { 107#if DEBUG 108 syslog(LOG_DEBUG, "opieauto_connect: stat: %s(%d)\n", strerror(errno), errno); 109#endif /* DEBUG */ 110 goto ret; 111 }; 112 113 if (connect(s, (struct sockaddr *)&sun, sizeof(struct sockaddr_un))) { 114#if DEBUG 115 syslog(LOG_DEBUG, "opieauto_connect: connect: %s(%d)\n", strerror(errno), errno); 116#endif /* DEBUG */ 117 goto ret; 118 }; 119 120 if ((st.st_uid != myuid) || (!S_ISSOCK(st.st_mode)) || ((st.st_mode & 07777) != 0600)) { 121#if DEBUG 122 syslog(LOG_DEBUG, "opieauto_connect: something's fishy about the socket\n"); 123#endif /* DEBUG */ 124 goto ret; 125 }; 126 }; 127 128 return s; 129 130ret: 131 close(s); 132 return -1; 133}; 134#endif /* OPIEAUTO */ 135 136int opiegenerator FUNCTION((challenge, secret, response), char *challenge AND char *secret AND char *response) 137{ 138 int algorithm; 139 int sequence; 140 char *seed; 141 struct opie_otpkey key; 142 int i; 143 int exts; 144#if OPIEAUTO 145 int s; 146 int window; 147 char cmd[1+1+1+1+4+1+OPIE_SEED_MAX+1+4+1+4+1+4+1+4+1]; 148 char *c; 149#endif /* OPIEAUTO */ 150 151 if (!(challenge = strstr(challenge, "otp-"))) 152 return 1; 153 154 challenge += 4; 155 156 if (__opieparsechallenge(challenge, &algorithm, &sequence, &seed, &exts)) 157 return 1; 158 159 if ((sequence < 2) || (sequence > 9999)) 160 return 1; 161 162 if (*secret) { 163 if (opiepasscheck(secret)) 164 return -2; 165 166 if (i = opiekeycrunch(algorithm, &key, seed, secret)) 167 return i; 168 169 if (sequence <= OPIE_SEQUENCE_RESTRICT) { 170 if (!(exts & 1)) 171 return 1; 172 173 { 174 char newseed[OPIE_SEED_MAX + 1]; 175 struct opie_otpkey newkey; 176 char *c; 177 char buf[OPIE_SEED_MAX + 48 + 1]; 178 179 while (sequence-- != 0) 180 opiehash(&key, algorithm); 181 182 if (opienewseed(strcpy(newseed, seed)) < 0) 183 return -1; 184 185 if (opiekeycrunch(algorithm, &newkey, newseed, secret)) 186 return -1; 187 188 for (i = 0; i < 499; i++) 189 opiehash(&newkey, algorithm); 190 191 strcpy(response, "init-hex:"); 192 strcat(response, opiebtoh(buf, &key)); 193 if (snprintf(buf, sizeof(buf), ":%s 499 %s:", algids[algorithm], 194 newseed) >= sizeof(buf)) { 195#ifdef DEBUG 196 syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at init-hex"); 197#endif /* DEBUG */ 198 return -1; 199 } 200 strcat(response, buf); 201 strcat(response, opiebtoh(buf, &newkey)); 202 }; 203 }; 204 }; 205 206#if OPIEAUTO 207 if ((s = opieauto_connect()) >= 0) { 208 if ((i = read(s, cmd, sizeof(cmd)-1)) < 0) { 209#if DEBUG 210 syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 211#endif /* DEBUG */ 212 close(s); 213 s = -1; 214 goto l0; 215 }; 216 cmd[i] = 0; 217 if ((cmd[0] != 'C') || (cmd[1] != '+') || (cmd[2] != ' ')) { 218#if DEBUG 219 syslog(LOG_DEBUG, "opiegenerator: got invalid/failing C+ response: %s\n", cmd); 220#endif /* DEBUG */ 221 close(s); 222 s = -1; 223 goto l0; 224 }; 225 226 window = strtoul(&cmd[3], &c, 10); 227 if (!window || (window >= (OPIE_SEQUENCE_MAX - OPIE_SEQUENCE_RESTRICT)) || !isspace(*c)) { 228#if DEBUG 229 syslog(LOG_DEBUG, "opiegenerator: got bogus option response: %s\n", cmd); 230#endif /* DEBUG */ 231 close(s); 232 s = -1; 233 goto l0; 234 }; 235 }; 236 237l0: 238 if (*secret) { 239 int j; 240 241 if (s < 0) { 242 j = 0; 243 goto l1; 244 }; 245 246 j = max(sequence - window + 1, OPIE_SEQUENCE_RESTRICT); 247 248 for (i = j; i > 0; i--) 249 opiehash(&key, algorithm); 250 251 { 252 char buf[16+1]; 253 254 opiebtoa8(buf, &key); 255 256 if (snprintf(cmd, sizeof(cmd), "S= %d %d %s %s\n", algorithm, sequence, 257 seed, buf) >= sizeof(cmd)) { 258#if DEBUG 259 syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at S=\n"); 260#endif /* DEBUG */ 261 goto l1; 262 } 263 } 264 265 if (write(s, cmd, i = strlen(cmd)) != i) { 266#if DEBUG 267 syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno); 268#endif /* DEBUG */ 269 goto l1; 270 }; 271 272 if ((i = read(s, cmd, sizeof(cmd))) < 0) { 273#if DEBUG 274 syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 275#endif /* DEBUG */ 276 }; 277 close(s); 278 279 cmd[i] = 0; 280 i = strlen(seed); 281 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')) { 282#if DEBUG 283 syslog(LOG_DEBUG, "opiegenerator: got invalid/failing S+ response: %s\n", cmd); 284#endif /* DEBUG */ 285 }; 286 287l1: 288 for (i = sequence - j; i > 0; i--) 289 opiehash(&key, algorithm); 290 291 opiebtoh(response, &key); 292 } else { 293 if (s < 0) 294 goto l2; 295 296 if ((snprintf(cmd, sizeof(cmd), "s= %d %d %s\n", algorithm, sequence, 297 seed) >= sizeof(cmd))) { 298#if DEBUG 299 syslog(LOG_DEBUG, "opiegenerator: snprintf truncation at s=\n"); 300#endif /* DEBUG */ 301 goto l2; 302 } 303 304 if (write(s, cmd, i = strlen(cmd)) != i) { 305#if DEBUG 306 syslog(LOG_DEBUG, "opiegenerator: write: %s(%d)\n", strerror(errno), errno); 307#endif /* DEBUG */ 308 goto l2; 309 }; 310 311 if ((i = read(s, cmd, sizeof(cmd))) < 0) { 312#if DEBUG 313 syslog(LOG_DEBUG, "opiegenerator: read: %s(%d)\n", strerror(errno), errno); 314#endif /* DEBUG */ 315 goto l2; 316 }; 317 close(s); 318 319 i = strlen(seed); 320 321 if ((cmd[0] != 's') || (cmd[2] != ' ') || (strtoul(&cmd[3], &c, 10) != algorithm) || (strtoul(c + 1, &c, 10) != sequence) || strncmp(++c, seed, i)) { 322#if DEBUG 323 if (c) 324 *c = 0; 325 else 326 cmd[3] = 0; 327 328 syslog(LOG_DEBUG, "opiegenerator: got bogus/invalid s response: %s\n", cmd); 329#endif /* DEBUG */ 330 goto l2; 331 }; 332 333 c += i; 334 335 if (cmd[1] == '-') { 336#if DEBUG 337 if (*c != '\n') { 338 *c = 0; 339 syslog(LOG_DEBUG, "opiegenerator: got invalid s- response: %s\n", cmd); 340 }; 341#endif /* DEBUG */ 342 goto l2; 343 }; 344 345 if (cmd[1] != '+') { 346#if DEBUG 347 *c = 0; 348 syslog(LOG_DEBUG, "opiegenerator: got invalid s response: %s\n", cmd); 349#endif /* DEBUG */ 350 goto l2; 351 }; 352 353 { 354 char *c2; 355 356 if (!(c2 = strchr(++c, '\n'))) { 357#if DEBUG 358 *c = 0; 359 syslog(LOG_DEBUG, "opiegenerator: got invalid s+ response: %s\n", cmd); 360#endif /* DEBUG */ 361 goto l2; 362 }; 363 364 *c2++ = 0; 365 }; 366 367 if (!opieatob8(&key, c)) 368 goto l2; 369 370 opiebtoh(response, &key); 371 }; 372 373 if (s >= 0) 374 close(s); 375#else /* OPIEAUTO */ 376 if (*secret) { 377 while (sequence-- != 0) 378 opiehash(&key, algorithm); 379 380 opiebtoh(response, &key); 381 } else 382 return -2; 383#endif /* OPIEAUTO */ 384 385 return 0; 386 387#if OPIEAUTO 388l2: 389#if DEBUG 390 syslog(LOG_DEBUG, "opiegenerator: no opieauto response available.\n"); 391#endif /* DEBUG */ 392 if (s >= 0) 393 close(s); 394 395 return -2; 396#endif /* OPIEAUTO */ 397}; 398