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