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