1255108Sjilles/* opiepasswd.c: Add/change an OTP password in the key database. 2255108Sjilles 3255108Sjilles%%% portions-copyright-cmetz-96 4255108SjillesPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights 5255108SjillesReserved. The Inner Net License Version 2 applies to these portions of 6255108Sjillesthe software. 7255108SjillesYou should have received a copy of the license with this software. If 8255108Sjillesyou didn't get a copy, you may request one from <license@inner.net>. 9255108Sjilles 10255108SjillesPortions of this software are Copyright 1995 by Randall Atkinson and Dan 11255108SjillesMcDonald, All Rights Reserved. All Rights under this copyright are assigned 12255108Sjillesto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 13255108SjillesLicense Agreement applies to this software. 14255108Sjilles 15255108Sjilles History: 16255108Sjilles 17255108Sjilles Modified by cmetz for OPIE 2.4. Use struct opie_key for key blocks. 18255108Sjilles Use opiestrncpy(). 19255108Sjilles Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of 20255108Sjilles hard coding the length. Unlock user on failed lookup. 21255108Sjilles Modified by cmetz for OPIE 2.3. Got of some variables and made some 22255108Sjilles local to where they're used. Split out the finishing code. Use 23255108Sjilles opielookup() instead of opiechallenge() to find user. Three 24255108Sjilles strikes on prompts. Use opiepasswd()'s new calling 25255108Sjilles convention. Changed OPIE_PASS_{MAX,MIN} to 26255108Sjilles OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning 27255108Sjilles below us. Got rid of unneeded headers. Use new opieatob8() 28255108Sjilles return value convention. Added -f flag. Added SHA support. 29255108Sjilles Modified by cmetz for OPIE 2.22. Finally got rid of the lock 30255108Sjilles filename kluge by implementing refcounts for locks. 31255108Sjilles Use opiepasswd() to update key file. Error if we can't 32255108Sjilles write to the key file. Check for minimum seed length. 33255108Sjilles Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to 34255108Sjilles opiestripcrlf. Check opiereadpass() return value. 35255108Sjilles Minor optimization. Change calls to opiereadpass() to 36255108Sjilles use echo arg. Use opiereadpass() where we can. 37255108Sjilles Make everything static. Ifdef around some headers. 38255108Sjilles Changed use of gethostname() to uname(). Got rid of 39255108Sjilles the need for buf[]. Properly check return value of 40255108Sjilles opieatob8. Check seed length. Always generate proper- 41255108Sjilles length seeds. 42255108Sjilles Modified at NRL for OPIE 2.1. Minor autoconf changes. 43255108Sjilles 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: releng/10.3/contrib/opie/opiepasswd.c 156997 2006-03-22 16:00:42Z cperciva $ 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 char *username; 122 uid_t ruid; 123 struct passwd *pp; 124 125 memset(seed, 0, sizeof(seed)); 126 127 ruid = getuid(); 128 username = getlogin(); 129 pp = getpwnam(username); 130 if (username == NULL || pp == NULL || pp->pw_uid != ruid) 131 pp = getpwuid(ruid); 132 if (pp == NULL) { 133 fprintf(stderr, "Who are you?"); 134 return 1; 135 } 136 137 while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) { 138 switch (i) { 139 case 'v': 140 opieversion(); 141 case 'f': 142#if INSECURE_OVERRIDE 143 force = OPIEPASSWD_FORCE; 144#else /* INSECURE_OVERRIDE */ 145 fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n"); 146#endif /* INSECURE_OVERRIDE */ 147 break; 148 case 'c': 149 mode = MODE_CONSOLE; 150 break; 151 case 'd': 152 mode = MODE_DISABLE; 153 break; 154 case 'n': 155 i = atoi(optarg); 156 if (!(i > 0 && i < 10000)) { 157 printf("Sequence numbers must be > 0 and < 10000\n"); 158 finish(NULL); 159 } 160 n = i; 161 break; 162 case 's': 163 i = strlen(optarg); 164 if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) { 165 printf("Seeds must be between %d and %d characters long.\n", 166 OPIE_SEED_MIN, OPIE_SEED_MAX); 167 finish(NULL); 168 } 169 opiestrncpy(seed, optarg, sizeof(seed)); 170 break; 171 default: 172 usage(argv[0]); 173 } 174 } 175 176 if (argc - optind >= 1) { 177 if (strcmp(argv[optind], pp->pw_name)) { 178 if (getuid()) { 179 printf("Only root can change others' passwords.\n"); 180 exit(1); 181 } 182 if ((pp = getpwnam(argv[optind])) == NULL) { 183 printf("%s: user unknown.\n", argv[optind]); 184 exit(1); 185 } 186 } 187 } 188 189 opielock(pp->pw_name); 190 rval = opielookup(&opie, pp->pw_name); 191 192 switch (rval) { 193 case 0: 194 printf("Updating %s:\n", pp->pw_name); 195 break; 196 case 1: 197 printf("Adding %s:\n", pp->pw_name); 198 break; 199 case 2: 200 fprintf(stderr, "Error: Can't update key database.\n"); 201 finish(NULL); 202 default: 203 fprintf(stderr, "Error reading key database\n"); 204 finish(NULL); 205 } 206 207 if (seed[0]) { 208 i = strlen(seed); 209 if (i > OPIE_SEED_MAX) { 210 fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX); 211 finish(NULL); 212 } 213 if (i < OPIE_SEED_MIN) { 214 fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN); 215 finish(NULL); 216 } 217 } else { 218 if (!rval) 219 strcpy(seed, opie.opie_seed); 220 221 if (opienewseed(seed) < 0) { 222 fprintf(stderr, "Error updating seed.\n"); 223 finish(NULL); 224 } 225 } 226 227 if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) { 228 fprintf(stderr, "You must use a different seed for the new OTP sequence.\n"); 229 finish(NULL); 230 } 231 232 switch(mode) { 233 case MODE_DEFAULT: 234 { 235 char tmp[OPIE_RESPONSE_MAX + 2]; 236 237 printf("You need the response from an OTP generator.\n"); 238#if DEBUG 239 if (!rval) { 240#else /* DEBUG */ 241 if (!rval && getuid()) { 242#endif /* DEBUG */ 243 char oseed[OPIE_SEED_MAX + 1]; 244 int on; 245 246 if (opiechallenge(&opie, pp->pw_name, tmp)) { 247 fprintf(stderr, "Error issuing challenge.\n"); 248 finish(NULL); 249 } 250 on = opiegetsequence(&opie); 251 { 252 char *c; 253 if (c = strrchr(tmp, ' ')) 254 opiestrncpy(oseed, c + 1, sizeof(oseed)); 255 else { 256#if DEBUG 257 fprintf(stderr, "opiepasswd: bogus challenge\n"); 258#endif /* DEBUG */ 259 finish(NULL); 260 } 261 } 262 printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp); 263 if (!opiereadpass(tmp, sizeof(tmp), 1)) 264 tmp[0] = 0; 265 i = opieverify(&opie, tmp); 266 if (!tmp[0]) { 267 fprintf(stderr, "Error reading response.\n"); 268 finish(NULL); 269 } 270 if (i) { 271 fprintf(stderr, "Error verifying response.\n"); 272#if DEBUG 273 fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i); 274#endif /* DEBUG */ 275 finish(NULL); 276 } 277 { 278 char nseed[OPIE_SEED_MAX + 1]; 279 int nn; 280 281 if (opiechallenge(&opie, pp->pw_name, tmp)) { 282 fprintf(stderr, "Error verifying database.\n"); 283 finish(NULL); 284 } 285 286 nn = opiegetsequence(&opie); 287 { 288 char *c; 289 if (c = strrchr(tmp, ' ')) 290 opiestrncpy(nseed, c + 1, sizeof(nseed)); 291 else { 292#if DEBUG 293 fprintf(stderr, "opiepasswd: bogus challenge\n"); 294#endif /* DEBUG */ 295 finish(NULL); 296 } 297 } 298 299 opieverify(&opie, ""); 300 nn++; 301 302 if ((nn != on) || strcmp(oseed, nseed)) 303 finish(pp->pw_name); 304 } 305 } 306 printf("New secret pass phrase:"); 307 for (i = 0;; i++) { 308 if (i > 2) 309 finish(NULL); 310 printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed); 311 if (!opiereadpass(tmp, sizeof(tmp), 1)) { 312 fprintf(stderr, "Error reading response.\n"); 313 finish(NULL); 314 } 315 if (tmp[0] == '?') { 316 printf("Enter the response from your OTP calculator: \n"); 317 continue; 318 } 319 if (tmp[0] == '\0') { 320 fprintf(stderr, "Secret pass phrase unchanged.\n"); 321 finish(NULL); 322 } 323 324 if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp))) 325 finish(pp->pw_name); 326 327 if (rval < 0) { 328 fprintf(stderr, "Error updating key database.\n"); 329 finish(NULL); 330 } 331 printf("\tThat is not a valid OTP response.\n"); 332 } 333 } 334 break; 335 case MODE_CONSOLE: 336 { 337 char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1]; 338 /* Get user's secret password */ 339 fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n"); 340 fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n"); 341 fprintf(stderr, "Then run opiepasswd without the -c parameter.\n"); 342 if (opieinsecure() && !force) { 343 fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n"); 344 if (force) 345 fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n"); 346 else 347 finish(NULL); 348 }; 349 printf("Using %s to compute responses.\n", algnames[MDX]); 350 if (!rval && getuid()) { 351 printf("Enter old secret pass phrase: "); 352 if (!opiereadpass(passwd, sizeof(passwd), 0)) { 353 fprintf(stderr, "Error reading secret pass phrase!\n"); 354 finish(NULL); 355 } 356 if (!passwd[0]) { 357 fprintf(stderr, "Secret pass phrase unchanged.\n"); 358 finish(NULL); 359 } 360 { 361 struct opie_otpkey key; 362 char tbuf[OPIE_RESPONSE_MAX + 1]; 363 364 if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) { 365 fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]); 366 finish(NULL); 367 } 368 memset(passwd, 0, sizeof(passwd)); 369 i = opie.opie_n - 1; 370 while (i-- != 0) 371 opiehash(&key, MDX); 372 opiebtoe(tbuf, &key); 373 if (opieverify(&opie, tbuf)) { 374 fprintf(stderr, "Sorry.\n"); 375 finish(NULL); 376 } 377 } 378 } 379 for (i = 0;; i++) { 380 if (i > 2) 381 finish(NULL); 382 printf("Enter new secret pass phrase: "); 383 if (!opiereadpass(passwd, sizeof(passwd), 0)) { 384 fprintf(stderr, "Error reading secret pass phrase.\n"); 385 finish(NULL); 386 } 387 if (!passwd[0] || feof(stdin)) { 388 fprintf(stderr, "Secret pass phrase unchanged.\n"); 389 finish(NULL); 390 } 391 if (opiepasscheck(passwd)) { 392 memset(passwd, 0, sizeof(passwd)); 393 fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX); 394 continue; 395 } 396 printf("Again new secret pass phrase: "); 397 if (!opiereadpass(passwd2, sizeof(passwd2), 0)) { 398 fprintf(stderr, "Error reading secret pass phrase.\n"); 399 finish(NULL); 400 } 401 if (feof(stdin)) { 402 fprintf(stderr, "Secret pass phrase unchanged.\n"); 403 finish(NULL); 404 } 405 if (!passwd[0] || !strcmp(passwd, passwd2)) 406 break; 407 fprintf(stderr, "Sorry, no match.\n"); 408 } 409 memset(passwd2, 0, sizeof(passwd2)); 410 if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) { 411 fprintf(stderr, "Error updating key database.\n"); 412 finish(NULL); 413 } 414 finish(pp->pw_name); 415 } 416 case MODE_DISABLE: 417 { 418 char tmp[4]; 419 int i; 420 421 for (i = 0;; i++) { 422 if (i > 2) 423 finish(NULL); 424 425 printf("Disable %s's OTP access? (yes or no) ", pp->pw_name); 426 if (!opiereadpass(tmp, sizeof(tmp), 1)) { 427 fprintf(stderr, "Error reading entry.\n"); 428 finish(NULL); 429 } 430 if (!strcmp(tmp, "no")) 431 finish(NULL); 432 if (!strcmp(tmp, "yes")) { 433 if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) { 434 fprintf(stderr, "Error updating key database.\n"); 435 finish(NULL); 436 } 437 finish(pp->pw_name); 438 } 439 } 440 } 441 } 442} 443