1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2013 Sourcefire, Inc.
4  *
5  *  Authors: David Raynor <draynor@sourcefire.com>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  */
21 
22 #if HAVE_CONFIG_H
23 #include "clamav-config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <errno.h>
28 #if HAVE_STRING_H
29 #include <string.h>
30 #endif
31 
32 #include "clamav.h"
33 #include "others.h"
34 #include "adc.h"
35 
36 /* #define DEBUG_ADC */
37 
38 #ifdef DEBUG_ADC
39 #define adc_dbgmsg(...) cli_dbgmsg(__VA_ARGS__)
40 #else
41 #define adc_dbgmsg(...) ;
42 #endif
43 
44 /* Initialize values and collect buffer
45  * NOTE: buffer size must be larger than largest lookback offset */
adc_decompressInit(adc_stream * strm)46 int adc_decompressInit(adc_stream *strm)
47 {
48     if (strm == NULL) {
49         return ADC_IO_ERROR;
50     }
51     if (strm->state != ADC_STATE_UNINIT) {
52         return ADC_DATA_ERROR;
53     }
54 
55     /* Have to buffer maximum backward lookup */
56     strm->buffer = (uint8_t *)calloc(ADC_BUFF_SIZE, 1);
57     if (strm->buffer == NULL) {
58         return ADC_MEM_ERROR;
59     }
60     strm->buffered = 0;
61     strm->state    = ADC_STATE_GETTYPE;
62     strm->length   = 0;
63     strm->offset   = 0;
64     strm->curr     = strm->buffer;
65 
66     return ADC_OK;
67 }
68 
69 /* Decompress routine
70  * NOTE: Reaching end of input buffer does not mean end of output.
71  * It may fill the output buffer but have more to output.
72  * It will only return ADC_STREAM_END if output buffer is not full.
73  * It will return ADC_DATA_ERROR if it ends in the middle of a phrase
74  * (i.e. in the middle of a lookback code or data run)
75  */
adc_decompress(adc_stream * strm)76 int adc_decompress(adc_stream *strm)
77 {
78     uint8_t bData;
79     uint8_t didNothing = 1;
80 
81     /* first, the error returns based on strm */
82     if ((strm == NULL) || (strm->next_in == NULL) || (strm->next_out == NULL)) {
83         return ADC_IO_ERROR;
84     }
85     if (strm->state == ADC_STATE_UNINIT) {
86         return ADC_DATA_ERROR;
87     }
88 
89     cli_dbgmsg("adc_decompress: avail_in %llu avail_out %llu state %u\n",
90                (long long unsigned)strm->avail_in, (long long unsigned)strm->avail_out, strm->state);
91 
92     while (strm->avail_out) {
93         /* Exit if needs more in bytes and none available */
94         int needsInput;
95         switch (strm->state) {
96             case ADC_STATE_SHORTLOOK:
97             case ADC_STATE_LONGLOOK:
98                 needsInput = 0;
99                 break;
100             default:
101                 needsInput = 1;
102                 break;
103         }
104         if (needsInput && (strm->avail_in == 0)) {
105             break;
106         } else {
107             didNothing = 0;
108         }
109 
110         /* Find or execute statecode */
111         switch (strm->state) {
112             case ADC_STATE_GETTYPE: {
113                 /* Grab action code */
114                 bData = *(strm->next_in);
115                 strm->next_in++;
116                 strm->avail_in--;
117                 if (bData & 0x80) {
118                     strm->state  = ADC_STATE_RAWDATA;
119                     strm->offset = 0;
120                     strm->length = (bData & 0x7F) + 1;
121                 } else if (bData & 0x40) {
122                     strm->state  = ADC_STATE_LONGOP2;
123                     strm->offset = 0;
124                     strm->length = (bData & 0x3F) + 4;
125                 } else {
126                     strm->state  = ADC_STATE_SHORTOP;
127                     strm->offset = (bData & 0x3) * 0x100;
128                     strm->length = ((bData & 0x3C) >> 2) + 3;
129                 }
130                 adc_dbgmsg("adc_decompress: GETTYPE bData %x state %u offset %u length %u\n",
131                            bData, strm->state, strm->offset, strm->length);
132                 break;
133             }
134             case ADC_STATE_LONGOP2: {
135                 /* Grab first offset byte */
136                 bData = *(strm->next_in);
137                 strm->next_in++;
138                 strm->avail_in--;
139                 strm->offset = bData * 0x100;
140                 strm->state  = ADC_STATE_LONGOP1;
141                 adc_dbgmsg("adc_decompress: LONGOP2 bData %x state %u offset %u length %u\n",
142                            bData, strm->state, strm->offset, strm->length);
143                 break;
144             }
145             case ADC_STATE_LONGOP1: {
146                 /* Grab second offset byte */
147                 bData = *(strm->next_in);
148                 strm->next_in++;
149                 strm->avail_in--;
150                 strm->offset += bData + 1;
151                 strm->state = ADC_STATE_LONGLOOK;
152                 adc_dbgmsg("adc_decompress: LONGOP1 bData %x state %u offset %u length %u\n",
153                            bData, strm->state, strm->offset, strm->length);
154                 break;
155             }
156             case ADC_STATE_SHORTOP: {
157                 /* Grab offset byte */
158                 bData = *(strm->next_in);
159                 strm->next_in++;
160                 strm->avail_in--;
161                 strm->offset += bData + 1;
162                 strm->state = ADC_STATE_SHORTLOOK;
163                 adc_dbgmsg("adc_decompress: SHORTOP bData %x state %u offset %u length %u\n",
164                            bData, strm->state, strm->offset, strm->length);
165                 break;
166             }
167 
168             case ADC_STATE_RAWDATA: {
169                 /* Grab data */
170                 adc_dbgmsg("adc_decompress: RAWDATA offset %u length %u\n", strm->offset, strm->length);
171                 while ((strm->avail_in > 0) && (strm->avail_out > 0) && (strm->length > 0)) {
172                     bData = *(strm->next_in);
173                     strm->next_in++;
174                     strm->avail_in--;
175                     /* store to output */
176                     *(strm->next_out) = bData;
177                     strm->next_out++;
178                     strm->avail_out--;
179                     /* store to buffer */
180                     if (strm->curr >= (strm->buffer + ADC_BUFF_SIZE)) {
181                         strm->curr = strm->buffer;
182                     }
183                     *(strm->curr) = bData;
184                     strm->curr++;
185                     if (strm->buffered < ADC_BUFF_SIZE) {
186                         strm->buffered++;
187                     }
188                     strm->length--;
189                 }
190                 if (strm->length == 0) {
191                     /* adc_dbgmsg("adc_decompress: RAWDATADONE buffered %u avail_in %u avail_out %u \n",
192                         strm->buffered, strm->avail_in, strm->avail_out); */
193                     strm->state = ADC_STATE_GETTYPE;
194                 }
195                 break;
196             }
197 
198             case ADC_STATE_SHORTLOOK:
199             case ADC_STATE_LONGLOOK: {
200                 /* Copy data */
201                 adc_dbgmsg("adc_decompress: LOOKBACK offset %u length %u avail_in %u avail_out %u\n",
202                            strm->offset, strm->length, strm->avail_in, strm->avail_out);
203                 while ((strm->avail_out > 0) && (strm->length > 0)) {
204                     /* state validation first */
205                     if (strm->offset > 0x10000) {
206                         cli_dbgmsg("adc_decompress: bad LOOKBACK offset %u\n", strm->offset);
207                         return ADC_DATA_ERROR;
208                     } else if ((strm->state == ADC_STATE_SHORTLOOK) && (strm->offset > 0x400)) {
209                         cli_dbgmsg("adc_decompress: bad LOOKBACK offset %u\n", strm->offset);
210                         return ADC_DATA_ERROR;
211                     }
212                     if (strm->offset > strm->buffered) {
213                         cli_dbgmsg("adc_decompress: too large LOOKBACK offset %u\n", strm->offset);
214                         return ADC_DATA_ERROR;
215                     }
216                     /* retrieve byte */
217                     if (strm->curr >= (strm->buffer + ADC_BUFF_SIZE)) {
218                         strm->curr = strm->buffer;
219                     }
220                     if (strm->curr >= (strm->buffer + strm->offset)) {
221                         bData = *(uint8_t *)(strm->curr - strm->offset);
222                     } else {
223                         bData = *(uint8_t *)(strm->curr + ADC_BUFF_SIZE - strm->offset);
224                     }
225                     /* store to output */
226                     *(strm->next_out) = bData;
227                     strm->next_out++;
228                     strm->avail_out--;
229                     /* store to buffer */
230                     *(strm->curr) = bData;
231                     strm->curr++;
232                     if (strm->buffered < ADC_BUFF_SIZE) {
233                         strm->buffered++;
234                     }
235                     strm->length--;
236                 }
237                 if (strm->length == 0) {
238                     strm->state = ADC_STATE_GETTYPE;
239                     /* adc_dbgmsg("adc_decompress: LOOKBACKDONE buffered %u avail_in %u avail_out %u \n",
240                         strm->buffered, strm->avail_in, strm->avail_out); */
241                 }
242                 break;
243             }
244 
245             default: {
246                 /* bad state */
247                 cli_errmsg("adc_decompress: invalid state %u\n", strm->state);
248                 return ADC_DATA_ERROR;
249             }
250         } /* end switch */
251     }     /* end while */
252 
253     /* There really isn't a terminator, just end of data */
254     if (didNothing && strm->avail_out) {
255         if (strm->state == ADC_STATE_GETTYPE) {
256             /* Nothing left to do */
257             return ADC_STREAM_END;
258         } else {
259             /* Ended mid phrase */
260             cli_dbgmsg("adc_decompress: stream ended mid-phrase, state %u\n", strm->state);
261             return ADC_DATA_ERROR;
262         }
263     }
264     return ADC_OK;
265 }
266 
267 /* Cleanup routine, frees buffer */
adc_decompressEnd(adc_stream * strm)268 int adc_decompressEnd(adc_stream *strm)
269 {
270     if (strm == NULL) {
271         return ADC_IO_ERROR;
272     }
273     if (strm->state == ADC_STATE_UNINIT) {
274         return ADC_DATA_ERROR;
275     }
276 
277     if (strm->buffer != NULL) {
278         free(strm->buffer);
279     }
280     strm->buffered = 0;
281     strm->state    = ADC_STATE_UNINIT;
282     strm->length   = 0;
283     strm->offset   = 0;
284 
285     return ADC_OK;
286 }
287