コード例 #1
0
ファイル: mod_suphp.c プロジェクト: cloudlinux/mod_securephp
static int suphp_handler(request_rec *r) {
    suphp_conf *sconf;
    suphp_conf *dconf;

#ifdef SUPHP_USE_USERGROUP
    char *ud_user = NULL;
    char *ud_group = NULL;
    int ud_success = 0;
#endif

    struct stat finfo;

    int rv;

    char *auth_user = NULL;
    char *auth_pass = NULL;

    pool *p;

    BUFF *script_in, *script_out, *script_err;

    const char *handler;

    sconf = ap_get_module_config(r->server->module_config, &suphp_module);
    dconf = ap_get_module_config(r->per_dir_config, &suphp_module);

    p = r->main ? r->main->pool : r->pool;

    /* only handle request if mod_suphp is active for this handler */
    /* check only first byte of value (second has to be \0) */
    if (r->handler != NULL) {
        handler = r->handler;
    } else {
        handler = r->content_type;
    }
    if ((ap_table_get(dconf->handlers, handler) == NULL)) {
        if ((ap_table_get(sconf->handlers, handler) == NULL)
            || (*(ap_table_get(sconf->handlers, handler)) == '0')) {
            return DECLINED;
        }
    } else if (*(ap_table_get(dconf->handlers, handler)) == '0') {
        return DECLINED;
    }

    /* check if suPHP is enabled for this request */

    if (((sconf->engine != SUPHP_ENGINE_ON)
         && (dconf->engine != SUPHP_ENGINE_ON))
        || ((sconf->engine == SUPHP_ENGINE_ON)
            && (dconf->engine == SUPHP_ENGINE_OFF)))
        return DECLINED;

    /* check if file is existing and accessible */

    rv = stat(ap_pstrdup(p, r->filename), &finfo);
    if (rv == 0) {
        ; /* do nothing */
    } else if (errno == EACCES) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "access to %s denied",
                      r->filename);
        return HTTP_FORBIDDEN;
    } else if (errno == ENOENT || errno == ENOTDIR) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "File does not exist: %s",
                      r->filename);
        return HTTP_NOT_FOUND;
    } else {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "could not get fileinfo: %s",
                      r->filename);
        return HTTP_NOT_FOUND;
    }

#ifdef SUPHP_USE_USERGROUP
    if ((sconf->target_user == NULL || sconf->target_group == NULL)
        && (dconf->target_user == NULL || dconf->target_group == NULL)) {

        /* Identify mod_userdir request
           As Apache 1.3 does not yet provide a clean way to see
           whether a request was handled by mod_userdir, we assume
           this is true for any request beginning with ~ */

        int ud_success = 0; /* set to 1 on success */

        if (!strncmp("/~", r->uri, 2)) {
            char *username = ap_pstrdup(r->pool, r->uri + 2);
            char *pos = strchr(username, '/');
            if (pos) {
                *pos = 0;
                if (strlen(username)) {
                    struct passwd *pw;
                    struct group *gr;
                    gid_t gid;
                    char *grpname;
                    if ((pw = getpwnam(username)) != NULL) {
                        gid = pw->pw_gid;

                        if ((gr = getgrgid(gid)) != NULL) {
                            grpname = gr->gr_name;
                        } else {
                            if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
                                return HTTP_INTERNAL_SERVER_ERROR;
                            }
                            ap_snprintf(grpname, 16, "#%ld", (long) gid);
                        }

                        ud_user = username;
                        ud_group = grpname;
                        ud_success = 1;
                    }
                }
            }
        }

        if (!ud_success) {
            /* This is not a userdir request and user/group are not
               set, so log the error and return */
            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
                          "No user or group set - set suPHP_UserGroup");
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    }
#endif /* SUPHP_USE_USERGROUP */


    /* prepare environment for new process */

    ap_add_common_vars(r);
    ap_add_cgi_vars(r);

    ap_table_unset(r->subprocess_env, "SUPHP_PHP_CONFIG");
    ap_table_unset(r->subprocess_env, "SUPHP_AUTH_USER");
    ap_table_unset(r->subprocess_env, "SUPHP_AUTH_PW");

#ifdef SUPHP_USE_USERGROUP
    ap_table_unset(r->subprocess_env, "SUPHP_USER");
    ap_table_unset(r->subprocess_env, "SUPHP_GROUP");
    ap_table_unset(r->subprocess_env, "SUPHP_USERDIR_USER");
    ap_table_unset(r->subprocess_env, "SUPHP_USERDIR_GROUP");
#endif /* SUPHP_USE_USERGROUP */

    if (dconf->php_config) {
        ap_table_set(r->subprocess_env, "SUPHP_PHP_CONFIG", dconf->php_config);
    }

    ap_table_set(r->subprocess_env, "SUPHP_HANDLER", handler);

    if (r->headers_in) {
        const char *auth;
        auth = ap_table_get(r->headers_in, "Authorization");
        if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0) {
            char *user;
            char *pass;
            user = ap_pbase64decode(p, auth + 6);
            if (user) {
                pass = strchr(user, ':');
                if (pass) {
                    *pass++ = '\0';
                    auth_user = ap_pstrdup(p, user);
                    auth_pass = ap_pstrdup(p, pass);
                }
            }
        }
    }

    if (auth_user && auth_pass) {
        ap_table_setn(r->subprocess_env, "SUPHP_AUTH_USER", auth_user);
        ap_table_setn(r->subprocess_env, "SUPHP_AUTH_PW", auth_pass);
    }

#ifdef SUPHP_USE_USERGROUP
    if (dconf->target_user) {
        ap_table_set(r->subprocess_env, "SUPHP_USER", dconf->target_user);
    } else if (sconf->target_user) {
        ap_table_set(r->subprocess_env, "SUPHP_USER", sconf->target_user);
    } else {
        ap_table_set(r->subprocess_env, "SUPHP_USER", ud_user);
    }

    if (dconf->target_group) {
        ap_table_set(r->subprocess_env, "SUPHP_GROUP", dconf->target_group);
    } else if (sconf->target_group) {
        ap_table_set(r->subprocess_env, "SUPHP_GROUP", sconf->target_group);
    } else {
        ap_table_set(r->subprocess_env, "SUPHP_GROUP", ud_group);
    }
    if (ud_success) {
	ap_table_set(r->subprocess_env, "SUPHP_USERDIR_USER", ud_user);
	ap_table_set(r->subprocess_env, "SUPHP_USERDIR_GROUP", ud_group);
    }
#endif /* SUPHP_USE_USERGROUP */

    /* Fork child process */

    if (!ap_bspawn_child(p, suphp_child, (void *) r, kill_after_timeout,
                         &script_in, &script_out, &script_err)) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
                      "couldn't spawn child process for: %s", r->filename);
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    /* Transfer request body to script */

    if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK) {
        /* Call failed, return status */
        return rv;
    }

    if (ap_should_client_block(r)) {
        char buffer[HUGE_STRING_LEN];
        int len_read;

        ap_hard_timeout("reading request body", r);

        while ((len_read = ap_get_client_block(r, buffer, HUGE_STRING_LEN))
               > 0) {
            ap_reset_timeout(r);
            if (ap_bwrite(script_in, buffer, len_read) < len_read) {
                /* silly script stopped reading, soak up remaining message */
                while (ap_get_client_block(r, buffer, HUGE_STRING_LEN) > 0) {
                    /* dump it */
                }
                break;
            }
        }

        ap_bflush(script_in);
        ap_kill_timeout(r);
    }

    ap_bclose(script_in);

    /* Transfer output from script to client */

    if (script_out) {
        const char *location;
        char hbuffer[MAX_STRING_LEN];
        char buffer[HUGE_STRING_LEN];

        rv = ap_scan_script_header_err_buff(r, script_out, hbuffer);
        if (rv == HTTP_NOT_MODIFIED) {
            return rv;
        } else if (rv) {
            return HTTP_INTERNAL_SERVER_ERROR;
        }

        location = ap_table_get(r->headers_out, "Location");
        if (location && r->status == 200) {
            /* Soak up all the script output */
            ap_hard_timeout("reading from script", r);
            while (ap_bgets(buffer, HUGE_STRING_LEN, script_out) > 0) {
                continue;
            }
            ap_kill_timeout(r);
            ap_bclose(script_out);
            ap_bclose(script_err);

            if (location[0] == '/') {
                /* Redirect has always GET method */
                r->method = ap_pstrdup(p, "GET");
                r->method_number = M_GET;

                /* Remove Content-Length - redirect should not read  *
                 * request body                                      */
                ap_table_unset(r->headers_in, "Content-Length");

                /* Do the redirect */
                ap_internal_redirect_handler(location, r);
                return OK;
            } else {
                /* Script did not set status 302 - so it does not want *
                 * to send its own body. Simply set redirect status    */
                return REDIRECT;
            }
        }

        /* Output headers and body */

        ap_send_http_header(r);
        if (!r->header_only) {
            ap_send_fb(script_out, r);
        }
        ap_bclose(script_out);
        /* Errors have already been logged by child */
        ap_bclose(script_err);
    }

    return OK;
}
コード例 #2
0
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;
}