/* ================================================================ */
/* Copyright (c) 2003 Yee Hsu. */
/* */
/* @ cgi.cpp */
/* @ 05-28-03 */
/* */
/* class CGI implementation file - */
/* This class implements the CGI engine that will serve as */
/* the bridge to connect the underlying data structure to */
/* the web (www). All queries will be supplied by the form */
/* input from the HTTP request and passed into this engine */
/* to search for the inventory record stored in the data list. */
/* The information is then passwd by up to the HTML generator */
/* for GUI display to the user. */
/* */
/* Member Functions/Variables - */
/* CGI(); */
/* ~CGI(); */
/* pcgi getcgivars(void); */
/* cgiv getcgivalue(const char*); */
/* char x2c(char*); */
/* void unescape_url(char*); */
/* void process_cgivars(void); */
/* */
/* pcgi cgivars; */
/* ================================================================ */
#include "invent.h"
// ====================================================================
// CGI constructor
// ====================================================================
CGI::CGI()
{
this->cgivars = NULL;
this->process_cgivars();
}
// ====================================================================
// CGI destructor
// ====================================================================
CGI::~CGI()
{
/** Free anything that needs to be freed **/
for (int i=0; cgivars[i]; i++)
{
free(cgivars[i]) ;
cgivars[i] = NULL;
}
free(cgivars) ;
cgivars = NULL;
}
// ====================================================================
// Identifier: pcgi CGI::getcgivars(void)
//
// Description: Returns the CGI key-value pair...
// cgivar[0] = key[0] cgivar[1] = value[0]
// cgivar[2] = key[1] cgivar[3] = value[1]
// cgivar[4] = key[2] cgivar[5] = value[2]
// ... ...
// cgivar[i] = NULL
// ====================================================================
pcgi CGI::getcgivars(void) const
{
return this->cgivars;
}
// ====================================================================
// Identifier: cgiv CGI::getcgivalue(const char* cgivar_key)
//
// Parameters: char* cgivar_key - the first pair (name) of CGI
// name-value pair
//
// Returns: cgi variable base on given key
//
// Description: Searches the cgi variable table base on the given key
// and returns the value associated to it
// ====================================================================
cgiv CGI::getcgivalue(const char* cgivar_key) const
{
for (int i=0; cgivars[i]; i+=2)
{
if (strcasecmp(cgivar_key, cgivars[i]) == 0)
return cgivars[i+1];
}
return NULL;
}
// ====================================================================
// Identifier: char CGI::x2c(char *what)
//
// Parameters: char* what - the hexidecimal representation of
// the character
//
// Returns: digit - the character represented by the hex(what)
//
// Description: Converts the 2-character hexidecimal character into the
// character it represents and return it
// ====================================================================
char CGI::x2c(const char *what) const
{
register char digit;
digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
digit *= 16;
digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
return(digit);
}
// ====================================================================
// Identifier: void CGI::unescape_url(char *url)
//
// Parameters: char* url - the QUERY_STRING remaining part of
// the URL to be unescaped.
//
// Description: Reduce any %xx escape sequences to the characters
// they represent.
// ====================================================================
void CGI::unescape_url(char *url)
{
register int i,j;
for(i=0,j=0; url[j]; ++i,++j)
{
if((url[i] = url[j]) == '%')
{
url[i] = x2c(&url[j+1]);
j+= 2;
}
}
url[i] = '\0';
}
// ====================================================================
// Identifier: void CGI::process_cgivars()
//
// Post: the member variable cgivars is created and contains
// all the name-value paris from the CGI input
//
// Description: The CGI engine. This module looks through the
// environment table and searches for the QUERY_STRING
// string and parse it's structure into tokenized
// cgivar paris. Method of input is either an
// HTTP GET or POST request
// ====================================================================
void CGI::process_cgivars(void)
{
register int i ;
int content_length, paircount;
char *request_method, *cgiinput, *nvpair, *eqpos;
char **pairlist;
/** Depending on the request method, read all CGI input into cgiinput. **/
if ((request_method = getenv("REQUEST_METHOD")) == NULL) {
/* DEVELOPMENT ONLY - perform any final task for production */
/* remove the next function during the final development phase */
/* it is only here to execute BEFORE testing on the browser */
epilogue();
/* REUEST_METHOD environment variable not found, meaning this was */
/* executed on command prompt. */
printf("Error: please execute CGI on browser.\n");
exit(1);
}
if (!strcmp(request_method, "GET") || !strcmp(request_method, "HEAD") ) {
/* Some servers apparently don't provide QUERY_STRING if it's empty, */
/* so avoid strdup()'ing a NULL pointer here. */
char *qs ;
qs = getenv("QUERY_STRING") ;
cgiinput = strdup(qs ? qs : "") ;
}
else if (!strcmp(request_method, "POST")) {
/* strcasecmp() is not supported in Windows-- use strcmpi() instead */
if ( strcasecmp(getenv("CONTENT_TYPE"), "application/x-www-form-urlencoded")) {
printf("Content-Type: text/plain\n\n") ;
printf("getcgivars(): Unsupported Content-Type.\n") ;
exit(1) ;
}
if ( !(content_length = atoi(getenv("CONTENT_LENGTH"))) ) {
printf("Content-Type: text/plain\n\n") ;
printf("getcgivars(): No Content-Length was sent with the POST request.\n") ;
exit(1) ;
}
if ( !(cgiinput= (char *) malloc(content_length+1)) ) {
printf("Content-Type: text/plain\n\n") ;
printf("getcgivars(): Couldn't malloc for cgiinput.\n") ;
exit(1) ;
}
if (!fread(cgiinput, content_length, 1, stdin)) {
printf("Content-Type: text/plain\n\n") ;
printf("getcgivars(): Couldn't read CGI input from STDIN.\n") ;
exit(1) ;
}
cgiinput[content_length] = '\0' ;
}
else {
printf("Content-Type: text/plain\n\n") ;
printf("getcgivars(): Unsupported REQUEST_METHOD.\n") ;
exit(1) ;
}
/** Change all plusses back to spaces. **/
for (i=0; cgiinput[i]; i++) if (cgiinput[i] == '+') cgiinput[i] = ' ' ;
/** First, split on "&" and ";" to extract the name-value pairs into **/
/** pairlist. **/
pairlist = (char **) malloc(256*sizeof(char **)) ;
paircount = 0 ;
nvpair = strtok(cgiinput, "&;") ;
while (nvpair) {
pairlist[paircount++] = strdup(nvpair) ;
if (!(paircount%256))
pairlist = (char **) realloc(pairlist,(paircount+256)*sizeof(char **)) ;
nvpair = strtok(NULL, "&;") ;
}
pairlist[paircount] = 0 ; /* terminate the list with NULL */
/** Then, from the list of pairs, extract the names and values. **/
this->cgivars = (char **) malloc((paircount*2+1)*sizeof(char **)) ;
for (i=0; i < paircount; i++) {
if (eqpos=strchr(pairlist[i], '=')) {
*eqpos = '\0' ;
unescape_url(cgivars[i*2+1] = strdup(eqpos+1)) ;
} else {
unescape_url(cgivars[i*2+1] = strdup("")) ;
}
unescape_url(cgivars[i*2] = strdup(pairlist[i])) ;
}
cgivars[paircount*2] = 0 ; /* terminate the list with NULL */
/** Free anything that needs to be freed. **/
free(cgiinput) ;
for (i=0; pairlist[i]; i++) free(pairlist[i]) ;
free(pairlist) ;
}
// ====================================================================
// Identifier: void CGI::ParseInputForBadData(void) const
//
// Description: This module acts as a secondary security subroutine
// behind the firewall. It parses the input supplied
// by the user to search for any possible hacker's
// code. If there are any anomolies within the input,
// the query is dropped and is considered to be
// tampering by an intruder looking for possible
// vulnerabilities in the system for any opportunity
// of security breach.
//
// If the event that the input string is considered bad,
// the session is logged for further analysis.
// ====================================================================
void CGI::ParseInputForBadData(void) const
{
/* Table Symbol to filter out in cgivar input buffer */
/* SYMBOL FILTER CHARACTER REASON */
/* ========================================================== */
/* ` backquote beg/end comannd */
/* ~ tilde enter ambigious dir */
/* ! exclamation mark not */
/* @ at email */
/* # pound script header */
/* $ dollar access env variable */
/* % percent mod / hex */
/* ^ carot grep / awk */
/* & ampersand and / separator */
/* [ left bracket begin test */
/* ] right bracket end test */
/* { left parenthesis begin expression */
/* } right parenthesis end expression */
/* ' single quote encapsulate string */
/* : colon separator */
/* ; semi-colon next shell command */
/* < left redirection get file */
/* > right redirection modify file */
/* ? question mark character wild card */
/* | pipe pipe I/O */
/* = equal assignment */
/* + plus add / concatenation */
/* , comma separator */
/* \ backslash access back characters */
/* \n newline new command */
/* \t tab separator */
/* " double quote encapsulate string */
/* ========================================================== */
const char filter[] = "`~!@#$%^&*[]{}':;<>?|=+,\\\n\t\"";
/* ========================================================== */
const int tablelen = strlen(filter);
for (int i = 0; cgivars[i] != NULL; i++) // loop all cgivars
{
for (int e = 0; e < tablelen; e++) // loop filter table
{
if (strchr(cgivars[i], filter[e]) != NULL)
{
printf("Content-Type: text/plain\n\n");
printf("BAD INPUT ( %c ). TRY AGAIN.\n", filter[e]);
fflush(stdout);
/* log intruder session */
logsession();
exit(1) ;
}
}
}
}