static int mod_fcgid_check_auth()

in modules/fcgid/mod_fcgid.c [486:639]


static int mod_fcgid_check_auth(request_rec *r,
                                enum fcgid_auth_check_mode auth_check_mode)
{
    int res = 0;
    const char *password = NULL;
    apr_table_t *saved_subprocess_env = NULL;
    fcgid_cmd_conf *auth_cmd_info = NULL;
    int authoritative;
    const char *auth_role = NULL;
    const char *role_log_msg = NULL;
    const char *user_log_msg = "";

    /* Because we don't function as authn/z providers, integration with
     * the standard httpd authn/z modules is somewhat problematic.
     *
     * With httpd 2.4 in particular, our hook functions may be
     * circumvented by mod_authz_core's check_access_ex hook, unless
     * Require directives specify that user-based authn/z is needed.
     *
     * Even then, APR_HOOK_MIDDLE may cause our authentication hook to be
     * ordered after mod_auth_basic's check_authn hook, in which case it
     * will be skipped unless AuthBasicAuthoritative is Off and no authn
     * provider recognizes the user or outright denies the request.
     *
     * Also, when acting as an authenticator, we don't have a mechanism to
     * set r->user based on the script response, so scripts can't implement
     * a private authentication scheme; instead we use ap_get_basic_auth_pw()
     * and only support Basic HTTP authentication.
     *
     * It is possible to act reliably as both authenticator and authorizer
     * if mod_authn_core is loaded to support AuthType and AuthName, but
     * mod_authz_core and mod_auth_basic are not loaded.  However, in this
     * case the Require directive is not available, which defeats many
     * common configuration tropes.
     */

    switch (auth_check_mode) {
    case FCGID_AUTH_CHECK_AUTHN:
        auth_cmd_info = get_authenticator_info(r, &authoritative);
        auth_role = "AUTHENTICATOR";
        role_log_msg = "Authentication";
        break;

    case FCGID_AUTH_CHECK_AUTHZ:
        auth_cmd_info = get_authorizer_info(r, &authoritative);
        auth_role = "AUTHORIZER";
        role_log_msg = "Authorization";
        break;

    case FCGID_AUTH_CHECK_ACCESS:
        auth_cmd_info = get_access_info(r, &authoritative);
        auth_role = "ACCESS_CHECKER";
        role_log_msg = "Access check";
        break;
    }

    /* Is this auth check command enabled? */
    if (auth_cmd_info == NULL)
        return DECLINED;

    /* Get the user password */
    if (auth_check_mode == FCGID_AUTH_CHECK_AUTHN
        && (res = ap_get_basic_auth_pw(r, &password)) != OK) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                      "mod_fcgid: authenticator requires "
                      "basic HTTP auth credentials");
        return res;
    }

    if (auth_check_mode != FCGID_AUTH_CHECK_ACCESS) {
        user_log_msg = apr_psprintf(r->pool, " of user %s", r->user);
    }

    /* Save old process environment */
    saved_subprocess_env = apr_table_copy(r->pool, r->subprocess_env);

    /* Add some environment variables */
    ap_add_common_vars(r);
    ap_add_cgi_vars(r);
    fcgid_add_cgi_vars(r);
    if (auth_check_mode == FCGID_AUTH_CHECK_AUTHN) {
        apr_table_setn(r->subprocess_env, "REMOTE_PASSWD", password);
    }
    apr_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", auth_role);

    /* Drop the variables CONTENT_LENGTH, PATH_INFO, PATH_TRANSLATED,
     * SCRIPT_NAME and most Hop-By-Hop headers - EXCEPT we will pass
     * PROXY_AUTH to allow CGI to perform proxy auth for httpd
     */
    apr_table_unset(r->subprocess_env, "CONTENT_LENGTH");
    apr_table_unset(r->subprocess_env, "PATH_INFO");
    apr_table_unset(r->subprocess_env, "PATH_TRANSLATED");
    apr_table_unset(r->subprocess_env, "SCRIPT_NAME");
    apr_table_unset(r->subprocess_env, "HTTP_KEEP_ALIVE");
    apr_table_unset(r->subprocess_env, "HTTP_TE");
    apr_table_unset(r->subprocess_env, "HTTP_TRAILER");
    apr_table_unset(r->subprocess_env, "HTTP_TRANSFER_ENCODING");
    apr_table_unset(r->subprocess_env, "HTTP_UPGRADE");

    /* Connection hop-by-hop header to prevent the CGI from hanging */
    apr_table_set(r->subprocess_env, "HTTP_CONNECTION", "close");

    /* Handle the request */
    res = bridge_request(r, FCGI_AUTHORIZER, auth_cmd_info);

    /* Restore r->subprocess_env */
    r->subprocess_env = saved_subprocess_env;

    if (res == OK && r->status == HTTP_OK
        && apr_table_get(r->headers_out, "Location") == NULL) {
        /* Pass */
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                      "mod_fcgid: %s%s to access %s succeeded",
                      role_log_msg, user_log_msg, r->uri);

        /* Modify headers: An Authorizer application's 200 response may include headers
           whose names are prefixed with Variable-.  */
        apr_table_do(mod_fcgid_modify_auth_header, r->subprocess_env,
                     r->err_headers_out, NULL);

        return OK;
    }
    else {
        const char *add_err_msg = "";

        /* Print error info first */
        if (res != OK) {
            add_err_msg =
                apr_psprintf(r->pool, "; error or unexpected condition "
                                      "while parsing response (%d)", res);
        }
        else if (r->status == HTTP_OK) {
            add_err_msg = "; internal redirection not allowed";
        }
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                      "mod_fcgid: %s%s to access %s failed, reason: "
                      "script returned status %d%s",
                      role_log_msg, user_log_msg, r->uri, r->status,
                      add_err_msg);

        /* Handle error */
        if (!authoritative) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                          "mod_fcgid: not authoritative");
            return DECLINED;
        }
        else {
            if (auth_check_mode != FCGID_AUTH_CHECK_ACCESS) {
                ap_note_basic_auth_failure(r);
            }
            return (res == OK) ? HTTP_UNAUTHORIZED : res;
        }
    }
}