/* 
 * Copyright (c) 2001 Secure Software Solutions
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_EXPAT_H
#  include "expat.h"
#  else HAVE_XMLPARSE_H
#    ifdef HAVE_XMLPARSE_H
#      include "xmlparse.h"
#    endif
#endif
#include "hash.h"
#include "vuln_db.h"


typedef struct udata_t  {


    char *fname[10]; /* Arbitrary max depth of XML doc */
    int depth;
    char databuf[2048];
    char *bufptr;
    Vuln_t *vuln;
    Hash myhash;

} udata_t;




void InitUdata(udata_t *udata)  {

    udata->depth = -1;
    udata->bufptr = NULL;
    udata->vuln = NULL;
    udata->myhash = NULL;
}



void FreeVuln(Vuln_t *vuln)  {
    if(vuln == NULL)
        return;
    if(vuln->Name != NULL)
        free(vuln->Name);
    if(vuln->FSProblem != NULL) 
        free(vuln->FSProblem);
    if(vuln->BOProblem != NULL)
        free(vuln->BOProblem);
    if(vuln->Info != NULL)  {
        FreeInfo(vuln->Info);
        free(vuln->Info);
    }
}

void InitVuln(Vuln_t *vuln)  {

    vuln->Name = NULL;
    vuln->FSProblem = NULL;
    vuln->BOProblem = NULL;
    vuln->RaceCheck = 0;
    vuln->RaceUse = 0;
    vuln->Input = 0;
    vuln->Info = NULL;
}


void InitFSProblem(FSProblem_t *prob)  {

    prob->Arg = 0;
    prob->Severity = Default;
}


void InitBOProblem(BOProblem_t *prob)  {

    prob->FormatArg = 0;
    prob->SrcBufArg = 0;
    prob->Severity = Default;
}


void InitInputProblem(InputProblem_t *prob)  {

    prob->Arg = 0;
    prob->Severity = Default;
}


void FreeInfo(Info_t *info)  {
    if(info == NULL)
        return; 
    if(info->Description != NULL)
        free(info->Description);
    if(info->URL != NULL)
        free(info->URL);
}
 

void InitInfo(Info_t *info)  {

    info->Description = NULL;
    info->URL = NULL;
    info->Severity = Default;
}


void StartElement(void *udata, const char *name, const char **atts)  {

    udata_t *mydata = (udata_t *) udata;
    mydata->fname[mydata->depth+1] = malloc(strlen(name)+1);
    strncpy(mydata->fname[mydata->depth+1], name, strlen(name));
    mydata->fname[mydata->depth+1][strlen(name)] = '\0';  
    mydata->depth++;
    mydata->bufptr = mydata->databuf;

    if(!strcmp(name, "Vulnerability"))  {
        if(mydata->vuln != NULL)  {
            fprintf(stderr, "Vulnerability open tag found, but already in one!?\n");
            return;
        }
        mydata->vuln = malloc(sizeof(Vuln_t));
        InitVuln(mydata->vuln);
    } else if(!strcmp(name, "FSProblem"))  {
        if(mydata->vuln == NULL)  {
            fprintf(stderr,"In FSProblem, but no Vuln struct\n"); 
            return;
        } 
        if(mydata->vuln->FSProblem != NULL)  {
            fprintf(stderr, "FSProblem open tag found but already in one!?\n");
            return;
        }              
        mydata->vuln->FSProblem = malloc(sizeof(FSProblem_t));
        InitFSProblem(mydata->vuln->FSProblem);
    } else if(!strcmp(name, "BOProblem"))  {
        if(mydata->vuln == NULL)  {
            fprintf(stderr, "In BOProblem, but no Vuln struct\n");
            return;
        }
        if(mydata->vuln->BOProblem != NULL)  {
            fprintf(stderr,"BOProblem open tag found, but already in one!?\n");
            return;
        }
        mydata->vuln->BOProblem = malloc(sizeof(BOProblem_t));
        InitBOProblem(mydata->vuln->BOProblem);
    } else if(!strcmp(name, "InputProblem"))  {
        if(mydata->vuln == NULL)  {
            fprintf(stderr, "In InputProblem, but no Vuln struct\n");
            return;
        }
        if(mydata->vuln->InputProblem != NULL)  {
            fprintf(stderr,"InputProblem open tag found, but already in one!?\n");
            return;
        }
        mydata->vuln->InputProblem = malloc(sizeof(InputProblem_t));
        InitInputProblem(mydata->vuln->InputProblem);
    } else if (!strcmp(name, "Info"))  {
        if(mydata->vuln == NULL)  {
            fprintf(stderr,"In Info, but no Vuln struct\n");
            return;
        }
        if(mydata->vuln->Info != NULL)  {
            fprintf(stderr, "Found Info open tag, but already in one!?\n");
            return;
        }
        mydata->vuln->Info = malloc(sizeof(Info_t));
        InitInfo(mydata->vuln->Info);
    }
}


int FrameIsName(udata_t *udata, const char *name)  {

    return strcmp(udata->fname[udata->depth], name);
}


Severity_t ConvertSeverity(const char *buf)  {

    if (!strcasecmp(buf, "high"))
        return High;
    if (!strcasecmp(buf, "medium"))
        return Medium;
    if (!strcasecmp(buf, "low"))
        return Low;
    return Default;
}


void EndElement(void *udata, const char *name)  {

    udata_t *mydata = (udata_t *)udata;
    
    free(mydata->fname[mydata->depth]);
    mydata->depth--;
    if(!strcmp(name, "Vulnerability"))  {
        if(mydata->vuln == NULL)  {
            fprintf(stderr, "At end of Vulnerability section, but no vuln data?!\n");
            return;
        } else {
            if(!HashInsert(mydata->myhash, (void *)mydata->vuln, mydata->vuln->Name))  {
                FreeVuln(mydata->vuln);
                free(mydata->vuln);
            }
                
            mydata->vuln = NULL;
        }
    } else if(!strcmp(name, "Name"))  {
        mydata->vuln->Name = malloc(strlen(mydata->databuf)+1);
        strncpy(mydata->vuln->Name, mydata->databuf, strlen(mydata->databuf));
        mydata->vuln->Name[strlen(mydata->databuf)] = '\0';
    } else if(!strcmp(name, "FormatArg"))  {
        mydata->vuln->BOProblem->FormatArg = atoi(mydata->databuf);
    } else if(!strcmp(name, "SrcBufArg"))  {
        mydata->vuln->BOProblem->SrcBufArg = atoi(mydata->databuf);
    } else if(!strcmp(name, "Description"))  {
        mydata->vuln->Info->Description = malloc(strlen(mydata->databuf)+1);
        strncpy(mydata->vuln->Info->Description, mydata->databuf, strlen(mydata->databuf));
        mydata->vuln->Info->Description[strlen(mydata->databuf)] = '\0';
    } else if(!strcmp(name, "URL"))  {
        mydata->vuln->Info->URL = malloc(strlen(mydata->databuf)+1);
        strncpy(mydata->vuln->Info->URL, mydata->databuf, strlen(mydata->databuf));
        mydata->vuln->Info->URL[strlen(mydata->databuf)] = '\0';
    } else if(!strcmp(name, "RaceCheck"))  {
        mydata->vuln->RaceCheck = atoi(mydata->databuf);
    } else if(!strcmp(name, "RaceUse"))  {
        mydata->vuln->RaceUse = atoi(mydata->databuf);
    } else if(!strcmp(name, "Input"))  {
        mydata->vuln->Input = 1;
    } else if(!strcmp(name, "Arg"))  {
        if(!FrameIsName(mydata, "FSProblem"))  {
            mydata->vuln->FSProblem->Arg = atoi(mydata->databuf);
        } else if(!FrameIsName(mydata, "InputProblem")) {
            mydata->vuln->InputProblem->Arg = atoi(mydata->databuf);
        }
    } else if(!strcmp(name, "Severity"))  {
        if(!FrameIsName(mydata, "FSProblem"))  {
            mydata->vuln->FSProblem->Severity = ConvertSeverity(mydata->databuf);
        } else if(!FrameIsName(mydata, "BOProblem"))  {
            mydata->vuln->BOProblem->Severity = ConvertSeverity(mydata->databuf);
        } else if(!FrameIsName(mydata, "InputProblem"))  {
            mydata->vuln->InputProblem->Severity = ConvertSeverity(mydata->databuf);
        } else if(!FrameIsName(mydata, "Info"))  {
            mydata->vuln->Info->Severity = ConvertSeverity(mydata->databuf);
        }

    } 
    mydata->bufptr = mydata->databuf;

}


void CharData(void *udata, const XML_Char *str, int len)  {

    udata_t *mydata = (udata_t *)udata;
    if(mydata->bufptr + len > mydata->databuf+sizeof(mydata->databuf))  {
        fprintf(stderr, "Possible data overflow, ignoring it.\n");
        return;
    }

    strncpy(mydata->bufptr, str, len);
    mydata->bufptr += len;
    *mydata->bufptr = '\0';
}
  



Hash ParseVulnDb(char *buf, Hash *usehash)  {


    XML_Parser parser;
    udata_t foo;

    parser = XML_ParserCreate(NULL);
    if(parser == NULL)  {
        fprintf(stderr, "Failed to create XML Parser\n");
        return NULL;
    }
    InitUdata(&foo);
    XML_SetUserData(parser, &foo);
    XML_SetElementHandler(parser, StartElement, EndElement);
    XML_SetCharacterDataHandler(parser, CharData);

    if(*usehash == NULL)  {
        *usehash = HashInit();
    }
    foo.myhash = *usehash;
    if(*usehash == NULL)  {
        fprintf(stderr, "Failed to create Hash table\n");
        return NULL;
    }
    if(!XML_Parse(parser, buf, strlen(buf), 1))  {
        fprintf(stderr,
              "%s at line %d\n",
              XML_ErrorString(XML_GetErrorCode(parser)),
              XML_GetCurrentLineNumber(parser));
        return NULL;
    }

    return *usehash;
}
