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