opiepasswd.c revision 59118
1/* opiepasswd.c: Add/change an OTP password in the key database. 2 3%%% portions-copyright-cmetz-96 4Portions of this software are Copyright 1996-1998 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 10Portions of this software are Copyright 1995 by Randall Atkinson and Dan 11McDonald, All Rights Reserved. All Rights under this copyright are assigned 12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 13License Agreement applies to this software. 14 15 History: 16 17 Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of 18 hard coding the length. Unlock user on failed lookup. 19 Modified by cmetz for OPIE 2.3. Got of some variables and made some 20 local to where they're used. Split out the finishing code. Use 21 opielookup() instead of opiechallenge() to find user. Three 22 strikes on prompts. Use opiepasswd()'s new calling 23 convention. Changed OPIE_PASS_{MAX,MIN} to 24 OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning 25 below us. Got rid of unneeded headers. Use new opieatob8() 26 return value convention. Added -f flag. Added SHA support. 27 Modified by cmetz for OPIE 2.22. Finally got rid of the lock 28 filename kluge by implementing refcounts for locks. 29 Use opiepasswd() to update key file. Error if we can't 30 write to the key file. Check for minimum seed length. 31 Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to 32 opiestripcrlf. Check opiereadpass() return value. 33 Minor optimization. Change calls to opiereadpass() to 34 use echo arg. Use opiereadpass() where we can. 35 Make everything static. Ifdef around some headers. 36 Changed use of gethostname() to uname(). Got rid of 37 the need for buf[]. Properly check return value of 38 opieatob8. Check seed length. Always generate proper- 39 length seeds. 40 Modified at NRL for OPIE 2.1. Minor autoconf changes. 41 Modified heavily at NRL for OPIE 2.0. 42 Written at Bellcore for the S/Key Version 1 software distribution 43 (skeyinit.c). 44*/ 45#include "opie_cfg.h" 46 47#if HAVE_PWD_H 48#include <pwd.h> 49#endif /* HAVE_PWD_H */ 50#include <stdio.h> 51#if HAVE_STRING_H 52#include <string.h> 53#endif /* HAVE_STRING_H */ 54#include <stdio.h> 55#include <sys/types.h> 56#if HAVE_UNISTD_H 57#include <unistd.h> 58#endif /* HAVE_UNISTD_H */ 59#if HAVE_STDLIB_H 60#include <stdlib.h> 61#endif /* HAVE_STDLIB_H */ 62 63#include "opie.h" 64 65#define MODE_DEFAULT 0 66#define MODE_CONSOLE 1 67#define MODE_DISABLE 2 68 69extern int optind; 70extern char *optarg; 71 72char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" }; 73char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" }; 74 75static VOIDRET usage FUNCTION((myname), char *myname) 76{ 77 fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n [-s seed] [username]\n", myname); 78 exit(1); 79} 80 81static VOIDRET finish FUNCTION((name), char *name) 82{ 83 struct opie opie; 84 char buf[OPIE_RESPONSE_MAX + 1]; 85 86 if (name) { 87 if (opiechallenge(&opie, name, buf)) { 88 fprintf(stderr, "Error verifying database.\n"); 89 finish(NULL); 90 } 91 printf("\nID %s ", opie.opie_principal); 92 if (opie.opie_val && (opie.opie_val[0] == '*')) { 93 printf("is disabled.\n"); 94 finish(NULL); 95 } 96 printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed); 97 { 98 char key[8]; 99 if (!opieatob8(key, opie.opie_val)) { 100 fprintf(stderr, "Error verifying key -- possible database corruption.\n"); 101 finish(NULL); 102 } 103 printf("%s\n", opiebtoe(buf, key)); 104 } 105 } 106 107 while(!opieunlock()); 108 exit(name ? 0 : 1); 109} 110 111int main FUNCTION((argc, argv), int argc AND char *argv[]) 112{ 113 struct opie opie; 114 int rval, n = 499, i, mode = MODE_DEFAULT, force = 0; 115 char seed[OPIE_SEED_MAX+1]; 116 struct passwd *pp; 117 118 memset(seed, 0, sizeof(seed)); 119 120 if (!(pp = getpwuid(getuid()))) { 121 fprintf(stderr, "Who are you?"); 122 return 1; 123 } 124 125 while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) { 126 switch (i) { 127 case 'v': 128 opieversion(); 129 case 'f': 130#if INSECURE_OVERRIDE 131 force = OPIEPASSWD_FORCE; 132#else /* INSECURE_OVERRIDE */ 133 fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n"); 134#endif /* INSECURE_OVERRIDE */ 135 break; 136 case 'c': 137 mode = MODE_CONSOLE; 138 break; 139 case 'd': 140 mode = MODE_DISABLE; 141 break; 142 case 'n': 143 i = atoi(optarg); 144 if (!(i > 0 && i < 10000)) { 145 printf("Sequence numbers must be > 0 and < 10000\n"); 146 finish(NULL); 147 } 148 n = i; 149 break; 150 case 's': 151 i = strlen(optarg); 152 if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) { 153 printf("Seeds must be between %d and %d characters long.\n", 154 OPIE_SEED_MIN, OPIE_SEED_MAX); 155 finish(NULL); 156 } 157 strncpy(seed, optarg, sizeof(seed)); 158 seed[sizeof(seed) - 1] = 0; 159 break; 160 default: 161 usage(argv[0]); 162 } 163 } 164 165 if (argc - optind >= 1) { 166 if (strcmp(argv[optind], pp->pw_name)) { 167 if (getuid()) { 168 printf("Only root can change others' passwords.\n"); 169 exit(1); 170 } 171 if ((pp = getpwnam(argv[optind])) == NULL) { 172 printf("%s: user unknown.\n", argv[optind]); 173 exit(1); 174 } 175 } 176 } 177 178 opielock(pp->pw_name); 179 rval = opielookup(&opie, pp->pw_name); 180 181 switch (rval) { 182 case 0: 183 printf("Updating %s:\n", pp->pw_name); 184 break; 185 case 1: 186 printf("Adding %s:\n", pp->pw_name); 187 break; 188 case 2: 189 fprintf(stderr, "Error: Can't update key database.\n"); 190 finish(NULL); 191 default: 192 fprintf(stderr, "Error reading key database\n"); 193 finish(NULL); 194 } 195 196 if (seed[0]) { 197 i = strlen(seed); 198 if (i > OPIE_SEED_MAX) { 199 fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX); 200 finish(NULL); 201 } 202 if (i < OPIE_SEED_MIN) { 203 fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN); 204 finish(NULL); 205 } 206 } else { 207 if (!rval) 208 strcpy(seed, opie.opie_seed); 209 210 if (opienewseed(seed) < 0) { 211 fprintf(stderr, "Error updating seed.\n"); 212 finish(NULL); 213 } 214 } 215 216 if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) { 217 fprintf(stderr, "You must use a different seed for the new OTP sequence.\n"); 218 finish(NULL); 219 } 220 221 switch(mode) { 222 case MODE_DEFAULT: 223 { 224 char tmp[OPIE_RESPONSE_MAX + 2]; 225 226 printf("You need the response from an OTP generator.\n"); 227#if DEBUG 228 if (!rval) { 229#else /* DEBUG */ 230 if (!rval && getuid()) { 231#endif /* DEBUG */ 232 char oseed[OPIE_SEED_MAX + 1]; 233 int on; 234 235 if (opiechallenge(&opie, pp->pw_name, tmp)) { 236 fprintf(stderr, "Error issuing challenge.\n"); 237 finish(NULL); 238 } 239 on = opiegetsequence(&opie); 240 { 241 char *c; 242 if (c = strrchr(tmp, ' ')) 243 strncpy(oseed, c + 1, sizeof(oseed)); 244 else { 245#if DEBUG 246 fprintf(stderr, "opiepasswd: bogus challenge\n"); 247#endif /* DEBUG */ 248 finish(NULL); 249 } 250 } 251 printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp); 252 if (!opiereadpass(tmp, sizeof(tmp), 1)) 253 tmp[0] = 0; 254 i = opieverify(&opie, tmp); 255 if (!tmp[0]) { 256 fprintf(stderr, "Error reading response.\n"); 257 finish(NULL); 258 } 259 if (i) { 260 fprintf(stderr, "Error verifying response.\n"); 261#if DEBUG 262 fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i); 263#endif /* DEBUG */ 264 finish(NULL); 265 } 266 { 267 char nseed[OPIE_SEED_MAX + 1]; 268 int nn; 269 270 if (opiechallenge(&opie, pp->pw_name, tmp)) { 271 fprintf(stderr, "Error verifying database.\n"); 272 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