Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Evergreen:Maintenance:4666
apache2.openSUSE_13.1_Update
httpd-2.4.x-bnc887768-CVE-2014-0231-mod_cgid-ha...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File httpd-2.4.x-bnc887768-CVE-2014-0231-mod_cgid-hang.diff of Package apache2.openSUSE_13.1_Update
diff -rNU 30 ../httpd-2.4.6-o/modules/generators/mod_cgid.c ./modules/generators/mod_cgid.c --- ../httpd-2.4.6-o/modules/generators/mod_cgid.c 2013-01-08 21:49:50.000000000 +0100 +++ ./modules/generators/mod_cgid.c 2014-07-29 18:04:39.000000000 +0200 @@ -71,60 +71,66 @@ #include <sys/un.h> /* for sockaddr_un */ #if APR_HAVE_STRUCT_RLIMIT #if defined (RLIMIT_CPU) || defined (RLIMIT_NPROC) || defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) #define AP_CGID_USE_RLIMIT #endif #endif module AP_MODULE_DECLARE_DATA cgid_module; static int cgid_start(apr_pool_t *p, server_rec *main_server, apr_proc_t *procnew); static int cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server); static int handle_exec(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb); static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgid_pfn_reg_with_ssi; static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgid_pfn_gtv; static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgid_pfn_ps; static apr_pool_t *pcgi = NULL; static int total_modules = 0; static pid_t daemon_pid; static int daemon_should_exit = 0; static server_rec *root_server = NULL; static apr_pool_t *root_pool = NULL; static const char *sockname; static struct sockaddr_un *server_addr; static apr_socklen_t server_addr_len; static pid_t parent_pid; static ap_unix_identity_t empty_ugid = { (uid_t)-1, (gid_t)-1, -1 }; +typedef struct { + apr_interval_time_t timeout; +} cgid_dirconf; + + + /* The APR other-child API doesn't tell us how the daemon exited * (SIGSEGV vs. exit(1)). The other-child maintenance function * needs to decide whether to restart the daemon after a failure * based on whether or not it exited due to a fatal startup error * or something that happened at steady-state. This exit status * is unlikely to collide with exit signals. */ #define DAEMON_STARTUP_ERROR 254 /* Read and discard the data in the brigade produced by a CGI script */ static void discard_script_output(apr_bucket_brigade *bb); /* This doer will only ever be called when we are sure that we have * a valid ugid. */ static ap_unix_identity_t *cgid_suexec_id_doer(const request_rec *r) { return (ap_unix_identity_t *) ap_get_module_config(r->request_config, &cgid_module); } /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI * in ScriptAliased directories, which means we need to know if this * request came through ScriptAlias or not... so the Alias module * leaves a note for us. */ static int is_scriptaliased(request_rec *r) { const char *t = apr_table_get(r->notes, "alias-forced-type"); @@ -946,125 +952,145 @@ cgid_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string); if ((cgid_pfn_reg_with_ssi) && (cgid_pfn_gtv) && (cgid_pfn_ps)) { /* Required by mod_include filter. This is how mod_cgid registers * with mod_include to provide processing of the exec directive. */ cgid_pfn_reg_with_ssi("exec", handle_exec); } } return ret; } static void *create_cgid_config(apr_pool_t *p, server_rec *s) { cgid_server_conf *c = (cgid_server_conf *) apr_pcalloc(p, sizeof(cgid_server_conf)); c->logname = NULL; c->logbytes = DEFAULT_LOGBYTES; c->bufbytes = DEFAULT_BUFBYTES; return c; } static void *merge_cgid_config(apr_pool_t *p, void *basev, void *overridesv) { cgid_server_conf *base = (cgid_server_conf *) basev, *overrides = (cgid_server_conf *) overridesv; return overrides->logname ? overrides : base; } +static void *create_cgid_dirconf(apr_pool_t *p, char *dummy) +{ + cgid_dirconf *c = (cgid_dirconf *) apr_pcalloc(p, sizeof(cgid_dirconf)); + return c; +} + static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) + { server_rec *s = cmd->server; cgid_server_conf *conf = ap_get_module_config(s->module_config, &cgid_module); conf->logname = ap_server_root_relative(cmd->pool, arg); if (!conf->logname) { return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ", arg, NULL); } return NULL; } static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; cgid_server_conf *conf = ap_get_module_config(s->module_config, &cgid_module); conf->logbytes = atol(arg); return NULL; } static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; cgid_server_conf *conf = ap_get_module_config(s->module_config, &cgid_module); conf->bufbytes = atoi(arg); return NULL; } static const char *set_script_socket(cmd_parms *cmd, void *dummy, const char *arg) { const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } /* Make sure the pid is appended to the sockname */ sockname = ap_append_pid(cmd->pool, arg, "."); sockname = ap_runtime_dir_relative(cmd->pool, sockname); if (!sockname) { return apr_pstrcat(cmd->pool, "Invalid ScriptSock path", arg, NULL); } return NULL; } +static const char *set_script_timeout(cmd_parms *cmd, void *dummy, const char *arg) +{ + cgid_dirconf *dc = dummy; + if (ap_timeout_parameter_parse(arg, &dc->timeout, "s") != APR_SUCCESS) { + return "CGIDScriptTimeout has wrong format"; + } + + return NULL; +} static const command_rec cgid_cmds[] = { AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF, "the name of a log for script debugging info"), AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, "the maximum length (in bytes) of the script debug log"), AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, "the maximum size (in bytes) to record of a POST request"), AP_INIT_TAKE1("ScriptSock", set_script_socket, NULL, RSRC_CONF, "the name of the socket to use for communication with " "the cgi daemon."), + AP_INIT_TAKE1("CGIDScriptTimeout", set_script_timeout, NULL, RSRC_CONF | ACCESS_CONF, + "The amount of time to wait between successful reads from " + "the CGI script, in seconds."), + {NULL} }; static int log_scripterror(request_rec *r, cgid_server_conf * conf, int ret, apr_status_t rv, char *error) { apr_file_t *f = NULL; struct stat finfo; char time_str[APR_CTIME_LEN]; int log_flags = rv ? APLOG_ERR : APLOG_ERR; ap_log_rerror(APLOG_MARK, log_flags, rv, r, "%s: %s", error, r->filename); /* XXX Very expensive mainline case! Open, then getfileinfo! */ if (!conf->logname || ((stat(conf->logname, &finfo) == 0) && (finfo.st_size > conf->logbytes)) || (apr_file_open(&f, conf->logname, APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { return ret; } /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ apr_ctime(time_str, apr_time_now()); apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, r->args ? "?" : "", r->args ? r->args : "", r->protocol); /* "%% 500 /usr/local/apache/cgid-bin */ apr_file_printf(f, "%%%% %d %s\n", ret, r->filename); @@ -1334,66 +1360,70 @@ /* wait for pid of script */ stat = sock_read(sd, &pid, sizeof(pid)); if (stat != APR_SUCCESS) { return stat; } if (pid == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, info->r, APLOGNO(01261) "daemon couldn't find CGI process for connection %lu", info->conn_id); return APR_EGENERAL; } return cleanup_nonchild_process(info->r, pid); } static int cgid_handler(request_rec *r) { int retval, nph, dbpos; char *argv0, *dbuf; apr_bucket_brigade *bb; apr_bucket *b; cgid_server_conf *conf; int is_included; int seen_eos, child_stopped_reading; int sd; char **env; apr_file_t *tempsock; struct cleanup_script_info *info; apr_status_t rv; + cgid_dirconf *dc; if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { return DECLINED; } conf = ap_get_module_config(r->server->module_config, &cgid_module); + dc = ap_get_module_config(r->per_dir_config, &cgid_module); + + is_included = !strcmp(r->protocol, "INCLUDED"); if ((argv0 = strrchr(r->filename, '/')) != NULL) { argv0++; } else { argv0 = r->filename; } nph = !(strncmp(argv0, "nph-", 4)); argv0 = r->filename; if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) { return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(01262) "Options ExecCGI is off in this directory"); } if (nph && is_included) { return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(01263) "attempt to include NPH CGI script"); } #if defined(OS2) || defined(WIN32) #error mod_cgid does not work on this platform. If you teach it to, look #error at mod_cgi.c for required code in this path. #else if (r->finfo.filetype == APR_NOFILE) { return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, APLOGNO(01264) "script not found or unable to stat"); @@ -1419,60 +1449,66 @@ } */ ap_add_common_vars(r); ap_add_cgi_vars(r); env = ap_create_environment(r->pool, r->subprocess_env); if ((retval = connect_to_daemon(&sd, r, conf)) != OK) { return retval; } rv = send_req(sd, r, argv0, env, CGI_REQ); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01268) "write to cgi daemon process"); } info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); info->r = r; info->conn_id = r->connection->id; info->conf = conf; apr_pool_cleanup_register(r->pool, info, cleanup_script, apr_pool_cleanup_null); /* We are putting the socket discriptor into an apr_file_t so that we can * use a pipe bucket to send the data to the client. APR will create * a cleanup for the apr_file_t which will close the socket, so we'll * get rid of the cleanup we registered when we created the socket. */ apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool); + if (dc->timeout > 0) { + apr_file_pipe_timeout_set(tempsock, dc->timeout); + } + else { + apr_file_pipe_timeout_set(tempsock, r->server->timeout); + } apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket); /* Transfer any put/post args, CERN style... * Note that we already ignore SIGPIPE in the core server. */ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); seen_eos = 0; child_stopped_reading = 0; dbuf = NULL; dbpos = 0; if (conf->logname) { dbuf = apr_palloc(r->pool, conf->bufbytes + 1); } do { apr_bucket *bucket; rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); if (rv != APR_SUCCESS) { if (APR_STATUS_IS_TIMEUP(rv)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01269) "Timeout during reading request entity data"); return HTTP_REQUEST_TIME_OUT; } ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01270) "Error reading request entity data"); return HTTP_INTERNAL_SERVER_ERROR; } @@ -1495,60 +1531,64 @@ /* If the child stopped, we still must read to EOS. */ if (child_stopped_reading) { continue; } /* read */ apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); if (conf->logname && dbpos < conf->bufbytes) { int cursize; if ((dbpos + len) > conf->bufbytes) { cursize = conf->bufbytes - dbpos; } else { cursize = len; } memcpy(dbuf + dbpos, data, cursize); dbpos += cursize; } /* Keep writing data to the child until done or too much time * elapses with no progress or an error occurs. */ rv = apr_file_write_full(tempsock, data, len, NULL); if (rv != APR_SUCCESS) { /* silly script stopped reading, soak up remaining message */ child_stopped_reading = 1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "Error writing request body to script %s", + r->filename); + } } apr_brigade_cleanup(bb); } while (!seen_eos); if (conf->logname) { dbuf[dbpos] = '\0'; } /* we're done writing, or maybe we didn't write at all; * force EOF on child's stdin so that the cgi detects end (or * absence) of data */ shutdown(sd, 1); /* Handle script return... */ if (!nph) { conn_rec *c = r->connection; const char *location; char sbuf[MAX_STRING_LEN]; int ret; bb = apr_brigade_create(r->pool, c->bucket_alloc); b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, @@ -1588,61 +1628,67 @@ if (location && location[0] == '/' && r->status == 200) { /* Soak up all the script output */ discard_script_output(bb); apr_brigade_destroy(bb); /* This redirect needs to be a GET no matter what the original * method was. */ r->method = "GET"; r->method_number = M_GET; /* We already read the message body (if any), so don't allow * the redirected request to think it has one. We can ignore * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. */ apr_table_unset(r->headers_in, "Content-Length"); ap_internal_redirect_handler(location, r); return OK; } else if (location && r->status == 200) { /* XXX: Note that if a script wants to produce its own Redirect * body, it now has to explicitly *say* "Status: 302" */ discard_script_output(bb); apr_brigade_destroy(bb); return HTTP_MOVED_TEMPORARILY; } - ap_pass_brigade(r->output_filters, bb); + rv = ap_pass_brigade(r->output_filters, bb); + if (rv != APR_SUCCESS) { + /* APLOG_ERR because the core output filter message is at error, + * but doesn't know it's passing CGI output + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02550) "Failed to flush CGI output to client"); + } } if (nph) { conn_rec *c = r->connection; struct ap_filter_t *cur; /* get rid of all filters up through protocol... since we * haven't parsed off the headers, there is no way they can * work */ cur = r->proto_output_filters; while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { cur = cur->next; } r->output_filters = r->proto_output_filters = cur; bb = apr_brigade_create(r->pool, c->bucket_alloc); b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); ap_pass_brigade(r->output_filters, bb); } return OK; /* NOT r->status, even if it has changed. */ } @@ -1719,89 +1765,98 @@ apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info)); pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, NULL); if (pa_req->filename) { apr_table_setn(e, "PATH_TRANSLATED", apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, NULL)); } ap_destroy_sub_req(pa_req); } if (r->args) { char *arg_copy = apr_pstrdup(r->pool, r->args); apr_table_setn(e, "QUERY_STRING", r->args); ap_unescape_url(arg_copy); apr_table_setn(e, "QUERY_STRING_UNESCAPED", ap_escape_shell_cmd(r->pool, arg_copy)); } } static int include_cmd(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb, char *command) { char **env; int sd; int retval; apr_file_t *tempsock = NULL; request_rec *r = f->r; cgid_server_conf *conf = ap_get_module_config(r->server->module_config, &cgid_module); + cgid_dirconf *dc = ap_get_module_config(r->per_dir_config, &cgid_module); + struct cleanup_script_info *info; add_ssi_vars(r); env = ap_create_environment(r->pool, r->subprocess_env); if ((retval = connect_to_daemon(&sd, r, conf)) != OK) { return retval; } send_req(sd, r, command, env, SSI_REQ); info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); info->r = r; info->conn_id = r->connection->id; info->conf = conf; /* for this type of request, the script is invoked through an * intermediate shell process... cleanup_script is only able * to knock out the shell process, not the actual script */ apr_pool_cleanup_register(r->pool, info, cleanup_script, apr_pool_cleanup_null); /* We are putting the socket discriptor into an apr_file_t so that we can * use a pipe bucket to send the data to the client. APR will create * a cleanup for the apr_file_t which will close the socket, so we'll * get rid of the cleanup we registered when we created the socket. */ apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool); + if (dc->timeout > 0) { + apr_file_pipe_timeout_set(tempsock, dc->timeout); + } + else { + apr_file_pipe_timeout_set(tempsock, r->server->timeout); + } + apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket); APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pipe_create(tempsock, f->c->bucket_alloc)); ctx->flush_now = 1; return APR_SUCCESS; } static apr_status_t handle_exec(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb) { char *tag = NULL; char *tag_val = NULL; request_rec *r = f->r; char *file = r->filename; char parsed_string[MAX_STRING_LEN]; if (!ctx->argc) { ap_log_rerror(APLOG_MARK, (ctx->flags & SSI_FLAG_PRINTING) ? APLOG_ERR : APLOG_WARNING, 0, r, "missing argument for exec element in %s", r->filename); } if (!(ctx->flags & SSI_FLAG_PRINTING)) { return APR_SUCCESS; } @@ -1853,38 +1908,38 @@ } } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01274) "unknown parameter " "\"%s\" to tag exec in %s", tag, file); SSI_CREATE_ERROR_BUCKET(ctx, f, bb); break; } } return APR_SUCCESS; } /*============================================================================ *============================================================================ * This is the end of the cgi filter code moved from mod_include. *============================================================================ *============================================================================*/ static void register_hook(apr_pool_t *p) { static const char * const aszPre[] = { "mod_include.c", NULL }; ap_hook_pre_config(cgid_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_config(cgid_init, aszPre, NULL, APR_HOOK_MIDDLE); ap_hook_handler(cgid_handler, NULL, NULL, APR_HOOK_MIDDLE); } AP_DECLARE_MODULE(cgid) = { STANDARD20_MODULE_STUFF, - NULL, /* dir config creater */ + create_cgid_dirconf, /* dir config creater */ NULL, /* dir merger --- default is to override */ create_cgid_config, /* server config */ merge_cgid_config, /* merge server config */ cgid_cmds, /* command table */ register_hook /* register_handlers */ };
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor