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