1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2007-2013 Sourcefire, Inc.
4  *
5  *  Authors: Tomasz Kojm
6  *
7  *  Acknowledgements: Decompression scheme by M. Winterhoff.
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2 as
11  *  published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  *  MA 02110-1301, USA.
22  */
23 
24 #if HAVE_CONFIG_H
25 #include "clamav-config.h"
26 #endif
27 
28 #include <stdio.h>
29 #include <stddef.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <string.h>
34 
35 #include "clamav.h"
36 #include "others.h"
37 #include "msexpand.h"
38 #include "fmap.h"
39 
40 #ifndef HAVE_ATTRIB_PACKED
41 #define __attribute__(x)
42 #endif
43 
44 #ifdef HAVE_PRAGMA_PACK
45 #pragma pack(1)
46 #endif
47 
48 #ifdef HAVE_PRAGMA_PACK_HPPA
49 #pragma pack 1
50 #endif
51 
52 #define EC32(x) le32_to_host(x)
53 #define EC16(x) le16_to_host(x)
54 
55 #define MAGIC1 0x44445a53
56 #define MAGIC2 0x3327f088
57 #define MAGIC3 0x0041
58 
59 struct msexp_hdr {
60     uint32_t magic1;
61     uint32_t magic2;
62     uint16_t magic3;
63     uint32_t fsize;
64 } __attribute__((packed));
65 
66 #ifdef HAVE_PRAGMA_PACK
67 #pragma pack()
68 #endif
69 
70 #ifdef HAVE_PRAGMA_PACK_HPPA
71 #pragma pack
72 #endif
73 
74 #define B_SIZE 4096
75 #define RW_SIZE 2048
76 
77 #define READBYTES                                     \
78     rbytes = MIN(RW_SIZE, map->len - cur_off);        \
79     if (!rbytes)                                      \
80         break;                                        \
81     rbuff = fmap_need_off_once(map, cur_off, rbytes); \
82     if (!rbuff)                                       \
83         return CL_EREAD;                              \
84     cur_off += rbytes;                                \
85     r = 0;
86 
87 #define WRITEBYTES                                   \
88     ret = cli_writen(ofd, wbuff, w);                 \
89     if (ret == (size_t)-1 || (unsigned int)ret != w) \
90         return CL_EWRITE;                            \
91     wbytes += w;                                     \
92     if (wbytes >= fsize)                             \
93         return CL_SUCCESS;                           \
94     w = 0;
95 
cli_msexpand(cli_ctx * ctx,int ofd)96 cl_error_t cli_msexpand(cli_ctx *ctx, int ofd)
97 {
98     const struct msexp_hdr *hdr;
99     uint8_t i, mask, bits;
100     unsigned char buff[B_SIZE], wbuff[RW_SIZE];
101     const unsigned char *rbuff = NULL; // rbuff will be set to a real address by READBYTES
102                                        // in the first iteration of the loop.
103     unsigned int j = B_SIZE - 16, k, l, r = 0, w = 0, rbytes = 0, wbytes = 0;
104     fmap_t *map   = ctx->fmap;
105     off_t cur_off = sizeof(*hdr);
106     unsigned int fsize;
107     size_t ret;
108 
109     if (!(hdr = fmap_need_off_once(map, 0, sizeof(*hdr))))
110         return CL_EREAD;
111 
112     if (EC32(hdr->magic1) != MAGIC1 || EC32(hdr->magic2) != MAGIC2 || EC16(hdr->magic3) != MAGIC3) {
113         cli_dbgmsg("MSEXPAND: Not supported file format\n");
114         return CL_EFORMAT;
115     }
116 
117     fsize = EC32(hdr->fsize);
118     cli_dbgmsg("MSEXPAND: File size from header: %u\n", fsize);
119 
120     if (cli_checklimits("MSEXPAND", ctx, fsize, 0, 0) != CL_CLEAN)
121         return CL_SUCCESS;
122 
123     memset(buff, 0, B_SIZE);
124     while (1) {
125 
126         if (!rbytes || (r == rbytes)) {
127             READBYTES;
128         }
129 
130         bits = rbuff[r];
131         r++;
132 
133         mask = 1;
134         for (i = 0; i < 8; i++) {
135             if (bits & mask) {
136                 if (r == rbytes) {
137                     READBYTES;
138                 }
139 
140                 if (w == RW_SIZE) {
141                     WRITEBYTES;
142                 }
143 
144                 wbuff[w] = buff[j] = rbuff[r];
145                 r++;
146                 w++;
147                 j++;
148                 j %= B_SIZE;
149             } else {
150                 if (r == rbytes) {
151                     READBYTES;
152                 }
153                 k = rbuff[r];
154                 r++;
155 
156                 if (r == rbytes) {
157                     READBYTES;
158                 }
159                 l = rbuff[r];
160                 r++;
161 
162                 k += (l & 0xf0) << 4;
163                 l = (l & 0x0f) + 3;
164                 while (l--) {
165                     if (w == RW_SIZE) {
166                         WRITEBYTES;
167                     }
168                     wbuff[w] = buff[j] = buff[k];
169                     w++;
170                     k++;
171                     k %= B_SIZE;
172                     j++;
173                     j %= B_SIZE;
174                 }
175             }
176             mask *= 2;
177         }
178     }
179 
180     if (w) {
181         WRITEBYTES;
182     }
183 
184     return CL_SUCCESS;
185 }
186