1/* opiekey.c: Stand-alone program for computing responses to OTP challenges. 2 3 Takes a sequence number and seed (presumably from an OPIE challenge) 4 as command line arguments, prompts for the user's secret pass phrase, 5 and outputs a response. 6 7%%% portions-copyright-cmetz-96 8Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights 9Reserved. The Inner Net License Version 2 applies to these portions of 10the software. 11You should have received a copy of the license with this software. If 12you didn't get a copy, you may request one from <license@inner.net>. 13 14Portions of this software are Copyright 1995 by Randall Atkinson and Dan 15McDonald, All Rights Reserved. All Rights under this copyright are assigned 16to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 17License Agreement applies to this software. 18 19 History: 20 21 Modified by cmetz for OPIE 2.4. Use struct opie_key for key blocks. 22 Modified by cmetz for OPIE 2.31. Renamed "init" and RESPONSE_INIT 23 to "init-hex" and RESPONSE_INIT_HEX. Removed active attack 24 protection support. 25 Modified by cmetz for OPIE 2.3. OPIE_PASS_MAX changed to 26 OPIE_SECRET_MAX. Added extended responses, which created 27 lots of changes. Eliminated extra variable. Added -x and 28 -t to help. Added -f flag. Added SHA support. 29 Modified by cmetz for OPIE 2.22. Print newline after seed too long 30 message. Check for minimum seed length. Correct a grammar 31 error. 32 Modified at NRL for OPIE 2.2. Check opiereadpass() return. 33 Change opiereadpass() calls to add echo arg. Use FUNCTION 34 definition et al. Check seed length here, too. Added back 35 hex output. Reworked final output function. 36 Modified at NRL for OPIE 2.0. 37 Written at Bellcore for the S/Key Version 1 software distribution 38 (skey.c). 39 40$FreeBSD$ 41 42*/ 43#include "opie_cfg.h" 44 45#include <stdio.h> 46#include <string.h> 47#include <stdlib.h> 48 49#include "opie.h" 50 51#ifdef __MSDOS__ 52#include <dos.h> 53#endif 54 55#if HAVE_FCNTL_H 56#include <fcntl.h> 57#endif /* HAVE_FCNTL_H */ 58 59extern char *optarg; 60extern int optind, opterr; 61 62int aflag = 0; 63 64char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" }; 65char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" }; 66 67/******** Begin real source code ***************/ 68 69static VOIDRET usage FUNCTION((s), char *s) 70{ 71 fprintf(stderr, "usage: %s [-v] [-h] [-f] [-x] [-t type] [-4 | -5 | -s] [-a] [-n count] sequence_number seed\n", s); 72 exit(1); 73} 74 75#define RESPONSE_STANDARD 0 76#define RESPONSE_WORD 1 77#define RESPONSE_HEX 2 78#define RESPONSE_INIT_HEX 3 79#define RESPONSE_INIT_WORD 4 80#define RESPONSE_UNKNOWN 5 81 82struct _rtrans { 83 int type; 84 char *name; 85}; 86 87static struct _rtrans rtrans[] = { 88 { RESPONSE_WORD, "word" }, 89 { RESPONSE_HEX, "hex" }, 90 { RESPONSE_INIT_HEX, "init-hex" }, 91 { RESPONSE_INIT_WORD, "init-word" }, 92 { RESPONSE_STANDARD, "" }, 93 { RESPONSE_STANDARD, "standard" }, 94 { RESPONSE_STANDARD, "otp" }, 95 { RESPONSE_UNKNOWN, NULL } 96}; 97 98static void getsecret FUNCTION((secret, promptextra, retype), char *secret AND char *promptextra AND int flags) 99{ 100 fprintf(stderr, "Enter %ssecret pass phrase: ", promptextra); 101 if (!opiereadpass(secret, OPIE_SECRET_MAX, 0)) { 102 fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra); 103 exit(1); 104 } 105 if (secret[0] && (flags & 1)) { 106 char verify[OPIE_SECRET_MAX + 1]; 107 108 fprintf(stderr, "Again %ssecret pass phrase: ", promptextra); 109 if (!opiereadpass(verify, OPIE_SECRET_MAX, 0)) { 110 fprintf(stderr, "Error reading %ssecret pass phrase!\n", promptextra); 111 memset(verify, 0, sizeof(verify)); 112 memset(secret, 0, OPIE_SECRET_MAX + 1); 113 exit(1); 114 } 115 if (verify[0] && strcmp(verify, secret)) { 116 fprintf(stderr, "They don't match. Try again.\n"); 117 memset(verify, 0, sizeof(verify)); 118 memset(secret, 0, OPIE_SECRET_MAX + 1); 119 exit(1); 120 } 121 memset(verify, 0, sizeof(verify)); 122 } 123 if (!(flags & 2) && !aflag && opiepasscheck(secret)) { 124 memset(secret, 0, OPIE_SECRET_MAX + 1); 125 fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX); 126 exit(1); 127 }; 128} 129 130int main FUNCTION((argc, argv), int argc AND char *argv[]) 131{ 132 /* variable declarations */ 133 unsigned algorithm = MDX; /* default algorithm per Makefile's MDX 134 symbol */ 135 int keynum = 0; 136 int i; 137 int count = 1; 138 char secret[OPIE_SECRET_MAX + 1], newsecret[OPIE_SECRET_MAX + 1]; 139 struct opie_otpkey key, newkey; 140 char *seed, newseed[OPIE_SEED_MAX + 1]; 141 char response[OPIE_RESPONSE_MAX + 1]; 142 char *slash; 143 int hex = 0; 144 int type = RESPONSE_STANDARD; 145 int force = 0; 146 147 if (slash = strchr(argv[0], '/')) 148 slash++; 149 else 150 slash = argv[0]; 151 152 if (!strcmp(slash, "key") || strstr(slash, "md4")) 153 algorithm = 4; 154 155 if (strstr(slash, "md5")) 156 algorithm = 5; 157 158 if (strstr(slash, "sha")) 159 algorithm = 3; 160 161 while ((i = getopt(argc, argv, "fhvn:x45at:s")) != EOF) { 162 switch (i) { 163 case 'v': 164 opieversion(); 165 166 case 'n': 167 count = atoi(optarg); 168 break; 169 170 case 'x': 171 hex = 1; 172 break; 173 174 case 'f': 175#if INSECURE_OVERRIDE 176 force = 1; 177#else /* INSECURE_OVERRIDE */ 178 fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n"); 179#endif /* INSECURE_OVERRIDE */ 180 break; 181 182 case '4': 183 /* use MD4 algorithm */ 184 algorithm = 4; 185 break; 186 187 case '5': 188 /* use MD5 algorithm */ 189 algorithm = 5; 190 break; 191 192 case 'a': 193 aflag = 1; 194 break; 195 196 case 't': 197 { 198 struct _rtrans *r; 199 for (r = rtrans; r->name && strcmp(r->name, optarg); r++); 200 if (!r->name) { 201 fprintf(stderr, "%s: %s: unknown response type.\n", argv[0], optarg); 202 exit(1); 203 } 204 type = r->type; 205 } 206 break; 207 208 case 's': 209 algorithm = 3; 210 break; 211 212 default: 213 usage(argv[0]); 214 } 215 } 216 217 if ((argc - optind) < 2) 218 usage(argv[0]); 219 220 fprintf(stderr, "Using the %s algorithm to compute response.\n", algnames[algorithm]); 221 222 /* get sequence number, which is next-to-last parameter */ 223 keynum = atoi(argv[optind]); 224 if (keynum < 1) { 225 fprintf(stderr, "Sequence number %s is not positive.\n", argv[optind]); 226 exit(1); 227 } 228 /* get seed string, which is last parameter */ 229 seed = argv[optind + 1]; 230 { 231 i = strlen(seed); 232 233 if (i > OPIE_SEED_MAX) { 234 fprintf(stderr, "Seeds must be less than %d characters long.\n", OPIE_SEED_MAX); 235 exit(1); 236 } 237 if (i < OPIE_SEED_MIN) { 238 fprintf(stderr, "Seeds must be greater than %d characters long.\n", OPIE_SEED_MIN); 239 exit(1); 240 } 241 } 242 243 fprintf(stderr, "Reminder: Don't use opiekey from telnet or dial-in sessions.\n"); 244 245 if (opieinsecure()) { 246 fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n"); 247#if INSECURE_OVERRIDE 248 if (force) 249 fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n"); 250 else 251#endif /* INSECURE_OVERRIDE */ 252 exit(1); 253 } 254 255 if ((type == RESPONSE_INIT_HEX) || (type == RESPONSE_INIT_WORD)) { 256#if RETYPE 257 getsecret(secret, "old ", 1); 258#else /* RETYPE */ 259 getsecret(secret, "old ", 0); 260#endif /* RETYPE */ 261 getsecret(newsecret, "new ", 1); 262 if (!newsecret[0]) 263 strcpy(newsecret, secret); 264 265 if (opienewseed(strcpy(newseed, seed)) < 0) { 266 fprintf(stderr, "Error updating seed.\n"); 267 goto error; 268 } 269 270 if (opiekeycrunch(algorithm, &newkey, newseed, newsecret)) { 271 fprintf(stderr, "%s: key crunch failed (1)\n", argv[0]); 272 goto error; 273 } 274 275 for (i = 0; i < 499; i++) 276 opiehash(&newkey, algorithm); 277 } else 278#if RETYPE 279 getsecret(secret, "", 1); 280#else /* RETYPE */ 281 getsecret(secret, "", 0); 282#endif /* RETYPE */ 283 284 /* Crunch seed and secret password into starting key normally */ 285 if (opiekeycrunch(algorithm, &key, seed, secret)) { 286 fprintf(stderr, "%s: key crunch failed\n", argv[0]); 287 goto error; 288 } 289 290 for (i = 0; i <= (keynum - count); i++) 291 opiehash(&key, algorithm); 292 293 { 294 char buf[OPIE_SEED_MAX + 48 + 1]; 295 char *c; 296 297 for (; i <= keynum; i++) { 298 if (count > 1) 299 printf("%d: %s", i, (type == RESPONSE_STANDARD) ? "" : "\n"); 300 301 switch(type) { 302 case RESPONSE_STANDARD: 303 if (hex) 304 opiebtoh(response, &key); 305 else 306 opiebtoe(response, &key); 307 break; 308 case RESPONSE_WORD: 309 strcpy(response, "word:"); 310 strcat(response, opiebtoe(buf, &key)); 311 break; 312 case RESPONSE_HEX: 313 strcpy(response, "hex:"); 314 strcat(response, opiebtoh(buf, &key)); 315 break; 316 case RESPONSE_INIT_HEX: 317 case RESPONSE_INIT_WORD: 318 if (type == RESPONSE_INIT_HEX) { 319 strcpy(response, "init-hex:"); 320 strcat(response, opiebtoh(buf, &key)); 321 sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed); 322 strcat(response, buf); 323 strcat(response, opiebtoh(buf, &newkey)); 324 } else { 325 strcpy(response, "init-word:"); 326 strcat(response, opiebtoe(buf, &key)); 327 sprintf(buf, ":%s 499 %s:", algids[algorithm], newseed); 328 strcat(response, buf); 329 strcat(response, opiebtoe(buf, &newkey)); 330 } 331 break; 332 } 333 puts(response); 334 opiehash(&key, algorithm); 335 } 336 } 337 338 memset(secret, 0, sizeof(secret)); 339 memset(newsecret, 0, sizeof(newsecret)); 340 return 0; 341 342error: 343 memset(secret, 0, sizeof(secret)); 344 memset(newsecret, 0, sizeof(newsecret)); 345 return 1; 346} 347