]> granicus.if.org Git - apache/blobdiff - support/suexec.c
Add CHANGES' security entries for 2.4.27.
[apache] / support / suexec.c
index cb048992825fe47a2f9f1a066ceabed1d31472eb..0b2491e28b394985edb6e3db98598f044b985472 100644 (file)
@@ -1,55 +1,17 @@
-/* ====================================================================
- * The Apache Software License, Version 1.1
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
  *
- * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
- * reserved.
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * 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/>.
+ * 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.
  */
 
 /*
@@ -58,8 +20,8 @@
  ***********************************************************************
  *
  * NOTE! : DO NOT edit this code!!!  Unless you know what you are doing,
- *         editing this code might open up your system in unexpected 
- *         ways to would-be crackers.  Every precaution has been taken 
+ *         editing this code might open up your system in unexpected
+ *         ways to would-be crackers.  Every precaution has been taken
  *         to make this code as safe as possible; alter it at your own
  *         risk.
  *
@@ -84,6 +46,9 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdlib.h>
+#if APR_HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #include <grp.h>
 #endif
 
-/*
- ***********************************************************************
- * There is no initgroups() in QNX, so I believe this is safe :-)
- * Use cc -osuexec -3 -O -mf -DQNX suexec.c to compile.
- *
- * May 17, 1997.
- * Igor N. Kovalenko -- infoh@mail.wplus.net
- ***********************************************************************
- */
-
-#if defined(NEED_INITGROUPS)
-int initgroups(const char *name, gid_t basegid)
-{
-    /* QNX and MPE do not appear to support supplementary groups. */
-    return 0;
-}
-#endif
-
-#if defined(SUNOS4)
-extern char *sys_errlist[];
-#define strerror(x) sys_errlist[(x)]
-#endif
-
 #if defined(PATH_MAX)
 #define AP_MAXPATH PATH_MAX
 #elif defined(MAXPATHLEN)
@@ -129,7 +71,7 @@ extern char *sys_errlist[];
 extern char **environ;
 static FILE *log = NULL;
 
-char *safe_env_lst[] =
+static const char *const safe_env_lst[] =
 {
     /* variable name starts with */
     "HTTP_",
@@ -139,13 +81,15 @@ char *safe_env_lst[] =
     "AUTH_TYPE=",
     "CONTENT_LENGTH=",
     "CONTENT_TYPE=",
+    "CONTEXT_DOCUMENT_ROOT=",
+    "CONTEXT_PREFIX=",
     "DATE_GMT=",
     "DATE_LOCAL=",
+    "DOCUMENT_ARGS=",
     "DOCUMENT_NAME=",
     "DOCUMENT_PATH_INFO=",
     "DOCUMENT_ROOT=",
     "DOCUMENT_URI=",
-    "FILEPATH_INFO=",
     "GATEWAY_INTERFACE=",
     "HTTPS=",
     "LAST_MODIFIED=",
@@ -158,13 +102,16 @@ char *safe_env_lst[] =
     "REMOTE_IDENT=",
     "REMOTE_PORT=",
     "REMOTE_USER=",
+    "REDIRECT_ERROR_NOTES=",
     "REDIRECT_HANDLER=",
     "REDIRECT_QUERY_STRING=",
     "REDIRECT_REMOTE_USER=",
+    "REDIRECT_SCRIPT_FILENAME=",
     "REDIRECT_STATUS=",
     "REDIRECT_URL=",
     "REQUEST_METHOD=",
     "REQUEST_URI=",
+    "REQUEST_SCHEME=",
     "SCRIPT_FILENAME=",
     "SCRIPT_NAME=",
     "SCRIPT_URI=",
@@ -174,6 +121,7 @@ char *safe_env_lst[] =
     "SERVER_ADDR=",
     "SERVER_PORT=",
     "SERVER_PROTOCOL=",
+    "SERVER_SIGNATURE=",
     "SERVER_SOFTWARE=",
     "UNIQUE_ID=",
     "USER_NAME=",
@@ -181,6 +129,12 @@ char *safe_env_lst[] =
     NULL
 };
 
+static void log_err(const char *fmt,...) 
+    __attribute__((format(printf,1,2)));
+static void log_no_err(const char *fmt,...)  
+    __attribute__((format(printf,1,2)));
+static void err_output(int is_error, const char *fmt, va_list ap) 
+    __attribute__((format(printf,2,0)));
 
 static void err_output(int is_error, const char *fmt, va_list ap)
 {
@@ -189,7 +143,11 @@ static void err_output(int is_error, const char *fmt, va_list ap)
     struct tm *lt;
 
     if (!log) {
+#if defined(_LARGEFILE64_SOURCE) && HAVE_FOPEN64
+        if ((log = fopen64(AP_LOG_EXEC, "a")) == NULL) {
+#else
         if ((log = fopen(AP_LOG_EXEC, "a")) == NULL) {
+#endif
             fprintf(stderr, "suexec failure: could not open log file\n");
             perror("fopen");
             exit(1);
@@ -255,16 +213,20 @@ static void clean_env(void)
      */
     char **envp = environ;
     char *empty_ptr = NULL;
+
     environ = &empty_ptr; /* VERY safe environment */
-    
+
     if ((cleanenv = (char **) calloc(AP_ENVBUF, sizeof(char *))) == NULL) {
         log_err("failed to malloc memory for environment\n");
-        exit(120);
+        exit(123);
     }
 
     sprintf(pathbuf, "PATH=%s", AP_SAFE_PATH);
     cleanenv[cidx] = strdup(pathbuf);
+    if (cleanenv[cidx] == NULL) {
+        log_err("failed to malloc memory for environment\n");
+        exit(124);
+    }
     cidx++;
 
     for (ep = envp; *ep && cidx < AP_ENVBUF-1; ep++) {
@@ -293,7 +255,6 @@ int main(int argc, char *argv[])
     char *target_homedir;   /* target home directory     */
     char *actual_uname;     /* actual user name          */
     char *actual_gname;     /* actual group name         */
-    char *prog;             /* name of this program      */
     char *cmd;              /* command to be executed    */
     char cwd[AP_MAXPATH];   /* current working directory */
     char dwd[AP_MAXPATH];   /* docroot working directory */
@@ -307,14 +268,13 @@ int main(int argc, char *argv[])
      */
     clean_env();
 
-    prog = argv[0];
     /*
      * Check existence/validity of the UID of the user
      * running this program.  Error out if invalid.
      */
     uid = getuid();
     if ((pw = getpwuid(uid)) == NULL) {
-        log_err("crit: invalid uid: (%ld)\n", uid);
+        log_err("crit: invalid uid: (%lu)\n", (unsigned long)uid);
         exit(102);
     }
     /*
@@ -433,12 +393,17 @@ int main(int argc, char *argv[])
             log_err("invalid target group name: (%s)\n", target_gname);
             exit(106);
         }
-        gid = gr->gr_gid;
-        actual_gname = strdup(gr->gr_name);
     }
     else {
-        gid = atoi(target_gname);
-        actual_gname = strdup(target_gname);
+        if ((gr = getgrgid(atoi(target_gname))) == NULL) {
+            log_err("invalid target group id: (%s)\n", target_gname);
+            exit(106);
+        }
+    }
+    gid = gr->gr_gid;
+    if ((actual_gname = strdup(gr->gr_name)) == NULL) {
+        log_err("failed to alloc memory\n");
+        exit(125);
     }
 
 #ifdef _OSD_POSIX
@@ -467,16 +432,20 @@ int main(int argc, char *argv[])
         }
     }
 #endif /*_OSD_POSIX*/
-    
+
     /*
      * Save these for later since initgroups will hose the struct
      */
     uid = pw->pw_uid;
     actual_uname = strdup(pw->pw_name);
     target_homedir = strdup(pw->pw_dir);
+    if (actual_uname == NULL || target_homedir == NULL) {
+        log_err("failed to alloc memory\n");
+        exit(126);
+    }
 
     /*
-     * Log the transaction here to be sure we have an open log 
+     * Log the transaction here to be sure we have an open log
      * before we setuid().
      */
     log_no_err("uid: (%s/%s) gid: (%s/%s) cmd: %s\n",
@@ -489,7 +458,7 @@ int main(int argc, char *argv[])
      * a UID less than AP_UID_MIN.  Tsk tsk.
      */
     if ((uid == 0) || (uid < AP_UID_MIN)) {
-        log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd);
+        log_err("cannot run as forbidden uid (%lu/%s)\n", (unsigned long)uid, cmd);
         exit(107);
     }
 
@@ -498,7 +467,7 @@ int main(int argc, char *argv[])
      * or as a GID less than AP_GID_MIN.  Tsk tsk.
      */
     if ((gid == 0) || (gid < AP_GID_MIN)) {
-        log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd);
+        log_err("cannot run as forbidden gid (%lu/%s)\n", (unsigned long)gid, cmd);
         exit(108);
     }
 
@@ -509,7 +478,7 @@ int main(int argc, char *argv[])
      * and setgid() to the target group. If unsuccessful, error out.
      */
     if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
-        log_err("failed to setgid (%ld: %s)\n", gid, cmd);
+        log_err("failed to setgid (%lu: %s)\n", (unsigned long)gid, cmd);
         exit(109);
     }
 
@@ -517,7 +486,7 @@ int main(int argc, char *argv[])
      * setuid() to the target user.  Error out on fail.
      */
     if ((setuid(uid)) != 0) {
-        log_err("failed to setuid (%ld: %s)\n", uid, cmd);
+        log_err("failed to setuid (%lu: %s)\n", (unsigned long)uid, cmd);
         exit(110);
     }
 
@@ -605,11 +574,11 @@ int main(int argc, char *argv[])
         (gid != dir_info.st_gid) ||
         (uid != prg_info.st_uid) ||
         (gid != prg_info.st_gid)) {
-        log_err("target uid/gid (%ld/%ld) mismatch "
-                "with directory (%ld/%ld) or program (%ld/%ld)\n",
-                uid, gid,
-                dir_info.st_uid, dir_info.st_gid,
-                prg_info.st_uid, prg_info.st_gid);
+        log_err("target uid/gid (%lu/%lu) mismatch "
+                "with directory (%lu/%lu) or program (%lu/%lu)\n",
+                (unsigned long)uid, (unsigned long)gid,
+                (unsigned long)dir_info.st_uid, (unsigned long)dir_info.st_gid,
+                (unsigned long)prg_info.st_uid, (unsigned long)prg_info.st_gid);
         exit(120);
     }
     /*
@@ -633,18 +602,27 @@ int main(int argc, char *argv[])
     umask(AP_SUEXEC_UMASK);
 #endif /* AP_SUEXEC_UMASK */
 
-    /* 
-     * Be sure to close the log file so the CGI can't
-     * mess with it.  If the exec fails, it will be reopened 
-     * automatically when log_err is called.  Note that the log
-     * might not actually be open if AP_LOG_EXEC isn't defined.
-     * However, the "log" cell isn't ifdef'd so let's be defensive
-     * and assume someone might have done something with it
-     * outside an ifdef'd AP_LOG_EXEC block.
-     */
+    /* Be sure to close the log file so the CGI can't mess with it. */
     if (log != NULL) {
+#if APR_HAVE_FCNTL_H
+        /*
+         * ask fcntl(2) to set the FD_CLOEXEC flag on the log file,
+         * so it'll be automagically closed if the exec() call succeeds.
+         */
+        fflush(log);
+        setbuf(log, NULL);
+        if ((fcntl(fileno(log), F_SETFD, FD_CLOEXEC) == -1)) {
+            log_err("error: can't set close-on-exec flag");
+            exit(122);
+        }
+#else
+        /*
+         * In this case, exec() errors won't be logged because we have already
+         * dropped privileges and won't be able to reopen the log file.
+         */
         fclose(log);
         log = NULL;
+#endif
     }
 
     /*