Source Code for fpexe Program
fpexe.c
/* ====================================================================
*
* FrontPage SUID Stub Executable
*
* Copyright (c) 1995-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.
*
* Version 1.2
*/
/*
* 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, etc. */
#endif
#endif
#define CLEAN_PATH "PATH=/usr/bin:/bin"
static struct SaveEnvVars
{
const char* szVar;
int iLen;
} gSafeEnvVars[] =
{
{ "AUTH_TYPE=", 0 },
{ "CONTENT_LENGTH=", 0 },
{ "CONTENT_TYPE=", 0 },
{ "DATE_GMT=", 0 },
{ "DATE_LOCAL=", 0 },
{ "DOCUMENT_NAME=", 0 },
{ "DOCUMENT_PATH_INFO=", 0 },
{ "DOCUMENT_ROOT=", 0 },
{ "DOCUMENT_URI=", 0 },
{ "FILEPATH_INFO=", 0 },
{ "GATEWAY_INTERFACE=", 0 },
{ "HTTP_", 0 },
{ "LAST_MODIFIED=", 0 },
{ "PATH_INFO=", 0 },
{ "PATH_TRANSLATED=", 0 },
{ "QUERY_STRING=", 0 },
{ "QUERY_STRING_UNESCAPED=", 0 },
{ "REDIRECT_QUERY_STRING=", 0 },
{ "REDIRECT_STATUS=", 0 },
{ "REDIRECT_URL=", 0 },
{ "REMOTE_ADDR=", 0 },
{ "REMOTE_HOST=", 0 },
{ "REMOTE_IDENT=", 0 },
{ "REMOTE_PORT=", 0 },
{ "REMOTE_USER=", 0 },
{ "REQUEST_METHOD=", 0 },
{ "SCRIPT_FILENAME=", 0 },
{ "SCRIPT_NAME=", 0 },
{ "SCRIPT_URI=", 0 },
{ "SCRIPT_URL=", 0 },
{ "SERVER_ADMIN=", 0 },
{ "SERVER_NAME=", 0 },
{ "SERVER_PORT=", 0 },
{ "SERVER_PROTOCOL=", 0 },
{ "SERVER_SOFTWARE=", 0 },
{ "TZ=", 0 },
{ "USER_NAME=", 0 },
{ 0, 0 }
};
/*
* End of user configurable items
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#if !defined(bsdi) && !defined(hpux) && !defined(sun) && !defined(linux) && !defined(SCO5)
#include <sys/mode.h>
#endif
extern char **environ;
extern int errno;
#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) */
#define FPKEYDIR "/usr/local/frontpage/currentversion/apache-fp"
#define KEYFILE "/usr/local/frontpage/currentversion/apache-fp/suidkey.%d"
#define FPDIR "/usr/local/frontpage/currentversion/exes"
/* Legal modules */
#define SHTML "/_vti_bin/shtml.exe"
#define FPCOUNT "/_vti_bin/fpcount.exe"
#define AUTHOR "/_vti_bin/_vti_aut/author.exe"
#define ADMIN "/_vti_bin/_vti_adm/admin.exe"
/*
* Something is not quite right - give up
*/
void die(const char *msg)
{
char timebuf[26];
time_t t = time(0);
strcpy(timebuf, ctime(&t));
timebuf[24] = '\0';
fprintf(stderr, "[%s] %s\n", timebuf, msg);
printf("Content-Type: text/html\n\n<HTML>*-*-* :-| :^| :-/ :-( 8-( *-*-*\n<ul>\n<li>status=1\n<li>osstatus=0\n<li>msg=FrontPage security violation.\n<li>osmsg=\n</ul>\n", msg);
exit(0);
}
/*
* Remove any variable that is not known to be a standard CGI or OS
* environment variable. Also, sanitizes the PATH.
*/
static void CleanEnvironment()
{
char** pp;
char** ppi;
struct SaveEnvVars* pOkEnv;
for (ppi = pp = environ; *pp; pp++)
{
/*
* Inefficient linear lookup; could be improved with binary search.
*/
for (pOkEnv = gSafeEnvVars; pOkEnv->szVar; pOkEnv++)
{
int iLen = pOkEnv->iLen;
if (!iLen)
pOkEnv->iLen = iLen = strlen(pOkEnv->szVar);
if (strncmp(pOkEnv->szVar, *pp, iLen) == 0)
break;
}
if (!strncmp(*pp, "PATH=", 5))
*ppi++ = CLEAN_PATH;
else if (pOkEnv->szVar)
*ppi++ = *pp;
}
*ppi = 0;
}
void main(int argc, char **argv)
{
struct passwd* pw;
const char* szFpUserName;
const char* szFpExe = getenv("FPEXE");
const char* szFpUid = getenv("FPUID");
const char* szFpGid = getenv("FPGID");
const char* szFpFd = getenv("FPFD");
char* pEnd;
char* pDir;
uid_t iFpUid;
uid_t iFpGid;
uid_t iBinUid;
int iFpFd;
int iKeyFd;
int iCount;
char szKeyFile[MAXPATHLEN];
char szWork[MAXPATHLEN];
char inpKey[KEYLEN];
char refKey[KEYLEN];
struct stat fs;
/*
* Assure that this program was actually SUID'd to root
*/
if (geteuid())
/*
* User recovery: Make sure fpexe is setuid to root
*/
die("FrontPage SUID Error: not running as root");
/*
* Assure that the user the web server runs as is a valid user
*/
if (!getpwuid(getuid()))
/*
* User recovery: Make sure that the web server user is in /etc/passwd
*/
die("FrontPage SUID Error: invalid uid");
/*
* Assure that we have the proper arguments (passed in the environment)
*/
if (!szFpExe || !szFpUid || !szFpGid || !szFpFd)
/*
* User recovery: Make sure fpexe is run from patched Apache server
*/
die("Frontpage SUID Error: invalid environment arguments");
/*
* Validate the arguments
*/
if (strcmp(szFpExe, SHTML) != 0 &&
strcmp(szFpExe, FPCOUNT) != 0 &&
strcmp(szFpExe, AUTHOR) != 0 &&
strcmp(szFpExe, ADMIN) != 0)
/*
* User recovery: Make sure fpexe is only invoked to run FrontPage
* server extension programs.
*/
die("FrontPage SUID Error: target program violation");
if (strlen(szFpExe) + strlen(FPDIR) + 1 > MAXPATHLEN)
die("FrontPage SUID Error: path too long");
strcpy(szWork, FPDIR);
strcat(szWork, szFpExe);
iFpUid = strtol(szFpUid, &pEnd, 10);
if (!pEnd || *pEnd)
iFpUid = 0;
if (iFpUid < LOWEST_VALID_UID || !(pw = getpwuid(iFpUid)))
/*
* User recovery: Make sure FrontPage user ids are above minimum
*/
die("FrontPage SUID Error: invalid target uid");
szFpUserName = strdup(pw->pw_name);
iFpGid = strtol(szFpGid, &pEnd, 10);
if (!pEnd || *pEnd)
iFpGid = 0;
if (iFpGid < LOWEST_VALID_GID || !getgrgid(iFpGid))
/*
* User recovery: Make sure FrontPage group ids are above minimum
*/
die("FrontPage SUID Error: invalid target gid");
iFpFd = strtol(szFpFd, &pEnd, 10);
if (!pEnd || *pEnd)
iFpFd = -1;
if (iFpFd < 0)
/*
* User recovery: Make sure fpexe is run from patched Apache server
*/
die("FrontPage SUID Error: invalid key file descriptor");
/*
* Read the key from our server. And, while we're still root and have
* access, read the key from the master key file. Verify the key matches.
*/
if (lstat(FPKEYDIR, &fs) == -1 ||
(fs.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) || fs.st_uid ||
!(S_ISDIR(fs.st_mode)))
/*
* User recovery is: set directory to be owned by by root with
* permissions rwx--x--x.
*/
die("FrontPage SUID Error: key file directory is insecure");
sprintf(szKeyFile, KEYFILE, (int)getpgrp());
if (stat(szKeyFile, &fs) == -1 ||
(fs.st_mode & (S_IRWXG | S_IRWXO)) || fs.st_uid)
/*
* User recovery is: Make sure the key file is properly protected
* (owned by root, permissions r**------), restart patched Apache
* server.
*/
die("FrontPage SUID Error: key file security violation");
iKeyFd = open(szKeyFile, O_RDONLY);
if (iKeyFd < 0)
/*
* User recovery is: Make sure fpexe is run from patched Apache
* server, restart the patched Apache server.
*/
die("FrontPage SUID Error: could not open key file" );
iCount = read(iKeyFd, refKey, sizeof(refKey));
close(iKeyFd);
if (iCount != sizeof(refKey))
/*
* User recovery is: Make sure fpexe is run from patched Apache
* server, restart the patched Apache server.
*/
die("FrontPage SUID Error: could not read valid key from key file");
iCount = read(iFpFd, inpKey, sizeof(inpKey));
close(iFpFd);
if (iCount != sizeof(inpKey))
/*
* User recovery is: Make sure fpexe is run from patched Apache server
*/
die("FrontPage SUID Error: could not read valid input key");
if (memcmp(inpKey, refKey, sizeof(refKey)) != 0)
/*
* User recovery is: Make sure fpexe is run from patched Apache server
*/
die("FrontPage SUID Error: key security violation");
/*
* Change user and group IDs to be the indicated user
*/
if (setgid(iFpGid) == -1 || initgroups(szFpUserName, iFpGid) == -1)
/*
* User recovery: Make sure user is properly registered in
* /etc/passwd and /etc/group.
*/
die("FrontPage SUID Error: setgid() failed");
if (setuid(iFpUid) == -1)
/*
* User recovery: Make sure user is properly registered in
* /etc/passwd.
*/
die("FrontPage SUID Error: setuid() failed");
/*
* Validate the target directory.
*/
iBinUid = 0;
if (pw = getpwnam("bin"))
iBinUid = pw->pw_uid;
pDir = strrchr(szWork, '/');
*pDir = 0;
if (lstat(szWork, &fs) == -1 || (fs.st_mode & (S_IWGRP | S_IWOTH)) ||
(fs.st_uid != iBinUid && fs.st_uid != 0) ||
!(S_ISDIR(fs.st_mode)))
/*
* User recovery is: make sure FrontPage exe programs are available,
* set directory to be owned by bin or root and have permissions
* rwx*-x*-x.
*/
die("FrontPage SUID Error: target directory not found or insecure");
*pDir = '/';
/*
* Validate the target program
*/
if (stat(szWork, &fs) == -1 || ((fs.st_mode & (S_IWGRP | S_IWOTH)) ||
(fs.st_mode & (S_ISUID | S_ISGID)) ||
(fs.st_uid != iBinUid && fs.st_uid != 0)))
/*
* User recovery is: make sure FrontPage exe programs are available,
* set programs to be owned by bin or root and have permissions
* rwx*-x*-x.
*/
die("FrontPage SUID Error: target program not found or insecure");
*pDir = '/';
/*
* Make sure the environment contains no unsafe values.
*/
CleanEnvironment();
/*
* Run the specified program.
*/
argv[0] = szWork;
umask(022);
execv(argv[0], argv);
/*
* We should never get here. Exit with error.
*/
exit(1);
}