1 #include "server.h" 2 #include "log.h" 3 #include "http_auth.h" 4 #include "inet_ntop_cache.h" 5 #include "stream.h" 6 7 #ifdef HAVE_CRYPT_H 8 # include <crypt.h> 9 #elif defined(__linux__) 10 /* linux needs _XOPEN_SOURCE */ 11 # define _XOPEN_SOURCE 12 #endif 13 14 #ifdef HAVE_LIBCRYPT 15 # define HAVE_CRYPT 16 #endif 17 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 21 #include <fcntl.h> 22 #include <stdlib.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <time.h> 26 #include <errno.h> 27 #include <unistd.h> 28 #include <ctype.h> 29 30 #include "md5.h" 31 32 #define HASHLEN 16 33 #define HASHHEXLEN 32 34 typedef unsigned char HASH[HASHLEN]; 35 typedef char HASHHEX[HASHHEXLEN+1]; 36 37 static void CvtHex(const HASH Bin, char Hex[33]) { 38 unsigned short i; 39 40 for (i = 0; i < 16; i++) { 41 Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf); 42 Hex[i*2+1] = int2hex(Bin[i] & 0xf); 43 } 44 Hex[32] = '\0'; 45 } 46 47 /** 48 * the $apr1$ handling is taken from apache 1.3.x 49 */ 50 51 /* 52 * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 53 * MD5 crypt() function, which is licenced as follows: 54 * ---------------------------------------------------------------------------- 55 * "THE BEER-WARE LICENSE" (Revision 42): 56 * <[email protected]> wrote this file. As long as you retain this notice you 57 * can do whatever you want with this stuff. If we meet some day, and you think 58 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 59 * ---------------------------------------------------------------------------- 60 */ 61 62 handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s); 63 64 static const char base64_pad = '='; 65 66 /* "A-Z a-z 0-9 + /" maps to 0-63 */ 67 static const short base64_reverse_table[256] = { 68 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 69 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 - 0x0F */ 70 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 - 0x1F */ 71 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 - 0x2F */ 72 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 - 0x3F */ 73 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */ 74 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 - 0x5F */ 75 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */ 76 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */ 77 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 - 0x8F */ 78 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 - 0x9F */ 79 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 - 0xAF */ 80 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 - 0xBF */ 81 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 - 0xCF */ 82 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 - 0xDF */ 83 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 - 0xEF */ 84 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xF0 - 0xFF */ 85 }; 86 87 88 static unsigned char * base64_decode(buffer *out, const char *in) { 89 unsigned char *result; 90 int ch, j = 0, k; 91 size_t i; 92 93 size_t in_len = strlen(in); 94 95 buffer_prepare_copy(out, in_len); 96 97 result = (unsigned char *)out->ptr; 98 99 ch = in[0]; 100 /* run through the whole string, converting as we go */ 101 for (i = 0; i < in_len; i++) { 102 ch = (unsigned char) in[i]; 103 104 if (ch == '\0') break; 105 106 if (ch == base64_pad) break; 107 108 ch = base64_reverse_table[ch]; 109 if (ch < 0) continue; 110 111 switch(i % 4) { 112 case 0: 113 result[j] = ch << 2; 114 break; 115 case 1: 116 result[j++] |= ch >> 4; 117 result[j] = (ch & 0x0f) << 4; 118 break; 119 case 2: 120 result[j++] |= ch >>2; 121 result[j] = (ch & 0x03) << 6; 122 break; 123 case 3: 124 result[j++] |= ch; 125 break; 126 } 127 } 128 k = j; 129 /* mop things up if we ended on a boundary */ 130 if (ch == base64_pad) { 131 switch(i % 4) { 132 case 0: 133 case 1: 134 return NULL; 135 case 2: 136 k++; 137 case 3: 138 result[k++] = 0; 139 } 140 } 141 result[k] = '\0'; 142 143 out->used = k; 144 145 return result; 146 } 147 148 static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) { 149 int ret = -1; 150 151 if (!username->used|| !realm->used) return -1; 152 153 if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { 154 stream f; 155 char * f_line; 156 157 if (buffer_is_empty(p->conf.auth_htdigest_userfile)) return -1; 158 159 if (0 != stream_open(&f, p->conf.auth_htdigest_userfile)) { 160 log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", p->conf.auth_htdigest_userfile, "failed:", strerror(errno)); 161 162 return -1; 163 } 164 165 f_line = f.start; 166 167 while (f_line - f.start != f.size) { 168 char *f_user, *f_pwd, *e, *f_realm; 169 size_t u_len, pwd_len, r_len; 170 171 f_user = f_line; 172 173 /* 174 * htdigest format 175 * 176 * user:realm:md5(user:realm:password) 177 */ 178 179 if (NULL == (f_realm = memchr(f_user, ':', f.size - (f_user - f.start) ))) { 180 log_error_write(srv, __FILE__, __LINE__, "sbs", 181 "parsed error in", p->conf.auth_htdigest_userfile, 182 "expected 'username:realm:hashed password'"); 183 184 stream_close(&f); 185 186 return -1; 187 } 188 189 if (NULL == (f_pwd = memchr(f_realm + 1, ':', f.size - (f_realm + 1 - f.start)))) { 190 log_error_write(srv, __FILE__, __LINE__, "sbs", 191 "parsed error in", p->conf.auth_plain_userfile, 192 "expected 'username:realm:hashed password'"); 193 194 stream_close(&f); 195 196 return -1; 197 } 198 199 /* get pointers to the fields */ 200 u_len = f_realm - f_user; 201 f_realm++; 202 r_len = f_pwd - f_realm; 203 f_pwd++; 204 205 if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) { 206 pwd_len = e - f_pwd; 207 } else { 208 pwd_len = f.size - (f_pwd - f.start); 209 } 210 211 if (username->used - 1 == u_len && 212 (realm->used - 1 == r_len) && 213 (0 == strncmp(username->ptr, f_user, u_len)) && 214 (0 == strncmp(realm->ptr, f_realm, r_len))) { 215 /* found */ 216 217 buffer_copy_string_len(password, f_pwd, pwd_len); 218 219 ret = 0; 220 break; 221 } 222 223 /* EOL */ 224 if (!e) break; 225 226 f_line = e + 1; 227 } 228 229 stream_close(&f); 230 } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD || 231 p->conf.auth_backend == AUTH_BACKEND_PLAIN) { 232 stream f; 233 char * f_line; 234 buffer *auth_fn; 235 236 auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile; 237 238 if (buffer_is_empty(auth_fn)) return -1; 239 240 if (0 != stream_open(&f, auth_fn)) { 241 log_error_write(srv, __FILE__, __LINE__, "sbss", 242 "opening plain-userfile", auth_fn, "failed:", strerror(errno)); 243 244 return -1; 245 } 246 247 f_line = f.start; 248 249 while (f_line - f.start != f.size) { 250 char *f_user, *f_pwd, *e; 251 size_t u_len, pwd_len; 252 253 f_user = f_line; 254 255 /* 256 * htpasswd format 257 * 258 * user:crypted passwd 259 */ 260 261 if (NULL == (f_pwd = memchr(f_user, ':', f.size - (f_user - f.start) ))) { 262 log_error_write(srv, __FILE__, __LINE__, "sbs", 263 "parsed error in", auth_fn, 264 "expected 'username:hashed password'"); 265 266 stream_close(&f); 267 268 return -1; 269 } 270 271 /* get pointers to the fields */ 272 u_len = f_pwd - f_user; 273 f_pwd++; 274 275 if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) { 276 pwd_len = e - f_pwd; 277 } else { 278 pwd_len = f.size - (f_pwd - f.start); 279 } 280 281 if (username->used - 1 == u_len && 282 (0 == strncmp(username->ptr, f_user, u_len))) { 283 /* found */ 284 285 buffer_copy_string_len(password, f_pwd, pwd_len); 286 287 ret = 0; 288 break; 289 } 290 291 /* EOL */ 292 if (!e) break; 293 294 f_line = e + 1; 295 } 296 297 stream_close(&f); 298 } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) { 299 ret = 0; 300 } else { 301 return -1; 302 } 303 304 return ret; 305 } 306 307 static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const char *url, const char *username, const char *group, const char *host) { 308 const char *r = NULL, *rules = NULL; 309 size_t i; 310 int username_len; 311 data_string *require; 312 array *req; 313 314 UNUSED(group); 315 UNUSED(host); 316 317 /* check what has to be match to fullfil the request */ 318 /* search auth-directives for path */ 319 for (i = 0; i < p->conf.auth_require->used; i++) { 320 if (p->conf.auth_require->data[i]->key->used == 0) continue; 321 322 if (0 == strncmp(url, p->conf.auth_require->data[i]->key->ptr, p->conf.auth_require->data[i]->key->used - 1)) { 323 break; 324 } 325 } 326 327 if (i == p->conf.auth_require->used) { 328 return -1; 329 } 330 331 req = ((data_array *)(p->conf.auth_require->data[i]))->value; 332 333 require = (data_string *)array_get_element(req, "require"); 334 335 /* if we get here, the user we got a authed user */ 336 if (0 == strcmp(require->value->ptr, "valid-user")) { 337 return 0; 338 } 339 340 /* user=name1|group=name3|host=name4 */ 341 342 /* seperate the string by | */ 343 #if 0 344 log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value); 345 #endif 346 347 username_len = username ? strlen(username) : 0; 348 349 r = rules = require->value->ptr; 350 351 while (1) { 352 const char *eq; 353 const char *k, *v, *e; 354 int k_len, v_len, r_len; 355 356 e = strchr(r, '|'); 357 358 if (e) { 359 r_len = e - r; 360 } else { 361 r_len = strlen(rules) - (r - rules); 362 } 363 364 /* from r to r + r_len is a rule */ 365 366 if (0 == strncmp(r, "valid-user", r_len)) { 367 log_error_write(srv, __FILE__, __LINE__, "sb", 368 "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules", 369 require->value); 370 return -1; 371 } 372 373 /* search for = in the rules */ 374 if (NULL == (eq = strchr(r, '='))) { 375 log_error_write(srv, __FILE__, __LINE__, "sb", 376 "parsing the 'require' section in 'auth.require' failed: a = is missing", 377 require->value); 378 return -1; 379 } 380 381 /* = out of range */ 382 if (eq > r + r_len) { 383 log_error_write(srv, __FILE__, __LINE__, "sb", 384 "parsing the 'require' section in 'auth.require' failed: = out of range", 385 require->value); 386 387 return -1; 388 } 389 390 /* the part before the = is user|group|host */ 391 392 k = r; 393 k_len = eq - r; 394 v = eq + 1; 395 v_len = r_len - k_len - 1; 396 397 if (k_len == 4) { 398 if (0 == strncmp(k, "user", k_len)) { 399 if (username && 400 username_len == v_len && 401 0 == strncmp(username, v, v_len)) { 402 return 0; 403 } 404 } else if (0 == strncmp(k, "host", k_len)) { 405 log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)"); 406 } else { 407 log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); 408 return -1; 409 } 410 } else if (k_len == 5) { 411 if (0 == strncmp(k, "group", k_len)) { 412 log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)"); 413 } else { 414 log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k); 415 return -1; 416 } 417 } else { 418 log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); 419 return -1; 420 } 421 422 if (!e) break; 423 r = e + 1; 424 } 425 426 log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched"); 427 428 return -1; 429 } 430 431 #define APR_MD5_DIGESTSIZE 16 432 #define APR1_ID "$apr1$" 433 434 /* 435 * The following MD5 password encryption code was largely borrowed from 436 * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is 437 * licenced as stated at the top of this file. 438 */ 439 440 static void to64(char *s, unsigned long v, int n) 441 { 442 static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ 443 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 444 445 while (--n >= 0) { 446 *s++ = itoa64[v&0x3f]; 447 v >>= 6; 448 } 449 } 450 451 static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) { 452 /* 453 * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL, 454 * plus 4 for the '$' separators, plus the password hash itself. 455 * Let's leave a goodly amount of leeway. 456 */ 457 458 char passwd[120], *p; 459 const char *sp, *ep; 460 unsigned char final[APR_MD5_DIGESTSIZE]; 461 ssize_t sl, pl, i; 462 li_MD5_CTX ctx, ctx1; 463 unsigned long l; 464 465 /* 466 * Refine the salt first. It's possible we were given an already-hashed 467 * string as the salt argument, so extract the actual salt value from it 468 * if so. Otherwise just use the string up to the first '$' as the salt. 469 */ 470 sp = salt; 471 472 /* 473 * If it starts with the magic string, then skip that. 474 */ 475 if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) { 476 sp += strlen(APR1_ID); 477 } 478 479 /* 480 * It stops at the first '$' or 8 chars, whichever comes first 481 */ 482 for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) { 483 continue; 484 } 485 486 /* 487 * Get the length of the true salt 488 */ 489 sl = ep - sp; 490 491 /* 492 * 'Time to make the doughnuts..' 493 */ 494 li_MD5_Init(&ctx); 495 496 /* 497 * The password first, since that is what is most unknown 498 */ 499 li_MD5_Update(&ctx, pw, strlen(pw)); 500 501 /* 502 * Then our magic string 503 */ 504 li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); 505 506 /* 507 * Then the raw salt 508 */ 509 li_MD5_Update(&ctx, sp, sl); 510 511 /* 512 * Then just as many characters of the MD5(pw, salt, pw) 513 */ 514 li_MD5_Init(&ctx1); 515 li_MD5_Update(&ctx1, pw, strlen(pw)); 516 li_MD5_Update(&ctx1, sp, sl); 517 li_MD5_Update(&ctx1, pw, strlen(pw)); 518 li_MD5_Final(final, &ctx1); 519 for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { 520 li_MD5_Update(&ctx, final, 521 (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); 522 } 523 524 /* 525 * Don't leave anything around in vm they could use. 526 */ 527 memset(final, 0, sizeof(final)); 528 529 /* 530 * Then something really weird... 531 */ 532 for (i = strlen(pw); i != 0; i >>= 1) { 533 if (i & 1) { 534 li_MD5_Update(&ctx, final, 1); 535 } 536 else { 537 li_MD5_Update(&ctx, pw, 1); 538 } 539 } 540 541 /* 542 * Now make the output string. We know our limitations, so we 543 * can use the string routines without bounds checking. 544 */ 545 strcpy(passwd, APR1_ID); 546 strncat(passwd, sp, sl); 547 strcat(passwd, "$"); 548 549 li_MD5_Final(final, &ctx); 550 551 /* 552 * And now, just to make sure things don't run too fast.. 553 * On a 60 Mhz Pentium this takes 34 msec, so you would 554 * need 30 seconds to build a 1000 entry dictionary... 555 */ 556 for (i = 0; i < 1000; i++) { 557 li_MD5_Init(&ctx1); 558 if (i & 1) { 559 li_MD5_Update(&ctx1, pw, strlen(pw)); 560 } 561 else { 562 li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); 563 } 564 if (i % 3) { 565 li_MD5_Update(&ctx1, sp, sl); 566 } 567 568 if (i % 7) { 569 li_MD5_Update(&ctx1, pw, strlen(pw)); 570 } 571 572 if (i & 1) { 573 li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); 574 } 575 else { 576 li_MD5_Update(&ctx1, pw, strlen(pw)); 577 } 578 li_MD5_Final(final,&ctx1); 579 } 580 581 p = passwd + strlen(passwd); 582 583 l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4; 584 l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4; 585 l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4; 586 l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4; 587 l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4; 588 l = final[11] ; to64(p, l, 2); p += 2; 589 *p = '\0'; 590 591 /* 592 * Don't leave anything around in vm they could use. 593 */ 594 memset(final, 0, sizeof(final)); 595 596 /* FIXME 597 */ 598 #define apr_cpystrn strncpy 599 apr_cpystrn(result, passwd, nbytes - 1); 600 } 601 602 603 /** 604 * 605 * 606 * @param password password-string from the auth-backend 607 * @param pw password-string from the client 608 */ 609 610 static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p, array *req, buffer *username, buffer *realm, buffer *password, const char *pw) { 611 UNUSED(srv); 612 UNUSED(req); 613 614 if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { 615 /* 616 * htdigest format 617 * 618 * user:realm:md5(user:realm:password) 619 */ 620 621 li_MD5_CTX Md5Ctx; 622 HASH HA1; 623 char a1[256]; 624 625 li_MD5_Init(&Md5Ctx); 626 li_MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1); 627 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 628 li_MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1); 629 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 630 li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); 631 li_MD5_Final(HA1, &Md5Ctx); 632 633 CvtHex(HA1, a1); 634 635 if (0 == strcmp(password->ptr, a1)) { 636 return 0; 637 } 638 } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) { 639 char sample[120]; 640 if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) { 641 /* 642 * The hash was created using $apr1$ custom algorithm. 643 */ 644 apr_md5_encode(pw, password->ptr, sample, sizeof(sample)); 645 return (strcmp(sample, password->ptr) == 0) ? 0 : 1; 646 } else { 647 #ifdef HAVE_CRYPT 648 char salt[32]; 649 char *crypted; 650 size_t salt_len = 0; 651 /* 652 * htpasswd format 653 * 654 * user:crypted password 655 */ 656 657 /* 658 * Algorithm Salt 659 * CRYPT_STD_DES 2-character (Default) 660 * CRYPT_EXT_DES 9-character 661 * CRYPT_MD5 12-character beginning with $1$ 662 * CRYPT_BLOWFISH 16-character beginning with $2$ 663 */ 664 665 if (password->used < 13 + 1) { 666 return -1; 667 } 668 669 if (password->used == 13 + 1) { 670 /* a simple DES password is 2 + 11 characters */ 671 salt_len = 2; 672 } else if (password->ptr[0] == '$' && password->ptr[2] == '$') { 673 char *dollar = NULL; 674 675 if (NULL == (dollar = strchr(password->ptr + 3, '$'))) { 676 return -1; 677 } 678 679 salt_len = dollar - password->ptr; 680 } 681 682 if (salt_len > sizeof(salt) - 1) { 683 return -1; 684 } 685 686 strncpy(salt, password->ptr, salt_len); 687 688 salt[salt_len] = '\0'; 689 690 crypted = crypt(pw, salt); 691 692 if (0 == strcmp(password->ptr, crypted)) { 693 return 0; 694 } 695 696 #endif 697 } 698 } else if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { 699 if (0 == strcmp(password->ptr, pw)) { 700 return 0; 701 } 702 } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) { 703 #ifdef USE_LDAP 704 LDAP *ldap; 705 LDAPMessage *lm, *first; 706 char *dn; 707 int ret; 708 char *attrs[] = { LDAP_NO_ATTRS, NULL }; 709 size_t i; 710 711 /* for now we stay synchronous */ 712 713 /* 714 * 1. connect anonymously (done in plugin init) 715 * 2. get DN for uid = username 716 * 3. auth against ldap server 717 * 4. (optional) check a field 718 * 5. disconnect 719 * 720 */ 721 722 /* check username 723 * 724 * we have to protect us againt username which modifies out filter in 725 * a unpleasant way 726 */ 727 728 for (i = 0; i < username->used - 1; i++) { 729 char c = username->ptr[i]; 730 731 if (!isalpha(c) && 732 !isdigit(c) && 733 (c != ' ') && 734 (c != '@') && 735 (c != '-') && 736 (c != '_') && 737 (c != '.') ) { 738 739 log_error_write(srv, __FILE__, __LINE__, "sbd", 740 "ldap: invalid character (- _.@a-zA-Z0-9 allowed) in username:", username, i); 741 742 return -1; 743 } 744 } 745 746 if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0') 747 return -1; 748 749 /* build filter */ 750 buffer_copy_string_buffer(p->ldap_filter, p->conf.ldap_filter_pre); 751 buffer_append_string_buffer(p->ldap_filter, username); 752 buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post); 753 754 755 /* 2. */ 756 if (p->anon_conf->ldap == NULL || 757 LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { 758 759 /* try again; the ldap library sometimes fails for the first call but reconnects */ 760 if (p->anon_conf->ldap == NULL || ret != LDAP_SERVER_DOWN || 761 LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { 762 763 if (auth_ldap_init(srv, p->anon_conf) != HANDLER_GO_ON) 764 return -1; 765 766 if (p->anon_conf->ldap == NULL || 767 LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) { 768 log_error_write(srv, __FILE__, __LINE__, "sssb", 769 "ldap:", ldap_err2string(ret), "filter:", p->ldap_filter); 770 return -1; 771 } 772 } 773 } 774 775 if (NULL == (first = ldap_first_entry(p->anon_conf->ldap, lm))) { 776 log_error_write(srv, __FILE__, __LINE__, "s", "ldap ..."); 777 778 ldap_msgfree(lm); 779 780 return -1; 781 } 782 783 if (NULL == (dn = ldap_get_dn(p->anon_conf->ldap, first))) { 784 log_error_write(srv, __FILE__, __LINE__, "s", "ldap ..."); 785 786 ldap_msgfree(lm); 787 788 return -1; 789 } 790 791 ldap_msgfree(lm); 792 793 794 /* 3. */ 795 if (NULL == (ldap = ldap_init(p->conf.auth_ldap_hostname->ptr, LDAP_PORT))) { 796 log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno)); 797 return -1; 798 } 799 800 ret = LDAP_VERSION3; 801 if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) { 802 log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); 803 804 ldap_unbind_s(ldap); 805 806 return -1; 807 } 808 809 if (p->conf.auth_ldap_starttls == 1) { 810 if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(ldap, NULL, NULL))) { 811 log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); 812 813 ldap_unbind_s(ldap); 814 815 return -1; 816 } 817 } 818 819 820 if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(ldap, dn, pw))) { 821 log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); 822 823 ldap_unbind_s(ldap); 824 825 return -1; 826 } 827 828 /* 5. */ 829 ldap_unbind_s(ldap); 830 831 /* everything worked, good, access granted */ 832 833 return 0; 834 #endif 835 } 836 return -1; 837 } 838 839 int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str) { 840 buffer *username, *password; 841 char *pw; 842 843 data_string *realm; 844 845 realm = (data_string *)array_get_element(req, "realm"); 846 847 username = buffer_init(); 848 849 if (!base64_decode(username, realm_str)) { 850 log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username); 851 852 buffer_free(username); 853 return 0; 854 } 855 856 /* r2 == user:password */ 857 if (NULL == (pw = strchr(username->ptr, ':'))) { 858 log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username); 859 860 buffer_free(username); 861 return 0; 862 } 863 864 *pw++ = '\0'; 865 866 username->used = pw - username->ptr; 867 868 password = buffer_init(); 869 /* copy password to r1 */ 870 if (http_auth_get_password(srv, p, username, realm->value, password)) { 871 buffer_free(username); 872 buffer_free(password); 873 874 if (AUTH_BACKEND_UNSET == p->conf.auth_backend) { 875 log_error_write(srv, __FILE__, __LINE__, "s", "auth.backend is not set"); 876 } else { 877 log_error_write(srv, __FILE__, __LINE__, "ss", "get_password failed, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 878 } 879 880 return 0; 881 } 882 883 /* password doesn't match */ 884 if (http_auth_basic_password_compare(srv, p, req, username, realm->value, password, pw)) { 885 log_error_write(srv, __FILE__, __LINE__, "sbsBss", "password doesn't match for", con->uri.path, "username:", username, ", IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 886 887 buffer_free(username); 888 buffer_free(password); 889 890 return 0; 891 } 892 893 /* value is our allow-rules */ 894 if (http_auth_match_rules(srv, p, url->ptr, username->ptr, NULL, NULL)) { 895 buffer_free(username); 896 buffer_free(password); 897 898 log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); 899 900 return 0; 901 } 902 903 /* remember the username */ 904 buffer_copy_string_buffer(p->auth_user, username); 905 906 buffer_free(username); 907 buffer_free(password); 908 909 return 1; 910 } 911 912 typedef struct { 913 const char *key; 914 int key_len; 915 char **ptr; 916 } digest_kv; 917 918 int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str) { 919 char a1[256]; 920 char a2[256]; 921 922 char *username = NULL; 923 char *realm = NULL; 924 char *nonce = NULL; 925 char *uri = NULL; 926 char *algorithm = NULL; 927 char *qop = NULL; 928 char *cnonce = NULL; 929 char *nc = NULL; 930 char *respons = NULL; 931 932 char *e, *c; 933 const char *m = NULL; 934 int i; 935 buffer *password, *b, *username_buf, *realm_buf; 936 937 li_MD5_CTX Md5Ctx; 938 HASH HA1; 939 HASH HA2; 940 HASH RespHash; 941 HASHHEX HA2Hex; 942 943 944 /* init pointers */ 945 #define S(x) \ 946 x, sizeof(x)-1, NULL 947 digest_kv dkv[10] = { 948 { S("username=") }, 949 { S("realm=") }, 950 { S("nonce=") }, 951 { S("uri=") }, 952 { S("algorithm=") }, 953 { S("qop=") }, 954 { S("cnonce=") }, 955 { S("nc=") }, 956 { S("response=") }, 957 958 { NULL, 0, NULL } 959 }; 960 #undef S 961 962 dkv[0].ptr = &username; 963 dkv[1].ptr = &realm; 964 dkv[2].ptr = &nonce; 965 dkv[3].ptr = &uri; 966 dkv[4].ptr = &algorithm; 967 dkv[5].ptr = &qop; 968 dkv[6].ptr = &cnonce; 969 dkv[7].ptr = &nc; 970 dkv[8].ptr = &respons; 971 972 UNUSED(req); 973 974 if (p->conf.auth_backend != AUTH_BACKEND_HTDIGEST && 975 p->conf.auth_backend != AUTH_BACKEND_PLAIN) { 976 log_error_write(srv, __FILE__, __LINE__, "s", 977 "digest: unsupported backend (only htdigest or plain)"); 978 979 return -1; 980 } 981 982 b = buffer_init_string(realm_str); 983 984 /* parse credentials from client */ 985 for (c = b->ptr; *c; c++) { 986 /* skip whitespaces */ 987 while (*c == ' ' || *c == '\t') c++; 988 if (!*c) break; 989 990 for (i = 0; dkv[i].key; i++) { 991 if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { 992 if ((c[dkv[i].key_len] == '"') && 993 (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { 994 /* value with "..." */ 995 *(dkv[i].ptr) = c + dkv[i].key_len + 1; 996 c = e; 997 998 *e = '\0'; 999 } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { 1000 /* value without "...", terminated by ',' */ 1001 *(dkv[i].ptr) = c + dkv[i].key_len; 1002 c = e; 1003 1004 *e = '\0'; 1005 } else { 1006 /* value without "...", terminated by EOL */ 1007 *(dkv[i].ptr) = c + dkv[i].key_len; 1008 c += strlen(c) - 1; 1009 } 1010 } 1011 } 1012 } 1013 1014 if (p->conf.auth_debug > 1) { 1015 log_error_write(srv, __FILE__, __LINE__, "ss", "username", username); 1016 log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm); 1017 log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce); 1018 log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri); 1019 log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm); 1020 log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop); 1021 log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce); 1022 log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc); 1023 log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons); 1024 } 1025 1026 /* check if everything is transmitted */ 1027 if (!username || 1028 !realm || 1029 !nonce || 1030 !uri || 1031 (qop && (!nc || !cnonce)) || 1032 !respons ) { 1033 /* missing field */ 1034 1035 log_error_write(srv, __FILE__, __LINE__, "s", 1036 "digest: missing field"); 1037 1038 buffer_free(b); 1039 return -1; 1040 } 1041 1042 /** 1043 * protect the md5-sess against missing cnonce and nonce 1044 */ 1045 if (algorithm && 1046 0 == strcasecmp(algorithm, "md5-sess") && 1047 (!nonce || !cnonce)) { 1048 log_error_write(srv, __FILE__, __LINE__, "s", 1049 "digest: (md5-sess: missing field"); 1050 1051 buffer_free(b); 1052 return -1; 1053 } 1054 1055 m = get_http_method_name(con->request.http_method); 1056 1057 /* password-string == HA1 */ 1058 password = buffer_init(); 1059 username_buf = buffer_init_string(username); 1060 realm_buf = buffer_init_string(realm); 1061 if (http_auth_get_password(srv, p, username_buf, realm_buf, password)) { 1062 buffer_free(password); 1063 buffer_free(b); 1064 buffer_free(username_buf); 1065 buffer_free(realm_buf); 1066 return 0; 1067 } 1068 1069 buffer_free(username_buf); 1070 buffer_free(realm_buf); 1071 1072 if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { 1073 /* generate password from plain-text */ 1074 li_MD5_Init(&Md5Ctx); 1075 li_MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username)); 1076 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1077 li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); 1078 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1079 li_MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1); 1080 li_MD5_Final(HA1, &Md5Ctx); 1081 } else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { 1082 /* HA1 */ 1083 /* transform the 32-byte-hex-md5 to a 16-byte-md5 */ 1084 for (i = 0; i < HASHLEN; i++) { 1085 HA1[i] = hex2int(password->ptr[i*2]) << 4; 1086 HA1[i] |= hex2int(password->ptr[i*2+1]); 1087 } 1088 } else { 1089 /* we already check that above */ 1090 SEGFAULT(); 1091 } 1092 1093 buffer_free(password); 1094 1095 if (algorithm && 1096 strcasecmp(algorithm, "md5-sess") == 0) { 1097 li_MD5_Init(&Md5Ctx); 1098 /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */ 1099 CvtHex(HA1, a1); 1100 li_MD5_Update(&Md5Ctx, (unsigned char *)a1, 32); 1101 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1102 li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 1103 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1104 li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 1105 li_MD5_Final(HA1, &Md5Ctx); 1106 } 1107 1108 CvtHex(HA1, a1); 1109 1110 /* calculate H(A2) */ 1111 li_MD5_Init(&Md5Ctx); 1112 li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); 1113 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1114 li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); 1115 if (qop && strcasecmp(qop, "auth-int") == 0) { 1116 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1117 li_MD5_Update(&Md5Ctx, (unsigned char *)"", HASHHEXLEN); 1118 } 1119 li_MD5_Final(HA2, &Md5Ctx); 1120 CvtHex(HA2, HA2Hex); 1121 1122 /* calculate response */ 1123 li_MD5_Init(&Md5Ctx); 1124 li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); 1125 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1126 li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 1127 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1128 if (qop && *qop) { 1129 li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); 1130 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1131 li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 1132 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1133 li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); 1134 li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); 1135 }; 1136 li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); 1137 li_MD5_Final(RespHash, &Md5Ctx); 1138 CvtHex(RespHash, a2); 1139 1140 if (0 != strcmp(a2, respons)) { 1141 /* digest not ok */ 1142 1143 if (p->conf.auth_debug) { 1144 log_error_write(srv, __FILE__, __LINE__, "sss", 1145 "digest: digest mismatch", a2, respons); 1146 } 1147 1148 log_error_write(srv, __FILE__, __LINE__, "ssss", 1149 "digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 1150 1151 buffer_free(b); 1152 return 0; 1153 } 1154 1155 /* value is our allow-rules */ 1156 if (http_auth_match_rules(srv, p, url->ptr, username, NULL, NULL)) { 1157 buffer_free(b); 1158 1159 log_error_write(srv, __FILE__, __LINE__, "s", 1160 "digest: rules did match"); 1161 1162 return 0; 1163 } 1164 1165 /* remember the username */ 1166 buffer_copy_string(p->auth_user, username); 1167 1168 buffer_free(b); 1169 1170 if (p->conf.auth_debug) { 1171 log_error_write(srv, __FILE__, __LINE__, "s", 1172 "digest: auth ok"); 1173 } 1174 return 1; 1175 } 1176 1177 1178 int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char out[33]) { 1179 HASH h; 1180 li_MD5_CTX Md5Ctx; 1181 char hh[32]; 1182 1183 UNUSED(p); 1184 1185 /* generate shared-secret */ 1186 li_MD5_Init(&Md5Ctx); 1187 li_MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1); 1188 li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); 1189 1190 /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ 1191 LI_ltostr(hh, srv->cur_ts); 1192 li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 1193 li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); 1194 LI_ltostr(hh, rand()); 1195 li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 1196 1197 li_MD5_Final(h, &Md5Ctx); 1198 1199 CvtHex(h, out); 1200 1201 return 0; 1202 } 1203