1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * Security options etc. 19 * 20 * Module derived from code originally written by Rob McCool 21 * 22 */ 23 24#include "apr_strings.h" 25#include "apr_network_io.h" 26#include "apr_md5.h" 27 28#define APR_WANT_STRFUNC 29#define APR_WANT_BYTEFUNC 30#include "apr_want.h" 31 32#include "ap_config.h" 33#include "httpd.h" 34#include "http_config.h" 35#include "http_core.h" 36#include "http_log.h" 37#include "http_request.h" 38#include "http_protocol.h" 39#include "ap_provider.h" 40#include "ap_expr.h" 41 42#include "mod_auth.h" 43 44#if APR_HAVE_NETINET_IN_H 45#include <netinet/in.h> 46#endif 47 48#undef AUTHZ_EXTRA_CONFIGS 49 50typedef struct provider_alias_rec { 51 char *provider_name; 52 char *provider_alias; 53 char *provider_args; 54 const void *provider_parsed_args; 55 ap_conf_vector_t *sec_auth; 56 const authz_provider *provider; 57} provider_alias_rec; 58 59typedef enum { 60 AUTHZ_LOGIC_AND, 61 AUTHZ_LOGIC_OR, 62 AUTHZ_LOGIC_OFF, 63 AUTHZ_LOGIC_UNSET 64} authz_logic_op; 65 66typedef struct authz_section_conf authz_section_conf; 67 68struct authz_section_conf { 69 const char *provider_name; 70 const char *provider_args; 71 const void *provider_parsed_args; 72 const authz_provider *provider; 73 apr_int64_t limited; 74 authz_logic_op op; 75 int negate; 76 /** true if this is not a real container but produced by AuthMerging; 77 * only used for logging */ 78 int is_merged; 79 authz_section_conf *first; 80 authz_section_conf *next; 81}; 82 83typedef struct authz_core_dir_conf authz_core_dir_conf; 84 85struct authz_core_dir_conf { 86 authz_section_conf *section; 87 authz_core_dir_conf *next; 88 authz_logic_op op; 89 signed char authz_forbidden_on_fail; 90}; 91 92#define UNSET -1 93 94typedef struct authz_core_srv_conf { 95 apr_hash_t *alias_rec; 96} authz_core_srv_conf; 97 98module AP_MODULE_DECLARE_DATA authz_core_module; 99 100static authz_core_dir_conf *authz_core_first_dir_conf; 101 102static void *create_authz_core_dir_config(apr_pool_t *p, char *dummy) 103{ 104 authz_core_dir_conf *conf = apr_pcalloc(p, sizeof(*conf)); 105 106 conf->op = AUTHZ_LOGIC_UNSET; 107 conf->authz_forbidden_on_fail = UNSET; 108 109 conf->next = authz_core_first_dir_conf; 110 authz_core_first_dir_conf = conf; 111 112 return (void *)conf; 113} 114 115static void *merge_authz_core_dir_config(apr_pool_t *p, 116 void *basev, void *newv) 117{ 118 authz_core_dir_conf *base = (authz_core_dir_conf *)basev; 119 authz_core_dir_conf *new = (authz_core_dir_conf *)newv; 120 authz_core_dir_conf *conf; 121 122 if (new->op == AUTHZ_LOGIC_UNSET && !new->section && base->section ) { 123 /* Only authz_forbidden_on_fail has been set in new. Don't treat 124 * it as a new auth config w.r.t. AuthMerging */ 125 conf = apr_pmemdup(p, base, sizeof(*base)); 126 } 127 else if (new->op == AUTHZ_LOGIC_OFF || new->op == AUTHZ_LOGIC_UNSET || 128 !(base->section || new->section)) { 129 conf = apr_pmemdup(p, new, sizeof(*new)); 130 } 131 else { 132 authz_section_conf *section; 133 134 if (base->section) { 135 if (new->section) { 136 section = apr_pcalloc(p, sizeof(*section)); 137 138 section->limited = 139 base->section->limited | new->section->limited; 140 141 section->op = new->op; 142 section->is_merged = 1; 143 144 section->first = apr_pmemdup(p, base->section, 145 sizeof(*base->section)); 146 section->first->next = apr_pmemdup(p, new->section, 147 sizeof(*new->section)); 148 } else { 149 section = apr_pmemdup(p, base->section, 150 sizeof(*base->section)); 151 } 152 } 153 else { 154 section = apr_pmemdup(p, new->section, sizeof(*new->section)); 155 } 156 157 conf = apr_pcalloc(p, sizeof(*conf)); 158 159 conf->section = section; 160 conf->op = new->op; 161 } 162 163 if (new->authz_forbidden_on_fail == UNSET) 164 conf->authz_forbidden_on_fail = base->authz_forbidden_on_fail; 165 else 166 conf->authz_forbidden_on_fail = new->authz_forbidden_on_fail; 167 168 return (void*)conf; 169} 170 171static void *create_authz_core_svr_config(apr_pool_t *p, server_rec *s) 172{ 173 authz_core_srv_conf *authcfg; 174 175 authcfg = apr_pcalloc(p, sizeof(*authcfg)); 176 authcfg->alias_rec = apr_hash_make(p); 177 178 return (void *)authcfg; 179} 180 181/* This is a fake authz provider that really merges various authz alias 182 * configurations and then invokes them. 183 */ 184static authz_status authz_alias_check_authorization(request_rec *r, 185 const char *require_args, 186 const void *parsed_require_args) 187{ 188 const char *provider_name; 189 authz_status ret = AUTHZ_DENIED; 190 191 /* Look up the provider alias in the alias list. 192 * Get the the dir_config and call ap_Merge_per_dir_configs() 193 * Call the real provider->check_authorization() function 194 * return the result of the above function call 195 */ 196 197 provider_name = apr_table_get(r->notes, AUTHZ_PROVIDER_NAME_NOTE); 198 199 if (provider_name) { 200 authz_core_srv_conf *authcfg; 201 provider_alias_rec *prvdraliasrec; 202 203 authcfg = ap_get_module_config(r->server->module_config, 204 &authz_core_module); 205 206 prvdraliasrec = apr_hash_get(authcfg->alias_rec, provider_name, 207 APR_HASH_KEY_STRING); 208 209 /* If we found the alias provider in the list, then merge the directory 210 configurations and call the real provider */ 211 if (prvdraliasrec) { 212 ap_conf_vector_t *orig_dir_config = r->per_dir_config; 213 214 r->per_dir_config = 215 ap_merge_per_dir_configs(r->pool, orig_dir_config, 216 prvdraliasrec->sec_auth); 217 218 ret = prvdraliasrec->provider-> 219 check_authorization(r, prvdraliasrec->provider_args, 220 prvdraliasrec->provider_parsed_args); 221 222 r->per_dir_config = orig_dir_config; 223 } 224 else { 225 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02305) 226 "no alias provider found for '%s' (BUG?)", 227 provider_name); 228 } 229 } 230 else { 231 ap_assert(provider_name != NULL); 232 } 233 234 return ret; 235} 236 237static const authz_provider authz_alias_provider = 238{ 239 &authz_alias_check_authorization, 240 NULL, 241}; 242 243static const char *authz_require_alias_section(cmd_parms *cmd, void *mconfig, 244 const char *args) 245{ 246 const char *endp = ap_strrchr_c(args, '>'); 247 char *provider_name; 248 char *provider_alias; 249 char *provider_args; 250 ap_conf_vector_t *new_authz_config; 251 int old_overrides = cmd->override; 252 const char *errmsg; 253 254 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); 255 if (err != NULL) { 256 return err; 257 } 258 259 if (endp == NULL) { 260 return apr_pstrcat(cmd->pool, cmd->cmd->name, 261 "> directive missing closing '>'", NULL); 262 } 263 264 args = apr_pstrndup(cmd->temp_pool, args, endp - args); 265 266 if (!args[0]) { 267 return apr_pstrcat(cmd->pool, cmd->cmd->name, 268 "> directive requires additional arguments", NULL); 269 } 270 271 /* Pull the real provider name and the alias name from the block header */ 272 provider_name = ap_getword_conf(cmd->pool, &args); 273 provider_alias = ap_getword_conf(cmd->pool, &args); 274 provider_args = ap_getword_conf(cmd->pool, &args); 275 276 if (!provider_name[0] || !provider_alias[0]) { 277 return apr_pstrcat(cmd->pool, cmd->cmd->name, 278 "> directive requires additional arguments", NULL); 279 } 280 281 new_authz_config = ap_create_per_dir_config(cmd->pool); 282 283 /* Walk the subsection configuration to get the per_dir config that we will 284 * merge just before the real provider is called. 285 */ 286 cmd->override = OR_AUTHCFG | ACCESS_CONF; 287 errmsg = ap_walk_config(cmd->directive->first_child, cmd, 288 new_authz_config); 289 cmd->override = old_overrides; 290 291 if (!errmsg) { 292 provider_alias_rec *prvdraliasrec; 293 authz_core_srv_conf *authcfg; 294 295 prvdraliasrec = apr_pcalloc(cmd->pool, sizeof(*prvdraliasrec)); 296 297 /* Save off the new directory config along with the original 298 * provider name and function pointer data 299 */ 300 prvdraliasrec->provider_name = provider_name; 301 prvdraliasrec->provider_alias = provider_alias; 302 prvdraliasrec->provider_args = provider_args; 303 prvdraliasrec->sec_auth = new_authz_config; 304 prvdraliasrec->provider = 305 ap_lookup_provider(AUTHZ_PROVIDER_GROUP, provider_name, 306 AUTHZ_PROVIDER_VERSION); 307 308 /* by the time the config file is used, the provider should be loaded 309 * and registered with us. 310 */ 311 if (!prvdraliasrec->provider) { 312 return apr_psprintf(cmd->pool, 313 "Unknown Authz provider: %s", 314 provider_name); 315 } 316 if (prvdraliasrec->provider->parse_require_line) { 317 err = prvdraliasrec->provider->parse_require_line(cmd, 318 provider_args, &prvdraliasrec->provider_parsed_args); 319 if (err) 320 return apr_psprintf(cmd->pool, 321 "Can't parse 'Require %s %s': %s", 322 provider_name, provider_args, err); 323 } 324 325 authcfg = ap_get_module_config(cmd->server->module_config, 326 &authz_core_module); 327 328 apr_hash_set(authcfg->alias_rec, provider_alias, 329 APR_HASH_KEY_STRING, prvdraliasrec); 330 331 /* Register the fake provider so that we get called first */ 332 ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP, 333 provider_alias, AUTHZ_PROVIDER_VERSION, 334 &authz_alias_provider, 335 AP_AUTH_INTERNAL_PER_CONF); 336 } 337 338 return errmsg; 339} 340 341static const char* format_authz_result(authz_status result) 342{ 343 return ((result == AUTHZ_DENIED) 344 ? "denied" 345 : ((result == AUTHZ_GRANTED) 346 ? "granted" 347 : ((result == AUTHZ_DENIED_NO_USER) 348 ? "denied (no authenticated user yet)" 349 : "neutral"))); 350} 351 352static const char* format_authz_command(apr_pool_t *p, 353 authz_section_conf *section) 354{ 355 return (section->provider 356 ? apr_pstrcat(p, "Require ", (section->negate ? "not " : ""), 357 section->provider_name, " ", 358 section->provider_args, NULL) 359 : apr_pstrcat(p, section->is_merged ? "AuthMerging " : "<Require", 360 ((section->op == AUTHZ_LOGIC_AND) 361 ? (section->negate ? "NotAll" : "All") 362 : (section->negate ? "None" : "Any")), 363 section->is_merged ? "" : ">", NULL)); 364} 365 366static authz_section_conf* create_default_section(apr_pool_t *p) 367{ 368 authz_section_conf *section = apr_pcalloc(p, sizeof(*section)); 369 370 section->op = AUTHZ_LOGIC_OR; 371 372 return section; 373} 374 375static const char *add_authz_provider(cmd_parms *cmd, void *config, 376 const char *args) 377{ 378 authz_core_dir_conf *conf = (authz_core_dir_conf*)config; 379 authz_section_conf *section = apr_pcalloc(cmd->pool, sizeof(*section)); 380 authz_section_conf *child; 381 382 section->provider_name = ap_getword_conf(cmd->pool, &args); 383 384 if (!strcasecmp(section->provider_name, "not")) { 385 section->provider_name = ap_getword_conf(cmd->pool, &args); 386 section->negate = 1; 387 } 388 389 section->provider_args = args; 390 391 /* lookup and cache the actual provider now */ 392 section->provider = ap_lookup_provider(AUTHZ_PROVIDER_GROUP, 393 section->provider_name, 394 AUTHZ_PROVIDER_VERSION); 395 396 /* by the time the config file is used, the provider should be loaded 397 * and registered with us. 398 */ 399 if (!section->provider) { 400 return apr_psprintf(cmd->pool, 401 "Unknown Authz provider: %s", 402 section->provider_name); 403 } 404 405 /* if the provider doesn't provide the appropriate function, reject it */ 406 if (!section->provider->check_authorization) { 407 return apr_psprintf(cmd->pool, 408 "The '%s' Authz provider is not supported by any " 409 "of the loaded authorization modules", 410 section->provider_name); 411 } 412 413 section->limited = cmd->limited; 414 415 if (section->provider->parse_require_line) { 416 const char *err; 417 apr_pool_userdata_setn(section->provider_name, 418 AUTHZ_PROVIDER_NAME_NOTE, 419 apr_pool_cleanup_null, 420 cmd->temp_pool); 421 err = section->provider->parse_require_line(cmd, args, 422 §ion->provider_parsed_args); 423 424 if (err) 425 return err; 426 } 427 428 if (!conf->section) { 429 conf->section = create_default_section(cmd->pool); 430 } 431 432 if (section->negate && conf->section->op == AUTHZ_LOGIC_OR) { 433 return apr_psprintf(cmd->pool, "negative %s directive has no effect " 434 "in %s directive", 435 cmd->cmd->name, 436 format_authz_command(cmd->pool, conf->section)); 437 } 438 439 conf->section->limited |= section->limited; 440 441 child = conf->section->first; 442 443 if (child) { 444 while (child->next) { 445 child = child->next; 446 } 447 448 child->next = section; 449 } 450 else { 451 conf->section->first = section; 452 } 453 454 return NULL; 455} 456 457static const char *add_authz_section(cmd_parms *cmd, void *mconfig, 458 const char *args) 459{ 460 authz_core_dir_conf *conf = mconfig; 461 const char *endp = ap_strrchr_c(args, '>'); 462 authz_section_conf *old_section = conf->section; 463 authz_section_conf *section; 464 int old_overrides = cmd->override; 465 apr_int64_t old_limited = cmd->limited; 466 const char *errmsg; 467 468 if (endp == NULL) { 469 return apr_pstrcat(cmd->pool, cmd->cmd->name, 470 "> directive missing closing '>'", NULL); 471 } 472 473 args = apr_pstrndup(cmd->temp_pool, args, endp - args); 474 475 if (args[0]) { 476 return apr_pstrcat(cmd->pool, cmd->cmd->name, 477 "> directive doesn't take additional arguments", 478 NULL); 479 } 480 481 section = apr_pcalloc(cmd->pool, sizeof(*section)); 482 483 if (!strcasecmp(cmd->cmd->name, "<RequireAll")) { 484 section->op = AUTHZ_LOGIC_AND; 485 } 486 else if (!strcasecmp(cmd->cmd->name, "<RequireAny")) { 487 section->op = AUTHZ_LOGIC_OR; 488 } 489 else if (!strcasecmp(cmd->cmd->name, "<RequireNotAll")) { 490 section->op = AUTHZ_LOGIC_AND; 491 section->negate = 1; 492 } 493 else { 494 section->op = AUTHZ_LOGIC_OR; 495 section->negate = 1; 496 } 497 498 conf->section = section; 499 500 /* trigger NOT_IN_LIMIT errors as if this were a <Limit> directive */ 501 cmd->limited &= ~(AP_METHOD_BIT << (METHODS - 1)); 502 503 cmd->override = OR_AUTHCFG; 504 errmsg = ap_walk_config(cmd->directive->first_child, cmd, cmd->context); 505 cmd->override = old_overrides; 506 507 cmd->limited = old_limited; 508 509 conf->section = old_section; 510 511 if (errmsg) { 512 return errmsg; 513 } 514 515 if (section->first) { 516 authz_section_conf *child; 517 518 if (!old_section) { 519 old_section = conf->section = create_default_section(cmd->pool); 520 } 521 522 if (section->negate && old_section->op == AUTHZ_LOGIC_OR) { 523 return apr_psprintf(cmd->pool, "%s directive has " 524 "no effect in %s directive", 525 format_authz_command(cmd->pool, section), 526 format_authz_command(cmd->pool, old_section)); 527 } 528 529 old_section->limited |= section->limited; 530 531 if (!section->negate && section->op == old_section->op) { 532 /* be associative */ 533 section = section->first; 534 } 535 536 child = old_section->first; 537 538 if (child) { 539 while (child->next) { 540 child = child->next; 541 } 542 543 child->next = section; 544 } 545 else { 546 old_section->first = section; 547 } 548 } 549 else { 550 return apr_pstrcat(cmd->pool, 551 format_authz_command(cmd->pool, section), 552 " directive contains no authorization directives", 553 NULL); 554 } 555 556 return NULL; 557} 558 559static const char *authz_merge_sections(cmd_parms *cmd, void *mconfig, 560 const char *arg) 561{ 562 authz_core_dir_conf *conf = mconfig; 563 564 if (!strcasecmp(arg, "Off")) { 565 conf->op = AUTHZ_LOGIC_OFF; 566 } 567 else if (!strcasecmp(arg, "And")) { 568 conf->op = AUTHZ_LOGIC_AND; 569 } 570 else if (!strcasecmp(arg, "Or")) { 571 conf->op = AUTHZ_LOGIC_OR; 572 } 573 else { 574 return apr_pstrcat(cmd->pool, cmd->cmd->name, " must be one of: " 575 "Off | And | Or", NULL); 576 } 577 578 return NULL; 579} 580 581static int authz_core_check_section(apr_pool_t *p, server_rec *s, 582 authz_section_conf *section, int is_conf) 583{ 584 authz_section_conf *prev = NULL; 585 authz_section_conf *child = section->first; 586 int ret = !OK; 587 588 while (child) { 589 if (child->first) { 590 if (authz_core_check_section(p, s, child, 0) != OK) { 591 return !OK; 592 } 593 594 if (child->negate && child->op != section->op) { 595 authz_section_conf *next = child->next; 596 597 /* avoid one level of recursion when De Morgan permits */ 598 child = child->first; 599 600 if (prev) { 601 prev->next = child; 602 } 603 else { 604 section->first = child; 605 } 606 607 do { 608 child->negate = !child->negate; 609 } while (child->next && (child = child->next)); 610 611 child->next = next; 612 } 613 } 614 615 prev = child; 616 child = child->next; 617 } 618 619 child = section->first; 620 621 while (child) { 622 if (!child->negate) { 623 ret = OK; 624 break; 625 } 626 627 child = child->next; 628 } 629 630 if (ret != OK) { 631 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, APR_SUCCESS, s, APLOGNO(01624) 632 "%s directive contains only negative authorization directives", 633 is_conf ? "<Directory>, <Location>, or similar" 634 : format_authz_command(p, section)); 635 } 636 637 return ret; 638} 639 640static int authz_core_pre_config(apr_pool_t *p, apr_pool_t *plog, 641 apr_pool_t *ptemp) 642{ 643 authz_core_first_dir_conf = NULL; 644 645 return OK; 646} 647 648static int authz_core_check_config(apr_pool_t *p, apr_pool_t *plog, 649 apr_pool_t *ptemp, server_rec *s) 650{ 651 authz_core_dir_conf *conf = authz_core_first_dir_conf; 652 653 while (conf) { 654 if (conf->section) { 655 if (authz_core_check_section(p, s, conf->section, 1) != OK) { 656 return !OK; 657 } 658 } 659 660 conf = conf->next; 661 } 662 663 return OK; 664} 665 666static const command_rec authz_cmds[] = 667{ 668 AP_INIT_RAW_ARGS("<AuthzProviderAlias", authz_require_alias_section, 669 NULL, RSRC_CONF, 670 "container for grouping an authorization provider's " 671 "directives under a provider alias"), 672 AP_INIT_RAW_ARGS("Require", add_authz_provider, NULL, OR_AUTHCFG, 673 "specifies authorization directives " 674 "which one must pass (or not) for a request to suceeed"), 675 AP_INIT_RAW_ARGS("<RequireAll", add_authz_section, NULL, OR_AUTHCFG, 676 "container for grouping authorization directives " 677 "of which none must fail and at least one must pass " 678 "for a request to succeed"), 679 AP_INIT_RAW_ARGS("<RequireAny", add_authz_section, NULL, OR_AUTHCFG, 680 "container for grouping authorization directives " 681 "of which one must pass " 682 "for a request to succeed"), 683#ifdef AUTHZ_EXTRA_CONFIGS 684 AP_INIT_RAW_ARGS("<RequireNotAll", add_authz_section, NULL, OR_AUTHCFG, 685 "container for grouping authorization directives " 686 "of which some must fail or none must pass " 687 "for a request to succeed"), 688#endif 689 AP_INIT_RAW_ARGS("<RequireNone", add_authz_section, NULL, OR_AUTHCFG, 690 "container for grouping authorization directives " 691 "of which none must pass " 692 "for a request to succeed"), 693 AP_INIT_TAKE1("AuthMerging", authz_merge_sections, NULL, OR_AUTHCFG, 694 "controls how a <Directory>, <Location>, or similar " 695 "directive's authorization directives are combined with " 696 "those of its predecessor"), 697 AP_INIT_FLAG("AuthzSendForbiddenOnFailure", ap_set_flag_slot_char, 698 (void *)APR_OFFSETOF(authz_core_dir_conf, authz_forbidden_on_fail), 699 OR_AUTHCFG, 700 "Controls if an authorization failure should result in a " 701 "'403 FORBIDDEN' response instead of the HTTP-conforming " 702 "'401 UNAUTHORIZED'"), 703 {NULL} 704}; 705 706static authz_status apply_authz_sections(request_rec *r, 707 authz_section_conf *section, 708 authz_logic_op parent_op) 709{ 710 authz_status auth_result; 711 712 /* check to make sure that the request method requires authorization */ 713 if (!(section->limited & (AP_METHOD_BIT << r->method_number))) { 714 auth_result = 715 (parent_op == AUTHZ_LOGIC_AND) ? AUTHZ_GRANTED : AUTHZ_NEUTRAL; 716 717 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01625) 718 "authorization result of %s: %s " 719 "(directive limited to other methods)", 720 format_authz_command(r->pool, section), 721 format_authz_result(auth_result)); 722 723 return auth_result; 724 } 725 726 if (section->provider) { 727 apr_table_setn(r->notes, AUTHZ_PROVIDER_NAME_NOTE, 728 section->provider_name); 729 730 auth_result = 731 section->provider->check_authorization(r, section->provider_args, 732 section->provider_parsed_args); 733 734 apr_table_unset(r->notes, AUTHZ_PROVIDER_NAME_NOTE); 735 } 736 else { 737 authz_section_conf *child = section->first; 738 739 auth_result = AUTHZ_NEUTRAL; 740 741 while (child) { 742 authz_status child_result; 743 744 child_result = apply_authz_sections(r, child, section->op); 745 746 if (child_result == AUTHZ_GENERAL_ERROR) { 747 return AUTHZ_GENERAL_ERROR; 748 } 749 750 if (child_result != AUTHZ_NEUTRAL) { 751 /* 752 * Handling of AUTHZ_DENIED/AUTHZ_DENIED_NO_USER: Return 753 * AUTHZ_DENIED_NO_USER if providing a user may change the 754 * result, AUTHZ_DENIED otherwise. 755 */ 756 if (section->op == AUTHZ_LOGIC_AND) { 757 if (child_result == AUTHZ_DENIED) { 758 auth_result = child_result; 759 break; 760 } 761 if ((child_result == AUTHZ_DENIED_NO_USER 762 && auth_result != AUTHZ_DENIED) 763 || (auth_result == AUTHZ_NEUTRAL)) { 764 auth_result = child_result; 765 } 766 } 767 else { 768 /* AUTHZ_LOGIC_OR */ 769 if (child_result == AUTHZ_GRANTED) { 770 auth_result = child_result; 771 break; 772 } 773 if ((child_result == AUTHZ_DENIED_NO_USER 774 && auth_result == AUTHZ_DENIED) 775 || (auth_result == AUTHZ_NEUTRAL)) { 776 auth_result = child_result; 777 } 778 } 779 } 780 781 child = child->next; 782 } 783 } 784 785 if (section->negate) { 786 if (auth_result == AUTHZ_GRANTED) { 787 auth_result = AUTHZ_DENIED; 788 } 789 else if (auth_result == AUTHZ_DENIED || 790 auth_result == AUTHZ_DENIED_NO_USER) { 791 /* For negated directives, if the original result was denied 792 * then the new result is neutral since we can not grant 793 * access simply because authorization was not rejected. 794 */ 795 auth_result = AUTHZ_NEUTRAL; 796 } 797 } 798 799 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01626) 800 "authorization result of %s: %s", 801 format_authz_command(r->pool, section), 802 format_authz_result(auth_result)); 803 804 return auth_result; 805} 806 807static int authorize_user_core(request_rec *r, int after_authn) 808{ 809 authz_core_dir_conf *conf; 810 authz_status auth_result; 811 812 conf = ap_get_module_config(r->per_dir_config, &authz_core_module); 813 814 if (!conf->section) { 815 if (ap_auth_type(r)) { 816 /* there's an AuthType configured, but no authorization 817 * directives applied to support it 818 */ 819 820 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01627) 821 "AuthType configured with no corresponding " 822 "authorization directives"); 823 824 return HTTP_INTERNAL_SERVER_ERROR; 825 } 826 827 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01628) 828 "authorization result: granted (no directives)"); 829 830 return OK; 831 } 832 833 auth_result = apply_authz_sections(r, conf->section, AUTHZ_LOGIC_AND); 834 835 if (auth_result == AUTHZ_GRANTED) { 836 return OK; 837 } 838 else if (auth_result == AUTHZ_DENIED_NO_USER) { 839 if (after_authn) { 840 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01629) 841 "authorization failure (no authenticated user): %s", 842 r->uri); 843 /* 844 * If we're returning 401 to an authenticated user, tell them to 845 * try again. If unauthenticated, note_auth_failure has already 846 * been called during auth. 847 */ 848 if (r->user) 849 ap_note_auth_failure(r); 850 851 return HTTP_UNAUTHORIZED; 852 } 853 else { 854 /* 855 * We need a user before we can decide what to do. 856 * Get out of the way and proceed with authentication. 857 */ 858 return DECLINED; 859 } 860 } 861 else if (auth_result == AUTHZ_DENIED || auth_result == AUTHZ_NEUTRAL) { 862 if (!after_authn || ap_auth_type(r) == NULL) { 863 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01630) 864 "client denied by server configuration: %s%s", 865 r->filename ? "" : "uri ", 866 r->filename ? r->filename : r->uri); 867 868 return HTTP_FORBIDDEN; 869 } 870 else { 871 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01631) 872 "user %s: authorization failure for \"%s\": ", 873 r->user, r->uri); 874 875 if (conf->authz_forbidden_on_fail > 0) { 876 return HTTP_FORBIDDEN; 877 } 878 else { 879 /* 880 * If we're returning 401 to an authenticated user, tell them to 881 * try again. If unauthenticated, note_auth_failure has already 882 * been called during auth. 883 */ 884 if (r->user) 885 ap_note_auth_failure(r); 886 return HTTP_UNAUTHORIZED; 887 } 888 } 889 } 890 else { 891 /* We'll assume that the module has already said what its 892 * error was in the logs. 893 */ 894 return HTTP_INTERNAL_SERVER_ERROR; 895 } 896} 897 898static int authorize_userless(request_rec *r) 899{ 900 return authorize_user_core(r, 0); 901} 902 903static int authorize_user(request_rec *r) 904{ 905 return authorize_user_core(r, 1); 906} 907 908static int authz_some_auth_required(request_rec *r) 909{ 910 authz_core_dir_conf *conf; 911 912 conf = ap_get_module_config(r->per_dir_config, &authz_core_module); 913 914 if (conf->section 915 && (conf->section->limited & (AP_METHOD_BIT << r->method_number))) { 916 return 1; 917 } 918 919 return 0; 920} 921 922/* 923 * env authz provider 924 */ 925 926static authz_status env_check_authorization(request_rec *r, 927 const char *require_line, 928 const void *parsed_require_line) 929{ 930 const char *t, *w; 931 932 /* The 'env' provider will allow the configuration to specify a list of 933 env variables to check rather than a single variable. This is different 934 from the previous host based syntax. */ 935 t = require_line; 936 while ((w = ap_getword_conf(r->pool, &t)) && w[0]) { 937 if (apr_table_get(r->subprocess_env, w)) { 938 return AUTHZ_GRANTED; 939 } 940 } 941 942 return AUTHZ_DENIED; 943} 944 945static const authz_provider authz_env_provider = 946{ 947 &env_check_authorization, 948 NULL, 949}; 950 951 952/* 953 * all authz provider 954 */ 955 956static authz_status all_check_authorization(request_rec *r, 957 const char *require_line, 958 const void *parsed_require_line) 959{ 960 if (parsed_require_line) { 961 return AUTHZ_GRANTED; 962 } 963 return AUTHZ_DENIED; 964} 965 966static const char *all_parse_config(cmd_parms *cmd, const char *require_line, 967 const void **parsed_require_line) 968{ 969 /* 970 * If the argument to the 'all' provider is 'granted' then just let 971 * everybody in. This would be equivalent to the previous syntax of 972 * 'allow from all'. If the argument is 'denied' we reject everbody, 973 * which is equivalent to 'deny from all'. 974 */ 975 if (strcasecmp(require_line, "granted") == 0) { 976 *parsed_require_line = (void *)1; 977 return NULL; 978 } 979 else if (strcasecmp(require_line, "denied") == 0) { 980 /* *parsed_require_line is already NULL */ 981 return NULL; 982 } 983 else { 984 return "Argument for 'Require all' must be 'granted' or 'denied'"; 985 } 986} 987 988static const authz_provider authz_all_provider = 989{ 990 &all_check_authorization, 991 &all_parse_config, 992}; 993 994 995/* 996 * method authz provider 997 */ 998 999static authz_status method_check_authorization(request_rec *r, 1000 const char *require_line, 1001 const void *parsed_require_line) 1002{ 1003 const apr_int64_t *allowed = parsed_require_line; 1004 if (*allowed & (AP_METHOD_BIT << r->method_number)) 1005 return AUTHZ_GRANTED; 1006 else 1007 return AUTHZ_DENIED; 1008} 1009 1010static const char *method_parse_config(cmd_parms *cmd, const char *require_line, 1011 const void **parsed_require_line) 1012{ 1013 const char *w, *t; 1014 apr_int64_t *allowed = apr_pcalloc(cmd->pool, sizeof(apr_int64_t)); 1015 1016 t = require_line; 1017 1018 while ((w = ap_getword_conf(cmd->temp_pool, &t)) && w[0]) { 1019 int m = ap_method_number_of(w); 1020 if (m == M_INVALID) { 1021 return apr_pstrcat(cmd->pool, "Invalid Method '", w, "'", NULL); 1022 } 1023 1024 *allowed |= (AP_METHOD_BIT << m); 1025 } 1026 1027 *parsed_require_line = allowed; 1028 return NULL; 1029} 1030 1031static const authz_provider authz_method_provider = 1032{ 1033 &method_check_authorization, 1034 &method_parse_config, 1035}; 1036 1037/* 1038 * expr authz provider 1039 */ 1040 1041#define REQUIRE_EXPR_NOTE "Require_expr_info" 1042struct require_expr_info { 1043 ap_expr_info_t *expr; 1044 int want_user; 1045}; 1046 1047static int expr_lookup_fn(ap_expr_lookup_parms *parms) 1048{ 1049 if (parms->type == AP_EXPR_FUNC_VAR 1050 && strcasecmp(parms->name, "REMOTE_USER") == 0) { 1051 struct require_expr_info *info; 1052 apr_pool_userdata_get((void**)&info, REQUIRE_EXPR_NOTE, parms->ptemp); 1053 AP_DEBUG_ASSERT(info != NULL); 1054 info->want_user = 1; 1055 } 1056 return ap_expr_lookup_default(parms); 1057} 1058 1059static const char *expr_parse_config(cmd_parms *cmd, const char *require_line, 1060 const void **parsed_require_line) 1061{ 1062 const char *expr_err = NULL; 1063 struct require_expr_info *info = apr_pcalloc(cmd->pool, sizeof(*info)); 1064 1065 apr_pool_userdata_setn(info, REQUIRE_EXPR_NOTE, apr_pool_cleanup_null, 1066 cmd->temp_pool); 1067 info->expr = ap_expr_parse_cmd(cmd, require_line, 0, &expr_err, 1068 expr_lookup_fn); 1069 1070 if (expr_err) 1071 return apr_pstrcat(cmd->temp_pool, 1072 "Cannot parse expression in require line: ", 1073 expr_err, NULL); 1074 1075 *parsed_require_line = info; 1076 1077 return NULL; 1078} 1079 1080static authz_status expr_check_authorization(request_rec *r, 1081 const char *require_line, 1082 const void *parsed_require_line) 1083{ 1084 const char *err = NULL; 1085 const struct require_expr_info *info = parsed_require_line; 1086 int rc = ap_expr_exec(r, info->expr, &err); 1087 1088 if (rc < 0) { 1089 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02320) 1090 "Error evaluating expression in 'Require expr': %s", 1091 err); 1092 return AUTHZ_GENERAL_ERROR; 1093 } 1094 else if (rc == 0) { 1095 if (info->want_user) 1096 return AUTHZ_DENIED_NO_USER; 1097 else 1098 return AUTHZ_DENIED; 1099 } 1100 else { 1101 return AUTHZ_GRANTED; 1102 } 1103} 1104 1105static const authz_provider authz_expr_provider = 1106{ 1107 &expr_check_authorization, 1108 &expr_parse_config, 1109}; 1110 1111 1112static void register_hooks(apr_pool_t *p) 1113{ 1114 APR_REGISTER_OPTIONAL_FN(authz_some_auth_required); 1115 1116 ap_hook_pre_config(authz_core_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 1117 ap_hook_check_config(authz_core_check_config, NULL, NULL, APR_HOOK_MIDDLE); 1118 ap_hook_check_authz(authorize_user, NULL, NULL, APR_HOOK_LAST, 1119 AP_AUTH_INTERNAL_PER_CONF); 1120 ap_hook_check_access_ex(authorize_userless, NULL, NULL, APR_HOOK_LAST, 1121 AP_AUTH_INTERNAL_PER_CONF); 1122 1123 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "env", 1124 AUTHZ_PROVIDER_VERSION, 1125 &authz_env_provider, AP_AUTH_INTERNAL_PER_CONF); 1126 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "all", 1127 AUTHZ_PROVIDER_VERSION, 1128 &authz_all_provider, AP_AUTH_INTERNAL_PER_CONF); 1129 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "method", 1130 AUTHZ_PROVIDER_VERSION, 1131 &authz_method_provider, AP_AUTH_INTERNAL_PER_CONF); 1132 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "expr", 1133 AUTHZ_PROVIDER_VERSION, 1134 &authz_expr_provider, AP_AUTH_INTERNAL_PER_CONF); 1135} 1136 1137AP_DECLARE_MODULE(authz_core) = 1138{ 1139 STANDARD20_MODULE_STUFF, 1140 create_authz_core_dir_config, /* dir config creater */ 1141 merge_authz_core_dir_config, /* dir merger */ 1142 create_authz_core_svr_config, /* server config */ 1143 NULL, /* merge server config */ 1144 authz_cmds, 1145 register_hooks /* register hooks */ 1146}; 1147 1148