static authz_status dbdgroup_check_authorization(request_rec *r, const char *require_args, const void *parsed_require_args) { int i, rv; const char *w; apr_array_header_t *groups = NULL; const char *t; authz_dbd_cfg *cfg = ap_get_module_config(r->per_dir_config, &authz_dbd_module); if (!r->user) { return AUTHZ_DENIED_NO_USER; } if (groups == NULL) { groups = apr_array_make(r->pool, 4, sizeof(const char*)); rv = authz_dbd_group_query(r, cfg, groups); if (rv != OK) { return AUTHZ_GENERAL_ERROR; } } t = require_args; while (t[0]) { w = ap_getword_white(r->pool, &t); for (i=0; i < groups->nelts; ++i) { if (!strcmp(w, ((const char**)groups->elts)[i])) { return AUTHZ_GRANTED; } } } return AUTHZ_DENIED; }
/** * This hook is used to check to see if the resource being requested * is available for the authenticated user (r->user and r->ap_auth_type). * It runs after the access_checker and check_user_id hooks. Note that * it will *only* be called if Apache determines that access control has * been applied to this resource (through a 'Require' directive). * * @param r the current request * @return OK, DECLINED, or HTTP_... */ static int auth_checker(request_rec *r) { authnz_crowd_dir_config *config = get_config(r); if (config == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } if (r->user == NULL) { ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, "Authorisation requested, but no user provided."); return HTTP_INTERNAL_SERVER_ERROR; } /* Iterate over requirements */ const apr_array_header_t *requires = ap_requires(r); apr_array_header_t *user_groups = NULL; int x; for (x = 0; x < requires->nelts; x++) { require_line require = APR_ARRAY_IDX(requires, x, require_line); /* Ignore this requirement if it does not apply to the HTTP method used in the request. */ if (!(require.method_mask & (AP_METHOD_BIT << r->method_number))) { continue; } const char *next_word = require.requirement; /* Only process group requirements */ if (strcasecmp(ap_getword_white(r->pool, &next_word), "group") == 0) { /* Fetch groups only if actually needed. */ if (user_groups == NULL) { user_groups = crowd_user_groups(r->user, r, config->crowd_config); if (user_groups == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } } /* Iterate over the groups mentioned in the requirement. */ while (*next_word != '\0') { const char *required_group = ap_getword_conf(r->pool, &next_word); /* Iterate over the user's groups. */ int y; for (y = 0; y < user_groups->nelts; y++) { const char *user_group = APR_ARRAY_IDX(user_groups, y, const char *); if (strcasecmp(user_group, required_group) == 0) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Granted authorisation to '%s' on the basis of membership of '%s'.", r->user, user_group); return OK; } } } } } ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Denied authorisation to '%s'.", r->user); return config->authoritative ? HTTP_UNAUTHORIZED : DECLINED; }
static int check_user_access(request_rec * r) { int m = r->method_number; const apr_array_header_t * reqs_arr = ap_requires(r); if (! reqs_arr) { return DECLINED; } require_line * reqs = (require_line *)reqs_arr->elts; int x; for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) { continue; } const char * t = reqs[x].requirement; const char * w = ap_getword_white(r->pool, &t); if (!strcasecmp(w, "pam-account")) { const char * pam_service = ap_getword_conf(r->pool, &t); if (pam_service && strlen(pam_service)) { authn_status ret = pam_authenticate_with_login_password(r, pam_service, r->user, NULL, _PAM_STEP_ACCOUNT); if (ret == AUTH_GRANTED) { return OK; } } } } return DECLINED; }
/************************************************** * Authorization phase (Apache 2.4) * * Handles Require persona-idp directives. * * When this is first called, the authentication context hasn't been setup * yet. Return AUTHZ_DENIED_NO_USER to force it to run, then this will be * called again, with the context setup. **************************************************/ static authz_status persona_idp_check_authorization(request_rec *r, const char *require_args, const void *parsed_require_args) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "Require persona-idp"); if (!r->user) // this triggers running authn hook, which we need return AUTHZ_DENIED_NO_USER; char *reqIdp = ap_getword_white(r->pool, &require_args); const char *issuer = apr_table_get(r->notes, PERSONA_ISSUER_NOTE); return issuer && !strcmp(issuer, reqIdp) ? AUTHZ_GRANTED : AUTHZ_DENIED; }
static bool parse_auth_header(apr_pool_t *pool, const char **auth_header, gss_buffer_t value) { char *auth_header_value; auth_header_value = ap_getword_white(pool, auth_header); if (!auth_header_value) return false; value->length = apr_base64_decode_len(auth_header_value) + 1; value->value = apr_pcalloc(pool, value->length); if (!value->value) return false; value->length = apr_base64_decode(value->value, auth_header_value); return true; }
/************************************************** * Authorization phase (Apache 2.2) * * Requires authentication phase to run first. * * Handles Require persona-idp directives. **************************************************/ static int Auth_persona_check_auth(request_rec *r) { const apr_array_header_t *reqs_arr=NULL; require_line *reqs=NULL; register int x; const char *szRequireLine; char *szRequire_cmd; if (!persona_authn_active(r)) { return DECLINED; } ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "Auth_persona_check_auth"); /* get require line */ reqs_arr = ap_requires(r); reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL; /* decline if no require line found */ if (!reqs_arr) return DECLINED; /* walk through the array to check each require command */ for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (AP_METHOD_BIT << r->method_number))) continue; /* get require line */ szRequireLine = reqs[x].requirement; ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO, 0,r,ERRTAG "Require Line is '%s'", szRequireLine); /* get the first word in require line */ szRequire_cmd = ap_getword_white(r->pool, &szRequireLine); ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO, 0,r,ERRTAG "Require Cmd is '%s'", szRequire_cmd); // persona-idp: check host part of user name if (!strcmp("persona-idp", szRequire_cmd)) { char *reqIdp = ap_getword_conf(r->pool, &szRequireLine); const char *issuer = apr_table_get(r->notes, PERSONA_ISSUER_NOTE); if (!issuer || strcmp(issuer, reqIdp)) { return HTTP_FORBIDDEN; } ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, r, ERRTAG "user '%s' is authorized", r->user); return OK; } } return DECLINED; }
static authz_status dbdgroup_check_authorization(request_rec *r, const char *require_args, const void *parsed_require_args) { int i, rv; const char *w; apr_array_header_t *groups = NULL; const char *err = NULL; const ap_expr_info_t *expr = parsed_require_args; const char *require; const char *t; authz_dbd_cfg *cfg = ap_get_module_config(r->per_dir_config, &authz_dbd_module); if (!r->user) { return AUTHZ_DENIED_NO_USER; } if (groups == NULL) { groups = apr_array_make(r->pool, 4, sizeof(const char*)); rv = authz_dbd_group_query(r, cfg, groups); if (rv != OK) { return AUTHZ_GENERAL_ERROR; } } require = ap_expr_str_exec(r, expr, &err); if (err) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02590) "authz_dbd authorize: require dbd-group: Can't " "evaluate require expression: %s", err); return AUTHZ_DENIED; } t = require; while (t[0]) { w = ap_getword_white(r->pool, &t); for (i=0; i < groups->nelts; ++i) { if (!strcmp(w, ((const char**)groups->elts)[i])) { return AUTHZ_GRANTED; } } } return AUTHZ_DENIED; }
static const char *parse_cmd(apr_pool_t *p, const char **args, ef_filter_t *filter) { if (**args == '"') { const char *start = *args + 1; char *parms; int escaping = 0; apr_status_t rv; ++*args; /* move past leading " */ /* find true end of args string (accounting for escaped quotes) */ while (**args && (**args != '"' || (**args == '"' && escaping))) { if (escaping) { escaping = 0; } else if (**args == '\\') { escaping = 1; } ++*args; } if (**args != '"') { return "Expected cmd= delimiter"; } /* copy *just* the arg string for parsing, */ parms = apr_pstrndup(p, start, *args - start); ++*args; /* move past trailing " */ /* parse and tokenize the args. */ rv = apr_tokenize_to_argv(parms, &(filter->args), p); if (rv != APR_SUCCESS) { return "cmd= parse error"; } } else { /* simple path */ /* Allocate space for two argv pointers and parse the args. */ filter->args = (char **)apr_palloc(p, 2 * sizeof(char *)); filter->args[0] = ap_getword_white(p, args); filter->args[1] = NULL; /* end of args */ } if (!filter->args[0]) { return "Invalid cmd= parameter"; } filter->command = filter->args[0]; return NULL; }
static int mod_authopenid_check_user_access(request_rec *r) { modauthopenid_config *s_cfg; s_cfg = (modauthopenid_config *) ap_get_module_config(r->per_dir_config, &authopenid_module); char *user = r->user; int m = r->method_number; int required_user = 0; register int x; const char *t, *w; const apr_array_header_t *reqs_arr = ap_requires(r); require_line *reqs; if (!reqs_arr) return DECLINED; reqs = (require_line *)reqs_arr->elts; for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) continue; t = reqs[x].requirement; w = ap_getword_white(r->pool, &t); if (!strcasecmp(w, "valid-user")) return OK; if (!strcasecmp(w, "user")) { required_user = 1; while (t[0]) { w = ap_getword_conf(r->pool, &t); if (!strcmp(user, w)) return OK; } } } if (!required_user) return DECLINED; APERR(r, "Access to %s failed: user '%s' invalid", r->uri, user); ap_note_auth_failure(r); return HTTP_UNAUTHORIZED; }
static const char *dbd_parse_config_query_parameters(cmd_parms *cmd, const char *require_line, const void **parsed_require_line) { const char *expr_err = NULL; ap_expr_info_t *expr; apr_array_header_t *expr_array = NULL; const char *t; char *w; /* parse each element of the require line and store it in an individual table entry. We will evaluate them in that order when passing the parameters to the db query */ t = require_line; while ((w = ap_getword_white(cmd->pool, &t)) && w[0]) { expr = ap_expr_parse_cmd(cmd, w, AP_EXPR_FLAG_STRING_RESULT, &expr_err, NULL); if (expr_err) return apr_pstrcat(cmd->temp_pool, "Cannot parse expression in require line: ", expr_err, NULL); if (expr_array == NULL) { expr_array = apr_array_make(cmd->pool, 1, sizeof(const ap_expr_info_t *)); } *(const ap_expr_info_t **)apr_array_push(expr_array) = expr; } *parsed_require_line = expr_array; return NULL; }
apr_status_t proc_spawn_process(const char *cmdline, fcgid_proc_info *procinfo, fcgid_procnode *procnode) { HANDLE *finish_event, listen_handle; SECURITY_ATTRIBUTES SecurityAttributes; fcgid_server_conf *sconf; apr_procattr_t *proc_attr; apr_status_t rv; apr_file_t *file; const char * const *proc_environ; char sock_path[FCGID_PATH_MAX]; int argc; char const * wargv[APACHE_ARG_MAX + 1], *word; /* For wrapper */ const char *tmp; /* Build wrapper args */ argc = 0; tmp = cmdline; while (1) { word = ap_getword_white(procnode->proc_pool, &tmp); if (word == NULL || *word == '\0') break; if (argc >= APACHE_ARG_MAX) break; wargv[argc++] = word; } wargv[argc] = NULL; memset(&SecurityAttributes, 0, sizeof(SecurityAttributes)); /* Prepare finish event */ finish_event = apr_palloc(procnode->proc_pool, sizeof(HANDLE)); *finish_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (*finish_event == NULL || !SetHandleInformation(*finish_event, HANDLE_FLAG_INHERIT, TRUE)) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: can't create mutex for subprocess"); return APR_ENOLOCK; } apr_pool_cleanup_register(procnode->proc_pool, finish_event, close_finish_event, apr_pool_cleanup_null); /* For proc_kill_gracefully() */ apr_pool_userdata_set(finish_event, FINISH_EVENT_DATA_NAME, NULL, procnode->proc_pool); /* Pass the finish event id to subprocess */ apr_table_setn(procinfo->proc_environ, SHUTDOWN_EVENT_NAME, apr_ltoa(procnode->proc_pool, (long) *finish_event)); /* Prepare the listen namedpipe file name (no check for truncation) */ apr_snprintf(sock_path, sizeof sock_path, "\\\\.\\pipe\\fcgidpipe-%lu.%d", GetCurrentProcessId(), g_process_counter++); /* Prepare the listen namedpipe handle */ SecurityAttributes.bInheritHandle = TRUE; SecurityAttributes.nLength = sizeof(SecurityAttributes); SecurityAttributes.lpSecurityDescriptor = NULL; listen_handle = CreateNamedPipe(sock_path, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 8192, 8192, 0, &SecurityAttributes); if (listen_handle == INVALID_HANDLE_VALUE) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: can't create namedpipe for subprocess"); return APR_ENOSOCKET; } apr_cpystrn(procnode->socket_path, sock_path, sizeof(procnode->socket_path)); apr_cpystrn(procnode->executable_path, wargv[0], sizeof(procnode->executable_path)); /* Build environment variables */ proc_environ = (const char * const *) ap_create_environment(procnode->proc_pool, procinfo->proc_environ); if (!proc_environ) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: can't build environment variables"); return APR_ENOMEM; } /* Create process now */ if ((rv = apr_procattr_create(&proc_attr, procnode->proc_pool)) != APR_SUCCESS || (rv = apr_procattr_dir_set(proc_attr, ap_make_dirstr_parent(procnode->proc_pool, wargv[0]))) != APR_SUCCESS || (rv = apr_procattr_cmdtype_set(proc_attr, APR_PROGRAM)) != APR_SUCCESS || (rv = apr_procattr_detach_set(proc_attr, 1)) != APR_SUCCESS || (rv = apr_procattr_io_set(proc_attr, APR_NO_PIPE, APR_NO_FILE, APR_NO_FILE)) != APR_SUCCESS || (rv = apr_os_file_put(&file, &listen_handle, 0, procnode->proc_pool)) != APR_SUCCESS || (rv = apr_procattr_child_in_set(proc_attr, file, NULL)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_WARNING, rv, procinfo->main_server, "mod_fcgid: can't create FastCGI process attribute"); CloseHandle(listen_handle); return APR_ENOPROC; } /* fork and exec now */ rv = apr_proc_create(&(procnode->proc_id), wargv[0], wargv, proc_environ, proc_attr, procnode->proc_pool); /* OK, I created the process, now put it back to idle list */ CloseHandle(listen_handle); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server, "mod_fcgid: can't run %s", wargv[0]); return rv; } /* FcgidWin32PreventOrphans feature */ sconf = ap_get_module_config(procinfo->main_server->module_config, &fcgid_module); if (sconf->hJobObjectForAutoCleanup != NULL) { /* Associate cgi process to current process */ if (AssignProcessToJobObject(sconf->hJobObjectForAutoCleanup, procnode->proc_id.hproc) == 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: unable to assign child process to " "job object"); } } return APR_SUCCESS; }
static int mag_auth(request_rec *req) { const char *type; struct mag_config *cfg; const char *auth_header; char *auth_header_type; char *auth_header_value; int ret = HTTP_UNAUTHORIZED; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t *pctx; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; gss_buffer_desc name = GSS_C_EMPTY_BUFFER; gss_name_t client = GSS_C_NO_NAME; gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; uint32_t flags; uint32_t vtime; uint32_t maj, min; char *reply; size_t replen; char *clientname; gss_OID mech_type = GSS_C_NO_OID; gss_buffer_desc lname = GSS_C_EMPTY_BUFFER; struct mag_conn *mc = NULL; type = ap_auth_type(req); if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) { return DECLINED; } /* ignore auth for subrequests */ if (!ap_is_initial_req(req)) { return OK; } cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module); if (cfg->ssl_only) { if (!mag_conn_is_https(req->connection)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "Not a TLS connection, refusing to authenticate!"); goto done; } } if (cfg->gss_conn_ctx) { mc = (struct mag_conn *)ap_get_module_config( req->connection->conn_config, &auth_gssapi_module); if (!mc) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req, "Failed to retrieve connection context!"); goto done; } } /* if available, session always supersedes connection bound data */ mag_check_session(req, cfg, &mc); if (mc) { /* register the context in the memory pool, so it can be freed * when the connection/request is terminated */ apr_pool_userdata_set(mc, "mag_conn_ptr", mag_conn_destroy, mc->parent); if (mc->established) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req, "Already established context found!"); apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name); req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate"); req->user = apr_pstrdup(req->pool, mc->user_name); ret = OK; goto done; } pctx = &mc->ctx; } else { pctx = &ctx; } auth_header = apr_table_get(req->headers_in, "Authorization"); if (!auth_header) goto done; auth_header_type = ap_getword_white(req->pool, &auth_header); if (!auth_header_type) goto done; if (strcasecmp(auth_header_type, "Negotiate") != 0) goto done; auth_header_value = ap_getword_white(req->pool, &auth_header); if (!auth_header_value) goto done; input.length = apr_base64_decode_len(auth_header_value) + 1; input.value = apr_pcalloc(req->pool, input.length); if (!input.value) goto done; input.length = apr_base64_decode(input.value, auth_header_value); #ifdef HAVE_GSS_ACQUIRE_CRED_FROM if (cfg->use_s4u2proxy) { maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET, GSS_C_BOTH, cfg->cred_store, &acquired_cred, NULL, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_acquire_cred_from() failed", maj, min)); goto done; } } #endif maj = gss_accept_sec_context(&min, pctx, acquired_cred, &input, GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_type, &output, &flags, &vtime, &delegated_cred); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_accept_sec_context() failed", maj, min)); goto done; } if (maj == GSS_S_CONTINUE_NEEDED) { if (!mc) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "Mechanism needs continuation but neither " "GssapiConnectionBound nor " "GssapiUseSessions are available"); gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER); gss_release_buffer(&min, &output); output.length = 0; } /* auth not complete send token and wait next packet */ goto done; } req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate"); /* Always set the GSS name in an env var */ maj = gss_display_name(&min, client, &name, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_accept_sec_context() failed", maj, min)); goto done; } clientname = apr_pstrndup(req->pool, name.value, name.length); apr_table_set(req->subprocess_env, "GSS_NAME", clientname); #ifdef HAVE_GSS_STORE_CRED_INTO if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) { char *ccachefile = NULL; mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname, delegated_cred, &ccachefile); if (ccachefile) { apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile); } } #endif if (cfg->map_to_local) { maj = gss_localname(&min, client, mech_type, &lname); if (maj != GSS_S_COMPLETE) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_localname() failed", maj, min)); goto done; } req->user = apr_pstrndup(req->pool, lname.value, lname.length); } else { req->user = clientname; } if (mc) { mc->user_name = apr_pstrdup(mc->parent, req->user); mc->gss_name = apr_pstrdup(mc->parent, clientname); mc->established = true; if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) { vtime = MIN_SESS_EXP_TIME; } mc->expiration = time(NULL) + vtime; mag_attempt_session(req, cfg, mc); } ret = OK; done: if (ret == HTTP_UNAUTHORIZED) { if (output.length != 0) { replen = apr_base64_encode_len(output.length) + 1; reply = apr_pcalloc(req->pool, 10 + replen); if (reply) { memcpy(reply, "Negotiate ", 10); apr_base64_encode(&reply[10], output.value, output.length); apr_table_add(req->err_headers_out, "WWW-Authenticate", reply); } } else { apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate"); } } gss_release_cred(&min, &delegated_cred); gss_release_buffer(&min, &output); gss_release_name(&min, &client); gss_release_buffer(&min, &name); gss_release_buffer(&min, &lname); return ret; }
/* * Apache <2.4 authorization routine: match the claims from the authenticated user against the Require primitive */ int oidc_authz_worker(request_rec *r, const json_t * const claims, const require_line * const reqs, int nelts) { const int m = r->method_number; const char *token; const char *requirement; int i; int have_oauthattr = 0; int count_oauth_claims = 0; /* go through applicable Require directives */ for (i = 0; i < nelts; ++i) { /* ignore this Require if it's in a <Limit> section that exclude this method */ if (!(reqs[i].method_mask & (AP_METHOD_BIT << m))) { continue; } /* ignore if it's not a "Require claim ..." */ requirement = reqs[i].requirement; token = ap_getword_white(r->pool, &requirement); if (apr_strnatcasecmp(token, OIDC_REQUIRE_NAME) != 0) { continue; } /* ok, we have a "Require claim" to satisfy */ have_oauthattr = 1; /* * If we have an applicable claim, but no claims were sent in the request, then we can * just stop looking here, because it's not satisfiable. The code after this loop will * give the appropriate response. */ if (!claims) { break; } /* * iterate over the claim specification strings in this require directive searching * for a specification that matches one of the claims. */ while (*requirement) { token = ap_getword_conf(r->pool, &requirement); count_oauth_claims++; oidc_debug(r, "evaluating claim specification: %s", token); if (oidc_authz_match_claim(r, token, claims) == TRUE) { /* if *any* claim matches, then authorization has succeeded and all of the others are ignored */ oidc_debug(r, "require claim '%s' matched", token); return OK; } } } /* if there weren't any "Require claim" directives, we're irrelevant */ if (!have_oauthattr) { oidc_debug(r, "no claim statements found, not performing authz"); return DECLINED; } /* if there was a "Require claim", but no actual claims, that's cause to warn the admin of an iffy configuration */ if (count_oauth_claims == 0) { oidc_warn(r, "'require claim' missing specification(s) in configuration, declining"); return DECLINED; } /* log the event, also in Apache speak */ oidc_debug(r, "authorization denied for client session"); ap_note_auth_failure(r); return HTTP_UNAUTHORIZED; }
static int mag_auth(request_rec *req) { const char *type; int auth_type = -1; struct mag_req_cfg *req_cfg; struct mag_config *cfg; const char *auth_header; char *auth_header_type; int ret = HTTP_UNAUTHORIZED; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t *pctx; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; gss_buffer_desc name = GSS_C_EMPTY_BUFFER; gss_buffer_desc ba_user; gss_buffer_desc ba_pwd; gss_name_t client = GSS_C_NO_NAME; gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; gss_cred_usage_t cred_usage = GSS_C_ACCEPT; uint32_t vtime; uint32_t maj, min; char *reply; size_t replen; gss_OID mech_type = GSS_C_NO_OID; gss_OID_set desired_mechs = GSS_C_NO_OID_SET; gss_buffer_desc lname = GSS_C_EMPTY_BUFFER; struct mag_conn *mc = NULL; int i; bool send_auth_header = true; type = ap_auth_type(req); if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) { return DECLINED; } req_cfg = mag_init_cfg(req); cfg = req_cfg->cfg; desired_mechs = req_cfg->desired_mechs; /* implicit auth for subrequests if main auth already happened */ if (!ap_is_initial_req(req) && req->main != NULL) { type = ap_auth_type(req->main); if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) { /* warn if the subrequest location and the main request * location have different configs */ if (cfg != ap_get_module_config(req->main->per_dir_config, &auth_gssapi_module)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "Subrequest authentication bypass on " "location with different configuration!"); } if (req->main->user) { req->user = apr_pstrdup(req->pool, req->main->user); return OK; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "The main request is tasked to establish the " "security context, can't proceed!"); return HTTP_UNAUTHORIZED; } } else { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "Subrequest GSSAPI auth with no auth on the main " "request. This operation may fail if other " "subrequests already established a context or the " "mechanism requires multiple roundtrips."); } } if (cfg->ssl_only) { if (!mag_conn_is_https(req->connection)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "Not a TLS connection, refusing to authenticate!"); goto done; } } if (cfg->gss_conn_ctx) { mc = (struct mag_conn *)ap_get_module_config( req->connection->conn_config, &auth_gssapi_module); if (!mc) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "Failed to retrieve connection context!"); goto done; } } /* if available, session always supersedes connection bound data */ if (req_cfg->use_sessions) { mag_check_session(req_cfg, &mc); } auth_header = apr_table_get(req->headers_in, req_cfg->req_proto); if (mc) { if (mc->established && (auth_header == NULL) && (mc->auth_type != AUTH_TYPE_BASIC)) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "Already established context found!"); mag_set_req_data(req, cfg, mc); ret = OK; goto done; } pctx = &mc->ctx; } else { /* no preserved mc, create one just for this request */ mc = mag_new_conn_ctx(req->pool); pctx = &ctx; } /* We can proceed only if we do have an auth header */ if (!auth_header) goto done; auth_header_type = ap_getword_white(req->pool, &auth_header); if (!auth_header_type) goto done; /* We got auth header, sending auth header would mean re-auth */ send_auth_header = !cfg->negotiate_once; for (i = 0; auth_types[i] != NULL; i++) { if (strcasecmp(auth_header_type, auth_types[i]) == 0) { auth_type = i; break; } } switch (auth_type) { case AUTH_TYPE_NEGOTIATE: if (!parse_auth_header(req->pool, &auth_header, &input)) { goto done; } break; case AUTH_TYPE_BASIC: if (!cfg->use_basic_auth) { goto done; } ba_pwd.value = ap_pbase64decode(req->pool, auth_header); if (!ba_pwd.value) goto done; ba_user.value = ap_getword_nulls_nc(req->pool, (char **)&ba_pwd.value, ':'); if (!ba_user.value) goto done; if (((char *)ba_user.value)[0] == '\0' || ((char *)ba_pwd.value)[0] == '\0') { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "Invalid empty user or password for Basic Auth"); goto done; } ba_user.length = strlen(ba_user.value); ba_pwd.length = strlen(ba_pwd.value); if (mc->is_preserved && mc->established && mag_basic_check(req_cfg, mc, ba_user, ba_pwd)) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "Already established BASIC AUTH context found!"); mag_set_req_data(req, cfg, mc); ret = OK; goto done; } break; case AUTH_TYPE_RAW_NTLM: if (!is_mech_allowed(desired_mechs, gss_mech_ntlmssp, cfg->gss_conn_ctx)) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "NTLM Authentication is not allowed!"); goto done; } if (!parse_auth_header(req->pool, &auth_header, &input)) { goto done; } desired_mechs = discard_const(gss_mech_set_ntlmssp); break; default: goto done; } if (mc->established) { /* if we are re-authenticating make sure the conn context * is cleaned up so we do not accidentally reuse an existing * established context */ mag_conn_clear(mc); } mc->auth_type = auth_type; #ifdef HAVE_CRED_STORE if (use_s4u2proxy(req_cfg)) { cred_usage = GSS_C_BOTH; } #endif if (auth_type == AUTH_TYPE_BASIC) { if (mag_auth_basic(req, cfg, ba_user, ba_pwd, &client, &mech_type, &delegated_cred, &vtime)) { goto complete; } goto done; } if (!mag_acquire_creds(req, cfg, desired_mechs, cred_usage, &acquired_cred, NULL)) { goto done; } if (auth_type == AUTH_TYPE_NEGOTIATE && cfg->allowed_mechs != GSS_C_NO_OID_SET) { maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_set_neg_mechs() failed", maj, min)); goto done; } } maj = gss_accept_sec_context(&min, pctx, acquired_cred, &input, GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_type, &output, NULL, &vtime, &delegated_cred); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_accept_sec_context() failed", maj, min)); goto done; } else if (maj == GSS_S_CONTINUE_NEEDED) { if (!mc->is_preserved) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "Mechanism needs continuation but neither " "GssapiConnectionBound nor " "GssapiUseSessions are available"); gss_release_buffer(&min, &output); output.length = 0; } /* auth not complete send token and wait next packet */ goto done; } complete: maj = gss_display_name(&min, client, &name, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_display_name() failed", maj, min)); goto done; } mc->gss_name = apr_pstrndup(req->pool, name.value, name.length); if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) { vtime = MIN_SESS_EXP_TIME; } mc->expiration = time(NULL) + vtime; mag_get_name_attributes(req, cfg, client, mc); #ifdef HAVE_CRED_STORE if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) { char *ccache_path; mc->ccname = 0; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "requester: %s", mc->gss_name); ccache_path = get_ccache_name(req, cfg->deleg_ccache_dir, mc->gss_name, cfg->deleg_ccache_unique, mc); if (ccache_path == NULL) { goto done; } mag_store_deleg_creds(req, ccache_path, delegated_cred); mc->delegated = true; if (!req_cfg->use_sessions && cfg->deleg_ccache_unique) { /* queue removing ccache to avoid littering filesystem */ apr_pool_cleanup_register(mc->pool, ccache_path, (int (*)(void *)) unlink, apr_pool_cleanup_null); } /* extract filename from full path */ mc->ccname = strrchr(ccache_path, '/') + 1; } #endif if (cfg->map_to_local) { maj = gss_localname(&min, client, mech_type, &lname); if (maj != GSS_S_COMPLETE) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_localname() failed", maj, min)); goto done; } mc->user_name = apr_pstrndup(req->pool, lname.value, lname.length); } else { mc->user_name = apr_pstrdup(mc->pool, mc->gss_name); } mc->established = true; if (auth_type == AUTH_TYPE_BASIC) { mag_basic_cache(req_cfg, mc, ba_user, ba_pwd); } if (req_cfg->use_sessions) { mag_attempt_session(req_cfg, mc); } /* Now set request data and env vars */ mag_set_req_data(req, cfg, mc); if (req_cfg->send_persist) apr_table_set(req->headers_out, "Persistent-Auth", cfg->gss_conn_ctx ? "true" : "false"); ret = OK; done: if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) { int prefixlen = strlen(mag_str_auth_type(auth_type)) + 1; replen = apr_base64_encode_len(output.length) + 1; reply = apr_pcalloc(req->pool, prefixlen + replen); if (reply) { memcpy(reply, mag_str_auth_type(auth_type), prefixlen - 1); reply[prefixlen - 1] = ' '; apr_base64_encode(&reply[prefixlen], output.value, output.length); apr_table_add(req->err_headers_out, req_cfg->rep_proto, reply); } } else if (ret == HTTP_UNAUTHORIZED) { if (send_auth_header) { apr_table_add(req->err_headers_out, req_cfg->rep_proto, "Negotiate"); if (is_mech_allowed(desired_mechs, gss_mech_ntlmssp, cfg->gss_conn_ctx)) { apr_table_add(req->err_headers_out, req_cfg->rep_proto, "NTLM"); } } if (cfg->use_basic_auth) { apr_table_add(req->err_headers_out, req_cfg->rep_proto, apr_psprintf(req->pool, "Basic realm=\"%s\"", ap_auth_name(req))); } } if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER); gss_release_cred(&min, &acquired_cred); gss_release_cred(&min, &delegated_cred); gss_release_buffer(&min, &output); gss_release_name(&min, &client); gss_release_buffer(&min, &name); gss_release_buffer(&min, &lname); return ret; }
static int check_file_owner(request_rec *r) { authz_owner_config_rec *conf = ap_get_module_config(r->per_dir_config, &authz_owner_module); int m = r->method_number; register int x; const char *t, *w; const apr_array_header_t *reqs_arr = ap_requires(r); require_line *reqs; int required_owner = 0; apr_status_t status = 0; char *reason = NULL; if (!reqs_arr) { return DECLINED; } reqs = (require_line *)reqs_arr->elts; for (x = 0; x < reqs_arr->nelts; x++) { /* if authoritative = On then break if a require already failed. */ if (reason && conf->authoritative) { break; } if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) { continue; } t = reqs[x].requirement; w = ap_getword_white(r->pool, &t); if (!strcmp(w, "file-owner")) { #if !APR_HAS_USER if ((required_owner & ~1) && conf->authoritative) { break; } required_owner |= 1; /* remember the requirement */ reason = "'Require file-owner' is not supported on this platform."; continue; #else /* APR_HAS_USER */ char *owner = NULL; apr_finfo_t finfo; if ((required_owner & ~1) && conf->authoritative) { break; } required_owner |= 1; /* remember the requirement */ if (!r->filename) { reason = "no filename available"; continue; } status = apr_stat(&finfo, r->filename, APR_FINFO_USER, r->pool); if (status != APR_SUCCESS) { reason = apr_pstrcat(r->pool, "could not stat file ", r->filename, NULL); continue; } if (!(finfo.valid & APR_FINFO_USER)) { reason = "no file owner information available"; continue; } status = apr_uid_name_get(&owner, finfo.user, r->pool); if (status != APR_SUCCESS || !owner) { reason = "could not get name of file owner"; continue; } if (strcmp(owner, r->user)) { reason = apr_psprintf(r->pool, "file owner %s does not match.", owner); continue; } /* this user is authorized */ return OK; #endif /* APR_HAS_USER */ } /* file-group only figures out the file's group and lets * other modules do the actual authorization (against a group file/db). * Thus, these modules have to hook themselves after * mod_authz_owner and of course recognize 'file-group', too. */ if (!strcmp(w, "file-group")) { #if !APR_HAS_USER if ((required_owner & ~6) && conf->authoritative) { break; } required_owner |= 2; /* remember the requirement */ reason = "'Require file-group' is not supported on this platform."; continue; #else /* APR_HAS_USER */ char *group = NULL; apr_finfo_t finfo; if ((required_owner & ~6) && conf->authoritative) { break; } required_owner |= 2; /* remember the requirement */ if (!r->filename) { reason = "no filename available"; continue; } status = apr_stat(&finfo, r->filename, APR_FINFO_GROUP, r->pool); if (status != APR_SUCCESS) { reason = apr_pstrcat(r->pool, "could not stat file ", r->filename, NULL); continue; } if (!(finfo.valid & APR_FINFO_GROUP)) { reason = "no file group information available"; continue; } status = apr_gid_name_get(&group, finfo.group, r->pool); if (status != APR_SUCCESS || !group) { reason = "could not get name of file group"; continue; } /* store group name in a note and let others decide... */ apr_table_setn(r->notes, AUTHZ_GROUP_NOTE, group); required_owner |= 4; continue; #endif /* APR_HAS_USER */ } } if (!required_owner || !conf->authoritative) { return DECLINED; } /* allow file-group passed to group db modules either if this is the * only applicable requirement here or if a file-owner failed but we're * not authoritative. * This allows configurations like: * * AuthzOwnerAuthoritative Off * require file-owner * require file-group * * with the semantical meaning of "either owner or group must match" * (inclusive or) * * [ 6 == 2 | 4; 7 == 1 | 2 | 4 ] should I use #defines instead? */ if (required_owner == 6 || (required_owner == 7 && !conf->authoritative)) { return DECLINED; } ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "Authorization of user %s to access %s failed, reason: %s", r->user, r->uri, reason ? reason : "unknown"); ap_note_auth_failure(r); return HTTP_UNAUTHORIZED; }
/** * Gets called whenever there is a "ReplaceFilterDefine" line in the config * file. The method creates a new configuration (replace_filter_t) for the * server (available through cmd->server). Returns an error message if things * didn't work out. * * @param cmd The command record that holds the general data for the * environment. * @param dummy To be ignored. * @param args The arguments from the filter definition (everything following * ReplaceFilterDefine. * @see cmd_parms */ static const char *define_filter(cmd_parms *cmd, void *dummy, const char *args) { replace_server_t *conf = ap_get_module_config(cmd->server->module_config, &replace_module); const char *token; const char *name; replace_filter_t *filter; /* Extract the name of the filter. */ name = ap_getword_white(cmd->pool, &args); if (!name) { return "Filter name not found"; } /* Check if there is another filter by the same name. */ if (apr_hash_get(conf->h, name, APR_HASH_KEY_STRING)) { return apr_psprintf(cmd->pool, "Replace Filter %s is already defined", name); } /* Create a new filter configuration and populate the filter configuration * with default values. */ filter = (replace_filter_t *)apr_pcalloc(conf->p, sizeof(replace_filter_t)); filter->name = name; filter->mode = OUTPUT_FILTER; filter->ftype = AP_FTYPE_RESOURCE; filter->pattern = NULL; filter->case_ignore = 0; apr_hash_set(conf->h, name, APR_HASH_KEY_STRING, filter); /* Parse the remaining arguments. */ while (*args) { /* Ignore whitespaces. */ while (apr_isspace(*args)) { ++args; } /* Set the filter mode. */ if (!strncasecmp(args, "mode=", 5)) { args += 5; token = ap_getword_white(cmd->pool, &args); if (!strcasecmp(token, "output")) { filter->mode = OUTPUT_FILTER; } else if (!strcasecmp(token, "input")) { filter->mode = INPUT_FILTER; } else { return apr_psprintf(cmd->pool, "Invalid mode: `%s'", token); } continue; } /* Set the filter type. */ if (!strncasecmp(args, "ftype=", 6)) { args += 6; token = ap_getword_white(cmd->pool, &args); filter->ftype = atoi(token); continue; } /* MIME type for incoming data. */ if (!strncasecmp(args, "intype=", 7)) { args += 7; filter->intype = ap_getword_white(cmd->pool, &args); continue; } /* MIME type for outgoing data, if different from intype. */ if (!strncasecmp(args, "outtype=", 8)) { args += 8; filter->outtype = ap_getword_white(cmd->pool, &args); continue; } /* check if the regular expression is to be handled case sensitive or * not. */ if (!strncasecmp(args, "caseignore", 10)) { token = ap_getword_white(cmd->pool, &args); if (!strncasecmp(token, "caseignore", 10)) { filter->case_ignore = 1; } else { return apr_psprintf(cmd->pool, "mangled argument `%s'", token); } continue; } /* If there is any other argument than the already checked ones, return * with an error message. */ return apr_psprintf(cmd->pool, "Unexpected parameter: `%s'", args); } /* parsing is done... register the filter */ if (filter->mode == OUTPUT_FILTER) { /* XXX need a way to ensure uniqueness among all filters */ if (!ap_register_output_filter(filter->name, replace_output_filter, NULL, filter->ftype)) { return apr_psprintf(cmd->pool, "Unable to register output filter '%s'", filter->name); } } else { ap_assert(1 != 1); /* we set the field wrong somehow */ } return NULL; }
/* Run an external authentication program using the given method for passing * in the data. The login name is always passed in. Dataname is "GROUP" or * "PASS" and data is the group list or password being checked. To launch * a detached daemon, run this with extmethod=NULL. * * If the authenticator was run, we return the numeric code from the * authenticator, normally 0 if the login was valid, some small positive * number if not. If we were not able to run the authenticator, we log * an error message and return a numeric error code: * * -1 Could not execute authenticator, usually a path or permission problem * -2 The external authenticator crashed or was killed. * -3 Could not create process attribute structure * -4 apr_proc_wait() did not return a status code. Should never happen. * -5 apr_proc_wait() returned before child finished. Should never happen. */ static int exec_external(const char *extpath, const char *extmethod, const request_rec *r, const char *dataname, const char *data) { conn_rec *c= r->connection; apr_pool_t *p= r->pool; int isdaemon, usecheck= 0, usepipeout= 0, usepipein= 0; apr_procattr_t *procattr; apr_proc_t proc; apr_status_t rc= APR_SUCCESS; char *child_env[12]; char *child_arg[MAX_ARG+2]; const char *t; int i, status= -4; apr_exit_why_e why= APR_PROC_EXIT; apr_sigfunc_t *sigchld; /* Set various flags based on the execution method */ isdaemon= (extmethod == NULL); if (!isdaemon) { usecheck= extmethod && !strcasecmp(extmethod, "checkpassword"); usepipeout= usecheck || (extmethod && !strcasecmp(extmethod, "pipes")); usepipein= usepipeout || (extmethod && !strcasecmp(extmethod, "pipe")); } /* Create the environment for the child. Daemons don't get these, they * just inherit apache's environment variables. */ if (!isdaemon) { const char *cookie, *host, *remote_host; authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *) ap_get_module_config(r->per_dir_config, &authnz_external_module); i= 0; if (!usepipein) { /* Put user name and password/group into environment */ child_env[i++]= apr_pstrcat(p, ENV_USER"=", r->user, NULL); child_env[i++]= apr_pstrcat(p, dataname, "=", data, NULL); } child_env[i++]= apr_pstrcat(p, "PATH=", getenv("PATH"), NULL); child_env[i++]= apr_pstrcat(p, "AUTHTYPE=", dataname, NULL); remote_host= ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST,NULL); if (remote_host != NULL) child_env[i++]= apr_pstrcat(p, ENV_HOST"=", remote_host,NULL); if (r->useragent_ip) child_env[i++]= apr_pstrcat(p, ENV_IP"=", r->useragent_ip, NULL); if (r->uri) child_env[i++]= apr_pstrcat(p, ENV_URI"=", r->uri, NULL); if ((host= apr_table_get(r->headers_in, "Host")) != NULL) child_env[i++]= apr_pstrcat(p, ENV_HTTP_HOST"=", host, NULL); if (dir->context) child_env[i++]= apr_pstrcat(r->pool, ENV_CONTEXT"=", dir->context, NULL); #ifdef ENV_COOKIE if ((cookie= apr_table_get(r->headers_in, "Cookie")) != NULL) child_env[i++]= apr_pstrcat(p, ENV_COOKIE"=", cookie, NULL); #endif /* NOTE: If you add environment variables, * remember to increase the size of the child_env[] array */ /* End of environment */ child_env[i]= NULL; } /* Construct argument array */ for (t= extpath, i=0; *t != '\0' && (i <= MAX_ARG + 1); child_arg[i++]= ap_getword_white(p, &t)) {} child_arg[i]= NULL; /* Create the process attribute structure describing the script we * want to run using the Thread/Process functions from the Apache * portable runtime library. */ if (((rc= apr_procattr_create(&procattr, p)) != APR_SUCCESS) || /* should we create pipes to stdin, stdout and stderr? */ ((rc= apr_procattr_io_set(procattr, (usepipein && !usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE, usepipeout ? APR_FULL_BLOCK : APR_NO_PIPE, (usepipein && usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE)) != APR_SUCCESS ) || /* will give full path of program and make a new environment */ ((rc= apr_procattr_cmdtype_set(procattr, isdaemon ? APR_PROGRAM_ENV : APR_PROGRAM)) != APR_SUCCESS) || /* detach the child only if it is a daemon */ ((rc= apr_procattr_detach_set(procattr, isdaemon)) != APR_SUCCESS) || /* function to call if child has error after fork, before exec */ ((rc= apr_procattr_child_errfn_set(procattr, extchilderr) != APR_SUCCESS))) { /* Failed. Probably never happens. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "could not set child process attributes"); return -3; } /* Sometimes other modules wil mess up sigchild. Need to fix it for * the wait call to work correctly. */ sigchld= apr_signal(SIGCHLD,SIG_DFL); /* Start the child process */ rc= apr_proc_create(&proc, child_arg[0], (const char * const *)child_arg, (const char * const *)child_env, procattr, p); if (rc != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not run external authenticator: %d: %s", rc, child_arg[0]); return -1; } if (isdaemon) return 0; apr_pool_note_subprocess(p, &proc, APR_KILL_AFTER_TIMEOUT); if (usepipein) { /* Select appropriate pipe to write to */ apr_file_t *pipe= (usecheck ? proc.err : proc.in); /* Send the user */ apr_file_write_full(pipe, r->user, strlen(r->user), NULL); apr_file_putc(usecheck ? '\0' : '\n', pipe); /* Send the password */ apr_file_write_full(pipe, data, strlen(data), NULL); apr_file_putc(usecheck ? '\0' : '\n', pipe); /* Send the uri/path */ apr_file_write_full(pipe, r->uri, strlen(r->uri), NULL); apr_file_putc(usecheck ? '\0' : '\n', pipe); /* Send dummy timestamp for checkpassword */ if (usecheck) apr_file_write_full(pipe, "0", 2, NULL); /* Close the file */ apr_file_close(pipe); } /* Wait for the child process to terminate, and get status */ rc= apr_proc_wait(&proc,&status,&why,APR_WAIT); /* Restore sigchild to whatever it was before we reset it */ apr_signal(SIGCHLD,sigchld); if (!APR_STATUS_IS_CHILD_DONE(rc)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not get status from child process"); return -5; } if (!APR_PROC_CHECK_EXIT(why)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "External authenticator died on signal %d",status); return -2; } return status; }
static int check_unix_group(request_rec *r, const char *grouplist) { char *user= r->user; char *at; /* Strip @ sign and anything following it from the username. Some * authentication modules, like mod_auth_kerb like appending such * stuff to user names, but an @ sign is never legal in a unix login * name, so it should be safe to always discard such stuff. */ if ((at= strchr(user, '@')) != NULL) *at= '\0'; /* Get info about login */ struct passwd *pwd= getpwnam(user); if (pwd == NULL) { /* No such user - forget it */ if (at != NULL) *at= '@'; return 0; } #ifdef HAVE_GETGROUPLIST int num_groups = 0; int grouplistresult = getgrouplist(user, pwd->pw_gid, NULL, &num_groups); if (grouplistresult != -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "expected -1 from first getgrouplist, got %d", grouplistresult); } gid_t* user_groups = (gid_t*)apr_palloc(r->pool, num_groups*sizeof(gid_t)); grouplistresult = getgrouplist(user, pwd->pw_gid, user_groups, &num_groups); /* Loop through list of groups passed in */ while (*grouplist != '\0') { const char* const word = ap_getword_white(r->pool, &grouplist); gid_t match_group; if (apr_isdigit(word[0])) { // numeric group id match_group = atoi(word); } else { struct group* const group = getgrnam(word); match_group = group == NULL ? -1 : group->gr_gid; } for (int i = 0; i < num_groups; i++) { gid_t const user_group = user_groups[i]; if (user_group == match_group) { if (at != NULL) *at= '@'; return 1; } } } #else /* Loop through list of groups passed in */ while (*grouplist != '\0') { struct group *grp; char* w= ap_getword_white(r->pool, &grouplist); if (apr_isdigit(w[0])) { /* Numeric group id */ int gid= atoi(w); /* Check if it matches the user's primary group */ if (gid == pwd->pw_gid) { if (at != NULL) *at= '@'; return 1; } /* Get list of group members for numeric group id */ grp= getgrgid(gid); } else { /* Get gid and list of group members for group name */ grp= getgrnam(w); /* Check if gid of this group matches user's primary gid */ if (grp != NULL && grp->gr_gid == pwd->pw_gid) { if (at != NULL) *at= '@'; return 1; } } /* Walk through list of members, seeing if any match user login */ if (grp != NULL) { char **p; for (p= grp->gr_mem; *p != NULL; p++) { if (!strcmp(user, *p)) { if (at != NULL) *at= '@'; return 1; } } } } #endif /* Didn't find any matches, flunk him */ if (at != NULL) *at= '@'; return 0; }
static int dbm_check_auth(request_rec *r) { dbm_auth_config_rec *conf = ap_get_module_config(r->per_dir_config, &auth_dbm_module); char *user = r->user; int m = r->method_number; const apr_array_header_t *reqs_arr = ap_requires(r); require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL; register int x; const char *t; char *w; if (!conf->auth_dbmgrpfile) return DECLINED; if (!reqs_arr) return DECLINED; for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) continue; t = reqs[x].requirement; w = ap_getword_white(r->pool, &t); if (!strcmp(w, "group") && conf->auth_dbmgrpfile) { const char *orig_groups, *groups; char *v; if (!(groups = get_dbm_grp(r, user, conf->auth_dbmgrpfile, conf->auth_dbmtype))) { if (!(conf->auth_dbmauthoritative)) return DECLINED; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "user %s not in DBM group file %s: %s", user, conf->auth_dbmgrpfile, r->filename); ap_note_basic_auth_failure(r); return HTTP_UNAUTHORIZED; } orig_groups = groups; while (t[0]) { w = ap_getword_white(r->pool, &t); groups = orig_groups; while (groups[0]) { v = ap_getword(r->pool, &groups, ','); if (!strcmp(v, w)) return OK; } } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "user %s not in right group: %s", user, r->filename); ap_note_basic_auth_failure(r); return HTTP_UNAUTHORIZED; } } return DECLINED; }
static int check_user_access(request_rec *r) { auth_config_rec *sec = (auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_module); char *user = r->connection->user; int m = r->method_number; int method_restricted = 0; register int x; const char *t, *w; table *grpstatus; const array_header *reqs_arr = ap_requires(r); require_line *reqs; /* BUG FIX: tadc, 11-Nov-1995. If there is no "requires" directive, * then any user will do. */ if (reqs_arr == NULL) { return (OK); } reqs = (require_line *) reqs_arr->elts; if (sec->auth_grpfile) { grpstatus = groups_for_user(r->pool, user, sec->auth_grpfile); } else { grpstatus = NULL; } for (x = 0; x < reqs_arr->nelts; x++) { if (! (reqs[x].method_mask & (1 << m))) { continue; } method_restricted = 1; t = reqs[x].requirement; w = ap_getword_white(r->pool, &t); if (strcmp(w, "valid-user") == 0) { return OK; } /* * If requested, allow access if the user is valid and the * owner of the document. */ if (strcmp(w, "file-owner") == 0) { #if defined(WIN32) || defined(NETWARE) || defined(OS2) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, "'Require file-owner' not supported " "on this platform, ignored"); continue; #else struct passwd *pwent; ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "checking for 'owner' access for file '%s'", r->filename); if (r->finfo.st_ino == 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "no stat info for '%s'", r->filename); continue; } pwent = getpwuid(r->finfo.st_uid); if (pwent == NULL) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "no username for UID %d (owner of '%s')", r->finfo.st_uid, r->filename); } else { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "checking authenticated user '%s' " "against owner '%s' of '%s'", user, pwent->pw_name, r->filename); if (strcmp(user, pwent->pw_name) == 0) { return OK; } else { continue; } } #endif } if (strcmp(w, "file-group") == 0) { #if defined(WIN32) || defined(NETWARE) || defined(OS2) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, "'Require file-group' not supported " "on this platform, ignored"); continue; #else struct group *grent; if (sec->auth_grpfile == NULL) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, "no AuthGroupFile, so 'file-group' " "requirement cannot succeed for file '%s'", r->filename); continue; } if (grpstatus == NULL) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, "authenticated user '%s' not a member of " "any groups, so 'file-group' requirement " "cannot succeed for file '%s'", user, r->filename); continue; } ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "checking for 'group' access for file '%s'", r->filename); if (r->finfo.st_ino == 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "no stat info for '%s'", r->filename); continue; } grent = getgrgid(r->finfo.st_gid); if (grent == NULL) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "no group name for GID %d (owner of '%s')", r->finfo.st_gid, r->filename); } else { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, "checking groups of authenticated user '%s' " "against owner group '%s' of '%s'", user, grent->gr_name, r->filename); if (ap_table_get(grpstatus, grent->gr_name) != NULL) { return OK; } else { continue; } } #endif } if (strcmp(w, "user") == 0) { while (t[0] != '\0') { w = ap_getword_conf(r->pool, &t); if (strcmp(user, w) == 0) { return OK; } } } else if (strcmp(w, "group") == 0) { if (grpstatus == NULL) { return DECLINED; /* DBM group? Something else? */ } while (t[0]) { w = ap_getword_conf(r->pool, &t); if (ap_table_get(grpstatus, w)) { return OK; } } } else if (sec->auth_authoritative) { /* if we aren't authoritative, any require directive could be * valid even if we don't grok it. However, if we are * authoritative, we can warn the user they did something wrong. * That something could be a missing "AuthAuthoritative off", but * more likely is a typo in the require directive. */ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "access to %s failed, " "reason: unknown require directive:" "\"%s\"", r->uri, reqs[x].requirement); } } if (! method_restricted) { return OK; } if (! sec->auth_authoritative) { return DECLINED; } ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "access to %s failed, reason: user %s not allowed access", r->uri, user); ap_note_basic_auth_failure(r); return AUTH_REQUIRED; }
static int check_user_access(request_rec *r) { auth_config_rec *conf = ap_get_module_config(r->per_dir_config, &auth_module); char *user = r->user; int m = r->method_number; int method_restricted = 0; register int x; const char *t, *w; apr_table_t *grpstatus; const apr_array_header_t *reqs_arr = ap_requires(r); require_line *reqs; /* BUG FIX: tadc, 11-Nov-1995. If there is no "requires" directive, * then any user will do. */ if (!reqs_arr) { return OK; } reqs = (require_line *)reqs_arr->elts; if (conf->auth_grpfile) { grpstatus = groups_for_user(r, user, conf->auth_grpfile); } else { grpstatus = NULL; } for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) { continue; } method_restricted = 1; t = reqs[x].requirement; w = ap_getword_white(r->pool, &t); if (!strcmp(w, "valid-user")) { return OK; } if (!strcmp(w, "user")) { while (t[0]) { w = ap_getword_conf(r->pool, &t); if (!strcmp(user, w)) { return OK; } } } else if (!strcmp(w, "group")) { if (!grpstatus) { return DECLINED; /* DBM group? Something else? */ } while (t[0]) { w = ap_getword_conf(r->pool, &t); if (apr_table_get(grpstatus, w)) { return OK; } } } else if (conf->auth_authoritative) { /* if we aren't authoritative, any require directive could be * valid even if we don't grok it. However, if we are * authoritative, we can warn the user they did something wrong. * That something could be a missing "AuthAuthoritative off", but * more likely is a typo in the require directive. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "access to %s failed, reason: unknown require " "directive:\"%s\"", r->uri, reqs[x].requirement); } } if (!method_restricted) { return OK; } if (!(conf->auth_authoritative)) { return DECLINED; } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "access to %s failed, reason: user %s not allowed access", r->uri, user); ap_note_basic_auth_failure(r); return HTTP_UNAUTHORIZED; }
static int authz_unixgroup_check_user_access(request_rec *r) { authz_unixgroup_dir_config_rec *dir= (authz_unixgroup_dir_config_rec *) ap_get_module_config(r->per_dir_config, &authz_unixgroup_module); int m= r->method_number; int required_group= 0; register int x; const char *t, *w; const apr_array_header_t *reqs_arr= ap_requires(r); const char *filegroup= NULL; require_line *reqs; /* If not enabled, pass */ if ( !dir->enabled ) return DECLINED; /* If there are no Require arguments, pass */ if (!reqs_arr) return DECLINED; reqs= (require_line *)reqs_arr->elts; /* Loop through the "Require" argument list */ for(x= 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) continue; t= reqs[x].requirement; w= ap_getword_white(r->pool, &t); /* The 'file-group' directive causes mod_authz_owner to store the * group name of the file we are trying to access in a note attached * to the request. It's our job to decide if the user actually is * in that group. If the note is missing, we just ignore it. * Probably mod_authz_owner is not installed. */ if ( !strcasecmp(w, "file-group")) { filegroup= apr_table_get(r->notes, AUTHZ_GROUP_NOTE); if (filegroup == NULL) continue; } if ( !strcmp(w,"group") || filegroup != NULL) { required_group= 1; if (filegroup) { /* Check if user is in the group that owns the file */ if (check_unix_group(r,filegroup)) return OK; } else if (t[0]) { /* Pass rest of require line to authenticator */ if (check_unix_group(r,t)) return OK; } } } /* If we didn't see a 'require group' or aren't authoritive, decline */ if (!required_group || !dir->authoritative) return DECLINED; /* Authentication failed and we are authoritive, declare unauthorized */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "access to %s failed, reason: user %s not allowed access", r->uri, r->user); ap_note_basic_auth_failure(r); return HTTP_UNAUTHORIZED; }
static const char *define_filter(cmd_parms *cmd, void *dummy, const char *args) { ef_server_t *conf = ap_get_module_config(cmd->server->module_config, &ext_filter_module); const char *token; const char *name; char *normalized_name; ef_filter_t *filter; name = ap_getword_white(cmd->pool, &args); if (!name) { return "Filter name not found"; } /* During request processing, we find information about the filter * by looking up the filter name provided by core server in our * hash table. But the core server has normalized the filter * name by converting it to lower case. Thus, when adding the * filter to our hash table we have to use lower case as well. */ normalized_name = apr_pstrdup(cmd->pool, name); ap_str_tolower(normalized_name); if (apr_hash_get(conf->h, normalized_name, APR_HASH_KEY_STRING)) { return apr_psprintf(cmd->pool, "ExtFilter %s is already defined", name); } filter = (ef_filter_t *)apr_pcalloc(conf->p, sizeof(ef_filter_t)); filter->name = name; filter->mode = OUTPUT_FILTER; filter->ftype = AP_FTYPE_RESOURCE; apr_hash_set(conf->h, normalized_name, APR_HASH_KEY_STRING, filter); while (*args) { while (apr_isspace(*args)) { ++args; } /* Nasty parsing... I wish I could simply use ap_getword_white() * here and then look at the token, but ap_getword_white() doesn't * do the right thing when we have cmd="word word word" */ if (!strncasecmp(args, "preservescontentlength", 22)) { token = ap_getword_white(cmd->pool, &args); if (!strcasecmp(token, "preservescontentlength")) { filter->preserves_content_length = 1; } else { return apr_psprintf(cmd->pool, "mangled argument `%s'", token); } continue; } if (!strncasecmp(args, "mode=", 5)) { args += 5; token = ap_getword_white(cmd->pool, &args); if (!strcasecmp(token, "output")) { filter->mode = OUTPUT_FILTER; } else if (!strcasecmp(token, "input")) { filter->mode = INPUT_FILTER; } else { return apr_psprintf(cmd->pool, "Invalid mode: `%s'", token); } continue; } if (!strncasecmp(args, "ftype=", 6)) { args += 6; token = ap_getword_white(cmd->pool, &args); filter->ftype = atoi(token); continue; } if (!strncasecmp(args, "enableenv=", 10)) { args += 10; token = ap_getword_white(cmd->pool, &args); filter->enable_env = token; continue; } if (!strncasecmp(args, "disableenv=", 11)) { args += 11; token = ap_getword_white(cmd->pool, &args); filter->disable_env = token; continue; } if (!strncasecmp(args, "intype=", 7)) { args += 7; filter->intype = ap_getword_white(cmd->pool, &args); continue; } if (!strncasecmp(args, "outtype=", 8)) { args += 8; filter->outtype = ap_getword_white(cmd->pool, &args); continue; } if (!strncasecmp(args, "cmd=", 4)) { args += 4; if ((token = parse_cmd(cmd->pool, &args, filter))) { return token; } continue; } return apr_psprintf(cmd->pool, "Unexpected parameter: `%s'", args); } /* parsing is done... register the filter */ if (filter->mode == OUTPUT_FILTER) { /* XXX need a way to ensure uniqueness among all filters */ ap_register_output_filter(filter->name, ef_output_filter, NULL, filter->ftype); } else if (filter->mode == INPUT_FILTER) { /* XXX need a way to ensure uniqueness among all filters */ ap_register_input_filter(filter->name, ef_input_filter, NULL, filter->ftype); } else { ap_assert(1 != 1); /* we set the field wrong somehow */ } return NULL; }
static authz_status dbmgroup_check_authorization(request_rec *r, const char *require_args, const void *parsed_require_args) { authz_dbm_config_rec *conf = ap_get_module_config(r->per_dir_config, &authz_dbm_module); char *user = r->user; const char *t; char *w; const char *orig_groups = NULL; const char *realm = ap_auth_name(r); const char *groups; char *v; if (!user) { return AUTHZ_DENIED_NO_USER; } if (!conf->grpfile) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01798) "No group file was specified in the configuration"); return AUTHZ_DENIED; } /* fetch group data from dbm file only once. */ if (!orig_groups) { apr_status_t status; status = get_dbm_grp(r, apr_pstrcat(r->pool, user, ":", realm, NULL), user, conf->grpfile, conf->dbmtype, &groups); if (status != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01799) "could not open dbm (type %s) group access " "file: %s", conf->dbmtype, conf->grpfile); return AUTHZ_GENERAL_ERROR; } if (groups == NULL) { /* no groups available, so exit immediately */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01800) "Authorization of user %s to access %s failed, reason: " "user doesn't appear in DBM group file (%s).", r->user, r->uri, conf->grpfile); return AUTHZ_DENIED; } orig_groups = groups; } t = require_args; while ((w = ap_getword_white(r->pool, &t)) && w[0]) { groups = orig_groups; while (groups[0]) { v = ap_getword(r->pool, &groups, ','); if (!strcmp(v, w)) { return AUTHZ_GRANTED; } } } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01801) "Authorization of user %s to access %s failed, reason: " "user is not part of the 'require'ed group(s).", r->user, r->uri); return AUTHZ_DENIED; }
/** * This function gets called whenever there is a 'HeaderReplacePattern' in the * config file. * Returns NULL if everything went alright, otherwise an error message. * * @param cmd The command record filled with general information * about the environment. * @param dummy To be ignored. * @param args The arguments passed from the pattern definition. Must * be in the following order: name, header, pattern, * replacement string. */ static const char *add_header_pattern(cmd_parms *cmd, void *dummy, const char *args) { const char *name; // the filter name const char *header; // the HTTP header field to match const char *pattern_str;// the textual representation of the pattern const char *replace; // the replacement string replace_server_t *conf; // the server configuration (hashtable) replace_filter_t *filter; // the filter configuration header_replace_pattern_t *pattern; // the pattern to add header_replace_pattern_t *previous; // the previous pattern, if any header_replace_pattern_t backup; pcre *re; // the regular expression pcre_extra *pe; // data from studying the pattern const char *error; // error text for the failed regex compilation int error_offset; // offset of the regex compilation error, if any int rc; // return count of the regex matching int i; // counter int rv; // return value for generic function calls int flags = 0; // the flags for the regex matching /* Get the configuration record */ conf = ap_get_module_config(cmd->server->module_config, &replace_module); if (conf == NULL) { return apr_pstrcat(cmd->temp_pool, "Illegal server record", NULL, NULL); } /* * Parse the arguments. */ /* Extract the name of the filter and check for its existence. */ name = ap_getword_white(cmd->pool, &args); if (!apr_hash_get(conf->h, name, APR_HASH_KEY_STRING)) { return "ReplaceFilter not defined"; } /* Extract the header field. */ header = ap_getword_conf(cmd->pool, &args); if (!header || strlen(header) == 0) { return "Header field missing"; } /* Extract the regex pattern */ pattern_str = ap_getword_conf(cmd->pool, &args); if (!pattern_str || strlen(pattern_str) == 0) { return "Pattern definition missing"; } if (!args || !strlen(args) > 0) { return "Replacement pattern missing"; } /* Extract the replacement string */ replace = ap_getword_conf(cmd->pool, &args); if (!replace) { return "Replacement pattern missing"; } /* Check for additional, illegal configuration directives */ if (args && strlen(args) > 0) { return apr_psprintf(cmd->temp_pool, "Illegal conf directive: \"%s\"", args); } /* Get the filter definition */ filter = (replace_filter_t*)apr_hash_get(conf->h, name, APR_HASH_KEY_STRING); if (filter == NULL) { return apr_pstrcat(cmd->temp_pool, "Unknown filter definition for replace filter"); } /* Check if we have to set the flag for case insensitive matching. */ if (filter->case_ignore == 1) { flags |= PCRE_CASELESS; } /* Compile the pattern. */ re = pcre_compile(pattern_str, flags, &error, &error_offset, NULL); /* Return ungraceful if the compilation of the regex failed. */ if (re == NULL) { return apr_pstrcat(cmd->temp_pool, "Error compiling regular expression: ", error, NULL); } /* Study the pattern. This is done for performance improvement, but most of * the time it doesn't speed up things, since the return value is simply * NULL. */ pe = pcre_study(re, 0, &error); if (error != NULL) { return apr_pstrcat(cmd->temp_pool, "Error studying compiled pattern: ", error, NULL); } /* Check for an already existing pattern. */ pattern = filter->header_pattern; previous = NULL; /* Find the last pattern in the list. */ while (pattern && pattern->next != NULL) { previous = pattern; pattern = pattern->next; } /* If there has been no pattern at all, create one. Otherwise save the last * pattern and create a new one. */ if (!pattern) { pattern = (header_replace_pattern_t *)apr_pcalloc(conf->p, sizeof(header_replace_pattern_t)); filter->header_pattern = pattern; } else { previous = pattern; pattern = (header_replace_pattern_t *)apr_pcalloc(conf->p, sizeof(header_replace_pattern_t)); } /* Assign the values to the structure and add the pattern to the list. */ pattern->pattern = re; pattern->extra = pe; pattern->replacement = apr_pstrdup(conf->p, replace); pattern->header = apr_pstrdup(conf->p, header); pattern->next = NULL; if (previous) { previous->next = pattern; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, "Filter %s: Added header pattern \"%s\"", name, pattern_str); return NULL; }