From ccf8e1947728585acd2301c9b8e459e6918bfb5f Mon Sep 17 00:00:00 2001 From: Victor Wagner Date: Tue, 26 Apr 2016 20:11:16 +0300 Subject: [PATCH] Fixed (some) segmentation faults when parsing broken OLE structure --- src/analyze.c | 2 +- src/ole.c | 240 +++++++++++++++++++++++++++----------------------- src/ole.h | 4 +- 3 files changed, 134 insertions(+), 112 deletions(-) diff --git a/src/analyze.c b/src/analyze.c index 1ac927e..6ed7ea6 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -70,7 +70,7 @@ int analyze_format(FILE *f) { set_std_func(); ole_finish(); } else { - fprintf(stderr,"Broken OLE file. Try using -b switch"); + fprintf(stderr,"Broken OLE file. Try using -b switch\n"); exit(1); } } else { diff --git a/src/ole.c b/src/ole.c index 805d8c7..1e26dd7 100644 --- a/src/ole.c +++ b/src/ole.c @@ -4,12 +4,12 @@ * @date Wed Jun 11 12:33:01 2003 * Version: $Id: ole.c,v 1.2 2006-02-25 15:28:14 vitus Exp $ * Copyright: Victor B Wagner, 1996-2003 Alex Ott, 2003 - * + * * @brief Parsing structure of MS Office compound document - * + * * This file is part of catdoc project * and distributed under GNU Public License - * + * */ #ifdef HAVE_CONFIG_H #include @@ -40,28 +40,29 @@ long int fileLength=0; char ole_sign[]={0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1,0}; char zip_sign[]="PK\003\004"; -/** +/** * Initializes ole structure - * + * * @param f (FILE *) compound document file, positioned at bufSize - * byte. Might be pipe or socket + * byte. Might be pipe or socket * @param buffer (void *) bytes already read from f * @param bufSize number of bytes already read from f should be less - * than 512 - * - * @return + * than 512 + * + * @return */ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { unsigned char oleBuf[BBD_BLOCK_SIZE]; unsigned char *tmpBuf; FILE *newfile; int ret=0, i; + long int bbdSize; long int sbdMaxLen, sbdCurrent, propMaxLen, propCurrent, mblock, msat_size; oleEntry *tEntry; /* deleting old data (if it was allocated) */ ole_finish(); - + if (fseek(f,0,SEEK_SET) == -1) { if ( errno == ESPIPE ) { /* We got non-seekable file, create temp file */ @@ -76,7 +77,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { return NULL; } } - + while(!feof(f)){ ret=fread(oleBuf,1,BBD_BLOCK_SIZE,f); fwrite(oleBuf, 1, ret, newfile); @@ -88,7 +89,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { } } else { newfile=f; - } + } fseek(newfile,0,SEEK_END); fileLength=ftell(newfile); /* fprintf(stderr, "fileLength=%ld\n", fileLength); */ @@ -105,20 +106,26 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { } sectorSize = 1< fileLength) { + /* broken file, BBD size greater than entire file*/ + return NULL; + } + if((BBD=malloc(bbdNumBlocks*sectorSize)) == NULL ) { return NULL; } - + if((tmpBuf=malloc(MSAT_ORIG_SIZE)) == NULL ) { return NULL; } memcpy(tmpBuf,oleBuf+0x4c,MSAT_ORIG_SIZE); mblock=getlong(oleBuf,0x44); msat_size=getlong(oleBuf,0x48); - + if (msat_size * sectorSize) /* fprintf(stderr, "msat_size=%ld\n", msat_size); */ i=0; @@ -133,7 +140,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { ole_finish(); return NULL; } - + fseek(newfile, 512+mblock*sectorSize, SEEK_SET); if(fread(tmpBuf+MSAT_ORIG_SIZE+(sectorSize-4)*i, 1, sectorSize, newfile) != sectorSize) { @@ -145,11 +152,11 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { i++; mblock=getlong(tmpBuf, MSAT_ORIG_SIZE+(sectorSize-4)*i); } - + /* fprintf(stderr, "bbdNumBlocks=%ld\n", bbdNumBlocks); */ for(i=0; i< bbdNumBlocks; i++) { long int bbdSector=getlong(tmpBuf,4*i); - + if (bbdSector >= fileLength/sectorSize || bbdSector < 0) { fprintf(stderr, "Bad BBD entry!\n"); ole_finish(); @@ -164,7 +171,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { } } free(tmpBuf); - + /* Read SBD into memory */ sbdLen=0; sbdMaxLen=10; @@ -180,7 +187,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { sbdLen++; if (sbdLen >= sbdMaxLen) { unsigned char *newSBD; - + sbdMaxLen+=5; if ((newSBD=realloc(SBD, sectorSize*sbdMaxLen)) != NULL) { SBD=newSBD; @@ -190,6 +197,10 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { return NULL; } } + if (sbdCurrent * 4 > bbdSize) { + ole_finish(); + return NULL; + } sbdCurrent = getlong(BBD, sbdCurrent*4); if(sbdCurrent < 0 || sbdCurrent >= fileLength/sectorSize) @@ -212,12 +223,19 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { while(1) { /* fprintf(stderr, "propCurrent=%ld\n",propCurrent); */ fseek(newfile, 512+propCurrent*sectorSize, SEEK_SET); - fread(properties+propLen*sectorSize, - 1, sectorSize, newfile); + errno=0; + if (fread(properties+propLen*sectorSize, + 1, sectorSize, newfile)!=sectorSize) { + if (errno != 0) { + perror("reading properties catalog"); + } + ole_finish(); + return NULL; + } propLen++; if (propLen >= propMaxLen) { unsigned char *newProp; - + propMaxLen+=5; if ((newProp=realloc(properties, propMaxLen*sectorSize)) != NULL) properties=newProp; @@ -227,11 +245,11 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { return NULL; } } - + propCurrent = getlong(BBD, propCurrent*4); if(propCurrent < 0 || - propCurrent >= fileLength/sectorSize ) { - break; + propCurrent >= bbdSize/4 ) { + break; } } /* fprintf(stderr, "propLen=%ld\n",propLen); */ @@ -242,8 +260,8 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { properties = NULL; return NULL; } - - + + /* Find Root Entry */ while((tEntry=(oleEntry*)ole_readdir(newfile)) != NULL) { if (tEntry->type == oleRootDir ) { @@ -257,39 +275,39 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize) { if (!rootEntry) { fprintf(stderr,"Broken OLE structure. Cannot find root entry in this file!\n"); ole_finish(); return NULL; - } + } return newfile; } -/** - * - * - * @param oleBuf - * - * @return +/** + * + * + * @param oleBuf + * + * @return */ int rightOleType(unsigned char *oleBuf) { return (oleBuf[0x42] == 1 || oleBuf[0x42] == 2 || oleBuf[0x42] == 3 || oleBuf[0x42] == 5 ); } -/** - * - * - * @param oleBuf - * - * @return +/** + * + * + * @param oleBuf + * + * @return */ oleType getOleType(unsigned char *oleBuf) { return (oleType)((unsigned char)oleBuf[0x42]); } -/** +/** * Reads next directory entry from file - * + * * @param name buffer for name converted to us-ascii should be at least 33 chars long - * @param size size of file - * + * @param size size of file + * * @return 0 if everything is ok -1 on error */ FILE *ole_readdir(FILE *f) { @@ -297,13 +315,13 @@ FILE *ole_readdir(FILE *f) { unsigned char *oleBuf; oleEntry *e=NULL; long int chainMaxLen, chainCurrent; - + if ( properties == NULL || propCurNumber >= propNumber || f == NULL ) return NULL; oleBuf=properties + propCurNumber*PROP_BLOCK_SIZE; if( !rightOleType(oleBuf)) return NULL; - if ((e = (oleEntry*)malloc(sizeof(oleEntry))) == NULL) { + if ((e = (oleEntry*) calloc(sizeof(oleEntry),1)) == NULL) { perror("Can\'t allocate memory"); return NULL; } @@ -312,8 +330,12 @@ FILE *ole_readdir(FILE *f) { e->file=f; e->startBlock=getlong(oleBuf,0x74); e->blocks=NULL; - + nLen=getshort(oleBuf,0x40); + if (nLen > OLENAMELENGTH) { + free(e); + return NULL; + } for (i=0 ; i < nLen /2; i++) e->name[i]=(char)oleBuf[i*2]; e->name[i]='\0'; @@ -358,9 +380,9 @@ FILE *ole_readdir(FILE *f) { long int *newChain; chainMaxLen+=25; if ((newChain=realloc(e->blocks, - chainMaxLen*sizeof(long int))) != NULL) + chainMaxLen*sizeof(long int))) != NULL) { e->blocks=newChain; - else { + } else { perror("Properties realloc error"); free(e->blocks); e->blocks=NULL; @@ -376,39 +398,39 @@ FILE *ole_readdir(FILE *f) { } } } - + if(e->length > (e->isBigBlock ? sectorSize : shortSectorSize)*e->numOfBlocks) e->length = (e->isBigBlock ? sectorSize : shortSectorSize)*e->numOfBlocks; /* fprintf(stderr, "READDIR: e->name=%s e->numOfBlocks=%ld length=%ld\n", */ /* e->name, e->numOfBlocks, e->length); */ - + return (FILE*)e; } -/** +/** * Open stream, which correspond to directory entry last read by - * ole_readdir - * - * + * ole_readdir + * + * * @return opaque pointer to pass to ole_read, casted to (FILE *) */ int ole_open(FILE *stream) { oleEntry *e=(oleEntry *)stream; if ( e->type != oleStream) return -2; - + e->ole_offset=0; e->file_offset= ftell(e->file); return 0; } -/** - * - * - * @param e - * @param blk - * - * @return +/** + * + * + * @param e + * @param blk + * + * @return */ long int calcFileBlockOffset(oleEntry *e, long int blk) { long int res; @@ -418,7 +440,7 @@ long int calcFileBlockOffset(oleEntry *e, long int blk) { long int sbdPerSector=sectorSize/shortSectorSize; long int sbdSecNum=e->blocks[blk]/sbdPerSector; long int sbdSecMod=e->blocks[blk]%sbdPerSector; -/* fprintf(stderr, "calcoffset: e->name=%s e->numOfBlocks=%ld length=%ld sbdSecNum=%ld rootEntry->blocks=%p\n", +/* fprintf(stderr, "calcoffset: e->name=%s e->numOfBlocks=%ld length=%ld sbdSecNum=%ld rootEntry->blocks=%p\n", e->name, e->numOfBlocks, e->length, sbdSecNum, rootEntry->blocks);*/ res=512 + rootEntry->blocks[sbdSecNum]*sectorSize + sbdSecMod*shortSectorSize; } @@ -426,14 +448,14 @@ long int calcFileBlockOffset(oleEntry *e, long int blk) { } -/** +/** * Reads block from open ole stream interface-compatible with fread - * + * * @param ptr pointer to buffer for read to * @param size size of block - * @param nmemb size in blocks + * @param nmemb size in blocks * @param stream pointer to FILE* structure - * + * * @return number of readed blocks */ size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream) { @@ -442,22 +464,22 @@ size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream) { long int blockNumber, modBlock, toReadBlocks, toReadBytes, bytesInBlock; long int ssize; /**< Size of block */ long int newoffset; - unsigned char *cptr = ptr; + unsigned char *cptr = ptr; if( e->ole_offset+llen > e->length ) llen= e->length - e->ole_offset; - + ssize = (e->isBigBlock ? sectorSize : shortSectorSize); blockNumber=e->ole_offset/ssize; /* fprintf(stderr, "blockNumber=%ld e->numOfBlocks=%ld llen=%ld\n", */ /* blockNumber, e->numOfBlocks, llen); */ if ( blockNumber >= e->numOfBlocks || llen <=0 ) return 0; - + modBlock=e->ole_offset%ssize; bytesInBlock = ssize - modBlock; if(bytesInBlock < llen) { toReadBlocks = (llen-bytesInBlock)/ssize; - toReadBytes = (llen-bytesInBlock)%ssize; + toReadBytes = (llen-bytesInBlock)%ssize; } else { toReadBlocks = toReadBytes = 0; } @@ -492,14 +514,14 @@ size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream) { e->ole_offset, rread, llen);*/ e->ole_offset+=rread; return rread; -} - -/** - * - * - * @param stream - * - * @return +} + +/** + * + * + * @param stream + * + * @return */ int ole_eof(FILE *stream) { oleEntry *e=(oleEntry*)stream; @@ -508,9 +530,9 @@ int ole_eof(FILE *stream) { return (e->ole_offset >= e->length); } -/** - * - * +/** + * + * */ void ole_finish(void) { if ( BBD != NULL ) free(BBD); @@ -521,12 +543,12 @@ void ole_finish(void) { rootEntry = NULL; } -/** - * - * - * @param stream - * - * @return +/** + * + * + * @param stream + * + * @return */ int ole_close(FILE *stream) { oleEntry *e=(oleEntry*)stream; @@ -539,32 +561,32 @@ int ole_close(FILE *stream) { } /** - * - * + * + * * @param stream pointer to OLE stream structure - * @param offset - * @param whence - * - * @return + * @param offset + * @param whence + * + * @return */ int ole_seek(FILE *stream, long offset, int whence) { oleEntry *e=(oleEntry*)stream; long int new_ole_offset=0, new_file_offset; int ssize, modBlock, blockNumber; - + switch(whence) { case SEEK_SET: new_ole_offset=offset; break; - + case SEEK_CUR: new_ole_offset=e->ole_offset+offset; break; - + case SEEK_END: new_ole_offset=e->length+offset; break; - + default: errno=EINVAL; return -1; @@ -578,20 +600,20 @@ int ole_seek(FILE *stream, long offset, int whence) { blockNumber=new_ole_offset/ssize; if ( blockNumber >= e->numOfBlocks ) return -1; - + modBlock=new_ole_offset%ssize; new_file_offset = calcFileBlockOffset(e,blockNumber)+modBlock; fseek(e->file, e->file_offset=new_file_offset, SEEK_SET); e->ole_offset=new_ole_offset; - + return 0; } -/** +/** * Tell position inside OLE stream - * + * * @param stream pointer to OLE stream - * + * * @return current position inside OLE stream */ long ole_tell(FILE *stream) { @@ -601,8 +623,8 @@ long ole_tell(FILE *stream) { /** - * - * + * + * */ size_t (*catdoc_read)(void *ptr, size_t size, size_t nmemb, FILE *stream); int (*catdoc_eof)(FILE *stream); @@ -619,10 +641,10 @@ void set_ole_func(void) { #ifdef feof /* feof is macro in Turbo C, so we need a real function to assign to * pointer - */ + */ int my_feof(FILE *f) { return feof(f); -} +} #define FEOF my_feof #else #define FEOF feof diff --git a/src/ole.h b/src/ole.h index 181c7e7..b0d6e8e 100644 --- a/src/ole.h +++ b/src/ole.h @@ -16,7 +16,7 @@ #define BBD_BLOCK_SIZE 512 #define SBD_BLOCK_SIZE 64 #define PROP_BLOCK_SIZE 128 -#define OLENAMELENGHT 32 +#define OLENAMELENGTH 32 #define MSAT_ORIG_SIZE 436 typedef enum { @@ -28,7 +28,7 @@ typedef enum { typedef struct { FILE *file; - char name[OLENAMELENGHT+1]; + char name[OLENAMELENGTH+1]; long int startBlock; long int curBlock; unsigned long int length; -- 2.39.2