Microsoft Home Microsoft Home
 Microsoft FrontPage 2000 服务器扩展资源工具箱    
 

附录


Fpsex.c 的源代码

当您在基于 UNIX 操作系统的 Apache 站点服务器上使用 FrontPage 2000 服务器扩展时,推荐的配置是修补 Apache 站点服务器。对 Apache 站点服务器的修补由两项修正所组成:

  • FrontPage Apache 模块 mod_frontpage.c,它可拦截 FrontPage 客户端程序集的远程要求、确认安全性以及将要求重新导向至 fpexe.c suid root 存根程序。
  • Fpexe.c 程序能够接受来自 FrontPage Apache 模块的创作要求,执行额外的安全性确认,将用户 ID 更改为创作 web 站点的所有者,然后运行相应的 FrontPage 服务器扩展 CGI 可执行程序。

以下便是 fpexe.c 的源代码。

 
* ====================================================================
 *
 * FrontPage SUID Stub Executable
 *
 * Copyright (c) 1995-1998 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 4. 0  4 3
 */


/*
 * 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(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 },
 { "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)
#include <sys/mode.h>
#endif

#if defined(sun) || defined(bsdi) || defined(sgi) || defined(SCO5) || defined(UWARE7)
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/version4.0/apache-fp"
#define KEYFILE "/usr/local/frontpage/version4.0/apache-fp/suidkey.%d"
#define FPDIR "/usr/local/frontpage/version4.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" 


/*
 * 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");
 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 (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);
}

  后退 回到页首  
 
 
  上次更新时间 1998 年 11 月
©1998 Microsoft Corporation。保留所有权利。使用规定。