diff -urNX apache-excludes apache_1.3.39.orig/src/include/ap_alloc.h apache_1.3.39/src/include/ap_alloc.h
--- apache_1.3.39.orig/src/include/ap_alloc.h	2007-12-05 21:11:38.000000000 +0800
+++ apache_1.3.39/src/include/ap_alloc.h	2007-12-05 21:11:42.000000000 +0800
@@ -295,9 +295,14 @@
 /* Common cases which want utility support..
  * the note_cleanups_for_foo routines are for 
  */
-
+struct request_rec;
+API_EXPORT(FILE *) ap_priv_pfopen(struct request_rec *r, const char *name, const char *fmode);
 API_EXPORT(FILE *) ap_pfopen(struct pool *, const char *name, const char *fmode);
 API_EXPORT(FILE *) ap_pfdopen(struct pool *, int fd, const char *fmode);
+API_EXPORT(int) ap_priv_popenf(struct request_rec *r, const char *name,
+			       int flg, int mode);
+API_EXPORT(int) ap_priv_popenf_ex(struct request_rec *r, const char *name,
+				  int flg, int mode, int domagic);
 API_EXPORT(int) ap_popenf(struct pool *, const char *name, int flg, int mode);
 API_EXPORT(int) ap_popenf_ex(struct pool *, const char *name, int flg,
                              int mode, int domagic);
@@ -332,6 +337,7 @@
 #endif
 
 /* routines to deal with directories */
+API_EXPORT(DIR *) ap_priv_popendir(struct request_rec *r, const char *name);
 API_EXPORT(DIR *) ap_popendir(pool *p, const char *name);
 API_EXPORT(void) ap_pclosedir(pool *p, DIR * d);
 
diff -urNX apache-excludes apache_1.3.39.orig/src/include/httpd.h apache_1.3.39/src/include/httpd.h
--- apache_1.3.39.orig/src/include/httpd.h	2007-12-05 21:11:38.000000000 +0800
+++ apache_1.3.39/src/include/httpd.h	2007-12-05 21:11:42.000000000 +0800
@@ -1114,6 +1114,8 @@
 API_EXPORT(gid_t) ap_gname2id(const char *name);
 API_EXPORT(int) ap_is_directory(const char *name);
 API_EXPORT(int) ap_is_rdirectory(const char *name);
+API_EXPORT(int) ap_priv_is_directory(struct request_rec *r, const char *path);
+API_EXPORT(int) ap_priv_is_rdirectory(struct request_rec *r, const char *path);
 API_EXPORT(int) ap_can_exec(const struct stat *);
 API_EXPORT(void) ap_chdir_file(const char *file);
 
diff -urNX apache-excludes apache_1.3.39.orig/src/include/privsep.h apache_1.3.39/src/include/privsep.h
--- apache_1.3.39.orig/src/include/privsep.h	1970-01-01 07:30:00.000000000 +0730
+++ apache_1.3.39/src/include/privsep.h	2007-12-05 21:11:42.000000000 +0800
@@ -0,0 +1,131 @@
+/* Copyright 2005 Metaparadigm Pte Ltd
+ *
+ * Authors: Jamie Clark,
+ *          Michael Clark <michael@metaparadigm.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APACHE_PRIVSEP_H
+#define APACHE_PRIVSEP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+#include <sys/types.h>
+#include "util_md5.h"
+
+
+#define PRIV_AUTH_KEY_LEN            16
+#define PRIV_TOKEN_SALT_LEN          8
+#define PRIV_USERNAME_LEN            32
+#define PRIV_AUTH_FAIL_DELAY         2000000 /* usecs */
+#define PRIVD_BUF_SIZE (sizeof(struct priv_command_msg) + PATH_MAX * 2)
+
+extern int priv_enabled;
+extern int priv_debug;
+extern int priv_inited;
+extern char priv_sep_root[PATH_MAX];
+extern struct priv_token priv_root_token;
+extern int priv_sem_id;
+extern int priv_client_fd;
+
+
+typedef struct {
+  const char *user;
+  const char *pass;
+} auth_pam_userinfo;
+
+struct priv_token {
+  uid_t uid;
+  gid_t gid;
+  unsigned char salt[PRIV_TOKEN_SALT_LEN];
+  unsigned char digest[MD5_DIGESTSIZE];
+};
+
+enum priv_command {
+  priv_command_auth,
+  priv_command_open,
+  priv_command_chmod,
+  priv_command_stat,
+  priv_command_lstat,
+  priv_command_mkdir,
+  priv_command_rename,
+  priv_command_remove
+};
+
+struct priv_command_msg {
+  enum priv_command             command;
+  int                           command_rc;
+  int                           command_errno;
+  int                           flags;
+  mode_t                        mode;
+  struct stat                   stat;
+  struct priv_token             token;
+  struct in_addr                remote;
+};
+
+
+/* start privilege separation */
+
+extern int
+ap_do_privd (pool *pconf, server_rec *server_conf);
+
+
+/* privileged wrapper functions */
+
+extern int
+priv_auth (request_rec *r, const char *pass);
+
+extern int
+priv_open (const request_rec *r, const char *path, int flags, ...);
+
+extern FILE*
+priv_fopen (const request_rec *r, const char *path, const char *fmode);
+
+extern int
+priv_chmod (const request_rec *r, const char *path, mode_t mode);
+
+extern int
+priv_stat (const request_rec *r, const char *path, struct stat *buf);
+
+extern int
+priv_lstat (const request_rec *r, const char *path, struct stat *buf);
+
+extern int
+priv_special_stat (const request_rec *r, const char *path, struct stat *buf);
+
+extern int
+priv_special_lstat (const request_rec *r, const char *path, struct stat *buf);
+
+extern int
+priv_mkdir (const request_rec *r, const char *path, mode_t mode);
+
+extern int
+priv_rename (const request_rec *r, const char *path, const char *newpath);
+
+extern DIR*
+priv_opendir (const request_rec *r, const char *path);
+
+extern int
+priv_remove (const request_rec *r, const char *path);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* !APACHE_PRIVSEP_H */
diff -urNX apache-excludes apache_1.3.39.orig/src/main/alloc.c apache_1.3.39/src/main/alloc.c
--- apache_1.3.39.orig/src/main/alloc.c	2007-12-05 21:11:38.000000000 +0800
+++ apache_1.3.39/src/main/alloc.c	2007-12-05 21:11:42.000000000 +0800
@@ -28,6 +28,7 @@
 #endif
 #include "multithread.h"
 #include "http_log.h"
+#include "privsep.h"
 
 #include <stdarg.h>
 
@@ -2070,6 +2071,31 @@
     ap_kill_cleanup(p, (void *) (long) fd, fd_cleanup);
 }
 
+
+API_EXPORT(int) ap_priv_popenf_ex(struct request_rec *r, const char *name,
+				  int flg, int mode, int domagic)
+{
+    int fd;
+    int save_errno;
+
+    ap_block_alarms();
+    fd = priv_open(r, name, flg, mode);
+    save_errno = errno;
+    if (fd >= 0) {
+	fd = ap_slack(fd, AP_SLACK_HIGH);
+	ap_note_cleanups_for_fd_ex(r->pool, fd, domagic);
+    }
+    ap_unblock_alarms();
+    errno = save_errno;
+    return fd;
+}
+
+API_EXPORT(int) ap_priv_popenf(struct request_rec *r, const char *name,
+			       int flg, int mode)
+{
+    return ap_priv_popenf_ex(r, name, flg, mode, 0);
+}
+
 API_EXPORT(int) ap_popenf_ex(pool *a, const char *name, int flg, int mode,
                              int domagic)
 {
@@ -2184,6 +2210,42 @@
     ap_note_cleanups_for_file_ex(p, fp, 0);
 }
 
+API_EXPORT(FILE *) ap_priv_pfopen(request_rec *r, const char *name, const char *mode)
+{
+    FILE *fd = NULL;
+    int baseFlag, desc;
+    int modeFlags = 0;
+    int saved_errno;
+
+#ifdef WIN32
+    modeFlags = _S_IREAD | _S_IWRITE;
+#else
+    modeFlags = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+#endif
+
+    ap_block_alarms();
+
+    if (*mode == 'a') {
+	/* Work around faulty implementations of fopen */
+	baseFlag = (*(mode + 1) == '+') ? O_RDWR : O_WRONLY;
+	desc = priv_open(r, name, baseFlag | O_APPEND | O_CREAT,
+			 modeFlags);
+	if (desc >= 0) {
+	    desc = ap_slack(desc, AP_SLACK_LOW);
+	    fd = ap_fdopen(desc, mode);
+	}
+    }
+    else {
+	fd = priv_fopen(r, name, mode);
+    }
+    saved_errno = errno;
+    if (fd != NULL)
+	ap_note_cleanups_for_file(r->pool, fd);
+    ap_unblock_alarms();
+    errno = saved_errno;
+    return fd;
+}
+
 API_EXPORT(FILE *) ap_pfopen(pool *a, const char *name, const char *mode)
 {
     FILE *fd = NULL;
@@ -2256,6 +2318,24 @@
     closedir((DIR *) dv);
 }
 
+API_EXPORT(DIR *) ap_priv_popendir(request_rec *r, const char *name)
+{
+    DIR *d;
+    int save_errno;
+
+    ap_block_alarms();
+    d = priv_opendir(r, name);
+    if (d == NULL) {
+       save_errno = errno;
+       ap_unblock_alarms();
+       errno = save_errno;
+       return NULL;
+    }
+    ap_register_cleanup(r->pool, (void *) d, dir_cleanup, dir_cleanup);
+    ap_unblock_alarms();
+    return d;
+}
+
 API_EXPORT(DIR *) ap_popendir(pool *p, const char *name)
 {
     DIR *d;
diff -urNX apache-excludes apache_1.3.39.orig/src/main/http_core.c apache_1.3.39/src/main/http_core.c
--- apache_1.3.39.orig/src/main/http_core.c	2006-07-12 16:16:05.000000000 +0800
+++ apache_1.3.39/src/main/http_core.c	2007-12-05 21:11:42.000000000 +0800
@@ -29,6 +29,7 @@
 #include "util_md5.h"
 #include "scoreboard.h"
 #include "fnmatch.h"
+#include "privsep.h"
 
 #ifdef USE_MMAP_FILES
 #include <sys/mman.h>
@@ -3370,6 +3371,53 @@
     return NULL;
 }
 
+static const char *set_priv_sep(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) return err;
+
+    /* We've changed it to On/Off, but used to use numbers
+     * so we accept anything but "Off" or "0" as "On"
+     */
+    if (!strcasecmp(arg, "off") || !strcmp(arg, "0"))
+      priv_enabled = 0;
+    else
+      priv_enabled = 1;
+
+    return NULL;
+}
+
+static const char *set_priv_sep_debug(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) return err;
+
+    /* We've changed it to On/Off, but used to use numbers
+     * so we accept anything but "Off" or "0" as "On"
+     */
+    if (!strcasecmp(arg, "off") || !strcmp(arg, "0"))
+      priv_debug = 0;
+    else
+      priv_debug = 1;
+
+    return NULL;
+}
+
+static const char *set_priv_sep_root(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (err != NULL) return err;
+
+    arg = ap_os_canonical_filename(cmd->pool, arg);
+
+    if (!ap_is_directory(arg)) {
+        return "PrivilegeSeparationRoot must be a valid directory";
+    }
+    ap_cpystrn(priv_sep_root, arg, sizeof(priv_sep_root));
+    return NULL;
+}
+
 static void log_backtrace(const request_rec *r)
 {
     const request_rec *top = r;
@@ -3773,6 +3821,14 @@
 
 { "TraceEnable", set_trace_enable, NULL, RSRC_CONF, TAKE1, 
   "'on' (default), 'off' or 'extended' to trace request body content"},
+
+{ "PrivilegeSeparation", set_priv_sep, NULL, RSRC_CONF, TAKE1,
+  "Whether privilege separation should be On or Off" },
+{ "PrivilegeSeparationDebug", set_priv_sep_debug, NULL, RSRC_CONF, TAKE1,
+ "Whether privilege separation debugging should be On or Off" },
+{ "PrivilegeSeparationRoot", set_priv_sep_root, NULL, RSRC_CONF, TAKE1,
+  "Root directory under which privileged operations are allowed" },
+
 { NULL }
 };
 
@@ -4151,7 +4207,7 @@
     /* Need binary mode for OS/2 */
     f = ap_pfopen(r->pool, r->filename, "rb");
 #else
-    f = ap_pfopen(r->pool, r->filename, "r");
+    f = ap_priv_pfopen(r, r->filename, "r");
 #endif
 
     if (f == NULL) {
diff -urNX apache-excludes apache_1.3.39.orig/src/main/http_main.c apache_1.3.39/src/main/http_main.c
--- apache_1.3.39.orig/src/main/http_main.c	2007-12-05 21:11:38.000000000 +0800
+++ apache_1.3.39/src/main/http_main.c	2007-12-05 21:11:42.000000000 +0800
@@ -62,6 +62,7 @@
 #include "util_uri.h"
 #include "scoreboard.h"
 #include "multithread.h"
+#include "privsep.h"
 #include <sys/stat.h>
 #ifdef USE_SHMGET_SCOREBOARD
 #include <sys/types.h>
@@ -3574,7 +3575,7 @@
     }
 #endif
 #ifndef NO_SETSID
-    if ((pgrp = setsid()) == -1) {
+    if (do_detach && (pgrp = setsid()) == -1) {
 	perror("setsid");
 	fprintf(stderr, "%s: setsid failed\n", ap_server_argv0);
 	if (!do_detach) 
@@ -5470,6 +5471,8 @@
 	ap_init_mutex_method(ap_default_mutex_method());
 
 	server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+	if(priv_enabled)
+	  ap_do_privd(pconf, server_conf);
 	setup_listeners(pconf);
 	ap_clear_pool(plog);
 	ap_open_logs(server_conf, plog);
diff -urNX apache-excludes apache_1.3.39.orig/src/main/http_request.c apache_1.3.39/src/main/http_request.c
--- apache_1.3.39.orig/src/main/http_request.c	2007-12-05 21:11:38.000000000 +0800
+++ apache_1.3.39/src/main/http_request.c	2007-12-05 21:11:42.000000000 +0800
@@ -35,6 +35,7 @@
 #include "http_main.h"
 #include "scoreboard.h"
 #include "fnmatch.h"
+#include "privsep.h"
 
 /*****************************************************************
  *
@@ -70,7 +71,7 @@
 }
 
 
-static int check_symlinks(char *d, int opts)
+static int check_symlinks(const request_rec *r, char *d, int opts)
 {
 #if defined(OS2) || defined(WIN32) || defined(NETWARE)
     /* OS/2 doesn't have symlinks */
@@ -102,7 +103,9 @@
     else
         lastp = NULL;
 
-    res = lstat(d, &lfi);
+    /* we need to lstat as root if we are not authenticated yet to make
+       sure we can do the symlink down paths not reachable by apache user */
+    res = priv_special_lstat(r, d, &lfi);
 
     if (lastp)
         *lastp = '/';
@@ -120,8 +123,10 @@
     if (!(opts & OPT_SYM_OWNER))
         return HTTP_FORBIDDEN;
 
-    if (stat(d, &fi) < 0)
-        return HTTP_FORBIDDEN;
+    /* we need to stat as root if we are not authenticated yet to make
+       sure we can do the symlink down paths not reachable by apache user */
+    if (priv_special_stat(r, d, &fi) < 0)
+      return HTTP_FORBIDDEN;
 
     return (fi.st_uid == lfi.st_uid) ? OK : HTTP_FORBIDDEN;
 
@@ -204,7 +209,10 @@
         }
         else {
             errno = 0;
-            rv = stat(path, &r->finfo);
+	    /* We need to do the stat as root if we are not
+	       authenticated yet. This is pretty safe as privd
+	       will only allow stat and lstat as root */
+	    rv = priv_special_stat(r, path, &r->finfo);
 #ifdef OS2
             r->finfo.st_ino = 0;
 #endif
@@ -466,7 +474,7 @@
         /* Test only legal names against the real filesystem */
         if (i >= iStart)
 #endif
-        if ((res = check_symlinks(test_dirname, core_dir->opts))) {
+        if ((res = check_symlinks(r, test_dirname, core_dir->opts))) {
             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
                         "Symbolic link not allowed: %s", test_dirname);
             return res;
@@ -584,7 +592,7 @@
      * you would *not* get the 403.
      */
     if (!S_ISDIR(r->finfo.st_mode)
-        && (res = check_symlinks(r->filename, ap_allow_options(r)))) {
+        && (res = check_symlinks(r, r->filename, ap_allow_options(r)))) {
         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
                     "Symbolic link not allowed: %s", r->filename);
         return res;
@@ -888,7 +896,7 @@
         rnew->uri = ap_make_full_path(rnew->pool, udir, new_file);
         rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file);
         ap_parse_uri(rnew, rnew->uri);    /* fill in parsed_uri values */
-        if (stat(rnew->filename, &rnew->finfo) < 0) {
+        if (priv_stat(r, rnew->filename, &rnew->finfo) < 0) {
             rnew->finfo.st_mode = 0;
 #ifdef ENAMETOOLONG
             /* Special case for filenames which exceed the maximum limit
@@ -927,7 +935,7 @@
             }
         }
         else {
-            if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew)))) {
+            if ((res = check_symlinks(r, rnew->filename, ap_allow_options(rnew)))) {
                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, rnew,
                             "Symbolic link not allowed: %s", rnew->filename);
                 rnew->status = res;
@@ -1066,7 +1074,7 @@
      */
     if ((r->status != HTTP_NOT_MODIFIED) && (r->status != HTTP_NO_CONTENT)
         && !ap_status_drops_connection(r->status)
-        && r->connection && (r->connection->keepalive > 0)) {
+        && r->connection && (r->connection->keepalive != -1)) {
 
         (void) ap_discard_request_body(r);
     }
diff -urNX apache-excludes apache_1.3.39.orig/src/main/Makefile.tmpl apache_1.3.39/src/main/Makefile.tmpl
--- apache_1.3.39.orig/src/main/Makefile.tmpl	2004-11-25 03:10:19.000000000 +0800
+++ apache_1.3.39/src/main/Makefile.tmpl	2007-12-05 21:11:42.000000000 +0800
@@ -7,7 +7,7 @@
 LIB=  libmain.a
 HEADERS= test_char.h uri_delims.h 
 
-OBJS= alloc.o buff.o \
+OBJS= alloc.o buff.o privsep_server.o privsep_client.o \
       http_config.o http_core.o http_log.o \
       http_main.o http_protocol.o http_request.o http_vhost.o \
       util.o util_date.o util_script.o util_uri.o util_md5.o \
diff -urNX apache-excludes apache_1.3.39.orig/src/main/privsep_client.c apache_1.3.39/src/main/privsep_client.c
--- apache_1.3.39.orig/src/main/privsep_client.c	1970-01-01 07:30:00.000000000 +0730
+++ apache_1.3.39/src/main/privsep_client.c	2007-12-05 21:11:42.000000000 +0800
@@ -0,0 +1,506 @@
+/* Copyright 2005 Metaparadigm Pte Ltd
+ *
+ * Authors: Jamie Clark,
+ *          Michael Clark <michael@metaparadigm.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>    
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <security/pam_appl.h>
+
+#include "httpd.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "http_conf_globals.h"
+#include "ap_md5.h"
+#include "privsep.h"
+
+
+static int
+priv_mutex_lock()
+{
+  int rv;
+
+  struct sembuf semb[] = {
+    { 0, 0, 0 },
+    { 0, 1, SEM_UNDO }
+  };
+
+  while ((rv = semop(priv_sem_id, semb, 2)) < 0 &&
+	 errno == EINTR);
+
+  return rv;
+}
+
+static void
+priv_mutex_unlock()
+{
+  int rv;
+
+  struct sembuf semb[] = {
+    { 0, -1, SEM_UNDO }
+  };
+
+  while ((rv = semop(priv_sem_id, semb, 1)) < 0 &&
+	 errno == EINTR);
+
+  if(rv < 0) {
+    perror ("priv_mutex_unlock: fatal error unlocking mutex. "
+	    "Child will exit to undo semaphore.");
+    exit(1);
+  }
+}
+
+static int
+priv_send_request (const request_rec *r,
+		   struct priv_command_msg *om,
+		   const struct priv_token *token,
+		   const char *path1, ...)
+{
+  va_list ap;
+  struct sockaddr_un clientname;
+  struct msghdr mh;
+  struct iovec iv[3];
+  int rv;
+  struct cmsghdr *cmsg;
+  char ccmsg[CMSG_SPACE (sizeof (int))];
+  const char *path2 = "\0";
+
+  va_start (ap, path1);
+  if (om->command == priv_command_rename ||
+      om->command == priv_command_auth)
+    path2 = va_arg (ap, const char*);
+  va_end (ap);
+
+  /* Make sure we don't send too much */
+  if (sizeof(struct priv_command_msg) + strlen(path1) + strlen(path2)
+      > PRIVD_BUF_SIZE)
+    {
+      perror ("priv_send_request: args to large");
+      errno = EIO;
+      return -1;
+    }
+
+  /* copy the token and request addr into the command structure */
+  memcpy(&om->token, token, sizeof(om->token));
+  om->remote = r->connection->remote_addr.sin_addr;
+
+  /* construct an iovec with the input parameters */
+  iv[0].iov_base = om;
+  iv[0].iov_len = sizeof (*om);
+  iv[1].iov_base = (void*)path1;
+  iv[1].iov_len = strlen(path1)+1;
+  iv[2].iov_base = (void*)path2;
+  iv[2].iov_len = strlen(path2)+1;
+
+  /* link the iovec into the message header */
+  memset(&mh, 0, sizeof(mh));
+  mh.msg_iov = iv;
+  mh.msg_iovlen = 3;
+
+  /* lock message serialization mutex */
+  if(priv_mutex_lock() < 0) {
+    perror ("priv_mutex_lock: error locking mutex");
+    errno = EIO;
+    return -1;
+  }
+
+  /* fire away */
+  if ((rv = sendmsg (priv_client_fd, &mh, 0)) < 0)
+    {
+      perror ("priv_send_request: sendmsg");
+
+      /* unlock message serialization mutex */
+      priv_mutex_unlock();
+
+      errno = EIO;
+      return -1;
+    }
+
+  /* prepare the header to receive a control message */
+  /* with the return payload */
+  mh.msg_control = ccmsg;
+  mh.msg_controllen = sizeof (ccmsg);
+
+  /* let the reply fall into the same struct */
+  mh.msg_iovlen = 1;
+  rv = recvmsg (priv_client_fd, &mh, 0);
+
+  if (rv < 0) {
+    perror ("priv_send_request: recvmsg");
+
+    /* unlock message serialization mutex */
+    priv_mutex_unlock();
+
+    errno = EIO;
+    return -1;
+  }
+
+  /* unlock message serialization mutex */
+  priv_mutex_unlock();
+
+  if (rv != sizeof(struct priv_command_msg)) {
+    fprintf(stderr, "priv_send_request: invalid reply length\n");
+    errno = EIO;
+    return -1;
+  }
+
+  if (om->command == priv_command_open)
+    {
+      /* attempt to extract control message containing fd */
+      cmsg = CMSG_FIRSTHDR (&mh);
+      if (cmsg == NULL)
+	{
+	  /* might be an error return */
+	  return -1;
+	}
+      if (!cmsg->cmsg_type == SCM_RIGHTS)
+	{
+	  fprintf (stderr, "got control message of unknown type %d\n",
+		   cmsg->cmsg_type);
+	  return -1;
+	}
+      /* otherwise return with the fd in the message */
+      return (*(int *) CMSG_DATA (cmsg));
+    }
+
+  return om->command_rc;
+}
+
+
+/* client wrapper functions */
+
+int
+priv_auth (request_rec *r, const char *pass)
+{
+  struct priv_command_msg om;
+  int result;
+
+  if (!priv_inited) return -1;
+
+  om.command = priv_command_auth;
+  result = priv_send_request (r, &om, &priv_root_token,
+			      r->connection->user, pass);
+  errno = om.command_errno;
+
+  if(result == PAM_SUCCESS) {
+    char *priv_token = ap_palloc(r->pool, sizeof(om.token));
+    memcpy(priv_token, &om.token, sizeof(om.token));
+    ap_table_setn(r->notes, "privsep-token", priv_token);
+  } else {
+    /* we've disabled delay in privd so we need to delay here */
+    usleep(PRIV_AUTH_FAIL_DELAY);
+  }
+
+  return result;
+}
+
+int
+priv_open (const request_rec *r, const char *path, int flags, ...)
+{
+  va_list ap;
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  om.mode = 0;
+  va_start (ap, flags);
+  if (flags & O_CREAT)
+    om.mode = va_arg (ap, mode_t);
+  va_end (ap);
+
+  if (!priv_inited || !priv_token ||
+      strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return open (path, flags, om.mode);
+
+  om.command = priv_command_open;
+  om.flags = flags;
+  result = priv_send_request (r, &om, priv_token, path);
+  errno = om.command_errno;
+
+  return result;
+}
+
+FILE*
+priv_fopen (const request_rec *r, const char *path, const char *fmode)
+{
+  int flags, fd;
+  FILE *f = NULL;
+  mode_t mode = 0;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token)
+    return fopen (path, fmode);
+
+  switch (*fmode)
+    {
+    case 'r':
+      flags = (*(fmode + 1) == '+') ? O_RDWR : O_RDONLY;
+      break;
+    case 'w':
+      flags = O_CREAT | O_TRUNC | ((*(fmode + 1) == '+') ? O_RDWR : O_WRONLY);
+      mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+      break;
+    case 'a':
+      flags =
+	O_CREAT | O_APPEND | ((*(fmode + 1) == '+') ? O_RDWR : O_WRONLY);
+      mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+      break;
+    }
+
+  fd = priv_open (r, path, flags, mode);
+
+  if (fd > 0)
+    f = fdopen (fd, fmode);
+
+  return f;
+}
+
+int
+priv_chmod (const request_rec *r, const char *path, mode_t mode)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token)
+    return chmod (path, mode);
+
+  om.command = priv_command_chmod;
+  om.mode = mode;
+  result = priv_send_request (r, &om, priv_token, path);
+  errno = om.command_errno;
+
+  return result;
+}
+
+int
+priv_stat (const request_rec *r, const char *path, struct stat *buf)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token ||
+      strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return stat (path, buf);
+
+  om.command = priv_command_stat;
+  result = priv_send_request (r, &om, priv_token, path);
+  if (result == 0) memcpy( buf, &om.stat, sizeof(*buf));
+  errno = om.command_errno;
+
+  return result;
+}
+
+int
+priv_lstat (const request_rec *r, const char *path, struct stat *buf)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token ||
+      strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return lstat (path, buf);
+
+  om.command = priv_command_lstat;
+  result = priv_send_request (r, &om, priv_token, path);
+  if (result == 0) memcpy( buf, &om.stat, sizeof(*buf));
+  errno = om.command_errno;
+
+  return result;
+}
+
+int
+priv_special_stat (const request_rec *r, const char *path, struct stat *buf)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return stat (path, buf);
+
+  om.command = priv_command_stat;
+  if(priv_token)
+    result = priv_send_request (r, &om, priv_token, path);
+  else
+    result = priv_send_request (r, &om, &priv_root_token, path);
+
+  if (result == 0) memcpy( buf, &om.stat, sizeof(*buf));
+  errno = om.command_errno;
+
+  return result;
+}
+
+int
+priv_special_lstat (const request_rec *r, const char *path, struct stat *buf)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return lstat (path, buf);
+
+  om.command = priv_command_lstat;
+  if(priv_token)
+    result = priv_send_request (r, &om, priv_token, path);
+  else
+    result = priv_send_request (r, &om, &priv_root_token, path);
+
+  if (result == 0) memcpy( buf, &om.stat, sizeof(*buf));
+  errno = om.command_errno;
+
+  return result;
+}
+
+
+int
+priv_mkdir (const request_rec *r, const char *path, mode_t mode)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token ||
+       strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return mkdir (path, mode);
+
+  om.command = priv_command_mkdir;
+  om.mode = mode;
+  result = priv_send_request (r, &om, priv_token, path);
+  errno = om.command_errno;
+
+  return result;
+}
+
+int
+priv_rename (const request_rec *r, const char *path,
+	     const char *newpath)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token ||
+      strncmp(priv_sep_root, path, strlen(priv_sep_root)) ||
+      strncmp(priv_sep_root, newpath, strlen(priv_sep_root)))
+    return rename (path, newpath);
+
+  om.command = priv_command_rename;
+  result = priv_send_request (r, &om, priv_token, path, newpath);
+  errno = om.command_errno;
+
+  return result;
+}
+
+DIR*
+priv_opendir(const request_rec *r, const char *path)
+{
+  DIR *dir;
+  int fd;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token ||
+      strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return opendir (path);
+
+  fd = priv_open (r, path, O_RDONLY|O_NDELAY|O_DIRECTORY );
+
+  if ( fd < 0)
+    return NULL;
+
+  fcntl (fd, F_SETFD, FD_CLOEXEC);
+
+  if ( NULL == (dir = opendir("/")))
+  {
+    close(fd);
+    return NULL;
+  }
+
+  /* Ok, now the nasty bit:
+     Here we assume that an integer file descriptor is the first 
+     element of the opaque __dirstream structure pointed at by dir */
+
+  if ( close( *(int *)dir))
+  {
+    fprintf(stderr, "priv_opendir: couldn't close fake dir\n");
+  }
+  
+  *(int *)dir = fd;
+
+  return dir;
+}
+
+int
+priv_remove (const request_rec *r, const char *path)
+{
+  struct priv_command_msg om;
+  int result;
+
+  const struct priv_token *priv_token =
+    (struct priv_token*)ap_table_get(r->notes, "privsep-token");
+
+  if (!priv_inited || !priv_token ||
+      strncmp(priv_sep_root, path, strlen(priv_sep_root)))
+    return remove (path);
+
+  om.command = priv_command_remove;
+  result = priv_send_request (r, &om, priv_token, path);
+  errno = om.command_errno;
+
+  return result;
+}
diff -urNX apache-excludes apache_1.3.39.orig/src/main/privsep_server.c apache_1.3.39/src/main/privsep_server.c
--- apache_1.3.39.orig/src/main/privsep_server.c	1970-01-01 07:30:00.000000000 +0730
+++ apache_1.3.39/src/main/privsep_server.c	2007-12-05 21:11:42.000000000 +0800
@@ -0,0 +1,605 @@
+/* Copyright 2005 Metaparadigm Pte Ltd
+ *
+ * Authors: Jamie Clark,
+ *          Michael Clark <michael@metaparadigm.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>    
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <security/pam_appl.h>
+
+#include "httpd.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "http_conf_globals.h"
+#include "ap_md5.h"
+#include "privsep.h"
+
+
+/* global */
+int priv_enabled = 0;
+int priv_debug = 0;
+int priv_inited = 0;
+char priv_sep_root[PATH_MAX];
+struct priv_token priv_root_token;
+int priv_sem_id;
+int priv_client_fd;
+
+/* server */
+static int priv_server_fd;
+static int urandom_fd;
+static const char pam_servicename[] = "apache";
+static char privd_msgbuf[PRIVD_BUF_SIZE];
+
+
+/* remove /./'s, /../'s and trailing .. */
+static int
+priv_secure_path(char *s)
+{
+  char *r, *w;
+  int last_was_slash = 0;
+
+  r = w = s;
+  while(*r != 0)
+    {
+      /* Ignore duplicate /'s */
+      if (*r == '/' && last_was_slash)
+	{
+	  r++;
+	  continue;
+	}
+      /* Calculate /../ in a secure way and avoid */
+      if (last_was_slash && *r == '.')
+	{
+	  if (*(r+1) == '.') {
+	    /* skip past .. or ../ with read pointer */
+	    if (*(r+2) == '/') r += 3;
+	    else if (*(r+2) == 0) r += 2;
+	    /* skip back to last / with write pointer */
+	    if (w > s+1)
+	      {
+		w--;
+		while(*(w-1) != '/') { w--; }
+		continue;
+	      }
+	    else
+	      {
+		return -1; /* Bad Request */
+	      }
+	  } else if (*(r+1) == '/') {
+	    r += 2;
+	    continue;
+	  }
+	}
+      *w = *r;
+      last_was_slash = (*r == '/');
+      r++;
+      w++;
+    }
+  *w = 0;
+  return 0;
+}
+
+
+/*
+ * auth_pam_talker: supply authentication information to PAM when asked
+ *
+ * (code from mod_auth_pam)
+ *
+ * Assumptions:
+ *   A password is asked for by requesting input without echoing
+ *   A username is asked for by requesting input _with_ echoing
+ *
+ */
+static
+int auth_pam_talker(int num_msg,
+                    const struct pam_message **msg,
+                    struct pam_response **resp,
+                    void *appdata_ptr)
+{
+  unsigned short i = 0;
+  auth_pam_userinfo *userinfo = (auth_pam_userinfo*)appdata_ptr;
+  struct pam_response *response = 0;
+
+  /* parameter sanity checking */
+  if (!resp || !msg || !userinfo)
+    return PAM_CONV_ERR;
+
+  /* allocate memory to store response */
+  response = malloc(num_msg * sizeof(struct pam_response));
+  if (!response)
+    return PAM_CONV_ERR;
+
+  /* copy values */
+  for(i = 0; i < num_msg; i++) {
+    /* initialize to safe values */
+    response[i].resp_retcode = 0;
+    response[i].resp = 0;
+
+    /* select response based on requested output style */
+    switch(msg[i]->msg_style) {
+    case PAM_PROMPT_ECHO_ON:
+      /* on memory allocation failure, auth fails */
+      response[i].resp = strdup(userinfo->user);
+      break;
+    case PAM_PROMPT_ECHO_OFF:
+      response[i].resp = strdup(userinfo->pass);
+      break;
+    default:
+      if (response) free(response);
+      return PAM_CONV_ERR;
+    }
+  }
+  /* everything okay, set PAM response values */
+  *resp = response;
+  return PAM_SUCCESS;
+}
+
+static int
+privd_authenticate (const char **msg, const char *user, const char *pass)
+{
+  int rv;
+  auth_pam_userinfo userinfo = { user, pass };
+  struct pam_conv conv_info = { &auth_pam_talker, (void*)&userinfo};
+  pam_handle_t *pamh  = NULL;
+
+  /* initialize pam */
+  if ((rv = pam_start(pam_servicename, user,
+		     &conv_info, &pamh)) != PAM_SUCCESS) {
+    *msg = pam_strerror(pamh, rv);
+    return rv;
+  }
+
+  /* We need to delay in client so we don't block other requests.
+     You also need to set "nodelay" flag in the apache auth configuration
+     so the auth module doesn't delay (pam_fail_delay only sets the
+     minimum delay which modules can override). */
+  pam_fail_delay(pamh, 0 /* usec */);
+
+  /* try to authenticate user, log error on failure */
+  if ((rv = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) {
+    *msg = pam_strerror(pamh, rv);
+    pam_end(pamh, PAM_SUCCESS);
+    return rv;
+  }
+
+  /* check that the account is healthy */
+  if ((rv = pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) {
+    *msg = pam_strerror(pamh, rv);
+    pam_end(pamh, PAM_SUCCESS);
+    return rv;
+  }
+
+  pam_end(pamh, PAM_SUCCESS);
+  return rv;
+}
+
+static int
+privd_salt_token(struct priv_token *token)
+{
+  int bytes;
+
+  bytes = read( urandom_fd, &token->salt, sizeof(token->salt));
+  if ( bytes < sizeof(token->salt)) return -1;
+  return 0;
+}
+
+static int
+privd_check_token(struct priv_token *token, unsigned char* auth_key)
+{
+  char digest[MD5_DIGESTSIZE];
+  AP_MD5_CTX md5_ctx;
+
+  /* save digest for comparison */
+  memcpy(digest, token->digest, MD5_DIGESTSIZE);
+
+  /* hash auth_key, uid, gid and salt */
+  ap_MD5Init(&md5_ctx);
+  ap_MD5Update(&md5_ctx, auth_key, PRIV_AUTH_KEY_LEN);
+  ap_MD5Update(&md5_ctx, (unsigned char*)&token->uid, sizeof(token->uid));
+  ap_MD5Update(&md5_ctx, (unsigned char*)&token->gid, sizeof(token->gid));
+  ap_MD5Update(&md5_ctx, token->salt, sizeof(token->salt));
+  ap_MD5Final(token->digest, &md5_ctx);
+
+  return memcmp(digest, token->digest, MD5_DIGESTSIZE);
+}
+
+static char*
+privd_print_token(struct priv_token *token)
+{
+  static char buf[sizeof(struct priv_token) * 2 + 32];
+  sprintf(buf, "uid=%d gid=%d salt=%04x%04x hash=%04x%04x%04x%04x",
+	  token->uid,
+	  token->gid,
+	  *((unsigned*)token->salt),
+	  *(((unsigned*)token->salt)+1),
+	  *((unsigned*)token->digest),
+	  *(((unsigned*)token->digest)+1),
+	  *(((unsigned*)token->digest)+2),
+	  *(((unsigned*)token->digest)+3));
+  return buf;
+}
+
+static void
+privd_service_request (unsigned char *auth_key, server_rec *server_conf)
+{
+  int rv;
+  struct msghdr mh;
+  struct iovec iv;
+  struct priv_command_msg *om = (struct priv_command_msg*)privd_msgbuf;
+  char *path1, *path2;
+  struct cmsghdr *cmsg;
+  int openfd;
+  char ccmsg[CMSG_SPACE (sizeof (openfd))];
+
+  /* construct an iovec large enough to receive the input parameters */
+  iv.iov_base = privd_msgbuf;
+  iv.iov_len = PRIVD_BUF_SIZE;
+
+  /* link the iovec into the message header */
+  memset (&mh, 0, sizeof (mh));
+  mh.msg_iov = &iv;
+  mh.msg_iovlen = 1;
+
+  /* wait for a message */
+  if ((rv = recvmsg (priv_server_fd, &mh, 0)) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+		 "privd_service_request: recvmsg");
+    return;
+  }
+
+  /* pull out the path arguments */
+  path1 = privd_msgbuf + sizeof(struct priv_command_msg);
+  path2 = path1 + strlen(path1) + 1;
+
+  /* check the token */
+  if (privd_check_token(&om->token, auth_key) != 0) {
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+		 "[client %s] privd_service_request: invalid token",
+		 inet_ntoa(om->remote));
+    goto fail_perm;
+  }
+
+  /* set iovec len for reply */
+  iv.iov_len = sizeof (*om);
+
+  mh.msg_control = NULL;
+  mh.msg_controllen = 0;
+
+  if (om->command == priv_command_auth)
+    {
+      struct passwd *pw = NULL;
+      const char *msg = "success";
+
+      if ((om->command_rc = privd_authenticate (&msg, path1, path2)) != 0) {
+	om->command_rc = -1;
+	om->command_errno = EPERM;
+      } else if ((pw = getpwnam (path1)) < 0) {
+	msg = "no such user";
+	om->command_rc = -1;
+	om->command_errno = EPERM;
+      } else if (pw->pw_uid == 0) {
+	msg = "disallowed uid 0";
+	om->command_rc = -1;
+	om->command_errno = EPERM;
+      } else if (pw->pw_gid == 0) {
+	msg = "disallowed gid 0";
+	om->command_rc = -1;
+	om->command_errno = EPERM;
+      } else if (privd_salt_token(&om->token) < 0) {
+	msg = "failed to create salt";
+	om->command_rc = -1;
+	om->command_errno = EPERM;	
+      } else {
+	om->token.uid = pw->pw_uid;
+	om->token.gid = pw->pw_gid;
+	privd_check_token(&om->token, auth_key);
+	msg = privd_print_token(&om->token);
+      }
+      if (priv_debug || om->command_rc < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s %-5d \"%s\" %s (rc=%d)",
+		     inet_ntoa(om->remote), "auth",
+		     pw ? pw->pw_uid : -1, path1, msg, om->command_rc);
+      rv = sendmsg (priv_server_fd, &mh, 0);
+      return;
+    }
+
+  if ( priv_secure_path (path1) < 0 ||
+       (strlen(path2) && priv_secure_path (path2) < 0) ) {
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+		 "privd_service_request: invalid path ignored.");
+    goto fail_perm;
+  }
+
+  /* Just do request as the apache user if it's outsite priv_sep_root,
+     We repeat the client side test for security reasons */
+  if (strncmp(priv_sep_root, path1, strlen(priv_sep_root)) ||
+       (strlen(path2) &&
+	strncmp(priv_sep_root, path2, strlen(priv_sep_root))) ) {
+    om->token.uid = ap_user_id;
+    om->token.gid = ap_group_id;
+  }
+
+  /* only allow stat for root uid or gid */
+  if ((om->token.uid == 0 || om->token.gid == 0) &&
+      !(om->command == priv_command_stat ||
+	om->command == priv_command_lstat)) {
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+		 "privd_service_request: uid 0 disallowed");
+    goto fail_perm;
+  }
+
+  if (setgroups (1, &om->token.gid) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+		 "privd_service_request: setgroups(1, [%d])", om->token.gid);
+    goto fail_perm;
+  }
+
+  if (setegid (om->token.gid) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+		 "privd_service_request: setegid(%d)\n", om->token.gid);
+    goto fail_perm;
+  }
+
+  if (seteuid (om->token.uid) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+		 "privd_service_request: seteuid(%d)", om->token.uid);
+    goto fail_perm;
+  }
+
+  switch (om->command)
+    {
+    case priv_command_open:
+      openfd = open (path1, om->flags, om->mode);
+      if (priv_debug || openfd < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s %-5d \"%s\", %d, %04o (rc=%d)",
+		     inet_ntoa(om->remote), "open",
+		     om->token.uid, path1, om->flags, om->mode, openfd);
+      if (openfd >= 0) {
+	om->command_errno = 0;
+	mh.msg_control = ccmsg;
+	mh.msg_controllen = sizeof (ccmsg);
+	cmsg = CMSG_FIRSTHDR (&mh);
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_RIGHTS;
+	cmsg->cmsg_len = CMSG_LEN (sizeof (openfd));
+	*(int *) CMSG_DATA (cmsg) = openfd;
+	mh.msg_controllen = cmsg->cmsg_len;
+	mh.msg_flags = 0;
+	rv = sendmsg (priv_server_fd, &mh, 0);
+	close (openfd);
+      }
+      else {
+	om->command_rc = -1;
+	om->command_errno = errno;
+	rv = sendmsg (priv_server_fd, &mh, 0);
+      }
+      break;
+
+    case priv_command_chmod:
+      om->command_rc = chmod (path1, om->mode);
+      om->command_errno = errno;
+      if (priv_debug || om->command_rc < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s  %-5d \"%s\", %04o (rc=%d)",
+		     inet_ntoa(om->remote), "chmod",
+		     om->token.uid, path1, om->mode, om->command_rc);
+      rv = sendmsg (priv_server_fd, &mh, 0);
+      break;
+
+    case priv_command_stat:
+      om->command_rc = stat (path1, &om->stat);
+      om->command_errno = errno;
+      if (priv_debug || om->command_rc < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s %-5d \"%s\" (rc=%d)",
+		     inet_ntoa(om->remote), "stat",
+		     om->token.uid, path1, om->command_rc);
+      rv = sendmsg (priv_server_fd, &mh, 0);
+      break;
+
+    case priv_command_lstat:
+      om->command_rc = lstat (path1, &om->stat);
+      om->command_errno = errno;
+      if (priv_debug || om->command_rc < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s %-5d \"%s\" (rc=%d)",
+		     inet_ntoa(om->remote), "lstat",
+		     om->token.uid, path1, om->command_rc);
+      rv = sendmsg (priv_server_fd, &mh, 0);
+      break;
+
+    case priv_command_mkdir:
+      om->command_rc = mkdir (path1, om->mode);
+      om->command_errno = errno;
+      if (priv_debug || om->command_rc < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s %-5d \"%s\", %04o (rc=%d)",
+		     inet_ntoa(om->remote), "mkdir",
+		     om->token.uid, path1, om->mode, om->command_rc);
+      rv = sendmsg (priv_server_fd, &mh, 0);
+      break;
+
+    case priv_command_rename:
+      om->command_rc = rename (path1, path2);
+      om->command_errno = errno;
+      if (priv_debug || om->command_rc < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s %-5d \"%s\", \"%s\" (rc=%d)",
+		     inet_ntoa(om->remote), "rename",
+		     om->token.uid, path1, path2, om->command_rc);
+      rv = sendmsg (priv_server_fd, &mh, 0);
+      break;
+
+    case priv_command_remove:
+      om->command_rc = remove (path1);
+      om->command_errno = errno;
+      if (priv_debug || om->command_rc < 0)
+	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+		     "[client %s] priv_%-6s %-5d \"%s\" (rc=%d)",
+		     inet_ntoa(om->remote), "remove",
+		     om->token.uid, path1, om->command_rc);
+      rv = sendmsg (priv_server_fd, &mh, 0);
+      break;
+
+    }
+  goto out;
+
+ fail_perm:
+  memset (om, 0, sizeof(*om));
+  om->command_rc = -1;
+  om->command_errno = EPERM;
+  rv = sendmsg (priv_server_fd, &mh, 0);
+
+ out:
+  seteuid (0);
+  setegid (0);
+}
+
+static void
+privd_cleanup(void *foo)
+{
+    if (priv_sem_id < 0) return;
+    semctl(priv_sem_id, 0, IPC_RMID, 0);
+    priv_inited = 0;
+    close (priv_client_fd);
+}
+
+static void
+privd_signal(int sig)
+{
+  close (priv_server_fd);
+  close (urandom_fd);
+  exit(0);
+}
+
+int
+ap_do_privd (pool *pconf, server_rec *server_conf)
+{
+  int sv[2];
+  int arg, bytes;
+  pid_t pid;
+  struct ipc_perm sem_perms;
+  unsigned char auth_key[PRIV_AUTH_KEY_LEN];
+
+  /* create auth key on stack and clear in client when we fork */
+  if ((urandom_fd = open("/dev/urandom", O_RDONLY)) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: couldn't open /dev/urandom");
+    exit(1);
+  }
+  if (read(urandom_fd, auth_key, PRIV_AUTH_KEY_LEN) != PRIV_AUTH_KEY_LEN) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: failed to initialize auth key\n");
+    exit(1);
+  }
+
+  /* create special limited root token (stat and lstat only) */
+  if (privd_salt_token(&priv_root_token) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: failed to initialize stat token\n");
+    exit(1);
+  }
+  priv_root_token.uid = 0;
+  priv_root_token.gid = 0;
+  privd_check_token(&priv_root_token, auth_key);
+
+  if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: error creating socketpair");
+    exit(1);
+  }
+
+  if ((priv_sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600)) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: error initializing semaphore: semget");
+    exit(1);
+  }
+  ap_register_cleanup(pconf, NULL, privd_cleanup, ap_null_cleanup);
+
+  if (semctl(priv_sem_id, 0, SETVAL, 0) <0) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: error initializing semaphore: semctl");
+    exit(1);
+  }
+
+  sem_perms.uid = ap_user_id;
+  sem_perms.gid = ap_group_id;
+  sem_perms.mode = 0600;
+  if (semctl(priv_sem_id, 0, IPC_SET, &sem_perms) <0) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: semctl");
+    exit(1);
+  }
+
+  priv_client_fd = sv[0];
+  priv_server_fd = sv[1];
+
+  arg = 1;
+  if (setsockopt(priv_server_fd, SOL_SOCKET, SO_PASSCRED,
+		 &arg, sizeof(arg))) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "ap_do_privd: setsockopt");
+    exit(1);
+  }
+
+  if ((pid = fork ()) < 0) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+		 "do_privd: fork");
+    exit (1);
+  }
+  else if (pid > 0) {
+    /* clear auth key off stack */
+    memset(auth_key, 0, PRIV_AUTH_KEY_LEN);
+    close(urandom_fd);
+    close(priv_server_fd);
+    priv_inited = 1;
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+		 "Privilege separation started (pid %d)", pid);
+    return 0;
+  }
+
+  close(priv_client_fd);
+  signal(SIGHUP, privd_signal);
+  signal(SIGTERM, privd_signal);
+  signal(SIGUSR1, privd_signal);
+  umask(002);
+
+  /* okay, here's the main loop */
+  while (1) privd_service_request (auth_key, server_conf);
+
+  return 0;
+}
diff -urNX apache-excludes apache_1.3.39.orig/src/main/util.c apache_1.3.39/src/main/util.c
--- apache_1.3.39.orig/src/main/util.c	2006-07-12 16:16:05.000000000 +0800
+++ apache_1.3.39/src/main/util.c	2007-12-05 21:11:42.000000000 +0800
@@ -30,6 +30,8 @@
 #include "httpd.h"
 #include "http_conf_globals.h"	/* for user_id & group_id */
 #include "http_log.h"
+#include "http_request.h"
+#include "privsep.h"
 #if defined(SUNOS4)
 /* stdio.h has been read in ap_config.h already. Add missing prototypes here: */
 extern int fgetc(FILE *);
@@ -1754,6 +1756,30 @@
     return x;
 }
 
+API_EXPORT(int) ap_priv_is_directory(struct request_rec *r, const char *path)
+{
+    struct stat finfo;
+
+    if (priv_stat(r, path, &finfo) == -1)
+       return 0;               /* in error condition, just return no */
+
+    return (S_ISDIR(finfo.st_mode));
+}
+
+/*
+ * see ap_is_directory() except this one is symlink aware, so it
+ * checks for a "real" directory
+ */
+API_EXPORT(int) ap_priv_is_rdirectory(struct request_rec *r, const char *path)
+{
+    struct stat finfo;
+
+    if (priv_lstat(r, path, &finfo) == -1)
+       return 0;               /* in error condition, just return no */
+
+    return ((!(S_ISLNK(finfo.st_mode))) && (S_ISDIR(finfo.st_mode)));
+}
+
 API_EXPORT(int) ap_is_directory(const char *path)
 {
     struct stat finfo;
diff -urNX apache-excludes apache_1.3.39.orig/src/.mkcert.serial apache_1.3.39/src/.mkcert.serial
--- apache_1.3.39.orig/src/.mkcert.serial	1970-01-01 07:30:00.000000000 +0730
+++ apache_1.3.39/src/.mkcert.serial	2007-12-05 21:19:14.000000000 +0800
@@ -0,0 +1 @@
+03
diff -urNX apache-excludes apache_1.3.39.orig/src/modules/experimental/mod_auth_privsep.c apache_1.3.39/src/modules/experimental/mod_auth_privsep.c
--- apache_1.3.39.orig/src/modules/experimental/mod_auth_privsep.c	1970-01-01 07:30:00.000000000 +0730
+++ apache_1.3.39/src/modules/experimental/mod_auth_privsep.c	2007-12-05 21:11:42.000000000 +0800
@@ -0,0 +1,195 @@
+/* Copyright 2005 Metaparadigm Pte Ltd
+ *
+ * Authors: Jamie Clark,
+ *          Michael Clark <michael@metaparadigm.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <security/pam_appl.h>
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "privsep.h"
+
+
+module MODULE_VAR_EXPORT privsep_auth_module;
+
+
+struct privsep_auth_config
+{
+  int authoritative; 	/* 0 to DECLINE instead of AUTH_REQUIRED if we
+			   can't find the username (defaults to 1) */
+  int enabled;	        /* 1 to use mod_auth_privsep, 0 otherwise
+			   (defaults to 0) */
+};
+
+static void*
+privsep_create_dir_config (pool *p, char *d)
+{
+  struct privsep_auth_config *privsep_conf;
+
+  privsep_conf = ap_palloc (p, sizeof(struct privsep_auth_config));
+  privsep_conf->authoritative = 1;
+  privsep_conf->enabled = 0;
+
+  return privsep_conf;
+}
+
+static
+const char* auth_authoritative(cmd_parms *cmd, void *config, int arg)
+{
+  struct privsep_auth_config *privsep_conf =
+    (struct privsep_auth_config *)config;
+  privsep_conf->authoritative = arg;
+  return NULL;
+}
+
+static
+const char* auth_enable(cmd_parms *cmd, void *config, int arg)
+{
+  struct privsep_auth_config *privsep_conf =
+    (struct privsep_auth_config *)config;
+  privsep_conf->enabled = arg;
+  return NULL;
+}
+
+static command_rec privsep_auth_cmds[] = {
+  { "PrivSepAuthoritative", auth_authoritative, NULL, OR_AUTHCFG, FLAG,
+    "on|off - fall through to other auth methods for failures; default on" },
+  { "PrivSepAuth", auth_enable, NULL, OR_AUTHCFG, FLAG,
+    "on|off - determines if privsep authentication is enabled; default off" },
+  { 0 }
+};
+
+
+static
+int privsep_auth_basic_user (request_rec *r)
+{
+  int rv;
+  const char *pass;
+  struct privsep_auth_config *privsep_conf = (struct privsep_auth_config *)
+    ap_get_module_config(r->per_dir_config, &privsep_auth_module);
+
+  if (!privsep_conf->enabled)
+    return DECLINED;
+
+  /* read sent pw */
+  if ((rv = ap_get_basic_auth_pw (r, &pass))) return rv;
+
+  if ((rv = priv_auth(r, pass)) != PAM_SUCCESS) {
+    if (!privsep_conf->authoritative) return DECLINED;
+    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+		  "user \"%s\" failed authentication, path=%s",
+		  r->connection->user, r->filename);
+    ap_note_basic_auth_failure(r);
+    return AUTH_REQUIRED;
+  }
+
+  return OK;
+}
+
+static
+int privsep_check_user_access (request_rec *r)
+{
+  struct privsep_auth_config *privsep_conf = (struct privsep_auth_config *)
+    ap_get_module_config(r->per_dir_config, &privsep_auth_module);
+  char *user = r->connection->user;
+  int m = r->method_number;
+  struct passwd *pwent;
+
+  const array_header *reqs_arr = ap_requires(r);
+  require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL;
+
+  register int x;
+  const char *line;
+  char *w;
+
+  if (!privsep_conf->enabled)
+    return DECLINED;
+  if(!(pwent = getpwnam(r->connection->user)))
+    return DECLINED;
+  if (!reqs_arr)
+    return DECLINED;
+
+  for (x = 0; x < reqs_arr->nelts; x++) {
+
+    if (!(reqs[x].method_mask & (1 << m)))
+      continue;
+
+    line = reqs[x].requirement;
+    w = ap_getword_white(r->pool, &line);
+
+    if (!strcmp(w, "group")) {
+      /* for each of the groups listed in the configuration line,
+       * retrieve a list of the group members and check wether the
+       * authenticated user is present in the list. */
+      while (*line) {
+	struct group *grent;
+	char* groupname = ap_getword_conf(r->pool, (const char**)&line);
+
+	if (!(grent = getgrnam (groupname))) {
+	  ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, r,
+			"group \"%s\" does not exist, path=%s",
+			groupname, r->filename);
+	  return DECLINED;
+	}
+	if (grent->gr_mem) {
+	  char **members = grent->gr_mem;
+	    
+	  /* maybe its the primary group? saves the comparisons */
+	  if(pwent->pw_gid == grent->gr_gid)
+	    return OK;
+
+	  while (*members) {
+	    if (strcmp (*members, pwent->pw_name) == 0)
+	      return OK;
+	    members ++;
+	  }
+	}
+      }
+      ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+		    "user \"%s\" not in right group, path=%s",
+		    user, r->filename);
+      ap_note_basic_auth_failure(r);
+      return AUTH_REQUIRED;
+    }
+  }
+
+  return DECLINED;
+}
+
+module privsep_auth_module = {
+  STANDARD_MODULE_STUFF,
+  NULL,                         /* initializer */
+  privsep_create_dir_config,    /* dir config creater */
+  NULL,                         /* dir merger --- default is to override */
+  NULL,                         /* server config */
+  NULL,                         /* merge server config */
+  privsep_auth_cmds,            /* command table */
+  NULL,                         /* [7] list of handlers */
+  NULL,                         /* [2] filename-to-URI translation */
+  privsep_auth_basic_user,      /* [5] check/validate user_id */
+  privsep_check_user_access,    /* [6] check auth */
+  NULL,                         /* [4] check access by host address */
+  NULL,                         /* [7] MIME type checker/setter */
+  NULL,                         /* [8] fixups */
+  NULL,                         /* [10] logger */
+  NULL,                         /* [3] header parser */
+  NULL,                         /* process initializer */
+  NULL,                         /* process exit/cleanup */
+  NULL                          /* [1] post read_request handling */
+};
diff -urNX apache-excludes apache_1.3.39.orig/src/modules/standard/mod_autoindex.c apache_1.3.39/src/modules/standard/mod_autoindex.c
--- apache_1.3.39.orig/src/modules/standard/mod_autoindex.c	2006-07-12 16:16:05.000000000 +0800
+++ apache_1.3.39/src/modules/standard/mod_autoindex.c	2007-12-05 21:11:42.000000000 +0800
@@ -32,6 +32,7 @@
 #include "http_main.h"
 #include "util_script.h"
 #include "fnmatch.h"
+#include "privsep.h"
 
 module MODULE_VAR_EXPORT autoindex_module;
 
@@ -1020,7 +1021,7 @@
 		 * the file's contents, any HTML header it had won't end up
 		 * where it belongs.
 		 */
-		if ((f = ap_pfopen(r->pool, rr->filename, "r")) != 0) {
+		if ((f = ap_priv_pfopen(r, rr->filename, "r")) != 0) {
 		    emit_preamble(r, title);
 		    emit_amble = 0;
 		    do_emit_plain(r, f);
@@ -1116,7 +1117,7 @@
 		/*
 		 * If we can open the file, suppress the signature.
 		 */
-		if ((f = ap_pfopen(r->pool, rr->filename, "r")) != 0) {
+		if ((f = ap_priv_pfopen(r, rr->filename, "r")) != 0) {
 		    do_emit_plain(r, f);
 		    ap_pfclose(r->pool, f);
 		    suppress_sig = 1;
@@ -1162,7 +1163,7 @@
 			"text/html")
 	    || !strcmp(r->content_type, INCLUDES_MAGIC_TYPE))
 	&& !r->content_encoding) {
-        if (!(thefile = ap_pfopen(r->pool, r->filename, "r"))) {
+        if (!(thefile = ap_priv_pfopen(r, r->filename, "r"))) {
 	    return NULL;
 	}
 	n = fread(titlebuf, sizeof(char), MAX_STRING_LEN - 1, thefile);
@@ -1642,7 +1643,7 @@
     char keyid;
     char direction;
 
-    if (!(d = ap_popendir(r->pool, name))) {
+    if (!(d = ap_priv_popendir(r, name))) {
 	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
 		    "Can't open directory for index: %s", r->filename);
 	return HTTP_FORBIDDEN;
diff -urNX apache-excludes apache_1.3.39.orig/src/modules/standard/mod_mime_magic.c apache_1.3.39/src/modules/standard/mod_mime_magic.c
--- apache_1.3.39.orig/src/modules/standard/mod_mime_magic.c	2006-07-12 16:16:05.000000000 +0800
+++ apache_1.3.39/src/modules/standard/mod_mime_magic.c	2007-12-05 21:11:42.000000000 +0800
@@ -830,7 +830,7 @@
 	return result;
     }
 
-    if ((fd = ap_popenf(r->pool, r->filename, O_RDONLY, 0)) < 0) {
+    if ((fd = ap_priv_popenf(r, r->filename, O_RDONLY, 0)) < 0) {
 	/* We can't open it, but we were able to stat it. */
 	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
 		    MODNAME ": can't read `%s'", r->filename);
diff -urNX apache-excludes apache_1.3.39.orig/src/modules/standard/mod_negotiation.c apache_1.3.39/src/modules/standard/mod_negotiation.c
--- apache_1.3.39.orig/src/modules/standard/mod_negotiation.c	2006-07-12 16:16:05.000000000 +0800
+++ apache_1.3.39/src/modules/standard/mod_negotiation.c	2007-12-05 21:11:42.000000000 +0800
@@ -28,6 +28,7 @@
 #include "http_core.h"
 #include "http_log.h"
 #include "util_script.h"
+#include "privsep.h"
 
 /* Commands --- configuring document caching on a per (virtual?)
  * server basis... 
@@ -740,7 +741,7 @@
     /* We are not using multiviews */
     neg->count_multiviews_variants = 0;
 
-    map = ap_pfopen(neg->pool, rr->filename, "r");
+    map = ap_priv_pfopen(rr, rr->filename, "r");
     if (map == NULL) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
                       "cannot access type map file: %s", rr->filename);
@@ -868,7 +869,7 @@
     ++filp;
     prefix_len = strlen(filp);
 
-    dirp = ap_popendir(neg->pool, neg->dir_name);
+    dirp = ap_priv_popendir(r, neg->dir_name);
 
     if (dirp == NULL) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
@@ -1486,7 +1487,7 @@
         char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
                                            variant->file_name);
 
-        if (stat(fullname, &statb) >= 0) {
+        if (priv_stat(neg->r, fullname, &statb) >= 0) {
             /* Note, precision may be lost */
             variant->bytes = (float) statb.st_size;
         }

