3352fe7273
PR: 8522 Submitted by: maintainer
785 lines
26 KiB
C
785 lines
26 KiB
C
/* ====================================================================
|
|
*
|
|
* Apache FrontPage module.
|
|
*
|
|
* Copyright (c) 1996-1997 Microsoft Corporation -- All Rights Reserved.
|
|
*
|
|
* NO WARRANTIES. Microsoft expressly disclaims any warranty for this code and
|
|
* information. This code and information and any related documentation is
|
|
* provided "as is" without warranty of any kind, either express or implied,
|
|
* including, without limitation, the implied warranties or merchantability,
|
|
* fitness for a particular purpose, or noninfringement. The entire risk
|
|
* arising out of use or performance of this code and information remains with
|
|
* you.
|
|
*
|
|
* NO LIABILITY FOR DAMAGES. In no event shall Microsoft or its suppliers be
|
|
* liable for any damages whatsoever (including, without limitation, damages
|
|
* for loss of business profits, business interruption, loss of business
|
|
* information, or any other pecuniary loss) arising out of the use of or
|
|
* inability to use this Microsoft product, even if Microsoft has been advised
|
|
* of the possibility of such damages. Because some states/jurisdictions do not
|
|
* allow the exclusion or limitation of liability for consequential or
|
|
* incidental damages, the above limitation may not apply to you.
|
|
*
|
|
* $Revision: 1.3 $
|
|
* $Date: 1997/10/15 17:23:46 $
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
* User configurable items. We will not run the server extensions with any
|
|
* UID/GID less than LOWEST_VALID_UID/LOWEST_VALID_GID.
|
|
*/
|
|
|
|
#if defined(LINUX)
|
|
#define LOWEST_VALID_UID 15
|
|
#else
|
|
#define LOWEST_VALID_UID 11
|
|
#endif
|
|
|
|
#if defined(HPUX) || defined(IRIX) || defined(SUNOS4)
|
|
#define LOWEST_VALID_GID 20
|
|
#else
|
|
#if defined(SCO)
|
|
#define LOWEST_VALID_GID 24
|
|
#else
|
|
#define LOWEST_VALID_GID 21 /* Solaris, AIX, Alpha, Bsdi, *BSD, etc. */
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* End of user configurable items
|
|
*/
|
|
|
|
|
|
#include "httpd.h"
|
|
#include "http_config.h"
|
|
#include "http_conf_globals.h"
|
|
#include "http_log.h"
|
|
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
|
|
#if defined(UWARE7) || UW==700
|
|
#define Vstat stat32
|
|
#define Vlstat lstat32
|
|
#else
|
|
#define Vstat stat
|
|
#define Vlstat lstat
|
|
#endif
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
#ifndef MAXPATHLEN
|
|
#define MAXPATHLEN 1024
|
|
#endif
|
|
#if (MAXPATHLEN < 1024)
|
|
#undef MAXPATHLEN
|
|
#define MAXPATHLEN 1024
|
|
#endif
|
|
|
|
#define KEYLEN 128 /* Should be a multiple of sizeof(int) */
|
|
|
|
static char gszKeyVal[KEYLEN+1]; /* SUID key value used by this module */
|
|
static int gfdKeyPipe[2]; /* Pipe to fpexe stub CGI */
|
|
static int gbKeyPipeActive = FALSE;/* Pipe to fpexe stub CGI is active */
|
|
static int gbEnabled = FALSE; /* TRUE when SUID scheme is enabled */
|
|
#if !defined(SHARED_MODULE)
|
|
static int giInitializeCount = 0; /* FrontPageInit called previously */
|
|
#endif
|
|
|
|
static const char* FP =
|
|
"/usr/local/frontpage/currentversion";
|
|
static const char* FPKEYDIR =
|
|
"/usr/local/frontpage/currentversion/apache-fp";
|
|
static const char* KEYFILEXOR =
|
|
"/usr/local/frontpage/currentversion/apache-fp/suidkey";
|
|
static const char* KEYFILE =
|
|
"/usr/local/frontpage/currentversion/apache-fp/suidkey.%d";
|
|
static const char* FPSTUBDIR =
|
|
"/usr/local/frontpage/currentversion/apache-fp/_vti_bin";
|
|
static const char* FPSTUB =
|
|
"/usr/local/frontpage/currentversion/apache-fp/_vti_bin/fpexe";
|
|
static const char* SHTML =
|
|
"/_vti_bin/shtml.exe";
|
|
static const char* SHTML2 =
|
|
"/_vti_bin/shtml.dll";
|
|
static const char* VTI_BIN =
|
|
"/_vti_bin";
|
|
static const char* FPCOUNT =
|
|
"/_vti_bin/fpcount.exe";
|
|
static const char* AUTHOR =
|
|
"/_vti_bin/_vti_aut/author.exe" ;
|
|
static const char* ADMIN =
|
|
"/_vti_bin/_vti_adm/admin.exe" ;
|
|
|
|
MODULE_VAR_EXPORT module frontpage_module;
|
|
|
|
/*
|
|
* Print a descriptive error in the httpd's error_log. The format string
|
|
* should be length limited so that it is no longer than 1800 bytes.
|
|
*/
|
|
static void LogFrontPageError(
|
|
server_rec* s,
|
|
const char* szFormat,
|
|
const char* szFile,
|
|
const char* szRoutine,
|
|
int bIsDisabled)
|
|
{
|
|
char szBuf[MAXPATHLEN * 2];
|
|
sprintf(szBuf, szFormat, szFile);
|
|
strcat(szBuf, " in ");
|
|
strcat(szBuf, szRoutine);
|
|
strcat(szBuf, ".");
|
|
if (bIsDisabled)
|
|
{
|
|
strcat(szBuf, " Until this problem is fixed, the FrontPage security patch is disabled and the FrontPage extensions may not work correctly.");
|
|
gbEnabled = FALSE; /* Make double sure we're not enabled */
|
|
}
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, s, szBuf);
|
|
}
|
|
|
|
|
|
/*
|
|
* Clean up stale keyfiles. Failure to clean up stale keyfiles does not
|
|
* stop the FrontPage SUID scheme.
|
|
*/
|
|
static void FrontPageCleanup(server_rec *s)
|
|
{
|
|
DIR *d;
|
|
struct DIR_TYPE *dstruct;
|
|
int myPid = getpid();
|
|
|
|
if (!(d = opendir(FPKEYDIR)))
|
|
{
|
|
/*
|
|
* This should be a rare occurrence, because we're running as root and
|
|
* should have access to the directory. Stale key files can be
|
|
* exploited. User recovery: Check that the directory exists and is
|
|
* properly protected (owned by root, permissions rwx--x--x), and that
|
|
* there are no stale key files in it (suidkey.*, where * is a
|
|
* non-existant PID).
|
|
*/
|
|
LogFrontPageError(s, "Can't clean stale key files from directory \"%-.1024s\"",
|
|
FPKEYDIR, "FrontPageCleanup()", FALSE);
|
|
return;
|
|
}
|
|
|
|
while ((dstruct = readdir(d)))
|
|
{
|
|
if (strncmp("suidkey.", dstruct->d_name, 8) == 0)
|
|
{
|
|
/*
|
|
* Make sure the key file contains a pid number - otherwise
|
|
* it is harmless and you can ignore it.
|
|
*/
|
|
char* pEnd = 0;
|
|
int pid = strtol(dstruct->d_name + 8, &pEnd, 10);
|
|
if (!pEnd || *pEnd)
|
|
continue;
|
|
|
|
/*
|
|
* Make sure there isn't some other server using this key file.
|
|
* If the process group isn't alive, then the file is stale
|
|
* and we want to remove it.
|
|
*/
|
|
if (pid == myPid || kill(pid, 0) == -1)
|
|
{
|
|
char szBuf[MAXPATHLEN];
|
|
sprintf(szBuf, "%-.500s/%-.500s", FPKEYDIR, dstruct->d_name);
|
|
if (unlink(szBuf) == -1)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence, because we're running
|
|
* as root and should always have permission to delete the
|
|
* file. Stale key files can be exploited. User recovery:
|
|
* delete the offending file.
|
|
*/
|
|
LogFrontPageError(s, "Can't unlink stale key file \"%-.1024s\"",
|
|
szBuf, "FrontPageCleanup()", FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(d);
|
|
}
|
|
|
|
/*
|
|
* Checks that all the permissions are currently correct for the FrontPage
|
|
* fpexe SUID stub to run correctly. If not, it logs an error and aborts
|
|
* initialization, effectively disabling the FrontPage SUID scheme.
|
|
* It checks both the file permissions (owned by root and not writable to
|
|
* group, other) and that the directory is not writable.
|
|
*/
|
|
static int FrontPageCheckup(server_rec *s)
|
|
{
|
|
struct stat fs;
|
|
|
|
if (geteuid() != 0)
|
|
{
|
|
/*
|
|
* We need to be root to have the security scheme work correctly.
|
|
* User recovery: run the server as root.
|
|
*/
|
|
LogFrontPageError(s, "Not running as root",
|
|
0, "FrontPageCheckup()", TRUE);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (Vlstat(FPKEYDIR, &fs) == -1 || /* We can't stat the key dir */
|
|
fs.st_uid || /* key dir not owned by root */
|
|
(fs.st_mode & (S_IRGRP | S_IROTH)) || /* key dir is readable */
|
|
(fs.st_mode & (S_IWGRP | S_IWOTH)) || /* key dir is writable */
|
|
!(fs.st_mode & (S_IXGRP | S_IXOTH)) || /* key dir is not executable */
|
|
!(S_ISDIR(fs.st_mode)))
|
|
{
|
|
/*
|
|
* User recovery: set directory to be owned by by root with permissions
|
|
* rwx--x--x. Note you need the execute bit for group and other so
|
|
* that non-root programs can run apache-fp/_vti_bin/fpexe (even though
|
|
* non-root cannot list the directory).
|
|
*/
|
|
LogFrontPageError(s, "Incorrect permissions on key directory \"%-.1024s\", needs root ownership and perms rwx--x--x",
|
|
FPKEYDIR, "FrontPageCheckup()", TRUE);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (Vlstat(FPSTUBDIR, &fs) == -1 || /* We can't stat the stub dir */
|
|
fs.st_uid || /* stub dir not owned by root */
|
|
(fs.st_mode & (S_IWGRP | S_IWOTH)) || /* stub dir is writable */
|
|
(!S_ISDIR(fs.st_mode)))
|
|
{
|
|
/*
|
|
* User recovery: set directory to be owned by by root with permissions
|
|
* r*x*-x*-x.
|
|
*/
|
|
LogFrontPageError(s, "Incorrect permissions on stub directory \"%-.1024s\", needs root ownership and perms r*x*-x*-x",
|
|
FPSTUBDIR, "FrontPageCheckup()", TRUE);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (Vstat(FPSTUB, &fs) == -1 || /* We can't stat the stub */
|
|
fs.st_uid || /* stub not owned by root */
|
|
!(fs.st_mode & S_ISUID) || /* stub is not set-uid */
|
|
(fs.st_mode & S_ISGID) || /* stub is set-gid */
|
|
(fs.st_mode & (S_IWGRP | S_IWOTH)) || /* stub is writable */
|
|
!(fs.st_mode & (S_IXGRP | S_IXOTH))) /* stub is not executable */
|
|
{
|
|
/*
|
|
* User recovery: set stub to be owned by by root with permissions
|
|
* r*s*-x*-x.
|
|
*/
|
|
LogFrontPageError(s, "Incorrect permissions on stub \"%-.1024s\", needs root ownership and perms r*s*-x*-x",
|
|
FPSTUB, "FrontPageCheckup()", TRUE);
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Module-initializer: Create the suidkey file and local value.
|
|
* Everything needs to be just right, or we don't create the key file, and
|
|
* therefore, the fpexe SUID stub refuses to run.
|
|
*/
|
|
static void FrontPageInit(server_rec *s, pool *p)
|
|
{
|
|
int fdPipe[2];
|
|
pid_t pid;
|
|
FILE *f;
|
|
struct stat fs;
|
|
int fd;
|
|
char szKeyFile[MAXPATHLEN];
|
|
int iRandom[5];
|
|
char* szRandom = (char*)iRandom;
|
|
struct timeval tp;
|
|
struct timezone tz;
|
|
|
|
(void)p; /* p is unused */
|
|
|
|
#if !defined(SHARED_MODULE)
|
|
/*
|
|
* Standalone servers call initialization twice: once in main() and again
|
|
* in standalone_main(). The fully initializing on the the first call is a
|
|
* waste of time, and a race condition can leave a stale suidkey.pgrpid
|
|
* file around.
|
|
*/
|
|
if (ap_standalone && !giInitializeCount++)
|
|
return;
|
|
#endif
|
|
|
|
/*
|
|
* Disable the suid scheme until everything falls perfectly into place.
|
|
*/
|
|
gbEnabled = FALSE;
|
|
gbKeyPipeActive = FALSE;
|
|
|
|
/*
|
|
* Clean up old key files before we start
|
|
*/
|
|
FrontPageCleanup(s);
|
|
if (!FrontPageCheckup(s))
|
|
return;
|
|
|
|
if (pipe(fdPipe) == -1)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence. User recovery: check to see why
|
|
* the system cannot allocate a pipe (is the file table full from
|
|
* run-away processes?), and fix the problem or reboot, then try again.
|
|
*/
|
|
LogFrontPageError(s, "pipe() failed", 0, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
gettimeofday(&tp, &tz);
|
|
iRandom[0] = tp.tv_sec;
|
|
iRandom[1] = tp.tv_usec | tp.tv_usec << 20;
|
|
|
|
pid = fork();
|
|
if (pid == -1)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence. User recovery: check to see why
|
|
* the system cannot allocate a process (is the process table full from
|
|
* run-away processes?), and fix the problem or reboot, then try again.
|
|
*/
|
|
LogFrontPageError(s, "fork() failed", 0, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
if (pid)
|
|
{
|
|
/*
|
|
* I am the parent process. Try to read a random number from the
|
|
* child process.
|
|
*/
|
|
|
|
unsigned int npos = (unsigned int)-1;
|
|
unsigned int v1 = npos, v2 = npos, v3 = npos, v4 = npos;
|
|
int stat;
|
|
int iCount;
|
|
|
|
close(fdPipe[1]);
|
|
if (waitpid(pid, &stat, 0) == -1 ||
|
|
(!WIFEXITED(stat) || WIFEXITED(stat) && WEXITSTATUS(stat)))
|
|
{
|
|
/*
|
|
* This should be a rare occurrence. User recovery: Make sure you
|
|
* have a /bin/sh, or change the shell location in the execl
|
|
* command below. Try the commands defined in RAND_CMD in a
|
|
* /bin/sh session to make sure they work properly. Rebuild this
|
|
* module and your httpd with the proper commands.
|
|
*/
|
|
LogFrontPageError(s, "Random number generator exited abnormally", 0,
|
|
"FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
iCount = read(fdPipe[0], gszKeyVal, KEYLEN);
|
|
close(fdPipe[0]);
|
|
if (iCount < 0)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence. See the above comment under
|
|
* the waitpid failure condition for user recovery steps.
|
|
*/
|
|
LogFrontPageError(s, "Could not read random numbers", 0,
|
|
"FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
gszKeyVal[iCount] = 0;
|
|
|
|
sscanf(gszKeyVal, "%u %u %u %u", &v2, &v1, &v4, &v3);
|
|
if (v1 == npos || v2 == npos || v3 == npos || v4 == npos)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence. See the above comment under
|
|
* the waitpid failure condition for user recovery steps.
|
|
*/
|
|
LogFrontPageError(s, "Could not scan random numbers", 0,
|
|
"FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
iRandom[2] = (v1 << 16) + v2 + (v4 << 12) + v3;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* I am the child process. Create a random number which shouldn't
|
|
* be easily duplicated.
|
|
*/
|
|
|
|
if (dup2(fdPipe[1], 1) == -1)
|
|
exit(1); /* Parent picks up the error */
|
|
|
|
close(fdPipe[0]);
|
|
|
|
#ifdef LINUX
|
|
#define RAND_CMD "/bin/ps laxww | /usr/bin/sum ; /bin/ps laxww | /usr/bin/sum"
|
|
#else
|
|
#if defined ( bsdi ) || ( defined ( BSD ) && ( BSD >=199103 ))
|
|
#define RAND_CMD "/bin/ps laxww | /usr/bin/cksum -o 1 ; /bin/ps laxww | /usr/bin/cksum -o 1"
|
|
#else
|
|
#define RAND_CMD "/bin/ps -ea | /bin/sum ; /bin/ps -ea | /bin/sum"
|
|
#endif
|
|
#endif
|
|
execl("/bin/sh", "/bin/sh", "-c", RAND_CMD, NULL);
|
|
exit(1);
|
|
}
|
|
|
|
gettimeofday(&tp, &tz);
|
|
iRandom[3] = tp.tv_sec;
|
|
iRandom[4] = tp.tv_usec | tp.tv_usec << 20;
|
|
|
|
/*
|
|
* See if there is an 'suidkey' file to merge into our key.
|
|
*/
|
|
if (Vstat(KEYFILEXOR, &fs) == -1)
|
|
{
|
|
/*
|
|
* It's a security violation if the key file is not present. User
|
|
* recovery: Make sure the key file is present and properly protected
|
|
* (owned by root, permissions r**------).
|
|
*/
|
|
LogFrontPageError(s, "The key file \"%-.1024s\" does not exist",
|
|
KEYFILEXOR, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
int i, iCount;
|
|
char szBuf[KEYLEN];
|
|
|
|
if ((fs.st_mode & (S_IRWXG | S_IRWXO)) || fs.st_uid)
|
|
{
|
|
/*
|
|
* It's a security violation if the key file is not owned by root,
|
|
* and is not protected from all other group. User recovery: Make
|
|
* sure the key file is properly protected (owned by root,
|
|
* permissions r**------).
|
|
*/
|
|
LogFrontPageError(s, "The key file \"%-.1024s\" must be owned by root and have permissions r**------",
|
|
KEYFILEXOR, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
if ((fd = open(KEYFILEXOR, O_RDONLY)) == -1)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence. User recovery: Make sure
|
|
* the key file exists, is properly owned and protected, and is
|
|
* readable.
|
|
*/
|
|
LogFrontPageError(s, "Cannot open key file \"%-.1024s\"",
|
|
KEYFILEXOR, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
iCount = read(fd, szBuf, KEYLEN);
|
|
if (iCount < 8)
|
|
{
|
|
/*
|
|
* The keyfile must be at least 8 bytes. If it longer than 128
|
|
* bytes, only the first 128 bytes will be used. Any character
|
|
* value from 0-255 is fine. User recovery: Make sure the key file
|
|
* is at least 8 bytes long.
|
|
*/
|
|
LogFrontPageError(s, "Key file \"%-.1024s\" is unreadable or is too short (must be at least 8 bytes)",
|
|
KEYFILEXOR, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Now generate the effective key we'll be using by XORing your key
|
|
* with 5 "random" 32-bit integers. The primary security of this
|
|
* scheme is your key; properly setting it and changing it often keeps
|
|
* the FrontPage SUID scheme secure. All this work above to generate 5
|
|
* random 32-bit integers is soley to make your key somewhat harder to
|
|
* crack (assuming the key files are properly protected). If you don't
|
|
* like the algorithm used to generate the 5 random integers, feel free
|
|
* to substitute as appropriate (check out SGI's Lavarand (TM) at
|
|
* lavarand.sgi.com).
|
|
*/
|
|
for (i = 0; i < KEYLEN; i++)
|
|
gszKeyVal[i] = szBuf[i % iCount] ^ szRandom[i % sizeof(iRandom)];
|
|
close(fd);
|
|
}
|
|
|
|
#if defined(SUNOS4)
|
|
pid = getpgrp(0);
|
|
#else
|
|
pid = getpgrp();
|
|
#endif
|
|
sprintf(szKeyFile, KEYFILE, (int)pid);
|
|
|
|
fd = creat(szKeyFile, 0600);
|
|
if (fd < 0)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence, because we're running as root and
|
|
* should always have permission to create the file. User recovery:
|
|
* check that you are not out of disk space, or that the file is not
|
|
* NFS-mounted on a share where you do not have permissions.
|
|
*/
|
|
LogFrontPageError(s, "Could not create key file \"%-.1024s\"",
|
|
szKeyFile, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
if (write(fd, gszKeyVal, 128) != 128)
|
|
{
|
|
/*
|
|
* This should be a rare occurrence. User recovery: check that you are
|
|
* not out of disk space.
|
|
*/
|
|
close(fd);
|
|
unlink(szKeyFile);
|
|
LogFrontPageError(s, "Could not write to key file \"%-.1024s\"",
|
|
szKeyFile, "FrontPageInit()", TRUE);
|
|
return;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
/*
|
|
* Everything looks OK enough to start the suid scheme.
|
|
*/
|
|
gbEnabled = TRUE;
|
|
|
|
/*
|
|
* Thanks to Scot Hetzel (hetzels@westbend.net)
|
|
*/
|
|
ap_add_version_component("FrontPage/3.0.4.3");
|
|
}
|
|
|
|
|
|
/*
|
|
* Look for a valid FrontPage extensions scenario and fake a scriptalias if
|
|
* appropriate. If there are any problems, we silently decline.
|
|
*/
|
|
static int FrontPageAlias(
|
|
request_rec* r,
|
|
char* szCgi,
|
|
const char* szFpexe)
|
|
{
|
|
int iLen;
|
|
struct stat webroot;
|
|
struct stat vti_pvt;
|
|
struct stat stub;
|
|
char szBuf[MAXPATHLEN];
|
|
char chSave;
|
|
char szFormat[MAXPATHLEN * 2];
|
|
|
|
/*
|
|
* Decline if we cannot run the stub, or it is writable.
|
|
*/
|
|
if (Vstat(FPSTUB, &stub) == -1 || !(stub.st_mode & S_IXOTH) ||
|
|
stub.st_mode & (S_IWGRP | S_IWOTH))
|
|
{
|
|
/*
|
|
* The stub used to be correctly permissioned; what happened? User
|
|
* recovery: set stub to be owned by by root with permissions
|
|
* r*s*-x*-x.
|
|
*/
|
|
LogFrontPageError(r->server, "Incorrect permissions on stub \"%-.1024s\", must be owned by root with permissions r*s*-x*-x",
|
|
FPSTUB, "FrontPageAlias()", FALSE);
|
|
return DECLINED;
|
|
}
|
|
|
|
chSave = szCgi[1];
|
|
szCgi[1] = '\0';
|
|
ap_translate_name(r);
|
|
szCgi[1] = chSave;
|
|
|
|
/*
|
|
* Zap trailing slash that confuses some OSes.
|
|
*/
|
|
iLen = strlen(r->filename);
|
|
r->filename[--iLen] = 0;
|
|
|
|
if (iLen > MAXPATHLEN - 10)
|
|
return DECLINED;
|
|
sprintf(szBuf, "%s/_vti_pvt", r->filename);
|
|
|
|
/*
|
|
* Decline if webroot and webroot/_vti_pvt don't have the same
|
|
* user and group or uid < LOWEST_VALID_UID or gid < LOWEST_VALID_GID.
|
|
*/
|
|
if (Vstat(szBuf, &vti_pvt) == -1 ||
|
|
vti_pvt.st_uid < LOWEST_VALID_UID ||
|
|
vti_pvt.st_gid < LOWEST_VALID_GID ||
|
|
Vstat(r->filename, &webroot) != 0 ||
|
|
webroot.st_uid != vti_pvt.st_uid ||
|
|
webroot.st_gid != vti_pvt.st_gid)
|
|
{
|
|
/*
|
|
* The webroot and webroot/_vti_pvt don't match. User recovery: fix
|
|
* the owners and groups of both directories to match, and have both a
|
|
* uid and gid in the allowable range.
|
|
*/
|
|
sprintf(szFormat, "Incorrect permissions on webroot \"\%-.0124s\" and webroot's _vti_pvt directory, the owners and groups must match and have a uid >= %d and gid >= %d", LOWEST_VALID_UID, LOWEST_VALID_GID);
|
|
|
|
LogFrontPageError(r->server, szFormat,
|
|
szBuf, "FrontPageAlias()", FALSE);
|
|
return DECLINED;
|
|
}
|
|
|
|
/*
|
|
* If the pipe is active, it was because we previously executed a CGI.
|
|
* That CGI must have finished by now (otherwise we wouldn't be processing
|
|
* this next request), so we can and should close the pipe to avoid a
|
|
* resource leak.
|
|
*/
|
|
if (gbKeyPipeActive)
|
|
{
|
|
close(gfdKeyPipe[0]);
|
|
gbKeyPipeActive = FALSE;
|
|
}
|
|
|
|
/*
|
|
* If we can't get a pipe, that's really bad. We'll log an error, and
|
|
* decline. This should be a rare occurrence. User recovery: check to see
|
|
* why the system cannot allocate a pipe (is the file table full from
|
|
* run-away processes?), and fix the problem or reboot, then try again.
|
|
*/
|
|
if (pipe(gfdKeyPipe) == -1)
|
|
{
|
|
LogFrontPageError(r->server, "pipe() failed", 0,
|
|
"FrontPageAlias()", FALSE);
|
|
return DECLINED;
|
|
}
|
|
|
|
/*
|
|
* Note: ap_pstrdup allocates memory, but it checks for out of memory
|
|
* conditions - it will not return if out of memory.
|
|
*/
|
|
r->handler = ap_pstrdup(r->pool, "cgi-script");
|
|
ap_table_set(r->notes, "alias-forced-type", r->handler);
|
|
|
|
ap_table_set(r->subprocess_env, "FPEXE", ap_pstrdup(r->pool, szFpexe));
|
|
sprintf(szBuf, "%d", webroot.st_uid );
|
|
ap_table_set(r->subprocess_env, "FPUID", ap_pstrdup(r->pool, szBuf));
|
|
sprintf(szBuf, "%d", webroot.st_gid );
|
|
ap_table_set(r->subprocess_env, "FPGID", ap_pstrdup(r->pool, szBuf));
|
|
sprintf(szBuf, "%d", gfdKeyPipe[0]);
|
|
ap_table_set(r->subprocess_env, "FPFD", ap_pstrdup(r->pool, szBuf));
|
|
|
|
r->execfilename = ap_pstrcat(r->pool, FPSTUB, szCgi + strlen(szFpexe), NULL);
|
|
r->filename = ap_pstrcat(r->pool, r->filename, szCgi, NULL);
|
|
|
|
if (write(gfdKeyPipe[1], gszKeyVal, 128) != 128)
|
|
{
|
|
/*
|
|
* If we can't write to the pipe, that's really bad. We'll log an
|
|
* error, and decline. This should be a rare occurrence. User
|
|
* recovery: check to see why the system cannot write to the pipe (is
|
|
* the system being choked with too much load?), and fix the problem or
|
|
* reboot, then try again.
|
|
*/
|
|
LogFrontPageError(r->server, "Write to pipe failed", 0,
|
|
"FrontPageAlias()", FALSE);
|
|
close (gfdKeyPipe[0]);
|
|
close (gfdKeyPipe[1]);
|
|
return DECLINED;
|
|
}
|
|
close(gfdKeyPipe[1]);
|
|
|
|
gbKeyPipeActive = TRUE;
|
|
return OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine looks for shtml.exe, fpcount.exe, author.exe and admin.exe
|
|
* in a URI, and if found we call FrontPageAlias() to check for a valid
|
|
* FrontPage scenario.
|
|
*
|
|
* The return value is OK or DECLINED.
|
|
*/
|
|
static int FrontPageXlate(
|
|
request_rec *r)
|
|
{
|
|
char *szVti;
|
|
char *szCgi;
|
|
|
|
/*
|
|
* Decline if we're improperly initialized.
|
|
*/
|
|
if (!gbEnabled)
|
|
return DECLINED;
|
|
|
|
/*
|
|
* Check once for anything with _vti_bin. This is much faster than
|
|
* checking all four paths, because anything without this is definitely
|
|
* not a FrontPage scenario.
|
|
*/
|
|
if (!(szVti = strstr(r->uri, VTI_BIN)))
|
|
return DECLINED;
|
|
|
|
/*
|
|
* Test for FrontPage server extenders:
|
|
* .../_vti_bin/shtml.exe...
|
|
* .../_vti_bin/shtml.dll...
|
|
* .../_vti_bin/fpcount.exe...
|
|
* .../_vti_bin/_vti_aut/author.exe...
|
|
* .../_vti_bin/_vti_adm/admin.exe...
|
|
*/
|
|
if (szCgi = strstr(szVti, AUTHOR ))
|
|
return FrontPageAlias(r, szCgi, AUTHOR);
|
|
/*
|
|
* Convert inadvertent shtml.dll to shtml.exe
|
|
* Thanks for the idea to Scot Hetzel (hetzels@westbend.net)
|
|
*/
|
|
if (szCgi = strstr(szVti, SHTML2 ))
|
|
{
|
|
int iShtmlExtPos = strlen(SHTML2) - 3;
|
|
strncpy(szCgi + iShtmlExtPos, SHTML + iShtmlExtPos, 3);
|
|
}
|
|
if (szCgi = strstr(szVti, SHTML ))
|
|
return FrontPageAlias(r, szCgi, SHTML);
|
|
if (szCgi = strstr(szVti, ADMIN ))
|
|
return FrontPageAlias(r, szCgi, ADMIN);
|
|
if (szCgi = strstr(szVti, FPCOUNT))
|
|
return FrontPageAlias(r, szCgi, FPCOUNT);
|
|
|
|
return DECLINED;
|
|
}
|
|
|
|
|
|
/*
|
|
* Declare ourselves so the configuration routines can find us.
|
|
*/
|
|
module MODULE_VAR_EXPORT frontpage_module =
|
|
{
|
|
STANDARD_MODULE_STUFF,
|
|
FrontPageInit, /* initializer */
|
|
NULL, /* per-directory config creater */
|
|
NULL, /* dir config merger - default is to override */
|
|
NULL, /* server config creator */
|
|
NULL, /* server config merger */
|
|
NULL, /* command table */
|
|
NULL, /* [6] list of handlers */
|
|
FrontPageXlate, /* [1] filename-to-URI translation */
|
|
NULL, /* [4] check/validate HTTP user_id */
|
|
NULL, /* [5] check HTTP user_id is valid *here* */
|
|
NULL, /* [3] check access by host address, etc. */
|
|
NULL, /* [6] MIME type checker/setter */
|
|
NULL, /* [7] fixups */
|
|
NULL, /* [9] logger */
|
|
NULL, /* [2] header parser */
|
|
NULL, /* child_init */
|
|
NULL, /* child_exit */
|
|
NULL /* post read-request */
|
|
};
|