|
37 | 37 | # include "md5.h"
|
38 | 38 | #endif
|
39 | 39 |
|
| 40 | +/** |
| 41 | + * the $apr1$ handling is taken from apache 1.3.x |
| 42 | + */ |
| 43 | + |
| 44 | +/* |
| 45 | + * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 |
| 46 | + * MD5 crypt() function, which is licenced as follows: |
| 47 | + * ---------------------------------------------------------------------------- |
| 48 | + * "THE BEER-WARE LICENSE" (Revision 42): |
| 49 | + * <[email protected]> wrote this file. As long as you retain this notice you |
| 50 | + * can do whatever you want with this stuff. If we meet some day, and you think |
| 51 | + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp |
| 52 | + * ---------------------------------------------------------------------------- |
| 53 | + */ |
| 54 | + |
40 | 55 | handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s);
|
41 | 56 |
|
42 | 57 | static const char base64_pad = '=';
|
@@ -403,6 +418,178 @@ static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const cha
|
403 | 418 | return -1;
|
404 | 419 | }
|
405 | 420 |
|
| 421 | +#define APR_MD5_DIGESTSIZE 16 |
| 422 | +#define APR1_ID "$apr1$" |
| 423 | + |
| 424 | +/* |
| 425 | + * The following MD5 password encryption code was largely borrowed from |
| 426 | + * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is |
| 427 | + * licenced as stated at the top of this file. |
| 428 | + */ |
| 429 | + |
| 430 | +static void to64(char *s, unsigned long v, int n) |
| 431 | +{ |
| 432 | + static unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ |
| 433 | + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
| 434 | + |
| 435 | + while (--n >= 0) { |
| 436 | + *s++ = itoa64[v&0x3f]; |
| 437 | + v >>= 6; |
| 438 | + } |
| 439 | +} |
| 440 | + |
| 441 | +static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) { |
| 442 | + /* |
| 443 | + * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL, |
| 444 | + * plus 4 for the '$' separators, plus the password hash itself. |
| 445 | + * Let's leave a goodly amount of leeway. |
| 446 | + */ |
| 447 | + |
| 448 | + char passwd[120], *p; |
| 449 | + const char *sp, *ep; |
| 450 | + unsigned char final[APR_MD5_DIGESTSIZE]; |
| 451 | + ssize_t sl, pl, i; |
| 452 | + MD5_CTX ctx, ctx1; |
| 453 | + unsigned long l; |
| 454 | + |
| 455 | + /* |
| 456 | + * Refine the salt first. It's possible we were given an already-hashed |
| 457 | + * string as the salt argument, so extract the actual salt value from it |
| 458 | + * if so. Otherwise just use the string up to the first '$' as the salt. |
| 459 | + */ |
| 460 | + sp = salt; |
| 461 | + |
| 462 | + /* |
| 463 | + * If it starts with the magic string, then skip that. |
| 464 | + */ |
| 465 | + if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) { |
| 466 | + sp += strlen(APR1_ID); |
| 467 | + } |
| 468 | + |
| 469 | + /* |
| 470 | + * It stops at the first '$' or 8 chars, whichever comes first |
| 471 | + */ |
| 472 | + for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) { |
| 473 | + continue; |
| 474 | + } |
| 475 | + |
| 476 | + /* |
| 477 | + * Get the length of the true salt |
| 478 | + */ |
| 479 | + sl = ep - sp; |
| 480 | + |
| 481 | + /* |
| 482 | + * 'Time to make the doughnuts..' |
| 483 | + */ |
| 484 | + MD5_Init(&ctx); |
| 485 | + |
| 486 | + /* |
| 487 | + * The password first, since that is what is most unknown |
| 488 | + */ |
| 489 | + MD5_Update(&ctx, pw, strlen(pw)); |
| 490 | + |
| 491 | + /* |
| 492 | + * Then our magic string |
| 493 | + */ |
| 494 | + MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); |
| 495 | + |
| 496 | + /* |
| 497 | + * Then the raw salt |
| 498 | + */ |
| 499 | + MD5_Update(&ctx, sp, sl); |
| 500 | + |
| 501 | + /* |
| 502 | + * Then just as many characters of the MD5(pw, salt, pw) |
| 503 | + */ |
| 504 | + MD5_Init(&ctx1); |
| 505 | + MD5_Update(&ctx1, pw, strlen(pw)); |
| 506 | + MD5_Update(&ctx1, sp, sl); |
| 507 | + MD5_Update(&ctx1, pw, strlen(pw)); |
| 508 | + MD5_Final(final, &ctx1); |
| 509 | + for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { |
| 510 | + MD5_Update(&ctx, final, |
| 511 | + (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); |
| 512 | + } |
| 513 | + |
| 514 | + /* |
| 515 | + * Don't leave anything around in vm they could use. |
| 516 | + */ |
| 517 | + memset(final, 0, sizeof(final)); |
| 518 | + |
| 519 | + /* |
| 520 | + * Then something really weird... |
| 521 | + */ |
| 522 | + for (i = strlen(pw); i != 0; i >>= 1) { |
| 523 | + if (i & 1) { |
| 524 | + MD5_Update(&ctx, final, 1); |
| 525 | + } |
| 526 | + else { |
| 527 | + MD5_Update(&ctx, pw, 1); |
| 528 | + } |
| 529 | + } |
| 530 | + |
| 531 | + /* |
| 532 | + * Now make the output string. We know our limitations, so we |
| 533 | + * can use the string routines without bounds checking. |
| 534 | + */ |
| 535 | + strcpy(passwd, APR1_ID); |
| 536 | + strncat(passwd, sp, sl); |
| 537 | + strcat(passwd, "$"); |
| 538 | + |
| 539 | + MD5_Final(final, &ctx); |
| 540 | + |
| 541 | + /* |
| 542 | + * And now, just to make sure things don't run too fast.. |
| 543 | + * On a 60 Mhz Pentium this takes 34 msec, so you would |
| 544 | + * need 30 seconds to build a 1000 entry dictionary... |
| 545 | + */ |
| 546 | + for (i = 0; i < 1000; i++) { |
| 547 | + MD5_Init(&ctx1); |
| 548 | + if (i & 1) { |
| 549 | + MD5_Update(&ctx1, pw, strlen(pw)); |
| 550 | + } |
| 551 | + else { |
| 552 | + MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); |
| 553 | + } |
| 554 | + if (i % 3) { |
| 555 | + MD5_Update(&ctx1, sp, sl); |
| 556 | + } |
| 557 | + |
| 558 | + if (i % 7) { |
| 559 | + MD5_Update(&ctx1, pw, strlen(pw)); |
| 560 | + } |
| 561 | + |
| 562 | + if (i & 1) { |
| 563 | + MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); |
| 564 | + } |
| 565 | + else { |
| 566 | + MD5_Update(&ctx1, pw, strlen(pw)); |
| 567 | + } |
| 568 | + MD5_Final(final,&ctx1); |
| 569 | + } |
| 570 | + |
| 571 | + p = passwd + strlen(passwd); |
| 572 | + |
| 573 | + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4; |
| 574 | + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4; |
| 575 | + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4; |
| 576 | + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4; |
| 577 | + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4; |
| 578 | + l = final[11] ; to64(p, l, 2); p += 2; |
| 579 | + *p = '\0'; |
| 580 | + |
| 581 | + /* |
| 582 | + * Don't leave anything around in vm they could use. |
| 583 | + */ |
| 584 | + memset(final, 0, sizeof(final)); |
| 585 | + |
| 586 | + /* FIXME |
| 587 | + */ |
| 588 | +#define apr_cpystrn strncpy |
| 589 | + apr_cpystrn(result, passwd, nbytes - 1); |
| 590 | +} |
| 591 | + |
| 592 | + |
406 | 593 | /**
|
407 | 594 | *
|
408 | 595 | *
|
@@ -439,6 +626,14 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p
|
439 | 626 | return 0;
|
440 | 627 | }
|
441 | 628 | } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) {
|
| 629 | + char sample[120]; |
| 630 | + if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) { |
| 631 | + /* |
| 632 | + * The hash was created using $apr1$ custom algorithm. |
| 633 | + */ |
| 634 | + apr_md5_encode(pw, password->ptr, sample, sizeof(sample)); |
| 635 | + return (strcmp(sample, password->ptr) == 0) ? 0 : 1; |
| 636 | + } else { |
442 | 637 | #ifdef HAVE_CRYPT
|
443 | 638 | char salt[32];
|
444 | 639 | char *crypted;
|
@@ -494,6 +689,7 @@ static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p
|
494 | 689 | }
|
495 | 690 |
|
496 | 691 | #endif
|
| 692 | + } |
497 | 693 | } else if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
|
498 | 694 | if (0 == strcmp(password->ptr, pw)) {
|
499 | 695 | return 0;
|
|
0 commit comments