opiepasswd.c revision 29964
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-1997 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.3. Got of some variables and made some 18 local to where they're used. Split out the finishing code. Use 19 opielookup() instead of opiechallenge() to find user. Three 20 strikes on prompts. Use opiepasswd()'s new calling 21 convention. Changed OPIE_PASS_{MAX,MIN} to 22 OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning 23 below us. Got rid of unneeded headers. Use new opieatob8() 24 return value convention. Added -f flag. Added SHA support. 25 Modified by cmetz for OPIE 2.22. Finally got rid of the lock 26 filename kluge by implementing refcounts for locks. 27 Use opiepasswd() to update key file. Error if we can't 28 write to the key file. Check for minimum seed length. 29 Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to 30 opiestripcrlf. Check opiereadpass() return value. 31 Minor optimization. Change calls to opiereadpass() to 32 use echo arg. Use opiereadpass() where we can. 33 Make everything static. Ifdef around some headers. 34 Changed use of gethostname() to uname(). Got rid of 35 the need for buf[]. Properly check return value of 36 opieatob8. Check seed length. Always generate proper- 37 length seeds. 38 Modified at NRL for OPIE 2.1. Minor autoconf changes. 39 Modified heavily at NRL for OPIE 2.0. 40 Written at Bellcore for the S/Key Version 1 software distribution 41 (skeyinit.c). 42*/ 43#include "opie_cfg.h" 44 45#if HAVE_PWD_H 46#include <pwd.h> 47#endif /* HAVE_PWD_H */ 48#include <stdio.h> 49#if HAVE_STRING_H 50#include <string.h> 51#endif /* HAVE_STRING_H */ 52#include <stdio.h> 53#include <sys/types.h> 54#if HAVE_UNISTD_H 55#include <unistd.h> 56#endif /* HAVE_UNISTD_H */ 57#if HAVE_STDLIB_H 58#include <stdlib.h> 59#endif /* HAVE_STDLIB_H */ 60 61#include "opie.h" 62 63#define MODE_DEFAULT 0 64#define MODE_CONSOLE 1 65#define MODE_DISABLE 2 66 67extern int optind; 68extern char *optarg; 69 70char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" }; 71char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" }; 72 73static VOIDRET usage FUNCTION((myname), char *myname) 74{ 75 fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n [-s seed] [username]\n", myname); 76 exit(1); 77} 78 79static VOIDRET finish FUNCTION((name), char *name) 80{ 81 struct opie opie; 82 char buf[OPIE_RESPONSE_MAX + 1]; 83 84 if (name) { 85 if (opiechallenge(&opie, name, buf)) { 86 fprintf(stderr, "Error verifying database.\n"); 87 finish(NULL); 88 } 89 printf("\nID %s ", opie.opie_principal); 90 if (opie.opie_val && (opie.opie_val[0] == '*')) { 91 printf("is disabled.\n"); 92 finish(NULL); 93 } 94 printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed); 95 { 96 char key[8]; 97 if (!opieatob8(key, opie.opie_val)) { 98 fprintf(stderr, "Error verifying key -- possible database corruption.\n"); 99 finish(NULL); 100 } 101 printf("%s\n", opiebtoe(buf, key)); 102 } 103 } 104 105 while(!opieunlock()); 106 exit(name ? 0 : 1); 107} 108 109int main FUNCTION((argc, argv), int argc AND char *argv[]) 110{ 111 struct opie opie; 112 int rval, n = 499, i, mode = MODE_DEFAULT, force = 0; 113 char seed[18]; 114 struct passwd *pp; 115 116 memset(seed, 0, sizeof(seed)); 117 118 if (!(pp = getpwuid(getuid()))) { 119 fprintf(stderr, "Who are you?"); 120 return 1; 121 } 122 123 while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) { 124 switch (i) { 125 case 'v': 126 opieversion(); 127 case 'f': 128#if INSECURE_OVERRIDE 129 force = 1; 130#else /* INSECURE_OVERRIDE */ 131 fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n"); 132#endif /* INSECURE_OVERRIDE */ 133 break; 134 case 'c': 135 mode = MODE_CONSOLE; 136 break; 137 case 'd': 138 mode = MODE_DISABLE; 139 break; 140 case 'n': 141 i = atoi(optarg); 142 if (!(i > 0 && i < 10000)) { 143 printf("Sequence numbers must be > 0 and < 10000\n"); 144 finish(NULL); 145 } 146 n = i; 147 break; 148 case 's': 149 i = strlen(optarg); 150 if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) { 151 printf("Seeds must be between %d and %d characters long.\n", 152 OPIE_SEED_MIN, OPIE_SEED_MAX); 153 finish(NULL); 154 } 155 strncpy(seed, optarg, sizeof(seed)); 156 seed[sizeof(seed) - 1] = 0; 157 break; 158 default: 159 usage(argv[0]); 160 } 161 } 162 163 if (argc - optind >= 1) { 164 if (strcmp(argv[optind], pp->pw_name)) { 165 if (getuid()) { 166 printf("Only root can change others' passwords.\n"); 167 exit(1); 168 } 169 if ((pp = getpwnam(argv[optind])) == NULL) { 170 printf("%s: user unknown.\n", argv[optind]); 171 exit(1); 172 } 173 } 174 } 175 176 opielock(pp->pw_name); 177 rval = opielookup(&opie, pp->pw_name); 178 179 switch (rval) { 180 case 0: 181 printf("Updating %s:\n", pp->pw_name); 182 break; 183 case 1: 184 printf("Adding %s:\n", pp->pw_name); 185 break; 186 case 2: 187 fprintf(stderr, "Error: Can't update key database.\n"); 188 exit(1); 189 default: 190 fprintf(stderr, "Error reading key database\n"); 191 exit(1); 192 } 193 194 if (seed[0]) { 195 i = strlen(seed); 196 if (i > OPIE_SEED_MAX) { 197 fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX); 198 finish(NULL); 199 } 200 if (i < OPIE_SEED_MIN) { 201 fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN); 202 finish(NULL); 203 } 204 } else { 205 if (!rval) 206 strcpy(seed, opie.opie_seed); 207 208 if (opienewseed(seed) < 0) { 209 fprintf(stderr, "Error updating seed.\n"); 210 finish(NULL); 211 } 212 } 213 214 if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) { 215 fprintf(stderr, "You must use a different seed for the new OTP sequence.\n"); 216 finish(NULL); 217 } 218 219 switch(mode) { 220 case MODE_DEFAULT: 221 { 222 char tmp[OPIE_RESPONSE_MAX + 2]; 223 224 printf("You need the response from an OTP generator.\n"); 225#if DEBUG 226 if (!rval) { 227#else /* DEBUG */ 228 if (!rval && getuid()) { 229#endif /* DEBUG */ 230 char oseed[OPIE_SEED_MAX + 1]; 231 int on; 232 233 if (opiechallenge(&opie, pp->pw_name, tmp)) { 234 fprintf(stderr, "Error issuing challenge.\n"); 235 finish(NULL); 236 } 237 on = opiegetsequence(&opie); 238 { 239 char *c; 240 if (c = strrchr(tmp, ' ')) 241 strncpy(oseed, c + 1, sizeof(oseed)); 242 else { 243#if DEBUG 244 fprintf(stderr, "opiepasswd: bogus challenge\n"); 245#endif /* DEBUG */ 246 finish(NULL); 247 } 248 } 249 printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp); 250 if (!opiereadpass(tmp, sizeof(tmp), 1)) 251 tmp[0] = 0; 252 i = opieverify(&opie, tmp); 253 if (!tmp[0]) { 254 fprintf(stderr, "Error reading response.\n"); 255 finish(NULL); 256 } 257 if (i) { 258 fprintf(stderr, "Error verifying response.\n"); 259#if DEBUG 260 fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i); 261#endif /* DEBUG */ 262 finish(NULL); 263 } 264 { 265 char nseed[OPIE_SEED_MAX + 1]; 266 int nn; 267 268 if (opiechallenge(&opie, pp->pw_name, tmp)) { 269 fprintf(stderr, "Error verifying database.\n"); 270 finish(NULL); 271 } 272 273 nn = opiegetsequence(&opie); 274 { 275 char *c; 276 if (c = strrchr(tmp, ' ')) 277 strncpy(nseed, c + 1, sizeof(nseed)); 278 else { 279#if DEBUG 280 fprintf(stderr, "opiepasswd: bogus challenge\n"); 281#endif /* DEBUG */ 282 finish(NULL); 283 } 284 } 285 286 opieverify(&opie, ""); 287 nn++; 288 289 if ((nn != on) || strcmp(oseed, nseed)) 290 finish(pp->pw_name); 291 } 292 } 293 printf("New secret pass phrase:"); 294 for (i = 0;; i++) { 295 if (i > 2) 296 finish(NULL); 297 printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed); 298 if (!opiereadpass(tmp, sizeof(tmp), 1)) { 299 fprintf(stderr, "Error reading response.\n"); 300 finish(NULL); 301 } 302 if (tmp[0] == '?') { 303 printf("Enter the response from your OTP calculator: \n"); 304 continue; 305 } 306 if (tmp[0] == '\0') { 307 fprintf(stderr, "Secret pass phrase unchanged.\n"); 308 finish(NULL); 309 } 310 311 if (!(rval = opiepasswd(&opie, 0, pp->pw_name, n, seed, tmp))) 312 finish(pp->pw_name); 313 314 if (rval < 0) { 315 fprintf(stderr, "Error updating key database.\n"); 316 finish(NULL); 317 } 318 printf("\tThat is not a valid OTP response.\n"); 319 } 320 } 321 break; 322 case MODE_CONSOLE: 323 { 324 char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1]; 325 /* Get user's secret password */ 326 fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n"); 327 fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n"); 328 fprintf(stderr, "Then run opiepasswd without the -c parameter.\n"); 329 if (opieinsecure()) { 330 fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n"); 331 if (force) 332 fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n"); 333 else 334 finish(NULL); 335 }; 336 printf("Using %s to compute responses.\n", algnames[MDX]); 337 if (!rval && getuid()) { 338 printf("Enter old secret pass phrase: "); 339 if (!opiereadpass(passwd, sizeof(passwd), 0)) { 340 fprintf(stderr, "Error reading secret pass phrase!\n"); 341 finish(NULL); 342 } 343 if (!passwd[0]) { 344 fprintf(stderr, "Secret pass phrase unchanged.\n"); 345 finish(NULL); 346 } 347 { 348 char key[8]; 349 char tbuf[OPIE_RESPONSE_MAX + 1]; 350 351 if (opiekeycrunch(MDX, key, opie.opie_seed, passwd) != 0) { 352 fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]); 353 finish(NULL); 354 } 355 memset(passwd, 0, sizeof(passwd)); 356 i = opie.opie_n - 1; 357 while (i-- != 0) 358 opiehash(key, MDX); 359 opiebtoe(tbuf, key); 360 if (opieverify(&opie, tbuf)) { 361 fprintf(stderr, "Sorry.\n"); 362 finish(NULL); 363 } 364 } 365 } 366 for (i = 0;; i++) { 367 if (i > 2) 368 finish(NULL); 369 printf("Enter new secret pass phrase: "); 370 if (!opiereadpass(passwd, sizeof(passwd), 0)) { 371 fprintf(stderr, "Error reading secret pass phrase.\n"); 372 finish(NULL); 373 } 374 if (!passwd[0] || feof(stdin)) { 375 fprintf(stderr, "Secret pass phrase unchanged.\n"); 376 finish(NULL); 377 } 378 if (opiepasscheck(passwd)) { 379 memset(passwd, 0, sizeof(passwd)); 380 fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX); 381 continue; 382 } 383 printf("Again new secret pass phrase: "); 384 if (!opiereadpass(passwd2, sizeof(passwd2), 0)) { 385 fprintf(stderr, "Error reading secret pass phrase.\n"); 386 finish(NULL); 387 } 388 if (feof(stdin)) { 389 fprintf(stderr, "Secret pass phrase unchanged.\n"); 390 finish(NULL); 391 } 392 if (!passwd[0] || !strcmp(passwd, passwd2)) 393 break; 394 fprintf(stderr, "Sorry, no match.\n"); 395 } 396 memset(passwd2, 0, sizeof(passwd2)); 397 if (opiepasswd(&opie, 1, pp->pw_name, n, seed, passwd)) { 398 fprintf(stderr, "Error updating key database.\n"); 399 finish(NULL); 400 } 401 finish(pp->pw_name); 402 } 403 case MODE_DISABLE: 404 { 405 char tmp[4]; 406 int i; 407 408 for (i = 0;; i++) { 409 if (i > 2) 410 finish(NULL); 411 412 printf("Disable %s's OTP access? (yes or no) ", pp->pw_name); 413 if (!opiereadpass(tmp, sizeof(tmp), 1)) { 414 fprintf(stderr, "Error reading entry.\n"); 415 finish(NULL); 416 } 417 if (!strcmp(tmp, "no")) 418 finish(NULL); 419 if (!strcmp(tmp, "yes")) { 420 if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) { 421 fprintf(stderr, "Error updating key database.\n"); 422 finish(NULL); 423 } 424 finish(pp->pw_name); 425 } 426 } 427 } 428 } 429} 430