Source Code for Fpexe.c

Archived content. No warranty is made as to technical accuracy. Content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

When using the Microsoft® FrontPage 2002 Server Extensions for UNIX with the Apache Web server, the supported configuration is to patch the Apache Web server. The patch to the Apache Web server consists of two modifications:

  • The FrontPage Apache module, mod_frontpage.c, which intercepts remote requests from the FrontPage® client programs, validates security, and redirects the request to the fpexe.c suid root stub program.

  • The fpexe.c program, which accepts authoring requests from the FrontPage Apache module, performs additional security validation, changes user ID to the owner of the Web site being authored, and then invokes the appropriate FrontPage Server Extensions CGI executable.

The source code to fpexe.c is presented here for review.

/* ====================================================================


* FrontPage SUID Stub Executable


* Copyright (c) 1995-2000 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 5.0.0.0

*/

/*

* User configurable items.  We will not run the server extensions with any

* UID/GID less than LOWEST_VALID_UID/LOWEST_VALID_GID.

*/

#if defined(RS6000)

#define _ALL_SOURCE

#endif

#if defined(RS6000) | defined(UWARE7)

int initgroups (char *, int);

#endif

#if defined(MIPS_LINUX)

#define _GNU_SOURCE

#endif

#if defined(LINUX) || defined(MIPS_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

#if defined(UWARE7)

#define Vstat stat32

#define Vlstat lstat32

int lstat32 (const char *, struct stat *);

int stat32 (const char *, struct stat *);

#else

#define Vstat stat

#define Vlstat lstat

#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 },

#ifdef ALPHAOSF

{ "FP_USE_FLOCK=", 0 },

#endif

{ "FRONTPAGE_TEST_INSTALL=", 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) && !defined(UWARE7) && !defined(FREEBSD)

#include <sys/mode.h>

#endif

#if defined(sun) || defined(bsdi) || defined(sgi) || defined(SCO5) || defined(UWARE7) || defined(FREEBSD) || defined(linux)

extern const char ** environ;

#endif

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/version5.0/apache-fp"

#define KEYFILE  "/usr/local/frontpage/version5.0/apache-fp/suidkey.%d"

#define FPDIR    "/usr/local/frontpage/version5.0/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" 

#define ADMINCGI "/_vti_bin/_vti_adm/fpadmcgi.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");

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() 

{

const char** pp;

const 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;

}

int main(int argc, char **argv)

{

struct passwd* pw = 0;

const char* szFpUserName;

const char* szFpExe = getenv("FPEXE");

const char* szFpUid = getenv("FPUID");

const char* szFpGid = getenv("FPGID");

const char* szFpFd  = getenv("FPFD");

const char* szFpDir = getenv("FPEXEDIR");

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;

/*

* Fall back to default location for the real FrontPage executables.

* if FPEXEDIR is not set in the environment.

*/

if (!szFpDir)

szFpDir = FPDIR;

/*

* 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 || !szFpDir)

/*

* 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  &&

strcmp(szFpExe, ADMINCGI) != 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(szFpDir) + 1 > MAXPATHLEN)

die("FrontPage SUID Error: path too long");

strcpy(szWork, szFpDir);

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 (Vlstat(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");

#if defined(sun) && !defined(__SVR4)

sprintf(szKeyFile, KEYFILE, (int)getpgrp(0));

#else

sprintf(szKeyFile, KEYFILE, (int)getpgrp());

#endif

if (Vstat(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 (Vlstat(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 (Vstat(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.

*/

return (1);

}