opiepasswd.c revision 59118
1228753Smm/* opiepasswd.c: Add/change an OTP password in the key database. 2228753Smm 3228753Smm%%% portions-copyright-cmetz-96 4228753SmmPortions of this software are Copyright 1996-1998 by Craig Metz, All Rights 5228753SmmReserved. The Inner Net License Version 2 applies to these portions of 6228753Smmthe software. 7228753SmmYou should have received a copy of the license with this software. If 8228753Smmyou didn't get a copy, you may request one from <license@inner.net>. 9228753Smm 10228753SmmPortions of this software are Copyright 1995 by Randall Atkinson and Dan 11228753SmmMcDonald, All Rights Reserved. All Rights under this copyright are assigned 12228753Smmto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 13228753SmmLicense Agreement applies to this software. 14228753Smm 15228753Smm History: 16228753Smm 17228753Smm Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of 18228753Smm hard coding the length. Unlock user on failed lookup. 19228753Smm Modified by cmetz for OPIE 2.3. Got of some variables and made some 20228753Smm local to where they're used. Split out the finishing code. Use 21228753Smm opielookup() instead of opiechallenge() to find user. Three 22228753Smm strikes on prompts. Use opiepasswd()'s new calling 23228753Smm convention. Changed OPIE_PASS_{MAX,MIN} to 24228753Smm OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning 25228753Smm below us. Got rid of unneeded headers. Use new opieatob8() 26228753Smm return value convention. Added -f flag. Added SHA support. 27229592Smm Modified by cmetz for OPIE 2.22. Finally got rid of the lock 28228753Smm filename kluge by implementing refcounts for locks. 29228753Smm Use opiepasswd() to update key file. Error if we can't 30228753Smm write to the key file. Check for minimum seed length. 31228753Smm Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to 32228753Smm opiestripcrlf. Check opiereadpass() return value. 33228753Smm Minor optimization. Change calls to opiereadpass() to 34228753Smm use echo arg. Use opiereadpass() where we can. 35228753Smm Make everything static. Ifdef around some headers. 36228753Smm Changed use of gethostname() to uname(). Got rid of 37228753Smm the need for buf[]. Properly check return value of 38228753Smm opieatob8. Check seed length. Always generate proper- 39228753Smm length seeds. 40228753Smm Modified at NRL for OPIE 2.1. Minor autoconf changes. 41228753Smm Modified heavily at NRL for OPIE 2.0. 42228753Smm Written at Bellcore for the S/Key Version 1 software distribution 43228753Smm (skeyinit.c). 44228753Smm*/ 45228753Smm#include "opie_cfg.h" 46228753Smm 47228753Smm#if HAVE_PWD_H 48228753Smm#include <pwd.h> 49228753Smm#endif /* HAVE_PWD_H */ 50228753Smm#include <stdio.h> 51228753Smm#if HAVE_STRING_H 52228753Smm#include <string.h> 53228753Smm#endif /* HAVE_STRING_H */ 54228753Smm#include <stdio.h> 55228753Smm#include <sys/types.h> 56228753Smm#if HAVE_UNISTD_H 57228753Smm#include <unistd.h> 58228753Smm#endif /* HAVE_UNISTD_H */ 59228753Smm#if HAVE_STDLIB_H 60228753Smm#include <stdlib.h> 61228753Smm#endif /* HAVE_STDLIB_H */ 62228753Smm 63228753Smm#include "opie.h" 64228753Smm 65228753Smm#define MODE_DEFAULT 0 66228753Smm#define MODE_CONSOLE 1 67228753Smm#define MODE_DISABLE 2 68228753Smm 69228753Smmextern int optind; 70228753Smmextern char *optarg; 71228753Smm 72228753Smmchar *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" }; 73228753Smmchar *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" }; 74228753Smm 75228753Smmstatic VOIDRET usage FUNCTION((myname), char *myname) 76228753Smm{ 77228753Smm fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n [-s seed] [username]\n", myname); 78228753Smm exit(1); 79228753Smm} 80228753Smm 81228753Smmstatic VOIDRET finish FUNCTION((name), char *name) 82228753Smm{ 83228753Smm struct opie opie; 84228753Smm char buf[OPIE_RESPONSE_MAX + 1]; 85228753Smm 86228753Smm if (name) { 87228753Smm if (opiechallenge(&opie, name, buf)) { 88228753Smm fprintf(stderr, "Error verifying database.\n"); 89228753Smm finish(NULL); 90228753Smm } 91228753Smm printf("\nID %s ", opie.opie_principal); 92228753Smm if (opie.opie_val && (opie.opie_val[0] == '*')) { 93228753Smm printf("is disabled.\n"); 94228753Smm finish(NULL); 95228753Smm } 96228753Smm printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed); 97228753Smm { 98228753Smm char key[8]; 99228753Smm if (!opieatob8(key, opie.opie_val)) { 100228753Smm fprintf(stderr, "Error verifying key -- possible database corruption.\n"); 101228753Smm finish(NULL); 102228753Smm } 103228753Smm printf("%s\n", opiebtoe(buf, key)); 104228753Smm } 105228753Smm } 106228753Smm 107228753Smm while(!opieunlock()); 108228753Smm exit(name ? 0 : 1); 109228753Smm} 110228753Smm 111228753Smmint main FUNCTION((argc, argv), int argc AND char *argv[]) 112228753Smm{ 113228753Smm struct opie opie; 114228753Smm int rval, n = 499, i, mode = MODE_DEFAULT, force = 0; 115228753Smm char seed[OPIE_SEED_MAX+1]; 116228753Smm struct passwd *pp; 117228753Smm 118228753Smm memset(seed, 0, sizeof(seed)); 119228753Smm 120228753Smm if (!(pp = getpwuid(getuid()))) { 121228753Smm fprintf(stderr, "Who are you?"); 122228753Smm return 1; 123228753Smm } 124228753Smm 125228753Smm while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) { 126228753Smm switch (i) { 127228753Smm case 'v': 128228753Smm opieversion(); 129228753Smm case 'f': 130228753Smm#if INSECURE_OVERRIDE 131228753Smm force = OPIEPASSWD_FORCE; 132228753Smm#else /* INSECURE_OVERRIDE */ 133228753Smm fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n"); 134228753Smm#endif /* INSECURE_OVERRIDE */ 135228753Smm break; 136228753Smm case 'c': 137228753Smm mode = MODE_CONSOLE; 138228753Smm break; 139228753Smm case 'd': 140228753Smm mode = MODE_DISABLE; 141228753Smm break; 142228753Smm case 'n': 143228753Smm i = atoi(optarg); 144228753Smm if (!(i > 0 && i < 10000)) { 145228753Smm printf("Sequence numbers must be > 0 and < 10000\n"); 146228753Smm finish(NULL); 147228753Smm } 148228753Smm n = i; 149228753Smm break; 150228753Smm case 's': 151228753Smm i = strlen(optarg); 152228753Smm if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) { 153228753Smm printf("Seeds must be between %d and %d characters long.\n", 154228753Smm OPIE_SEED_MIN, OPIE_SEED_MAX); 155228753Smm finish(NULL); 156228753Smm } 157228753Smm strncpy(seed, optarg, sizeof(seed)); 158228753Smm seed[sizeof(seed) - 1] = 0; 159228753Smm break; 160228753Smm default: 161228753Smm usage(argv[0]); 162228753Smm } 163228753Smm } 164228753Smm 165228753Smm if (argc - optind >= 1) { 166228753Smm if (strcmp(argv[optind], pp->pw_name)) { 167228753Smm if (getuid()) { 168228753Smm printf("Only root can change others' passwords.\n"); 169228753Smm exit(1); 170228753Smm } 171228753Smm if ((pp = getpwnam(argv[optind])) == NULL) { 172228753Smm printf("%s: user unknown.\n", argv[optind]); 173228753Smm exit(1); 174228753Smm } 175228753Smm } 176228753Smm } 177228753Smm 178228753Smm opielock(pp->pw_name); 179228753Smm rval = opielookup(&opie, pp->pw_name); 180228753Smm 181228753Smm switch (rval) { 182228753Smm case 0: 183228753Smm printf("Updating %s:\n", pp->pw_name); 184228753Smm break; 185228753Smm case 1: 186228753Smm printf("Adding %s:\n", pp->pw_name); 187228753Smm break; 188228753Smm case 2: 189228753Smm fprintf(stderr, "Error: Can't update key database.\n"); 190228753Smm finish(NULL); 191228753Smm default: 192228753Smm fprintf(stderr, "Error reading key database\n"); 193228753Smm finish(NULL); 194228753Smm } 195228753Smm 196228753Smm if (seed[0]) { 197228753Smm i = strlen(seed); 198228753Smm if (i > OPIE_SEED_MAX) { 199228753Smm fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX); 200228753Smm finish(NULL); 201228753Smm } 202228753Smm if (i < OPIE_SEED_MIN) { 203228753Smm fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN); 204228753Smm finish(NULL); 205228753Smm } 206228753Smm } else { 207228753Smm if (!rval) 208228753Smm strcpy(seed, opie.opie_seed); 209228753Smm 210228753Smm if (opienewseed(seed) < 0) { 211228753Smm fprintf(stderr, "Error updating seed.\n"); 212228753Smm finish(NULL); 213228753Smm } 214228753Smm } 215228753Smm 216228753Smm if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) { 217228753Smm fprintf(stderr, "You must use a different seed for the new OTP sequence.\n"); 218228753Smm finish(NULL); 219228753Smm } 220228753Smm 221228753Smm switch(mode) { 222228753Smm case MODE_DEFAULT: 223228753Smm { 224228753Smm char tmp[OPIE_RESPONSE_MAX + 2]; 225228753Smm 226228753Smm printf("You need the response from an OTP generator.\n"); 227228753Smm#if DEBUG 228228753Smm if (!rval) { 229228753Smm#else /* DEBUG */ 230228753Smm if (!rval && getuid()) { 231228753Smm#endif /* DEBUG */ 232228753Smm char oseed[OPIE_SEED_MAX + 1]; 233228753Smm int on; 234228753Smm 235228753Smm if (opiechallenge(&opie, pp->pw_name, tmp)) { 236228753Smm fprintf(stderr, "Error issuing challenge.\n"); 237228753Smm finish(NULL); 238228753Smm } 239228753Smm on = opiegetsequence(&opie); 240228753Smm { 241228753Smm char *c; 242228753Smm if (c = strrchr(tmp, ' ')) 243228753Smm strncpy(oseed, c + 1, sizeof(oseed)); 244228753Smm else { 245228753Smm#if DEBUG 246228753Smm fprintf(stderr, "opiepasswd: bogus challenge\n"); 247228753Smm#endif /* DEBUG */ 248228753Smm finish(NULL); 249228753Smm } 250228753Smm } 251228753Smm printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp); 252228753Smm if (!opiereadpass(tmp, sizeof(tmp), 1)) 253228753Smm tmp[0] = 0; 254228753Smm i = opieverify(&opie, tmp); 255228753Smm if (!tmp[0]) { 256228753Smm fprintf(stderr, "Error reading response.\n"); 257228753Smm finish(NULL); 258228753Smm } 259228753Smm if (i) { 260228753Smm fprintf(stderr, "Error verifying response.\n"); 261228753Smm#if DEBUG 262228753Smm fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i); 263228753Smm#endif /* DEBUG */ 264228753Smm finish(NULL); 265228753Smm } 266228753Smm { 267228753Smm char nseed[OPIE_SEED_MAX + 1]; 268228753Smm int nn; 269228753Smm 270228753Smm if (opiechallenge(&opie, pp->pw_name, tmp)) { 271228753Smm fprintf(stderr, "Error verifying database.\n"); 272228753Smm finish(NULL); 273 } 274 275 nn = opiegetsequence(&opie); 276 { 277 char *c; 278 if (c = strrchr(tmp, ' ')) 279 strncpy(nseed, c + 1, sizeof(nseed)); 280 else { 281#if DEBUG 282 fprintf(stderr, "opiepasswd: bogus challenge\n"); 283#endif /* DEBUG */ 284 finish(NULL); 285 } 286 } 287 288 opieverify(&opie, ""); 289 nn++; 290 291 if ((nn != on) || strcmp(oseed, nseed)) 292 finish(pp->pw_name); 293 } 294 } 295 printf("New secret pass phrase:"); 296 for (i = 0;; i++) { 297 if (i > 2) 298 finish(NULL); 299 printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed); 300 if (!opiereadpass(tmp, sizeof(tmp), 1)) { 301 fprintf(stderr, "Error reading response.\n"); 302 finish(NULL); 303 } 304 if (tmp[0] == '?') { 305 printf("Enter the response from your OTP calculator: \n"); 306 continue; 307 } 308 if (tmp[0] == '\0') { 309 fprintf(stderr, "Secret pass phrase unchanged.\n"); 310 finish(NULL); 311 } 312 313 if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp))) 314 finish(pp->pw_name); 315 316 if (rval < 0) { 317 fprintf(stderr, "Error updating key database.\n"); 318 finish(NULL); 319 } 320 printf("\tThat is not a valid OTP response.\n"); 321 } 322 } 323 break; 324 case MODE_CONSOLE: 325 { 326 char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1]; 327 /* Get user's secret password */ 328 fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n"); 329 fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n"); 330 fprintf(stderr, "Then run opiepasswd without the -c parameter.\n"); 331 if (opieinsecure() && !force) { 332 fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n"); 333 if (force) 334 fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n"); 335 else 336 finish(NULL); 337 }; 338 printf("Using %s to compute responses.\n", algnames[MDX]); 339 if (!rval && getuid()) { 340 printf("Enter old secret pass phrase: "); 341 if (!opiereadpass(passwd, sizeof(passwd), 0)) { 342 fprintf(stderr, "Error reading secret pass phrase!\n"); 343 finish(NULL); 344 } 345 if (!passwd[0]) { 346 fprintf(stderr, "Secret pass phrase unchanged.\n"); 347 finish(NULL); 348 } 349 { 350 char key[8]; 351 char tbuf[OPIE_RESPONSE_MAX + 1]; 352 353 if (opiekeycrunch(MDX, key, opie.opie_seed, passwd) != 0) { 354 fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]); 355 finish(NULL); 356 } 357 memset(passwd, 0, sizeof(passwd)); 358 i = opie.opie_n - 1; 359 while (i-- != 0) 360 opiehash(key, MDX); 361 opiebtoe(tbuf, key); 362 if (opieverify(&opie, tbuf)) { 363 fprintf(stderr, "Sorry.\n"); 364 finish(NULL); 365 } 366 } 367 } 368 for (i = 0;; i++) { 369 if (i > 2) 370 finish(NULL); 371 printf("Enter new secret pass phrase: "); 372 if (!opiereadpass(passwd, sizeof(passwd), 0)) { 373 fprintf(stderr, "Error reading secret pass phrase.\n"); 374 finish(NULL); 375 } 376 if (!passwd[0] || feof(stdin)) { 377 fprintf(stderr, "Secret pass phrase unchanged.\n"); 378 finish(NULL); 379 } 380 if (opiepasscheck(passwd)) { 381 memset(passwd, 0, sizeof(passwd)); 382 fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX); 383 continue; 384 } 385 printf("Again new secret pass phrase: "); 386 if (!opiereadpass(passwd2, sizeof(passwd2), 0)) { 387 fprintf(stderr, "Error reading secret pass phrase.\n"); 388 finish(NULL); 389 } 390 if (feof(stdin)) { 391 fprintf(stderr, "Secret pass phrase unchanged.\n"); 392 finish(NULL); 393 } 394 if (!passwd[0] || !strcmp(passwd, passwd2)) 395 break; 396 fprintf(stderr, "Sorry, no match.\n"); 397 } 398 memset(passwd2, 0, sizeof(passwd2)); 399 if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) { 400 fprintf(stderr, "Error updating key database.\n"); 401 finish(NULL); 402 } 403 finish(pp->pw_name); 404 } 405 case MODE_DISABLE: 406 { 407 char tmp[4]; 408 int i; 409 410 for (i = 0;; i++) { 411 if (i > 2) 412 finish(NULL); 413 414 printf("Disable %s's OTP access? (yes or no) ", pp->pw_name); 415 if (!opiereadpass(tmp, sizeof(tmp), 1)) { 416 fprintf(stderr, "Error reading entry.\n"); 417 finish(NULL); 418 } 419 if (!strcmp(tmp, "no")) 420 finish(NULL); 421 if (!strcmp(tmp, "yes")) { 422 if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) { 423 fprintf(stderr, "Error updating key database.\n"); 424 finish(NULL); 425 } 426 finish(pp->pw_name); 427 } 428 } 429 } 430 } 431} 432