71 72 /* Safety first and last, Scouts. */ 73 if (isfile (passfile)) 74 /* xchmod() is too polite. */ 75 chmod (passfile, 0600); 76 77 return passfile; 78} 79 80 81 82/* 83 * static char * 84 * password_entry_parseline ( 85 * const char *cvsroot_canonical, 86 * const unsigned char warn, 87 * const int linenumber, 88 * char *linebuf 89 * ); 90 * 91 * Internal function used by password_entry_operation. Parse a single line 92 * from a ~/.cvsroot password file and return a pointer to the password if the 93 * line refers to the same cvsroot as cvsroot_canonical 94 * 95 * INPUTS 96 * cvsroot_canonical the root we are looking for 97 * warn Boolean: print warnings for invalid lines? 98 * linenumber the line number for error messages 99 * linebuf the current line 100 * 101 * RETURNS 102 * NULL if the line doesn't match 103 * char *password as a pointer into linebuf 104 * 105 * NOTES 106 * This function temporarily alters linebuf, so it isn't thread safe when 107 * called on the same linebuf 108 */ 109static char * 110password_entry_parseline (cvsroot_canonical, warn, linenumber, linebuf) 111 const char *cvsroot_canonical; 112 const unsigned char warn; 113 const int linenumber; 114 char *linebuf; 115{ 116 char *password = NULL; 117 char *p; 118 119 /* look for '^/' */ 120 if (*linebuf == '/') 121 { 122 /* Yes: slurp '^/\d+\D' and parse the rest of the line according to version number */ 123 char *q; 124 unsigned long int entry_version; 125 126 if (isspace(*(linebuf + 1))) 127 /* special case since strtoul ignores leading white space */ 128 entry_version = 0; 129 else 130 entry_version = strtoul (linebuf + 1, &q, 10); 131 132 if (q == linebuf + 1) 133 /* no valid digits found by strtoul */ 134 entry_version = 0; 135 else 136 /* assume a delimiting seperator */ 137 q++; 138 139 switch (entry_version) 140 { 141 case 1: 142 /* this means the same normalize_cvsroot we are using was 143 * used to create this entry. strcmp is good enough for 144 * us. 145 */ 146 p = strchr (q, ' '); 147 if (p == NULL) 148 { 149 if (warn && !really_quiet) 150 error (0, 0, "warning: skipping invalid entry in password file at line %d", 151 linenumber); 152 } 153 else 154 { 155 *p = '\0'; 156 if (strcmp (cvsroot_canonical, q) == 0) 157 password = p + 1; 158 *p = ' '; 159 } 160 break; 161 case ULONG_MAX: 162 if (warn && !really_quiet) 163 { 164 error (0, errno, "warning: unable to convert version number in password file at line %d", 165 linenumber); 166 error (0, 0, "skipping entry"); 167 } 168 break; 169 case 0: 170 if (warn && !really_quiet) 171 error (0, 0, "warning: skipping entry with invalid version string in password file at line %d", 172 linenumber); 173 break; 174 default: 175 if (warn && !really_quiet) 176 error (0, 0, "warning: skipping entry with unknown version (%lu) in password file at line %d", 177 entry_version, linenumber); 178 break; 179 } 180 } 181 else 182 { 183 /* No: assume: 184 * 185 * ^cvsroot Aencoded_password$ 186 * 187 * as header comment specifies and parse accordingly 188 */ 189 cvsroot_t *tmp_root; 190 char *tmp_root_canonical; 191 192 p = strchr (linebuf, ' '); 193 if (p == NULL) 194 { 195 if (warn && !really_quiet) 196 error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber); 197 return NULL;; 198 } 199 200 *p = '\0'; 201 if ((tmp_root = parse_cvsroot (linebuf)) == NULL) 202 { 203 if (warn && !really_quiet) 204 error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber); 205 *p = ' '; 206 return NULL; 207 } 208 *p = ' '; 209 tmp_root_canonical = normalize_cvsroot (tmp_root); 210 if (strcmp (cvsroot_canonical, tmp_root_canonical) == 0) 211 password = p + 1; 212 213 free (tmp_root_canonical); 214 free_cvsroot_t (tmp_root); 215 } 216 217 return password; 218} 219 220 221 222/* 223 * static char * 224 * password_entry_operation ( 225 * password_entry_operation_t operation, 226 * cvsroot_t *root, 227 * char *newpassword 228 * ); 229 * 230 * Search the password file and depending on the value of operation: 231 * 232 * Mode Action 233 * password_entry_lookup Return the password 234 * password_entry_delete Delete the entry from the file, if it exists 235 * password_entry_add Replace the line with the new one, else append it 236 * 237 * Because the user might be accessing multiple repositories, with 238 * different passwords for each one, the format of ~/.cvspass is: 239 * 240 * [user@]host:[port]/path Aencoded_password 241 * [user@]host:[port]/path Aencoded_password 242 * ... 243 * 244 * New entries are always of the form: 245 * 246 * /1 user@host:port/path Aencoded_password 247 * 248 * but the old format is supported for backwards compatibility. 249 * The entry version string wasn't strictly necessary, but it avoids the 250 * overhead of parsing some entries since we know it is already in canonical 251 * form and allows room for expansion later, say, if we want to allow spaces 252 * and/or other characters to be escaped in the string. Also, the new entries 253 * would have been ignored by old versions of CVS anyhow since those versions 254 * didn't know how to parse a port number. 255 * 256 * The "A" before "encoded_password" is a literal capital A. It's a 257 * version number indicating which form of scrambling we're doing on 258 * the password -- someday we might provide something more secure than 259 * the trivial encoding we do now, and when that day comes, it would 260 * be nice to remain backward-compatible. 261 * 262 * Like .netrc, the file's permissions are the only thing preventing 263 * it from being read by others. Unlike .netrc, we will not be 264 * fascist about it, at most issuing a warning, and never refusing to 265 * work. 266 * 267 * INPUTS 268 * operation operation to perform 269 * root cvsroot_t to look up 270 * newpassword prescrambled new password, for password_entry_add_mode 271 * 272 * RETURNS 273 * -1 if password_entry_lookup_mode not specified 274 * NULL on failed lookup 275 * pointer to a copy of the password string otherwise, which the caller is 276 * responsible for disposing of 277 */ 278 279typedef enum password_entry_operation_e { 280 password_entry_lookup, 281 password_entry_delete, 282 password_entry_add 283} password_entry_operation_t; 284 285static char * 286password_entry_operation (operation, root, newpassword) 287 password_entry_operation_t operation; 288 cvsroot_t *root; 289 char *newpassword; 290{ 291 char *passfile; 292 FILE *fp; 293 char *cvsroot_canonical = NULL; 294 char *password = NULL; 295 int line_length; 296 long line; 297 char *linebuf = NULL; 298 size_t linebuf_len; 299 char *p; 300 int save_errno = 0; 301 302 if (root->method != pserver_method) 303 { 304 error (0, 0, "internal error: can only call password_entry_operation with pserver method"); 305 error (1, 0, "CVSROOT: %s", root->original); 306 } 307
|
323 /* Check each line to see if we have this entry already. */ 324 line = 0; 325 while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) 326 { 327 line++; 328 password = password_entry_parseline(cvsroot_canonical, 1, line, linebuf); 329 if (password != NULL) 330 /* this is it! break out and deal with linebuf */ 331 break; 332 } 333 if (line_length < 0 && !feof (fp)) 334 { 335 error (0, errno, "cannot read %s", passfile); 336 goto error_exit; 337 } 338 if (fclose (fp) < 0) 339 /* not fatal, unless it cascades */ 340 error (0, errno, "cannot close %s", passfile); 341 fp = NULL; 342 343 /* Utter, total, raving paranoia, I know. */ 344 chmod (passfile, 0600); 345 346 /* a copy to return or keep around so we can reuse linebuf */ 347 if (password != NULL) 348 { 349 /* chomp the EOL */ 350 p = strchr (password, '\n'); 351 if (p != NULL) 352 *p = '\0'; 353 password = xstrdup (password); 354 } 355 356process: 357 358 /* might as well return now */ 359 if (operation == password_entry_lookup) 360 goto out; 361 362 /* same here */ 363 if (operation == password_entry_delete && password == NULL) 364 { 365 error (0, 0, "Entry not found."); 366 goto out; 367 } 368 369 /* okay, file errors can simply be fatal from now on since we don't do 370 * anything else if we're in lookup mode 371 */ 372 373 /* copy the file with the entry deleted unless we're in add 374 * mode and the line we found contains the same password we're supposed to 375 * add 376 */ 377 if (!noexec && password != NULL && (operation == password_entry_delete 378 || (operation == password_entry_add && strcmp (password, newpassword)))) 379 { 380 long found_at = line; 381 char *tmp_name; 382 FILE *tmp_fp; 383 384 /* open the original file again */ 385 fp = CVS_FOPEN (passfile, "r"); 386 if (fp == NULL) 387 error (1, errno, "failed to open %s for reading", passfile); 388 389 /* create and open a temp file */ 390 if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL) 391 error (1, errno, "unable to open temp file %s", tmp_name); 392 393 line = 0; 394 while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) 395 { 396 line++; 397 if (line < found_at 398 || (line != found_at 399 && !password_entry_parseline(cvsroot_canonical, 0, line, linebuf))) 400 { 401 if (fprintf (tmp_fp, "%s", linebuf) == EOF) 402 { 403 /* try and clean up anyhow */ 404 error (0, errno, "fatal error: cannot write %s", tmp_name); 405 if (fclose (tmp_fp) == EOF) 406 error (0, errno, "cannot close %s", tmp_name); 407 /* call CVS_UNLINK instead of unlink_file since the file 408 * got created in noexec mode 409 */ 410 if (CVS_UNLINK (tmp_name) < 0) 411 error (0, errno, "cannot remove %s", tmp_name); 412 /* but quit so we don't remove all the entries from a 413 * user's password file accidentally 414 */ 415 error (1, 0, "exiting"); 416 } 417 } 418 } 419 if (line_length < 0 && !feof (fp)) 420 { 421 error (0, errno, "cannot read %s", passfile); 422 goto error_exit; 423 } 424 if (fclose (fp) < 0) 425 /* not fatal, unless it cascades */ 426 error (0, errno, "cannot close %s", passfile); 427 if (fclose (tmp_fp) < 0) 428 /* not fatal, unless it cascades */ 429 /* FIXME - does copy_file return correct results if the file wasn't 430 * closed? should this be fatal? 431 */ 432 error (0, errno, "cannot close %s", tmp_name); 433 434 /* FIXME: rename_file would make more sense (e.g. almost 435 * always faster). 436 * 437 * I don't think so, unless we change the way rename_file works to 438 * attempt a cp/rm sequence when rename fails since rename doesn't 439 * work across file systems and it isn't uncommon to have /tmp 440 * on its own partition. 441 * 442 * For that matter, it's probably not uncommon to have a home 443 * directory on an NFS mount. 444 */ 445 copy_file (tmp_name, passfile); 446 if (CVS_UNLINK (tmp_name) < 0) 447 error (0, errno, "cannot remove %s", tmp_name); 448 free (tmp_name); 449 } 450 451 /* in add mode, if we didn't find an entry or found an entry with a 452 * different password, append the new line 453 */ 454 if (!noexec && operation == password_entry_add 455 && (password == NULL || strcmp (password, newpassword))) 456 { 457 if ((fp = CVS_FOPEN (passfile, "a")) == NULL) 458 error (1, errno, "could not open %s for writing", passfile); 459 460 if (fprintf (fp, "/1 %s %s\n", cvsroot_canonical, newpassword) == EOF) 461 error (1, errno, "cannot write %s", passfile); 462 if (fclose (fp) < 0) 463 error (0, errno, "cannot close %s", passfile); 464 } 465 466 /* Utter, total, raving paranoia, I know. */ 467 chmod (passfile, 0600); 468 469 if (password) 470 { 471 free (password); 472 password = NULL; 473 } 474 if (linebuf) 475 free (linebuf); 476 477out: 478 free (cvsroot_canonical); 479 free (passfile); 480 return password; 481 482error_exit: 483 /* just exit when we're not in lookup mode */ 484 if (operation != password_entry_lookup) 485 error (1, 0, "fatal error: exiting"); 486 /* clean up and exit in lookup mode so we can try a login with a NULL 487 * password anyhow in case that's what we would have found 488 */ 489 save_errno = errno; 490 if (fp != NULL) 491 { 492 /* Utter, total, raving paranoia, I know. */ 493 chmod (passfile, 0600); 494 if(fclose (fp) < 0) 495 error (0, errno, "cannot close %s", passfile); 496 } 497 if (linebuf) 498 free (linebuf); 499 if (cvsroot_canonical) 500 free (cvsroot_canonical); 501 free (passfile); 502 errno = save_errno; 503 return NULL; 504} 505 506 507 508/* Prompt for a password, and store it in the file "CVS/.cvspass". 509 */ 510 511static const char *const login_usage[] = 512{ 513 "Usage: %s %s\n", 514 "(Specify the --help global option for a list of other help options)\n", 515 NULL 516}; 517 518int 519login (argc, argv) 520 int argc; 521 char **argv; 522{ 523 char *typed_password; 524 char *cvsroot_canonical; 525 526 if (argc < 0) 527 usage (login_usage); 528 529 if (current_parsed_root->method != pserver_method) 530 { 531 error (0, 0, "can only use `login' command with the 'pserver' method"); 532 error (1, 0, "CVSROOT: %s", current_parsed_root->original); 533 } 534 535 cvsroot_canonical = normalize_cvsroot(current_parsed_root); 536 printf ("Logging in to %s\n", cvsroot_canonical); 537 fflush (stdout); 538 539 if (current_parsed_root->password) 540 { 541 typed_password = scramble (current_parsed_root->password); 542 } 543 else 544 { 545 char *tmp; 546 tmp = GETPASS ("CVS password: "); 547 /* Must deal with a NULL return value here. I haven't managed to 548 * disconnect the CVS process from the tty and force a NULL return 549 * in sanity.sh, but the Linux version of getpass is documented 550 * to return NULL when it can't open /dev/tty... 551 */ 552 if (!tmp) error (1, errno, "login: Failed to read password."); 553 typed_password = scramble (tmp); 554 memset (tmp, 0, strlen (tmp)); 555 } 556 557 /* Force get_cvs_password() to use this one (when the client 558 * confirms the new password with the server), instead of 559 * consulting the file. We make a new copy because cvs_password 560 * will get zeroed by connect_to_server(). */ 561 cvs_password = xstrdup (typed_password); 562 563 connect_to_pserver (current_parsed_root, NULL, NULL, 1, 0); 564 565 password_entry_operation (password_entry_add, current_parsed_root, typed_password); 566 567 memset (typed_password, 0, strlen (typed_password)); 568 free (typed_password); 569 570 free (cvs_password); 571 free (cvsroot_canonical); 572 cvs_password = NULL; 573 574 return 0; 575} 576 577/* Returns the _scrambled_ password. The server must descramble 578 before hashing and comparing. If password file not found, or 579 password not found in the file, just return NULL. */ 580char * 581get_cvs_password () 582{ 583 if (current_parsed_root->password) 584 return (scramble(current_parsed_root->password)); 585 586 /* If someone (i.e., login()) is calling connect_to_pserver() out of 587 context, then assume they have supplied the correct, scrambled 588 password. */ 589 if (cvs_password) 590 return cvs_password; 591 592 if (getenv ("CVS_PASSWORD") != NULL) 593 { 594 /* In previous versions of CVS one could specify a password in 595 * CVS_PASSWORD. This is a bad idea, because in BSD variants 596 * of unix anyone can see the environment variable with 'ps'. 597 * But for users who were using that feature we want to at 598 * least let them know what is going on. After printing this 599 * warning, we should fall through to the regular error where 600 * we tell them to run "cvs login" (unless they already ran 601 * it, of course). 602 */ 603 error (0, 0, "CVS_PASSWORD is no longer supported; ignored"); 604 } 605 606 if (current_parsed_root->method != pserver_method) 607 { 608 error (0, 0, "can only call get_cvs_password with pserver method"); 609 error (1, 0, "CVSROOT: %s", current_parsed_root->original); 610 } 611 612 return password_entry_operation (password_entry_lookup, current_parsed_root, NULL); 613} 614 615static const char *const logout_usage[] = 616{ 617 "Usage: %s %s\n", 618 "(Specify the --help global option for a list of other help options)\n", 619 NULL 620}; 621 622/* Remove any entry for the CVSRoot repository found in .cvspass. */ 623int 624logout (argc, argv) 625 int argc; 626 char **argv; 627{ 628 char *cvsroot_canonical; 629 630 if (argc < 0) 631 usage (logout_usage); 632 633 if (current_parsed_root->method != pserver_method) 634 { 635 error (0, 0, "can only use pserver method with `logout' command"); 636 error (1, 0, "CVSROOT: %s", current_parsed_root->original); 637 } 638 639 /* Hmm. Do we want a variant of this command which deletes _all_ 640 the entries from the current .cvspass? Might be easier to 641 remember than "rm ~/.cvspass" but then again if people are 642 mucking with HOME (common in Win95 as the system doesn't set 643 it), then this variant of "cvs logout" might give a false sense 644 of security, in that it wouldn't delete entries from any 645 .cvspass files but the current one. */ 646 647 if (!quiet) 648 { 649 cvsroot_canonical = normalize_cvsroot(current_parsed_root); 650 printf ("Logging out of %s\n", cvsroot_canonical); 651 fflush (stdout); 652 free (cvsroot_canonical); 653 } 654 655 password_entry_operation (password_entry_delete, current_parsed_root, NULL); 656 657 return 0; 658} 659 660#endif /* AUTH_CLIENT_SUPPORT from beginning of file. */
|