1 /* OpenBSD S/Key (skeysubr.c) 2 * 3 * Authors: 4 * Neil M. Haller <nmh@thumper.bellcore.com> 5 * Philip R. Karn <karn@chicago.qualcomm.com> 6 * John S. Walden <jsw@thumper.bellcore.com> 7 * Scott Chasin <chasin@crimelab.com> 8 * Todd C. Miller <Todd.Miller@courtesan.com> 9 * 10 * S/Key misc routines. 11 * 12 * $OpenBSD: skeysubr.c,v 1.33 2014/03/25 04:28:28 lteo Exp $ 13 */ 14 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <ctype.h> 19 #include <signal.h> 20 #include <termios.h> 21 #include <unistd.h> 22 #include <md5.h> 23 #include <sha1.h> 24 #include <rmd160.h> 25 26 #include "skey.h" 27 28 /* Default hash function to use (index into skey_algorithm_table array) */ 29 #ifndef SKEY_HASH_DEFAULT 30 #define SKEY_HASH_DEFAULT 0 /* md5 */ 31 #endif 32 33 static int keycrunch_md5(char *, char *, char *); 34 static int keycrunch_sha1(char *, char *, char *); 35 static int keycrunch_rmd160(char *, char *, char *); 36 static void lowcase(char *); 37 static void skey_echo(int); 38 static void trapped(int); 39 40 /* Current hash type (index into skey_algorithm_table array) */ 41 static int skey_hash_type = SKEY_HASH_DEFAULT; 42 43 /* 44 * Hash types we support. 45 * Each has an associated keycrunch() and f() function. 46 */ 47 struct skey_algorithm_table { 48 const char *name; 49 int (*keycrunch)(char *, char *, char *); 50 }; 51 static struct skey_algorithm_table skey_algorithm_table[] = { 52 { "md5", keycrunch_md5 }, 53 { "sha1", keycrunch_sha1 }, 54 { "rmd160", keycrunch_rmd160 }, 55 { NULL } 56 }; 57 58 59 /* 60 * Crunch a key: 61 * Concatenate the seed and the password, run through hash function and 62 * collapse to 64 bits. This is defined as the user's starting key. 63 * The result pointer must have at least SKEY_BINKEY_SIZE bytes of storage. 64 * The seed and password may be of any length. 65 */ 66 int 67 keycrunch(char *result, char *seed, char *passwd) 68 { 69 return(skey_algorithm_table[skey_hash_type].keycrunch(result, seed, passwd)); 70 } 71 72 static int 73 keycrunch_md5(char *result, char *seed, char *passwd) 74 { 75 char *buf; 76 MD5_CTX md; 77 u_int32_t results[4]; 78 unsigned int buflen; 79 80 /* 81 * If seed and passwd are defined we are in keycrunch() mode, 82 * else we are in f() mode. 83 */ 84 if (seed && passwd) { 85 buflen = strlen(seed) + strlen(passwd); 86 if ((buf = malloc(buflen + 1)) == NULL) 87 return(-1); 88 (void)strlcpy(buf, seed, buflen + 1); 89 lowcase(buf); 90 (void)strlcat(buf, passwd, buflen + 1); 91 sevenbit(buf); 92 } else { 93 buf = result; 94 buflen = SKEY_BINKEY_SIZE; 95 } 96 97 /* Crunch the key through MD5 */ 98 MD5Init(&md); 99 MD5Update(&md, (unsigned char *)buf, buflen); 100 MD5Final((unsigned char *)results, &md); 101 102 /* Fold result from 128 to 64 bits */ 103 results[0] ^= results[2]; 104 results[1] ^= results[3]; 105 106 (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); 107 108 if (buf != result) 109 (void)free(buf); 110 111 return(0); 112 } 113 114 static int 115 keycrunch_sha1(char *result, char *seed, char *passwd) 116 { 117 char *buf; 118 SHA1_CTX sha; 119 unsigned int buflen; 120 int i, j; 121 122 /* 123 * If seed and passwd are defined we are in keycrunch() mode, 124 * else we are in f() mode. 125 */ 126 if (seed && passwd) { 127 buflen = strlen(seed) + strlen(passwd); 128 if ((buf = malloc(buflen + 1)) == NULL) 129 return(-1); 130 (void)strlcpy(buf, seed, buflen + 1); 131 lowcase(buf); 132 (void)strlcat(buf, passwd, buflen + 1); 133 sevenbit(buf); 134 } else { 135 buf = result; 136 buflen = SKEY_BINKEY_SIZE; 137 } 138 139 /* Crunch the key through SHA1 */ 140 SHA1Init(&sha); 141 SHA1Update(&sha, (unsigned char *)buf, buflen); 142 SHA1Pad(&sha); 143 144 /* Fold 160 to 64 bits */ 145 sha.state[0] ^= sha.state[2]; 146 sha.state[1] ^= sha.state[3]; 147 sha.state[0] ^= sha.state[4]; 148 149 /* 150 * SHA1 is a big endian algorithm but RFC2289 mandates that 151 * the result be in little endian form, so we copy to the 152 * result buffer manually. 153 */ 154 for (i = 0, j = 0; j < 8; i++, j += 4) { 155 result[j] = (u_char)(sha.state[i] & 0xff); 156 result[j+1] = (u_char)((sha.state[i] >> 8) & 0xff); 157 result[j+2] = (u_char)((sha.state[i] >> 16) & 0xff); 158 result[j+3] = (u_char)((sha.state[i] >> 24) & 0xff); 159 } 160 161 if (buf != result) 162 (void)free(buf); 163 164 return(0); 165 } 166 167 static int 168 keycrunch_rmd160(char *result, char *seed, char *passwd) 169 { 170 char *buf; 171 RMD160_CTX rmd; 172 u_int32_t results[5]; 173 unsigned int buflen; 174 175 /* 176 * If seed and passwd are defined we are in keycrunch() mode, 177 * else we are in f() mode. 178 */ 179 if (seed && passwd) { 180 buflen = strlen(seed) + strlen(passwd); 181 if ((buf = malloc(buflen + 1)) == NULL) 182 return(-1); 183 (void)strlcpy(buf, seed, buflen + 1); 184 lowcase(buf); 185 (void)strlcat(buf, passwd, buflen + 1); 186 sevenbit(buf); 187 } else { 188 buf = result; 189 buflen = SKEY_BINKEY_SIZE; 190 } 191 192 /* Crunch the key through RMD-160 */ 193 RMD160Init(&rmd); 194 RMD160Update(&rmd, (unsigned char *)buf, buflen); 195 RMD160Final((unsigned char *)results, &rmd); 196 197 /* Fold 160 to 64 bits */ 198 results[0] ^= results[2]; 199 results[1] ^= results[3]; 200 results[0] ^= results[4]; 201 202 (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); 203 204 if (buf != result) 205 (void)free(buf); 206 207 return(0); 208 } 209 210 /* 211 * The one-way hash function f(). 212 * Takes SKEY_BINKEY_SIZE bytes and returns SKEY_BINKEY_SIZE bytes in place. 213 */ 214 void 215 f(char *x) 216 { 217 (void)skey_algorithm_table[skey_hash_type].keycrunch(x, NULL, NULL); 218 } 219 220 /* Strip trailing cr/lf from a line of text */ 221 void 222 rip(char *buf) 223 { 224 buf += strcspn(buf, "\r\n"); 225 226 if (*buf) 227 *buf = '\0'; 228 } 229 230 /* Read in secret password (turns off echo) */ 231 char * 232 readpass(char *buf, int n) 233 { 234 void (*old_handler)(int); 235 236 /* Turn off echoing */ 237 skey_echo(0); 238 239 /* Catch SIGINT and save old signal handler */ 240 old_handler = signal(SIGINT, trapped); 241 242 if (fgets(buf, n, stdin) == NULL) 243 buf[0] = '\0'; 244 rip(buf); 245 246 (void)putc('\n', stderr); 247 (void)fflush(stderr); 248 249 /* Restore signal handler and turn echo back on */ 250 if (old_handler != SIG_ERR) 251 (void)signal(SIGINT, old_handler); 252 skey_echo(1); 253 254 sevenbit(buf); 255 256 return(buf); 257 } 258 259 /* Read in an s/key OTP (does not turn off echo) */ 260 char * 261 readskey(char *buf, int n) 262 { 263 if (fgets(buf, n, stdin) == NULL) 264 buf[0] = '\0'; 265 rip(buf); 266 267 sevenbit(buf); 268 269 return(buf); 270 } 271 272 /* Signal handler for trapping ^C */ 273 /*ARGSUSED*/ 274 static void 275 trapped(int sig) 276 { 277 write(STDERR_FILENO, "^C\n", 3); 278 279 /* Turn on echo if necessary */ 280 skey_echo(1); 281 282 _exit(1); 283 } 284 285 /* 286 * Convert 16-byte hex-ascii string to 8-byte binary array 287 * Returns 0 on success, -1 on error 288 */ 289 int 290 atob8(char *out, char *in) 291 { 292 int i; 293 int val; 294 295 if (in == NULL || out == NULL) 296 return(-1); 297 298 for (i=0; i < 8; i++) { 299 if ((in = skipspace(in)) == NULL) 300 return(-1); 301 if ((val = htoi(*in++)) == -1) 302 return(-1); 303 *out = val << 4; 304 305 if ((in = skipspace(in)) == NULL) 306 return(-1); 307 if ((val = htoi(*in++)) == -1) 308 return(-1); 309 *out++ |= val; 310 } 311 return(0); 312 } 313 314 /* Convert 8-byte binary array to 16-byte hex-ascii string */ 315 int 316 btoa8(char *out, char *in) 317 { 318 if (in == NULL || out == NULL) 319 return(-1); 320 321 (void)snprintf(out, 17, "%02x%02x%02x%02x%02x%02x%02x%02x", 322 in[0] & 0xff, in[1] & 0xff, in[2] & 0xff, in[3] & 0xff, 323 in[4] & 0xff, in[5] & 0xff, in[6] & 0xff, in[7] & 0xff); 324 325 return(0); 326 } 327 328 /* Convert hex digit to binary integer */ 329 int 330 htoi(int c) 331 { 332 if ('0' <= c && c <= '9') 333 return(c - '0'); 334 if ('a' <= c && c <= 'f') 335 return(10 + c - 'a'); 336 if ('A' <= c && c <= 'F') 337 return(10 + c - 'A'); 338 return(-1); 339 } 340 341 /* Skip leading spaces from the string */ 342 char * 343 skipspace(char *cp) 344 { 345 while (*cp == ' ' || *cp == '\t') 346 cp++; 347 348 if (*cp == '\0') 349 return(NULL); 350 else 351 return(cp); 352 } 353 354 /* Remove backspaced over characters from the string */ 355 void 356 backspace(char *buf) 357 { 358 char bs = 0x8; 359 char *cp = buf; 360 char *out = buf; 361 362 while (*cp) { 363 if (*cp == bs) { 364 if (out == buf) { 365 cp++; 366 continue; 367 } else { 368 cp++; 369 out--; 370 } 371 } else { 372 *out++ = *cp++; 373 } 374 375 } 376 *out = '\0'; 377 } 378 379 /* Make sure line is all seven bits */ 380 void 381 sevenbit(char *s) 382 { 383 while (*s) 384 *s++ &= 0x7f; 385 } 386 387 /* Set hash algorithm type */ 388 char * 389 skey_set_algorithm(char *new) 390 { 391 int i; 392 393 for (i = 0; skey_algorithm_table[i].name; i++) { 394 if (strcmp(new, skey_algorithm_table[i].name) == 0) { 395 skey_hash_type = i; 396 return(new); 397 } 398 } 399 400 return(NULL); 401 } 402 403 /* Get current hash type */ 404 const char * 405 skey_get_algorithm(void) 406 { 407 return(skey_algorithm_table[skey_hash_type].name); 408 } 409 410 /* Turn echo on/off */ 411 static void 412 skey_echo(int action) 413 { 414 static struct termios term; 415 static int echo = 0; 416 417 if (action == 0) { 418 /* Turn echo off */ 419 (void) tcgetattr(fileno(stdin), &term); 420 if ((echo = (term.c_lflag & ECHO))) { 421 term.c_lflag &= ~ECHO; 422 (void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); 423 } 424 } else if (action && echo) { 425 /* Turn echo on */ 426 term.c_lflag |= ECHO; 427 (void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); 428 echo = 0; 429 } 430 } 431 432 /* Convert string to lower case */ 433 static void 434 lowcase(char *s) 435 { 436 char *p; 437 438 for (p = s; *p; p++) { 439 if (isupper((unsigned char)*p)) 440 *p = (char)tolower((unsigned char)*p); 441 } 442 } 443