Esempio n. 1
0
int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status)
{
    int signum = status;
    const char *sigdesc = apr_signal_description_get(signum);

    /* Child died... if it died due to a fatal error,
     * we should simply bail out.  The caller needs to
     * check for bad rc from us and exit, running any
     * appropriate cleanups.
     *
     * If the child died due to a resource shortage,
     * the parent should limit the rate of forking
     */
    if (APR_PROC_CHECK_EXIT(why)) {
        if (status == APEXIT_CHILDSICK) {
            return status;
        }

        if (status == APEXIT_CHILDFATAL) {
            ap_log_error(APLOG_MARK, APLOG_ALERT,
                         0, ap_server_conf,
                         "Child %" APR_PID_T_FMT
                         " returned a Fatal error... Apache is exiting!",
                         pid->pid);
            return APEXIT_CHILDFATAL;
        }

        return 0;
    }

    if (APR_PROC_CHECK_SIGNALED(why)) {
        switch (signum) {
        case SIGTERM:
        case SIGHUP:
        case AP_SIG_GRACEFUL:
        case SIGKILL:
            break;

        default:
            if (APR_PROC_CHECK_CORE_DUMP(why)) {
                ap_log_error(APLOG_MARK, APLOG_NOTICE,
                             0, ap_server_conf,
                             "child pid %ld exit signal %s (%d), "
                             "possible coredump in %s",
                             (long)pid->pid, sigdesc, signum,
                             ap_coredump_dir);
            }
            else {
                ap_log_error(APLOG_MARK, APLOG_NOTICE,
                             0, ap_server_conf,
                             "child pid %ld exit signal %s (%d)",
                             (long)pid->pid, sigdesc, signum);
            }
        }
    }
    return 0;
}
Esempio n. 2
0
/* Helper function for run_hook_cmd().  Wait for a hook to finish
   executing and return either SVN_NO_ERROR if the hook script completed
   without error, or an error describing the reason for failure.

   NAME and CMD are the name and path of the hook program, CMD_PROC
   is a pointer to the structure representing the running process,
   and READ_ERRHANDLE is an open handle to the hook's stderr.

   Hooks are considered to have failed if we are unable to wait for the
   process, if we are unable to read from the hook's stderr, if the
   process has failed to exit cleanly (due to a coredump, for example),
   or if the process returned a non-zero return code.

   Any error output returned by the hook's stderr will be included in an
   error message, though the presence of output on stderr is not itself
   a reason to fail a hook. */
static svn_error_t *
check_hook_result(const char *name, const char *cmd, apr_proc_t *cmd_proc,
                  apr_file_t *read_errhandle, apr_pool_t *pool)
{
  svn_error_t *err, *err2;
  svn_stringbuf_t *native_stderr, *failure_message;
  const char *utf8_stderr;
  int exitcode;
  apr_exit_why_e exitwhy;

  err2 = svn_stringbuf_from_aprfile(&native_stderr, read_errhandle, pool);

  err = svn_io_wait_for_cmd(cmd_proc, cmd, &exitcode, &exitwhy, pool);
  if (err)
    {
      svn_error_clear(err2);
      return svn_error_trace(err);
    }

  if (APR_PROC_CHECK_EXIT(exitwhy) && exitcode == 0)
    {
      /* The hook exited cleanly.  However, if we got an error reading
         the hook's stderr, fail the hook anyway, because this might be
         symptomatic of a more important problem. */
      if (err2)
        {
          return svn_error_createf
            (SVN_ERR_REPOS_HOOK_FAILURE, err2,
             _("'%s' hook succeeded, but error output could not be read"),
             name);
        }

      return SVN_NO_ERROR;
    }

  /* The hook script failed. */

  /* If we got the stderr output okay, try to translate it into UTF-8.
     Ensure there is something sensible in the UTF-8 string regardless. */
  if (!err2)
    {
      err2 = svn_utf_cstring_to_utf8(&utf8_stderr, native_stderr->data, pool);
      if (err2)
        utf8_stderr = _("[Error output could not be translated from the "
                        "native locale to UTF-8.]");
    }
  else
    {
      utf8_stderr = _("[Error output could not be read.]");
    }
  /*### It would be nice to include the text of any translation or read
        error in the messages above before we clear it here. */
  svn_error_clear(err2);

  if (!APR_PROC_CHECK_EXIT(exitwhy))
    {
      failure_message = svn_stringbuf_createf(pool,
        _("'%s' hook failed (did not exit cleanly: "
          "apr_exit_why_e was %d, exitcode was %d).  "),
        name, exitwhy, exitcode);
    }
  else
    {
      const char *action;
      if (strcmp(name, "start-commit") == 0
          || strcmp(name, "pre-commit") == 0)
        action = _("Commit");
      else if (strcmp(name, "pre-revprop-change") == 0)
        action = _("Revprop change");
      else if (strcmp(name, "pre-lock") == 0)
        action = _("Lock");
      else if (strcmp(name, "pre-unlock") == 0)
        action = _("Unlock");
      else
        action = NULL;
      if (action == NULL)
        failure_message = svn_stringbuf_createf(
            pool, _("%s hook failed (exit code %d)"),
            name, exitcode);
      else
        failure_message = svn_stringbuf_createf(
            pool, _("%s blocked by %s hook (exit code %d)"),
            action, name, exitcode);
    }

  if (utf8_stderr[0])
    {
      svn_stringbuf_appendcstr(failure_message,
                               _(" with output:\n"));
      svn_stringbuf_appendcstr(failure_message, utf8_stderr);
    }
  else
    {
      svn_stringbuf_appendcstr(failure_message,
                               _(" with no output."));
    }

  return svn_error_create(SVN_ERR_REPOS_HOOK_FAILURE, err,
                          failure_message->data);
}
/* 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;
}