Mercurial > libpst
comparison src/lzfu.c @ 41:183ae993b9ad
security fix for potential buffer overrun in lz decompress
author | carl |
---|---|
date | Tue, 02 Oct 2007 15:49:44 -0700 |
parents | ddfb25318812 |
children | f6db1f060a95 |
comparison
equal
deleted
inserted
replaced
40:be6d5329cc01 | 41:183ae993b9ad |
---|---|
13 #include "libpst.h" | 13 #include "libpst.h" |
14 #include <sys/types.h> | 14 #include <sys/types.h> |
15 #include <string.h> | 15 #include <string.h> |
16 #include <stdio.h> | 16 #include <stdio.h> |
17 | 17 |
18 #ifndef _MSC_VER | |
19 #include <stdint.h> | |
20 #endif | |
21 | |
22 #ifdef _MSC_VER | |
23 #define uint32_t unsigned int | |
24 #endif | |
25 | |
26 #include "lzfu.h" | 18 #include "lzfu.h" |
27 | 19 |
28 #define LZFU_COMPRESSED 0x75465a4c | 20 #define LZFU_COMPRESSED 0x75465a4c |
29 #define LZFU_UNCOMPRESSED 0x414c454d | 21 #define LZFU_UNCOMPRESSED 0x414c454d |
30 | 22 |
31 // initital dictionary | 23 // initital dictionary |
32 #define LZFU_INITDICT "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \ | 24 #define LZFU_INITDICT "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \ |
33 "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscrip" \ | 25 "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscrip" \ |
34 "t \\fdecor MS Sans SerifSymbolArialTimes Ne" \ | 26 "t \\fdecor MS Sans SerifSymbolArialTimes Ne" \ |
35 "w RomanCourier{\\colortbl\\red0\\green0\\blue0" \ | 27 "w RomanCourier{\\colortbl\\red0\\green0\\blue0" \ |
36 "\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab" \ | 28 "\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab" \ |
37 "\\tx" | 29 "\\tx" |
38 // initial length of dictionary | 30 // initial length of dictionary |
39 #define LZFU_INITLENGTH 207 | 31 #define LZFU_INITLENGTH 207 |
40 | 32 |
41 // header for compressed rtf | 33 // header for compressed rtf |
42 typedef struct _lzfuheader { | 34 typedef struct _lzfuheader { |
43 uint32_t cbSize; | 35 u_int32_t cbSize; |
44 uint32_t cbRawSize; | 36 u_int32_t cbRawSize; |
45 uint32_t dwMagic; | 37 u_int32_t dwMagic; |
46 uint32_t dwCRC; | 38 u_int32_t dwCRC; |
47 } lzfuheader; | 39 } lzfuheader; |
48 | 40 |
49 | 41 |
50 /** | 42 unsigned char* lzfu_decompress (unsigned char* rtfcomp, u_int32_t compsize, size_t *size) { |
51 We always need to add 0x10 to the buffer offset because we need to skip past the header info | |
52 */ | |
53 | |
54 unsigned char* lzfu_decompress (unsigned char* rtfcomp, size_t *size) { | |
55 // the dictionary buffer | 43 // the dictionary buffer |
56 unsigned char dict[4096]; | 44 unsigned char dict[4096]; |
57 // the dictionary pointer | 45 // the dictionary pointer |
58 unsigned int dict_length=0; | 46 unsigned int dict_length=0; |
59 // the header of the lzfu block | 47 // the header of the lzfu block |
60 lzfuheader lzfuhdr; | 48 lzfuheader lzfuhdr; |
61 // container for the data blocks | 49 // container for the data blocks |
62 unsigned char flags; | 50 unsigned char flags; |
63 // temp value for determining the bits in the flag | 51 // temp value for determining the bits in the flag |
64 unsigned char flag_mask; | 52 unsigned char flag_mask; |
65 unsigned int i, in_size; | 53 u_int32_t i; |
66 unsigned char *out_buf; | 54 unsigned char *out_buf; |
67 unsigned int out_ptr = 0; | 55 u_int32_t out_ptr = 0; |
56 u_int32_t out_size; | |
57 u_int32_t in_ptr; | |
58 u_int32_t in_size; | |
68 | 59 |
69 memcpy(dict, LZFU_INITDICT, LZFU_INITLENGTH); | 60 memcpy(dict, LZFU_INITDICT, LZFU_INITLENGTH); |
70 dict_length = LZFU_INITLENGTH; | 61 dict_length = LZFU_INITLENGTH; |
71 memcpy(&lzfuhdr, rtfcomp, sizeof(lzfuhdr)); | 62 memcpy(&lzfuhdr, rtfcomp, sizeof(lzfuhdr)); |
72 LE32_CPU(lzfuhdr.cbSize); | 63 LE32_CPU(lzfuhdr.cbSize); |
73 LE32_CPU(lzfuhdr.cbRawSize); | 64 LE32_CPU(lzfuhdr.cbRawSize); |
74 LE32_CPU(lzfuhdr.dwMagic); | 65 LE32_CPU(lzfuhdr.dwMagic); |
75 LE32_CPU(lzfuhdr.dwCRC); | 66 LE32_CPU(lzfuhdr.dwCRC); |
76 /* printf("total size: %d\n", lzfuhdr.cbSize+4); | 67 //printf("total size: %d\n", lzfuhdr.cbSize+4); |
77 printf("raw size : %d\n", lzfuhdr.cbRawSize); | 68 //printf("raw size : %d\n", lzfuhdr.cbRawSize); |
78 printf("compressed: %s\n", (lzfuhdr.dwMagic == LZFU_COMPRESSED ? "yes" : "no")); | 69 //printf("compressed: %s\n", (lzfuhdr.dwMagic == LZFU_COMPRESSED ? "yes" : "no")); |
79 printf("CRC : %#x\n", lzfuhdr.dwCRC); | 70 //printf("CRC : %#x\n", lzfuhdr.dwCRC); |
80 printf("\n");*/ | 71 //printf("\n"); |
81 out_buf = (unsigned char*)xmalloc(lzfuhdr.cbRawSize+20); //plus 4 cause we have 2x'}' and a \0 | 72 out_size = lzfuhdr.cbRawSize + 3; // two braces and a null terminator |
82 in_size = 0; | 73 out_buf = (unsigned char*)xmalloc(out_size); |
83 // we add plus one here cause when referencing an array, the index is always one less | 74 in_ptr = sizeof(lzfuhdr); |
84 // (ie, when accessing 2 element array, highest index is [1]) | 75 in_size = (lzfuhdr.cbSize < compsize) ? lzfuhdr.cbSize : compsize; |
85 while (in_size+0x11 < lzfuhdr.cbSize) { | 76 while (in_ptr < in_size) { |
86 memcpy(&flags, &(rtfcomp[in_size+0x10]), 1); | 77 flags = rtfcomp[in_ptr++]; |
87 in_size += 1; | |
88 | |
89 flag_mask = 1; | 78 flag_mask = 1; |
90 while (flag_mask != 0 && in_size+0x11 < lzfuhdr.cbSize) { | 79 while (flag_mask) { |
91 if (flag_mask & flags) { | 80 if (flag_mask & flags) { |
92 // read 2 bytes from input | 81 // two bytes available? |
93 unsigned short int blkhdr, offset, length; | 82 if (in_ptr+1 < in_size) { |
94 memcpy(&blkhdr, &(rtfcomp[in_size+0x10]), 2); | 83 // read 2 bytes from input |
95 LE16_CPU(blkhdr); | 84 unsigned short int blkhdr, offset, length; |
96 in_size += 2; | 85 memcpy(&blkhdr, rtfcomp+in_ptr, 2); |
97 /* swap the upper and lower bytes of blkhdr */ | 86 LE16_CPU(blkhdr); |
98 blkhdr = (((blkhdr&0xFF00)>>8)+ | 87 in_ptr += 2; |
99 ((blkhdr&0x00FF)<<8)); | 88 /* swap the upper and lower bytes of blkhdr */ |
100 /* the offset is the first 24 bits of the 32 bit value */ | 89 blkhdr = (((blkhdr&0xFF00)>>8)+ |
101 offset = (blkhdr&0xFFF0)>>4; | 90 ((blkhdr&0x00FF)<<8)); |
102 /* the length of the dict entry are the last 8 bits */ | 91 /* the offset is the first 12 bits of the 16 bit value */ |
103 length = (blkhdr&0x000F)+2; | 92 offset = (blkhdr&0xFFF0)>>4; |
104 // add the value we are about to print to the dictionary | 93 /* the length of the dict entry are the last 4 bits */ |
105 for (i=0; i < length; i++) { | 94 length = (blkhdr&0x000F)+2; |
106 unsigned char c1; | 95 // add the value we are about to print to the dictionary |
107 c1 = dict[(offset+i)%4096]; | 96 for (i=0; i < length; i++) { |
108 dict[dict_length]=c1; | 97 unsigned char c1; |
109 dict_length = (dict_length+1) % 4096; | 98 c1 = dict[(offset+i)%4096]; |
110 out_buf[out_ptr++] = c1; | 99 dict[dict_length]=c1; |
100 dict_length = (dict_length+1) % 4096; | |
101 if (out_ptr < out_size) out_buf[out_ptr++] = c1; | |
102 } | |
111 } | 103 } |
112 } else { | 104 } else { |
113 // uncompressed chunk (single byte) | 105 // one byte available? |
114 char c1 = rtfcomp[in_size+0x10]; | 106 if (in_ptr < in_size) { |
115 in_size ++; | 107 // uncompressed chunk (single byte) |
116 dict[dict_length] = c1; | 108 char c1 = rtfcomp[in_ptr++]; |
117 dict_length = (dict_length+1)%4096; | 109 dict[dict_length] = c1; |
118 out_buf[out_ptr++] = c1; | 110 dict_length = (dict_length+1)%4096; |
111 if (out_ptr < out_size) out_buf[out_ptr++] = c1; | |
112 } | |
119 } | 113 } |
120 flag_mask <<= 1; | 114 flag_mask <<= 1; |
121 } | 115 } |
122 } | 116 } |
123 // the compressed version doesn't appear to drop the closing braces onto the doc. | 117 // the compressed version doesn't appear to drop the closing |
124 // we should do that | 118 // braces onto the doc, so we do that here. |
125 out_buf[out_ptr++] = '}'; | 119 if (out_ptr < out_size) out_buf[out_ptr++] = '}'; |
126 out_buf[out_ptr++] = '}'; | 120 if (out_ptr < out_size) out_buf[out_ptr++] = '}'; |
127 *size = out_ptr; | 121 *size = out_ptr; |
128 out_buf[out_ptr++] = '\0'; | 122 if (out_ptr < out_size) out_buf[out_ptr++] = '\0'; |
129 return out_buf; | 123 return out_buf; |
130 } | 124 } |