1 #include "first.h" 2 3 4 /*(htpasswd)*/ 5 #ifdef HAVE_CRYPT_H 6 # include <crypt.h> 7 #elif defined(__linux__) 8 /* linux needs _XOPEN_SOURCE */ 9 # define _XOPEN_SOURCE 10 #endif 11 12 #if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT) 13 /* always assume crypt() is present if we have -lcrypt */ 14 # define HAVE_CRYPT 15 #endif 16 17 #include "sys-crypto-md.h" /* USE_LIB_CRYPTO */ 18 19 #include "safe_memclear.h" 20 21 #include "base.h" 22 #include "plugin.h" 23 #include "http_auth.h" 24 #include "log.h" 25 26 #include "base64.h" 27 28 #include <sys/types.h> 29 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <string.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 36 /* 37 * htdigest, htpasswd, plain auth backends 38 */ 39 40 typedef struct { 41 const buffer *auth_plain_groupfile; 42 const buffer *auth_plain_userfile; 43 const buffer *auth_htdigest_userfile; 44 const buffer *auth_htpasswd_userfile; 45 } plugin_config; 46 47 typedef struct { 48 PLUGIN_DATA; 49 plugin_config defaults; 50 plugin_config conf; 51 } plugin_data; 52 53 static handler_t mod_authn_file_htdigest_digest(request_st *r, void *p_d, http_auth_info_t *ai); 54 static handler_t mod_authn_file_htdigest_basic(request_st *r, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); 55 static handler_t mod_authn_file_plain_digest(request_st *r, void *p_d, http_auth_info_t *ai); 56 static handler_t mod_authn_file_plain_basic(request_st *r, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); 57 static handler_t mod_authn_file_htpasswd_basic(request_st *r, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); 58 59 INIT_FUNC(mod_authn_file_init) { 60 static http_auth_backend_t http_auth_backend_htdigest = 61 { "htdigest", mod_authn_file_htdigest_basic, mod_authn_file_htdigest_digest, NULL }; 62 static http_auth_backend_t http_auth_backend_htpasswd = 63 { "htpasswd", mod_authn_file_htpasswd_basic, NULL, NULL }; 64 static http_auth_backend_t http_auth_backend_plain = 65 { "plain", mod_authn_file_plain_basic, mod_authn_file_plain_digest, NULL }; 66 plugin_data *p = calloc(1, sizeof(*p)); 67 68 /* register http_auth_backend_htdigest */ 69 http_auth_backend_htdigest.p_d = p; 70 http_auth_backend_set(&http_auth_backend_htdigest); 71 72 /* register http_auth_backend_htpasswd */ 73 http_auth_backend_htpasswd.p_d = p; 74 http_auth_backend_set(&http_auth_backend_htpasswd); 75 76 /* register http_auth_backend_plain */ 77 http_auth_backend_plain.p_d = p; 78 http_auth_backend_set(&http_auth_backend_plain); 79 80 return p; 81 } 82 83 static void mod_authn_file_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 84 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 85 case 0: /* auth.backend.plain.groupfile */ 86 pconf->auth_plain_groupfile = cpv->v.b; 87 break; 88 case 1: /* auth.backend.plain.userfile */ 89 pconf->auth_plain_userfile = cpv->v.b; 90 break; 91 case 2: /* auth.backend.htdigest.userfile */ 92 pconf->auth_htdigest_userfile = cpv->v.b; 93 break; 94 case 3: /* auth.backend.htpasswd.userfile */ 95 pconf->auth_htpasswd_userfile = cpv->v.b; 96 break; 97 default:/* should not happen */ 98 return; 99 } 100 } 101 102 static void mod_authn_file_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 103 do { 104 mod_authn_file_merge_config_cpv(pconf, cpv); 105 } while ((++cpv)->k_id != -1); 106 } 107 108 static void mod_authn_file_patch_config(request_st * const r, plugin_data * const p) { 109 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 110 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 111 for (int i = 1, used = p->nconfig; i < used; ++i) { 112 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 113 mod_authn_file_merge_config(&p->conf, 114 p->cvlist + p->cvlist[i].v.u2[0]); 115 } 116 } 117 118 SETDEFAULTS_FUNC(mod_authn_file_set_defaults) { 119 static const config_plugin_keys_t cpk[] = { 120 { CONST_STR_LEN("auth.backend.plain.groupfile"), 121 T_CONFIG_STRING, 122 T_CONFIG_SCOPE_CONNECTION } 123 ,{ CONST_STR_LEN("auth.backend.plain.userfile"), 124 T_CONFIG_STRING, 125 T_CONFIG_SCOPE_CONNECTION } 126 ,{ CONST_STR_LEN("auth.backend.htdigest.userfile"), 127 T_CONFIG_STRING, 128 T_CONFIG_SCOPE_CONNECTION } 129 ,{ CONST_STR_LEN("auth.backend.htpasswd.userfile"), 130 T_CONFIG_STRING, 131 T_CONFIG_SCOPE_CONNECTION } 132 ,{ NULL, 0, 133 T_CONFIG_UNSET, 134 T_CONFIG_SCOPE_UNSET } 135 }; 136 137 plugin_data * const p = p_d; 138 if (!config_plugin_values_init(srv, p, cpk, "mod_authn_file")) 139 return HANDLER_ERROR; 140 141 /* initialize p->defaults from global config context */ 142 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 143 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 144 if (-1 != cpv->k_id) 145 mod_authn_file_merge_config(&p->defaults, cpv); 146 } 147 148 return HANDLER_GO_ON; 149 } 150 151 152 153 154 #ifdef USE_LIB_CRYPTO 155 156 static void mod_authn_file_digest_sha256(http_auth_info_t *ai, const char *pw, size_t pwlen) { 157 SHA256_CTX ctx; 158 SHA256_Init(&ctx); 159 SHA256_Update(&ctx, (const unsigned char *)ai->username, ai->ulen); 160 SHA256_Update(&ctx, CONST_STR_LEN(":")); 161 SHA256_Update(&ctx, (const unsigned char *)ai->realm, ai->rlen); 162 SHA256_Update(&ctx, CONST_STR_LEN(":")); 163 SHA256_Update(&ctx, (const unsigned char *)pw, pwlen); 164 SHA256_Final(ai->digest, &ctx); 165 } 166 167 #ifdef USE_LIB_CRYPTO_SHA512_256 168 static void mod_authn_file_digest_sha512_256(http_auth_info_t *ai, const char *pw, size_t pwlen) { 169 SHA512_CTX ctx; 170 SHA512_256_Init(&ctx); 171 SHA512_256_Update(&ctx, (const unsigned char *)ai->username, ai->ulen); 172 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 173 SHA512_256_Update(&ctx, (const unsigned char *)ai->realm, ai->rlen); 174 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 175 SHA512_256_Update(&ctx, (const unsigned char *)pw, pwlen); 176 SHA512_256_Final(ai->digest, &ctx); 177 } 178 #endif 179 180 #endif 181 182 static void mod_authn_file_digest_md5(http_auth_info_t *ai, const char *pw, size_t pwlen) { 183 li_MD5_CTX ctx; 184 li_MD5_Init(&ctx); 185 li_MD5_Update(&ctx, (const unsigned char *)ai->username, ai->ulen); 186 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 187 li_MD5_Update(&ctx, (const unsigned char *)ai->realm, ai->rlen); 188 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 189 li_MD5_Update(&ctx, (const unsigned char *)pw, pwlen); 190 li_MD5_Final(ai->digest, &ctx); 191 } 192 193 static void mod_authn_file_digest(http_auth_info_t *ai, const char *pw, size_t pwlen) { 194 195 if (ai->dalgo & HTTP_AUTH_DIGEST_MD5) 196 mod_authn_file_digest_md5(ai, pw, pwlen); 197 #ifdef USE_LIB_CRYPTO 198 else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA256) 199 mod_authn_file_digest_sha256(ai, pw, pwlen); 200 #ifdef USE_LIB_CRYPTO_SHA512_256 201 else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA512_256) 202 mod_authn_file_digest_sha512_256(ai, pw, pwlen); 203 #endif 204 #endif 205 } 206 207 208 209 210 static int mod_authn_file_htdigest_get_loop(FILE *fp, const buffer *auth_fn, http_auth_info_t *ai, log_error_st *errh) { 211 char f_user[1024]; 212 213 while (NULL != fgets(f_user, sizeof(f_user), fp)) { 214 char *f_pwd, *f_realm; 215 size_t u_len, r_len; 216 217 /* skip blank lines and comment lines (beginning '#') */ 218 if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue; 219 220 /* 221 * htdigest format 222 * 223 * user:realm:md5(user:realm:password) 224 */ 225 226 if (NULL == (f_realm = strchr(f_user, ':')) 227 || NULL == (f_pwd = strchr(f_realm + 1, ':'))) { 228 log_error(errh, __FILE__, __LINE__, 229 "parsed error in %s expected 'username:realm:hashed password'", 230 auth_fn->ptr); 231 continue; /* skip bad lines */ 232 } 233 234 /* get pointers to the fields */ 235 u_len = f_realm - f_user; 236 f_realm++; 237 r_len = f_pwd - f_realm; 238 f_pwd++; 239 240 if (ai->ulen == u_len && ai->rlen == r_len 241 && 0 == memcmp(ai->username, f_user, u_len) 242 && 0 == memcmp(ai->realm, f_realm, r_len)) { 243 /* found */ 244 245 size_t pwd_len = strlen(f_pwd); 246 if (f_pwd[pwd_len-1] == '\n') --pwd_len; 247 248 if (pwd_len != (ai->dlen << 1)) continue; 249 return http_auth_digest_hex2bin(f_pwd, pwd_len, 250 ai->digest, sizeof(ai->digest)); 251 } 252 } 253 254 return -1; 255 } 256 257 static int mod_authn_file_htdigest_get(request_st * const r, void *p_d, http_auth_info_t * const ai) { 258 plugin_data *p = (plugin_data *)p_d; 259 const buffer *auth_fn; 260 FILE *fp; 261 262 mod_authn_file_patch_config(r, p); 263 auth_fn = p->conf.auth_htdigest_userfile; 264 if (buffer_string_is_empty(auth_fn)) return -1; 265 266 fp = fopen(auth_fn->ptr, "r"); 267 if (NULL != fp) { 268 int rc = mod_authn_file_htdigest_get_loop(fp,auth_fn,ai,r->conf.errh); 269 fclose(fp); 270 return rc; 271 } 272 else { 273 log_perror(r->conf.errh, __FILE__, __LINE__, 274 "opening digest-userfile %s", auth_fn->ptr); 275 return -1; 276 } 277 } 278 279 static handler_t mod_authn_file_htdigest_digest(request_st * const r, void *p_d, http_auth_info_t * const ai) { 280 return (0 == mod_authn_file_htdigest_get(r, p_d, ai)) 281 ? HANDLER_GO_ON 282 : HANDLER_ERROR; 283 } 284 285 static handler_t mod_authn_file_htdigest_basic(request_st * const r, void *p_d, const http_auth_require_t * const require, const buffer * const username, const char * const pw) { 286 http_auth_info_t ai; 287 unsigned char htdigest[sizeof(ai.digest)]; 288 289 /* supports single choice of algorithm for digest stored in htdigest file */ 290 ai.dalgo = (require->algorithm & ~HTTP_AUTH_DIGEST_SESS); 291 ai.dlen = http_auth_digest_len(ai.dalgo); 292 ai.username = username->ptr; 293 ai.ulen = buffer_string_length(username); 294 ai.realm = require->realm->ptr; 295 ai.rlen = buffer_string_length(require->realm); 296 297 if (mod_authn_file_htdigest_get(r, p_d, &ai)) return HANDLER_ERROR; 298 299 if (ai.dlen > sizeof(htdigest)) return HANDLER_ERROR;/*(should not happen)*/ 300 memcpy(htdigest, ai.digest, ai.dlen); /*(save digest before reuse of ai)*/ 301 302 mod_authn_file_digest(&ai, pw, strlen(pw)); 303 304 return (http_auth_const_time_memeq(htdigest, ai.digest, ai.dlen) 305 && http_auth_match_rules(require, username->ptr, NULL, NULL)) 306 ? HANDLER_GO_ON 307 : HANDLER_ERROR; 308 } 309 310 311 312 313 static int mod_authn_file_htpasswd_get(const buffer *auth_fn, const char *username, size_t userlen, buffer *password, log_error_st *errh) { 314 FILE *fp; 315 char f_user[1024]; 316 317 if (NULL == username) return -1; 318 319 if (buffer_string_is_empty(auth_fn)) return -1; 320 fp = fopen(auth_fn->ptr, "r"); 321 if (NULL == fp) { 322 log_perror(errh, __FILE__, __LINE__, 323 "opening plain-userfile %s", auth_fn->ptr); 324 325 return -1; 326 } 327 328 while (NULL != fgets(f_user, sizeof(f_user), fp)) { 329 char *f_pwd; 330 size_t u_len; 331 332 /* skip blank lines and comment lines (beginning '#') */ 333 if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue; 334 335 /* 336 * htpasswd format 337 * 338 * user:crypted passwd 339 */ 340 341 if (NULL == (f_pwd = strchr(f_user, ':'))) { 342 log_error(errh, __FILE__, __LINE__, 343 "parsed error in %s expected 'username:hashed password'", 344 auth_fn->ptr); 345 continue; /* skip bad lines */ 346 } 347 348 /* get pointers to the fields */ 349 u_len = f_pwd - f_user; 350 f_pwd++; 351 352 if (userlen == u_len && 0 == memcmp(username, f_user, u_len)) { 353 /* found */ 354 355 size_t pwd_len = strlen(f_pwd); 356 if (f_pwd[pwd_len-1] == '\n') --pwd_len; 357 358 buffer_copy_string_len(password, f_pwd, pwd_len); 359 360 fclose(fp); 361 return 0; 362 } 363 } 364 365 fclose(fp); 366 return -1; 367 } 368 369 static handler_t mod_authn_file_plain_digest(request_st * const r, void *p_d, http_auth_info_t * const ai) { 370 plugin_data *p = (plugin_data *)p_d; 371 buffer *password_buf = buffer_init();/* password-string from auth-backend */ 372 int rc; 373 mod_authn_file_patch_config(r, p); 374 rc = mod_authn_file_htpasswd_get(p->conf.auth_plain_userfile, ai->username, ai->ulen, password_buf, r->conf.errh); 375 if (0 == rc) { 376 /* generate password from plain-text */ 377 mod_authn_file_digest(ai, CONST_BUF_LEN(password_buf)); 378 } 379 buffer_free(password_buf); 380 return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR; 381 } 382 383 static handler_t mod_authn_file_plain_basic(request_st * const r, void *p_d, const http_auth_require_t * const require, const buffer * const username, const char * const pw) { 384 plugin_data *p = (plugin_data *)p_d; 385 buffer *password_buf = buffer_init();/* password-string from auth-backend */ 386 int rc; 387 mod_authn_file_patch_config(r, p); 388 rc = mod_authn_file_htpasswd_get(p->conf.auth_plain_userfile, CONST_BUF_LEN(username), password_buf, r->conf.errh); 389 if (0 == rc) { 390 rc = http_auth_const_time_memeq_pad(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1; 391 } 392 buffer_free(password_buf); 393 return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL) 394 ? HANDLER_GO_ON 395 : HANDLER_ERROR; 396 } 397 398 399 400 401 /** 402 * the $apr1$ handling is taken from apache 1.3.x 403 */ 404 405 /* 406 * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 407 * MD5 crypt() function, which is licenced as follows: 408 * ---------------------------------------------------------------------------- 409 * "THE BEER-WARE LICENSE" (Revision 42): 410 * <[email protected]> wrote this file. As long as you retain this notice you 411 * can do whatever you want with this stuff. If we meet some day, and you think 412 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 413 * ---------------------------------------------------------------------------- 414 */ 415 416 #define APR_MD5_DIGESTSIZE 16 417 #define APR1_ID "$apr1$" 418 419 /* 420 * The following MD5 password encryption code was largely borrowed from 421 * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is 422 * licenced as stated above. 423 */ 424 425 static void to64(char *s, unsigned long v, int n) 426 { 427 static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ 428 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 429 430 while (--n >= 0) { 431 *s++ = itoa64[v&0x3f]; 432 v >>= 6; 433 } 434 } 435 436 static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) { 437 /* 438 * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL, 439 * plus 4 for the '$' separators, plus the password hash itself. 440 * Let's leave a goodly amount of leeway. 441 */ 442 443 char passwd[120], *p; 444 const char *sp, *ep; 445 unsigned char final[APR_MD5_DIGESTSIZE]; 446 ssize_t sl, pl, i; 447 li_MD5_CTX ctx, ctx1; 448 unsigned long l; 449 450 /* 451 * Refine the salt first. It's possible we were given an already-hashed 452 * string as the salt argument, so extract the actual salt value from it 453 * if so. Otherwise just use the string up to the first '$' as the salt. 454 */ 455 sp = salt; 456 457 /* 458 * If it starts with the magic string, then skip that. 459 */ 460 if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) { 461 sp += strlen(APR1_ID); 462 } 463 464 /* 465 * It stops at the first '$' or 8 chars, whichever comes first 466 */ 467 for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) { 468 continue; 469 } 470 471 /* 472 * Get the length of the true salt 473 */ 474 sl = ep - sp; 475 476 /* 477 * 'Time to make the doughnuts..' 478 */ 479 li_MD5_Init(&ctx); 480 481 /* 482 * The password first, since that is what is most unknown 483 */ 484 li_MD5_Update(&ctx, pw, strlen(pw)); 485 486 /* 487 * Then our magic string 488 */ 489 li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); 490 491 /* 492 * Then the raw salt 493 */ 494 li_MD5_Update(&ctx, sp, sl); 495 496 /* 497 * Then just as many characters of the MD5(pw, salt, pw) 498 */ 499 li_MD5_Init(&ctx1); 500 li_MD5_Update(&ctx1, pw, strlen(pw)); 501 li_MD5_Update(&ctx1, sp, sl); 502 li_MD5_Update(&ctx1, pw, strlen(pw)); 503 li_MD5_Final(final, &ctx1); 504 for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { 505 li_MD5_Update(&ctx, final, 506 (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); 507 } 508 509 /* 510 * Don't leave anything around in vm they could use. 511 */ 512 memset(final, 0, sizeof(final)); 513 514 /* 515 * Then something really weird... 516 */ 517 for (i = strlen(pw); i != 0; i >>= 1) { 518 if (i & 1) { 519 li_MD5_Update(&ctx, final, 1); 520 } 521 else { 522 li_MD5_Update(&ctx, pw, 1); 523 } 524 } 525 526 /* 527 * Now make the output string. We know our limitations, so we 528 * can use the string routines without bounds checking. 529 */ 530 strcpy(passwd, APR1_ID); 531 strncat(passwd, sp, sl); 532 strcat(passwd, "$"); 533 534 li_MD5_Final(final, &ctx); 535 536 /* 537 * And now, just to make sure things don't run too fast.. 538 * On a 60 Mhz Pentium this takes 34 msec, so you would 539 * need 30 seconds to build a 1000 entry dictionary... 540 */ 541 for (i = 0; i < 1000; i++) { 542 li_MD5_Init(&ctx1); 543 if (i & 1) { 544 li_MD5_Update(&ctx1, pw, strlen(pw)); 545 } 546 else { 547 li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); 548 } 549 if (i % 3) { 550 li_MD5_Update(&ctx1, sp, sl); 551 } 552 553 if (i % 7) { 554 li_MD5_Update(&ctx1, pw, strlen(pw)); 555 } 556 557 if (i & 1) { 558 li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); 559 } 560 else { 561 li_MD5_Update(&ctx1, pw, strlen(pw)); 562 } 563 li_MD5_Final(final,&ctx1); 564 } 565 566 p = passwd + strlen(passwd); 567 568 l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4; 569 l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4; 570 l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4; 571 l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4; 572 l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4; 573 l = final[11] ; to64(p, l, 2); p += 2; 574 *p = '\0'; 575 576 /* 577 * Don't leave anything around in vm they could use. 578 */ 579 safe_memclear(final, sizeof(final)); 580 581 /* FIXME 582 */ 583 #define apr_cpystrn strncpy 584 apr_cpystrn(result, passwd, nbytes - 1); 585 } 586 587 static void apr_sha_encode(const char *pw, char *result, size_t nbytes) { 588 unsigned char digest[20]; 589 size_t base64_written; 590 SHA_CTX sha1; 591 592 SHA1_Init(&sha1); 593 SHA1_Update(&sha1, (const unsigned char *) pw, strlen(pw)); 594 SHA1_Final(digest, &sha1); 595 596 memset(result, 0, nbytes); 597 598 /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */ 599 if (nbytes < 5 + 28 + 1) return; 600 601 memcpy(result, "{SHA}", 5); 602 base64_written = li_to_base64(result + 5, nbytes - 5, digest, 20, BASE64_STANDARD); 603 force_assert(base64_written == 28); 604 result[5 + base64_written] = '\0'; /* terminate string */ 605 } 606 607 static handler_t mod_authn_file_htpasswd_basic(request_st * const r, void *p_d, const http_auth_require_t * const require, const buffer * const username, const char * const pw) { 608 plugin_data *p = (plugin_data *)p_d; 609 buffer *password = buffer_init();/* password-string from auth-backend */ 610 int rc; 611 mod_authn_file_patch_config(r, p); 612 rc = mod_authn_file_htpasswd_get(p->conf.auth_htpasswd_userfile, CONST_BUF_LEN(username), password, r->conf.errh); 613 if (0 == rc) { 614 char sample[256]; 615 rc = -1; 616 if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) { 617 /* 618 * The hash was created using $apr1$ custom algorithm. 619 */ 620 apr_md5_encode(pw, password->ptr, sample, sizeof(sample)); 621 rc = strcmp(sample, password->ptr); 622 } 623 else if (0 == strncmp(password->ptr, "{SHA}", 5)) { 624 apr_sha_encode(pw, sample, sizeof(sample)); 625 rc = strcmp(sample, password->ptr); 626 } 627 #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT) 628 /* a simple DES password is 2 + 11 characters. everything else should be longer. */ 629 else if (buffer_string_length(password) >= 13) { 630 char *crypted; 631 #if defined(HAVE_CRYPT_R) 632 struct crypt_data crypt_tmp_data; 633 #ifdef _AIX 634 memset(&crypt_tmp_data, 0, sizeof(crypt_tmp_data)); 635 #else 636 crypt_tmp_data.initialized = 0; 637 #endif 638 #endif 639 #ifdef USE_LIB_CRYPTO_MD4 /*(for MD4_*() (e.g. MD4_Update()))*/ 640 if (0 == memcmp(password->ptr, CONST_STR_LEN("$1+ntlm$"))) { 641 /* CRYPT-MD5-NTLM algorithm 642 * This algorithm allows for the construction of (slight more) 643 * secure, salted password hashes from an environment where only 644 * legacy NTLM hashes are available and where it is not feasible 645 * to re-hash all the passwords with the MD5-based crypt(). */ 646 /* Note: originally, LM password were limited to 14 chars. 647 * NTLM passwords limited to 127 chars, and encoding to UCS-2LE 648 * requires double that, so sample[256] buf is large enough. 649 * Prior sample[120] size likely taken from apr_md5_encode(). */ 650 char *b = password->ptr+sizeof("$1+ntlm$")-1; 651 char *e = strchr(b, '$'); 652 size_t slen = (NULL != e) ? (size_t)(e - b) : sizeof(sample); 653 size_t pwlen = strlen(pw) * 2; 654 if (slen < sizeof(sample) - (sizeof("$1$")-1) 655 && pwlen < sizeof(sample)) { 656 /* compute NTLM hash and convert to lowercase hex chars 657 * (require lc hex chars from li_tohex()) */ 658 char ntlmhash[16]; 659 char ntlmhex[33]; /*(sizeof(ntlmhash)*2 + 1)*/ 660 MD4_CTX c; 661 MD4_Init(&c); 662 if (pwlen) { 663 /*(reuse sample buffer to encode pw into UCS-2LE) 664 *(Note: assumes pw input in ISO-8859-1) */ 665 /*(buffer sizes checked above)*/ 666 for (int i=0; i < (int)pwlen; i+=2) { 667 sample[i] = pw[(i >> 1)]; 668 sample[i+1] = 0; 669 } 670 MD4_Update(&c, (unsigned char *)sample, pwlen); 671 } 672 MD4_Final((unsigned char *)ntlmhash, &c); 673 li_tohex(ntlmhex,sizeof(ntlmhex),ntlmhash,sizeof(ntlmhash)); 674 675 /*(reuse sample buffer for salt (FYI: expect slen == 8))*/ 676 memcpy(sample, "$1$", sizeof("$1$")-1); 677 memcpy(sample+sizeof("$1$")-1, b, slen); 678 sample[sizeof("$1$")-1+slen] = '\0'; 679 #if defined(HAVE_CRYPT_R) 680 crypted = crypt_r(ntlmhex, sample, &crypt_tmp_data); 681 #else 682 crypted = crypt(ntlmhex, sample); 683 #endif 684 if (NULL != crypted 685 && 0 == strncmp(crypted, "$1$", sizeof("$1$")-1)) { 686 rc = strcmp(b, crypted+3); /*skip crypted "$1$" prefix*/ 687 } 688 } 689 } 690 else 691 #endif 692 { 693 #if defined(HAVE_CRYPT_R) 694 crypted = crypt_r(pw, password->ptr, &crypt_tmp_data); 695 #else 696 crypted = crypt(pw, password->ptr); 697 #endif 698 if (NULL != crypted) { 699 rc = strcmp(password->ptr, crypted); 700 } 701 } 702 } 703 #endif 704 } 705 buffer_free(password); 706 return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL) 707 ? HANDLER_GO_ON 708 : HANDLER_ERROR; 709 } 710 711 712 int mod_authn_file_plugin_init(plugin *p); 713 int mod_authn_file_plugin_init(plugin *p) { 714 p->version = LIGHTTPD_VERSION_ID; 715 p->name = "authn_file"; 716 p->init = mod_authn_file_init; 717 p->set_defaults= mod_authn_file_set_defaults; 718 719 return 0; 720 } 721