Apache 2.0 STATUS:
-Last modified at [$Date: 2000/10/23 14:48:06 $]
+Last modified at [$Date: 2000/10/23 15:30:44 $]
Release:
* Some buff functionality is currently missing: translation of protocol
data for EBCDIC machines
- * suEXEC doesn't work
- Status: Manoj has posted an patch to fix this.
- Message-ID: <20000825024943.A17578@manojk.users.mindspring.com>
-
* Win32: Enable the Windows MPM to honor max_requests_per_child
Status: Bill will fix this.
AP_DECLARE(void) ap_start_shutdown(void);
+/**
+ * Spawn a process with privileges that another module has requested
+ * @param r The request_rec of the current request
+ * @param newproc The resulting process handle.
+ * @param progname The program to run
+ * @param const_args the arguments to pass to the new program. The first
+ * one should be the program name.
+ * @param env The new environment apr_table_t for the new process. This
+ * should be a list of NULL-terminated strings.
+ * @param attr the procattr we should use to determine how to create the new
+ * process
+ * @param p The pool to use.
+ */
+extern apr_status_t ap_os_create_privileged_process(const request_rec *r,
+ apr_proc_t *newproc, const char *progname,
+ char *const *args, char **env,
+ apr_procattr_t *attr, apr_pool_t *p);
+
+
#endif
/** Wildcarded names for ServerAlias servers */
apr_array_header_t *wild_names;
- /** effective user id when calling exec wrapper */
- uid_t server_uid;
- /** effective group id when calling exec wrapper */
- gid_t server_gid;
-
/** limit on size of the HTTP request line */
int limit_req_line;
/** limit on size of any request header field */
APACHE_CHECK_STANDARD_MODULE(actions, Action triggering on requests, action, yes)
APACHE_CHECK_STANDARD_MODULE(speling, correct common URL misspellings, , no)
APACHE_CHECK_STANDARD_MODULE(userdir, mapping of user requests, , yes)
+APACHE_CHECK_STANDARD_MODULE(suexec, set uid and gid for spawned processes, , yes)
APACHE_CHECK_STANDARD_MODULE(alias, translation of requests, , yes)
APACHE_CHECK_STANDARD_MODULE(rewrite, regex URL translation, , no, [
build_argv_list(&argv, r, r->pool);
argv[0] = apr_pstrdup(r->pool, s);
procnew = apr_pcalloc(r->pool, sizeof(*procnew));
- rc = apr_create_process(procnew, s, argv, ap_create_environment(r->pool, env), procattr, r->pool);
+ rc = ap_os_create_privileged_process(r, procnew, s, argv, ap_create_environment(r->pool, env), procattr, r->pool);
if (rc != APR_SUCCESS) {
/* Bad things happened. Everyone should have cleaned up. */
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
+#include "ap_mpm.h"
#include "http_conf_globals.h"
#ifdef HAVE_STRING_H
#include <string.h>
"couldn't set child process attributes: %s", r->filename);
}
else {
- rc = apr_create_process(procnew, command, argv, env, procattr, p);
+ rc = ap_os_create_privileged_process(r, procnew, command, argv, env, procattr, p);
if (rc != APR_SUCCESS) {
/* Bad things happened. Everyone should have cleaned up. */
static apr_status_t build_command_line(char **cmd, request_rec *r, apr_pool_t *p)
{
+ char *argv0;
+
+ /* Allow suexec's "/" check to succeed */
+ if ((argv0 = strrchr(r->filename, '/')) != NULL)
+ argv0++;
+ else
+ argv0 = r->filename;
+
#ifdef WIN32
char *quoted_filename = NULL;
char *interpreter = NULL;
}
/*
- * Build the command string to pass to apr_create_process()
+ * Build the command string to pass to ap_os_create_privileged_process()
*/
- quoted_filename = apr_pstrcat(p, "\"", r->filename, "\"", NULL);
+ quoted_filename = apr_pstrcat(p, "\"", argv0, "\"", NULL);
if (interpreter && *interpreter) {
if (arguments && *arguments)
*cmd = apr_pstrcat(p, interpreter, " ", quoted_filename, " ",
*cmd = apr_pstrcat(p, quoted_filename, NULL);
}
#else
- *cmd = apr_pstrcat(p, r->filename, NULL);
+ *cmd = argv0;
#endif
return APR_SUCCESS;
}
* since that is better than allowing errors to go unnoticed.
*/
apr_put_os_file(&r->server->error_log, &errfileno, r->pool);
- /* TODO: reimplement suexec */
-#if 0
- if (ap_suexec_enabled
- && ((r->server->server_uid != ap_user_id)
- || (r->server->server_gid != ap_group_id)
- || (!strncmp("/~", r->uri, 2)))) {
-
- char *execuser, *grpname;
- struct passwd *pw;
- struct group *gr;
-
- if (!strncmp("/~", r->uri, 2)) {
- gid_t user_gid;
- char *username = apr_pstrdup(r->pool, r->uri + 2);
- char *pos = strchr(username, '/');
-
- if (pos) {
- *pos = '\0';
- }
-
- if ((pw = getpwnam(username)) == NULL) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
- "getpwnam: invalid username %s", username);
- return (pid);
- }
- execuser = apr_pstrcat(r->pool, "~", pw->pw_name, NULL);
- user_gid = pw->pw_gid;
-
- if ((gr = getgrgid(user_gid)) == NULL) {
- if ((grpname = apr_palloc(r->pool, 16)) == NULL) {
- return (pid);
- }
- else {
- apr_snprintf(grpname, 16, "%ld", (long) user_gid);
- }
- }
- else {
- grpname = gr->gr_name;
- }
- }
- else {
- if ((pw = getpwuid(r->server->server_uid)) == NULL) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
- "getpwuid: invalid userid %ld",
- (long) r->server->server_uid);
- return (pid);
- }
- execuser = apr_pstrdup(r->pool, pw->pw_name);
-
- if ((gr = getgrgid(r->server->server_gid)) == NULL) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
- "getgrgid: invalid groupid %ld",
- (long) r->server->server_gid);
- return (pid);
- }
- grpname = gr->gr_name;
- }
-
- if (shellcmd) {
- execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
- NULL, env);
- }
-
- else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
- execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
- NULL, env);
- }
-
- else {
- execve(SUEXEC_BIN,
- create_argv(r->pool, SUEXEC_BIN, execuser, grpname,
- argv0, r->args),
- env);
- }
+ if (shellcmd) {
+ execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
}
- else {
-#endif
- if (shellcmd) {
- execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
- }
- else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
- execle(r->filename, argv0, NULL, env);
- }
+ else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
+ execle(r->filename, argv0, NULL, env);
+ }
- else {
- execve(r->filename,
- create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
- env);
- }
-#if 0
+ else {
+ execve(r->filename,
+ create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
+ env);
}
-#endif
return (pid);
}
*env = environ;
r->args = ap_getword(r->pool, (const char **)&data, '\n');
- read(fd, &r->server->server_uid, sizeof(uid_t));
- read(fd, &r->server->server_gid, sizeof(gid_t));
-
read(fd, &i, sizeof(int));
/* add 1, so that if i == 0, we still malloc something. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
"write to cgi daemon process");
}
- if (write(fd, &r->server->server_uid, sizeof(uid_t)) < 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
- "write to cgi daemon process");
- }
- if (write(fd, &r->server->server_gid, sizeof(gid_t)) < 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
- "write to cgi daemon process");
- }
if (write(fd, &core_module.module_index, sizeof(int)) < 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
"write to cgi daemon process");
--- /dev/null
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_request.h"
+#include "apr_strings.h"
+#include "suexec.h"
+#include "unixd.h"
+
+module MODULE_VAR_EXPORT suexec_module;
+
+typedef struct {
+ ap_unix_identity_t ugid;
+ int active;
+} suexec_config_t;
+
+/*
+ * Create a configuration specific to this module for a server or directory
+ * location, and fill it with the default settings.
+ */
+static void *mkconfig(apr_pool_t *p)
+{
+ suexec_config_t *cfg = apr_palloc(p, sizeof(suexec_config_t));
+
+ cfg->active = 0;
+ return cfg;
+}
+
+/*
+ * Respond to a callback to create configuration record for a server or
+ * vhost environment.
+ */
+static void *create_mconfig_for_server(apr_pool_t *p, server_rec *s)
+{
+ return mkconfig(p);
+}
+
+/*
+ * Respond to a callback to create a config record for a specific directory.
+ */
+static void *create_mconfig_for_directory(apr_pool_t *p, char *dir)
+{
+ return mkconfig(p);
+}
+
+static const char *set_suexec_ugid(cmd_parms *cmd, void *mconfig,
+ char *uid, char *gid)
+{
+ suexec_config_t *cfg = (suexec_config_t *) mconfig;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+
+ if (err != NULL) {
+ return err;
+ }
+ if (unixd_config.suexec_enabled) {
+ cfg->ugid.uid = ap_uname2id(uid);
+ cfg->ugid.gid = ap_gname2id(gid);
+ cfg->active = 1;
+ }
+ else {
+ fprintf(stderr,
+ "Warning: SuexecUserGroup directive requires SUEXEC wrapper.\n");
+ }
+ return NULL;
+}
+
+static ap_unix_identity_t *get_suexec_id_doer(const request_rec *r)
+{
+ suexec_config_t *cfg =
+ (suexec_config_t *) ap_get_module_config(r->per_dir_config, &suexec_module);
+
+ return cfg->active ? &cfg->ugid : NULL;
+}
+
+/*
+ * Define the directives specific to this module. This structure is referenced
+ * later by the 'module' structure.
+ */
+static const command_rec suexec_cmds[] =
+{
+ /* XXX - Another important reason not to allow this in .htaccess is that
+ * the ap_[ug]name2id() is not thread-safe */
+ AP_INIT_TAKE2("SuexecUserGroup", set_suexec_ugid, NULL, RSRC_CONF,
+ "User and group for spawned processes"),
+ { NULL }
+};
+
+static void suexec_hooks(void)
+{
+ ap_hook_get_suexec_identity(get_suexec_id_doer,NULL,NULL,AP_HOOK_MIDDLE);
+}
+
+module MODULE_VAR_EXPORT suexec_module =
+{
+ STANDARD20_MODULE_STUFF,
+ create_mconfig_for_directory, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ create_mconfig_for_server, /* server config */
+ NULL, /* merge server config */
+ suexec_cmds, /* command table */
+ NULL, /* handlers */
+ suexec_hooks /* register hooks */
+};
}
else {
procnew = apr_pcalloc(p, sizeof(*procnew));
- rc = apr_create_process(procnew, progname, NULL, NULL, procattr, p);
+ rc = ap_os_create_privileged_process(r, procnew, progname, NULL, NULL, procattr, p);
if (rc == APR_SUCCESS) {
apr_note_subprocess(p, procnew, kill_after_timeout);
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
+#include "suexec.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
*/
if (*userdirs && dname[0] == 0)
r->finfo = statbuf;
+
+ /* For use in the get_suexec_identity phase */
+ apr_table_setn(r->notes, "mod_userdir_user", w);
+
return OK;
}
}
return DECLINED;
}
+static ap_unix_identity_t *get_suexec_id_doer(const request_rec *r)
+{
+ const char *username = apr_table_get(r->notes, "mod_userdir_user");
+ struct passwd *pw = NULL;
+ ap_unix_identity_t *ugid = NULL;
+
+ if (username == NULL) {
+ return NULL;
+ }
+
+ /* XXX - NOT thread-safe! Need APR version of this function */
+ if ((pw = getpwnam(username)) == NULL) {
+ /* This should never happen. */
+ return NULL;
+ }
+
+ if ((ugid = apr_palloc(r->pool, sizeof(ap_unix_identity_t *))) == NULL) {
+ return NULL;
+ }
+
+ ugid->uid = pw->pw_uid;
+ ugid->gid = pw->pw_gid;
+
+ return ugid;
+}
+
static void register_hooks(void)
{
static const char * const aszSucc[]={ "mod_alias.c",NULL };
ap_hook_translate_name(translate_userdir,NULL,aszSucc,AP_HOOK_MIDDLE);
+ ap_hook_get_suexec_identity(get_suexec_id_doer,NULL,NULL,AP_HOOK_MIDDLE);
}
module userdir_module = {
}
procnew = apr_pcalloc(child_context, sizeof(*procnew));
- rc = apr_create_process(procnew, compr[parm->method].argv[0],
+ rc = ap_os_create_privileged_rrocess(r, procnew, compr[parm->method].argv[0],
new_argv, env, procattr, child_context);
if (rc != APR_SUCCESS) {
#include "ap_config.h"
#include "os.h"
+#include "httpd.h"
int ap_os_is_path_absolute(const char *file)
{
return file[0] == '/';
}
+
+AP_DECLARE(apr_status_t) ap_os_create_privileged_process(const request_rec *r,
+ apr_proc_t *newproc, const char *progname,
+ char *const *args, char **env,
+ apr_procattr_t *attr, apr_pool_t *p)
+{
+ return apr_create_process(newproc, progname, args, env, attr, p);
+}
#include "httpd.h"
#include "http_core.h"
#include "os.h"
+#include "httpd.h"
/* Check the Content-Type to decide if conversion is needed */
int ap_checkconv(struct request_rec *r)
return convert_to_ascii;
}
+AP_DECLARE(apr_status_t) ap_os_create_privileged_process(const request_rec *r,
+ apr_proc_t *newproc, const char *progname,
+ char *const *args, char **env,
+ apr_procattr_t *attr, apr_pool_t *p)
+{
+ return apr_create_process(newproc, progname, args, env, attr, p);
+}
+
strlwr(szCanonicalFile);
return szCanonicalFile;
}
+
+AP_DECLARE(apr_status_t) ap_os_create_privileged_process(const request_rec *r,
+ apr_proc_t *newproc, const char *progname,
+ char *const *args, char **env,
+ apr_procattr_t *attr, apr_pool_t *p)
+{
+ return apr_create_process(newproc, progname, args, env, attr, p);
+}
ap_restart_time = input_parms->restart_time;
}
-
+AP_DECLARE(apr_status_t) ap_os_create_privileged_process(const request_rec *r,
+ apr_proc_t *newproc, const char *progname,
+ char *const *args, char **env,
+ apr_procattr_t *attr, apr_pool_t *p)
+{
+ return apr_create_process(newproc, progname, args, env, attr, p);
+}
LTLIBRARY_NAME = libos.la
-LTLIBRARY_SOURCES = os-inline.c unixd.c
+LTLIBRARY_SOURCES = os-inline.c unixd.c suexec.c
include $(top_srcdir)/build/ltlib.mk
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
+/* XXX */
+#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
void unixd_pre_config(apr_pool_t *ptemp)
{
+ apr_finfo_t wrapper;
+
unixd_config.user_name = DEFAULT_USER;
unixd_config.user_id = ap_uname2id(DEFAULT_USER);
unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
+
+ /* Check for suexec */
+ unixd_config.suexec_enabled = 0;
+ if ((apr_stat(&wrapper, SUEXEC_BIN, ptemp)) != APR_SUCCESS) {
+ return;
+ }
+
+ /* XXX - apr_stat is incapable of checking suid bits (grumble) */
+ /* if ((wrapper.filetype & S_ISUID) && wrapper.user == 0) { */
+ unixd_config.suexec_enabled = 1;
+ /* } */
}
#ifdef NEED_AP_SYS_SIGLIST
#define UNIXD_H
#include "httpd.h"
+#include "http_config.h"
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
const char *user_name;
uid_t user_id;
gid_t group_id;
+ int suexec_enabled;
} unixd_config_rec;
extern unixd_config_rec unixd_config;
s->module_config = create_empty_config(p);
s->lookup_defaults = ap_create_per_dir_config(p);
-#if 0
- s->server_uid = ap_user_id;
- s->server_gid = ap_group_id;
-#endif
-
s->limit_req_line = main_server->limit_req_line;
s->limit_req_fieldsize = main_server->limit_req_fieldsize;
s->limit_req_fields = main_server->limit_req_fields;
#include "suexec.h"
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#if HAVE_GRP_H
+#include <grp.h>
+#endif
+
/*
***********************************************************************
* There is no initgroups() in QNX, so I believe this is safe :-)
/*
* Error out if the target username is invalid.
*/
- if ((pw = getpwnam(target_uname)) == NULL) {
- log_err("invalid target user name: (%s)\n", target_uname);
- exit(105);
+ if (strspn(target_uname, "1234567890") != strlen(target_uname)) {
+ if ((pw = getpwnam(target_uname)) == NULL) {
+ log_err("invalid target user name: (%s)\n", target_uname);
+ exit(105);
+ }
+ }
+ else {
+ if ((pw = getpwuid(atoi(target_uname))) == NULL) {
+ log_err("invalud target user id: (%s)\n", target_uname);
+ exit(121);
+ }
}
/*