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 */
 };
 
openSUSE Build Service is sponsored by