Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : routines for marshalling/unmarshalling cab structures
5 :
6 : Copyright (C) Guenther Deschner 2016
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "librpc/gen_ndr/ndr_cab.h"
24 : #include "librpc/ndr/ndr_compression.h"
25 :
26 : #define OFFSET_OF_FOLDER_COFFCABSTART(folder) (36 /* cfheader size */ + (size_t)(folder)*8)
27 :
28 8 : _PUBLIC_ void ndr_print_cf_time(struct ndr_print *ndr, const char *name, const struct cf_time *r)
29 : {
30 8 : uint8_t hour = 0, minute = 0, seconds = 0;
31 8 : char *s;
32 8 : if (r == NULL) { ndr_print_null(ndr); return; }
33 8 : hour = r->time >> 11;
34 8 : minute = (r->time >> 5) & 0x3f;
35 8 : seconds = (r->time << 1) & 0x3e;
36 8 : s = talloc_asprintf(ndr, "%02d:%02d:%02d", hour, minute, seconds);
37 8 : if (s == NULL) { return; }
38 8 : ndr_print_string(ndr, "time", s);
39 8 : talloc_free(s);
40 : }
41 :
42 8 : _PUBLIC_ void ndr_print_cf_date(struct ndr_print *ndr, const char *name, const struct cf_date *r)
43 : {
44 8 : uint16_t year = 0;
45 8 : uint8_t month = 0, day = 0;
46 8 : char *s;
47 8 : if (r == NULL) { ndr_print_null(ndr); return; }
48 8 : year = (r->date >> 9);
49 8 : year += 1980;
50 8 : month = (r->date >> 5 & 0xf);
51 8 : day = (r->date & 0x1f);
52 8 : s = talloc_asprintf(ndr, "%02"PRIu8"/%02"PRIu8"/%04"PRIu16, day, month, year);
53 8 : if (s == NULL) { return; }
54 8 : ndr_print_string(ndr, "date", s);
55 8 : talloc_free(s);
56 : }
57 :
58 8 : uint32_t ndr_count_cfdata(const struct cab_file *r)
59 : {
60 8 : uint32_t count = 0, i;
61 :
62 16 : for (i = 0; i < r->cfheader.cFolders; i++) {
63 8 : if (count + r->cffolders[i].cCFData < count) {
64 : /* Integer wrap. */
65 0 : return 0;
66 : }
67 8 : count += r->cffolders[i].cCFData;
68 : }
69 :
70 0 : return count;
71 : }
72 :
73 4 : static uint32_t ndr_cab_compute_checksum(uint8_t *data, uint32_t length, uint32_t seed)
74 : {
75 4 : int num_ulong;
76 4 : uint32_t checksum;
77 4 : uint8_t *pb;
78 4 : uint32_t ul;
79 :
80 4 : num_ulong = length / 4;
81 4 : checksum = seed;
82 4 : pb = data;
83 :
84 16390 : while (num_ulong-- > 0) {
85 16386 : ul = (uint32_t)(*pb++);
86 16386 : ul |= (((uint32_t)(*pb++)) << 8);
87 16386 : ul |= (((uint32_t)(*pb++)) << 16);
88 16386 : ul |= (((uint32_t)(*pb++)) << 24);
89 :
90 16386 : checksum ^= ul;
91 : }
92 :
93 4 : ul = 0;
94 :
95 4 : switch (length % 4) {
96 0 : case 3:
97 0 : ul |= (((uint32_t)(*pb++)) << 16);
98 0 : FALL_THROUGH;
99 0 : case 2:
100 0 : ul |= (((uint32_t)(*pb++)) << 8);
101 0 : FALL_THROUGH;
102 0 : case 1:
103 0 : ul |= (uint32_t)(*pb++);
104 4 : FALL_THROUGH;
105 0 : default:
106 4 : break;
107 : }
108 :
109 4 : checksum ^= ul;
110 :
111 4 : return checksum;
112 : }
113 :
114 : /* Push all CFDATA of a folder.
115 : *
116 : * This works on a folder level because compression type is set per
117 : * folder, and a compression state can be shared between CFDATA of the
118 : * same folder.
119 : *
120 : * This is not a regular NDR func as we pass the compression type and
121 : * the number of CFDATA as extra arguments
122 : */
123 2 : static enum ndr_err_code ndr_push_folder_cfdata(struct ndr_push *ndr,
124 : const struct CFDATA *r,
125 : enum cf_compress_type cab_ctype,
126 : size_t num_cfdata)
127 : {
128 2 : size_t i;
129 2 : enum ndr_compression_alg ndr_ctype = 0;
130 :
131 2 : ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
132 :
133 2 : if (cab_ctype == CF_COMPRESS_MSZIP) {
134 0 : ndr_ctype = NDR_COMPRESSION_MSZIP_CAB;
135 0 : NDR_CHECK(ndr_push_compression_state_init(ndr, ndr_ctype));
136 : }
137 :
138 4 : for (i = 0; i < num_cfdata; i++, r++) {
139 2 : uint32_t compressed_length = 0;
140 2 : uint32_t csum, csumPartial;
141 2 : size_t compressed_offset, csum_offset, data_offset;
142 :
143 2 : if (!r->ab.data) {
144 0 : return ndr_push_error(ndr, NDR_ERR_LENGTH,
145 : "NULL uncompressed data blob");
146 : }
147 2 : if (r->ab.length != r->cbUncomp) {
148 0 : return ndr_push_error(ndr, NDR_ERR_LENGTH,
149 : "Uncompressed data blob size != uncompressed data size field");
150 : }
151 :
152 : /*
153 : * checksum is a function of the size fields
154 : * and the potentially compressed data bytes,
155 : * which haven't been compressed yet so
156 : * remember offset, write zeroes, fill out
157 : * later
158 : */
159 2 : csum_offset = ndr->offset;
160 2 : NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
161 :
162 : /*
163 : * similarly, we don't know the compressed
164 : * size yet, remember offset, write zeros,
165 : * fill out later
166 : */
167 2 : compressed_offset = ndr->offset;
168 2 : NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 0));
169 2 : NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, r->cbUncomp));
170 :
171 2 : data_offset = ndr->offset;
172 :
173 2 : switch (cab_ctype) {
174 2 : case CF_COMPRESS_NONE:
175 : /* just copy the data */
176 2 : NDR_PUSH_NEED_BYTES(ndr, r->ab.length);
177 2 : NDR_CHECK(ndr_push_bytes(ndr, r->ab.data, r->ab.length));
178 2 : compressed_length = r->ab.length;
179 2 : break;
180 0 : case CF_COMPRESS_LZX:
181 : /*
182 : * we have not yet worked out the details of LZX
183 : * compression
184 : */
185 0 : return NDR_ERR_COMPRESSION;
186 :
187 0 : case CF_COMPRESS_MSZIP: {
188 0 : struct ndr_push *push_sub, *push_compress;
189 :
190 : /* compress via subcontext */
191 0 : NDR_CHECK(ndr_push_subcontext_start(ndr, &push_sub, 0, -1));
192 :
193 : /*
194 : * This assignment replaces a call to
195 : * ndr_push_compression_state_init(push_sub, ndr_ctype))
196 : * here. This is instead done outside the loop.
197 : */
198 0 : push_sub->cstate = ndr->cstate;
199 :
200 0 : NDR_CHECK(ndr_push_compression_start(push_sub, &push_compress));
201 0 : ndr_set_flags(&push_compress->flags, LIBNDR_FLAG_REMAINING);
202 0 : NDR_CHECK(ndr_push_DATA_BLOB(push_compress, NDR_SCALARS, r->ab));
203 0 : NDR_CHECK(ndr_push_compression_end(push_sub, push_compress));
204 0 : NDR_CHECK(ndr_push_subcontext_end(ndr, push_sub, 0, -1));
205 0 : compressed_length = push_sub->offset;
206 :
207 0 : break;
208 : }
209 0 : default:
210 0 : return NDR_ERR_BAD_SWITCH;
211 : }
212 :
213 : /* we can now write the compressed size and the checksum */
214 2 : SSVAL(ndr->data, compressed_offset, compressed_length);
215 :
216 : /*
217 : * Create checksum over compressed data.
218 : *
219 : * The 8 bytes are the header size.
220 : *
221 : * We have already have written the checksum and set it to zero,
222 : * earlier. So we know that after the checksum end the value
223 : * for the compressed length comes the blob data.
224 : *
225 : * NDR already did all the checks for integer wraps.
226 : */
227 2 : csumPartial = ndr_cab_compute_checksum(&ndr->data[data_offset],
228 : compressed_length, 0);
229 :
230 : /*
231 : * Checksum over header (compressed and uncompressed length).
232 : *
233 : * The first 4 bytes are the checksum size.
234 : * The second 4 bytes are the size of the compressed and
235 : * uncompressed length fields.
236 : *
237 : * NDR already did all the checks for integer wraps.
238 : */
239 2 : csum = ndr_cab_compute_checksum(&ndr->data[compressed_offset],
240 : data_offset - compressed_offset,
241 : csumPartial);
242 :
243 2 : SIVAL(ndr->data, csum_offset, csum);
244 : }
245 :
246 2 : TALLOC_FREE(ndr->cstate);
247 :
248 0 : return NDR_ERR_SUCCESS;
249 : }
250 :
251 2 : _PUBLIC_ enum ndr_err_code ndr_push_cab_file(struct ndr_push *ndr, ndr_flags_type ndr_flags, const struct cab_file *r)
252 : {
253 2 : uint32_t cntr_cffolders_0;
254 2 : uint32_t cntr_cffiles_0;
255 2 : size_t processed_cfdata = 0;
256 : {
257 2 : libndr_flags _flags_save_STRUCT = ndr->flags;
258 2 : ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
259 2 : NDR_PUSH_CHECK_FLAGS(ndr, ndr_flags);
260 :
261 2 : if (ndr_flags & NDR_SCALARS) {
262 2 : uint32_t i;
263 2 : NDR_CHECK(ndr_push_align(ndr, 4));
264 2 : NDR_CHECK(ndr_push_CFHEADER(ndr, NDR_SCALARS, &r->cfheader));
265 4 : for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (r->cfheader.cFolders); cntr_cffolders_0++) {
266 2 : NDR_CHECK(ndr_push_CFFOLDER(ndr, NDR_SCALARS, &r->cffolders[cntr_cffolders_0]));
267 : }
268 4 : for (cntr_cffiles_0 = 0; cntr_cffiles_0 < (r->cfheader.cFiles); cntr_cffiles_0++) {
269 2 : NDR_CHECK(ndr_push_CFFILE(ndr, NDR_SCALARS, &r->cffiles[cntr_cffiles_0]));
270 : }
271 : #if 0
272 : NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_count_cfdata(r)));
273 : #endif
274 :
275 : /* write in the folder header the offset of its first data block */
276 4 : for (i = 0; i < r->cfheader.cFolders; i++) {
277 2 : size_t off = OFFSET_OF_FOLDER_COFFCABSTART(i);
278 : /* check that the offset we want to
279 : * write to is always inside our
280 : * current push buffer
281 : */
282 2 : if (off >= ndr->offset) {
283 0 : return ndr_push_error(ndr, NDR_ERR_OFFSET,
284 : "trying to write past current push buffer size");
285 : }
286 2 : SIVAL(ndr->data, off, ndr->offset);
287 2 : NDR_CHECK(ndr_push_folder_cfdata(ndr, r->cfdata + processed_cfdata, r->cffolders[i].typeCompress, r->cffolders[i].cCFData));
288 2 : processed_cfdata += r->cffolders[i].cCFData;
289 : }
290 2 : NDR_CHECK(ndr_push_trailer_align(ndr, 4));
291 : }
292 2 : if (ndr_flags & NDR_BUFFERS) {
293 2 : }
294 2 : ndr->flags = _flags_save_STRUCT;
295 : }
296 :
297 :
298 : /* write total file size in header */
299 2 : SIVAL(ndr->data, 8, ndr->offset);
300 :
301 2 : return NDR_ERR_SUCCESS;
302 : }
303 :
304 :
305 : /* Pull all CFDATA of a folder.
306 : *
307 : * This works on a folder level because compression type is set per
308 : * folder, and a compression state can be shared between CFDATA of the
309 : * same folder.
310 : *
311 : * This is not a regular NDR func as we pass the compression type and
312 : * the number of CFDATA as extra arguments
313 : */
314 8 : static enum ndr_err_code ndr_pull_folder_cfdata(struct ndr_pull *ndr,
315 : struct CFDATA *r,
316 : enum cf_compress_type cab_ctype,
317 : size_t num_cfdata)
318 : {
319 8 : size_t i;
320 8 : enum ndr_compression_alg ndr_ctype = 0;
321 :
322 8 : if (cab_ctype == CF_COMPRESS_MSZIP) {
323 2 : ndr_ctype = NDR_COMPRESSION_MSZIP_CAB;
324 2 : NDR_CHECK(ndr_pull_compression_state_init(ndr, NDR_COMPRESSION_MSZIP_CAB, &ndr->cstate));
325 : }
326 :
327 16 : for (i = 0; i < num_cfdata; i++, r++) {
328 8 : NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->csum));
329 8 : NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbData));
330 8 : NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbUncomp));
331 :
332 8 : switch (cab_ctype) {
333 4 : case CF_COMPRESS_NONE:
334 : /* just copy the data */
335 4 : NDR_PULL_NEED_BYTES(ndr, r->cbUncomp);
336 4 : r->ab = data_blob_talloc(ndr->current_mem_ctx,
337 : ndr->data+ndr->offset,
338 : r->cbUncomp);
339 4 : if (r->ab.data == NULL) {
340 0 : return ndr_pull_error(ndr, NDR_ERR_ALLOC,
341 : "failed to allocate buffer for uncompressed CFDATA block");
342 : }
343 4 : ndr->offset += r->cbUncomp;
344 4 : break;
345 :
346 2 : case CF_COMPRESS_LZX:
347 : /* just copy the data (LZX decompression not implemented yet) */
348 2 : NDR_PULL_NEED_BYTES(ndr, r->cbData);
349 2 : r->ab = data_blob_talloc(ndr->current_mem_ctx,
350 : ndr->data+ndr->offset,
351 : r->cbData);
352 2 : if (r->ab.data == NULL) {
353 0 : return ndr_pull_error(ndr, NDR_ERR_ALLOC,
354 : "failed to allocate buffer for LZX-compressed CFDATA block");
355 : }
356 2 : ndr->offset += r->cbData;
357 2 : break;
358 :
359 2 : case CF_COMPRESS_MSZIP: {
360 2 : struct ndr_pull *pull_sub, *pull_compress;
361 2 : NDR_PULL_NEED_BYTES(ndr, r->cbData);
362 : /* decompress via subcontext */
363 2 : NDR_CHECK(ndr_pull_subcontext_start(ndr, &pull_sub, 0, r->cbData));
364 2 : pull_sub->cstate = ndr->cstate;
365 2 : NDR_CHECK(ndr_pull_compression_start(pull_sub, &pull_compress,
366 : ndr_ctype, r->cbUncomp, r->cbData));
367 2 : ndr_set_flags(&pull_compress->flags, LIBNDR_FLAG_REMAINING);
368 2 : NDR_CHECK(ndr_pull_DATA_BLOB(pull_compress, NDR_SCALARS, &r->ab));
369 2 : NDR_CHECK(ndr_pull_compression_end(pull_sub, pull_compress, ndr_ctype, r->cbUncomp));
370 2 : NDR_CHECK(ndr_pull_subcontext_end(ndr, pull_sub, 0, r->cbData));
371 :
372 2 : break;
373 : }
374 0 : default:
375 0 : return NDR_ERR_BAD_SWITCH;
376 : }
377 : }
378 :
379 8 : TALLOC_FREE(ndr->cstate);
380 :
381 0 : return NDR_ERR_SUCCESS;
382 : }
383 :
384 8 : _PUBLIC_ enum ndr_err_code ndr_pull_cab_file(struct ndr_pull *ndr, ndr_flags_type ndr_flags, struct cab_file *r)
385 : {
386 8 : uint32_t size_cffolders_0 = 0;
387 8 : uint32_t cntr_cffolders_0;
388 8 : TALLOC_CTX *_mem_save_cffolders_0 = NULL;
389 8 : uint32_t size_cffiles_0 = 0;
390 8 : uint32_t cntr_cffiles_0;
391 8 : TALLOC_CTX *_mem_save_cffiles_0 = NULL;
392 8 : uint32_t size_cfdata_0 = 0;
393 8 : size_t processed_cfdata = 0;
394 8 : TALLOC_CTX *_mem_save_cfdata_0 = NULL;
395 : {
396 8 : libndr_flags _flags_save_STRUCT = ndr->flags;
397 8 : ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
398 8 : NDR_PULL_CHECK_FLAGS(ndr, ndr_flags);
399 8 : if (ndr_flags & NDR_SCALARS) {
400 8 : NDR_CHECK(ndr_pull_align(ndr, 4));
401 8 : NDR_CHECK(ndr_pull_CFHEADER(ndr, NDR_SCALARS, &r->cfheader));
402 8 : size_cffolders_0 = r->cfheader.cFolders;
403 8 : NDR_PULL_ALLOC_N(ndr, r->cffolders, size_cffolders_0);
404 8 : _mem_save_cffolders_0 = NDR_PULL_GET_MEM_CTX(ndr);
405 8 : NDR_PULL_SET_MEM_CTX(ndr, r->cffolders, 0);
406 16 : for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (size_cffolders_0); cntr_cffolders_0++) {
407 8 : NDR_CHECK(ndr_pull_CFFOLDER(ndr, NDR_SCALARS, &r->cffolders[cntr_cffolders_0]));
408 : }
409 8 : NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cffolders_0, 0);
410 8 : size_cffiles_0 = r->cfheader.cFiles;
411 8 : NDR_PULL_ALLOC_N(ndr, r->cffiles, size_cffiles_0);
412 8 : _mem_save_cffiles_0 = NDR_PULL_GET_MEM_CTX(ndr);
413 8 : NDR_PULL_SET_MEM_CTX(ndr, r->cffiles, 0);
414 16 : for (cntr_cffiles_0 = 0; cntr_cffiles_0 < (size_cffiles_0); cntr_cffiles_0++) {
415 8 : NDR_CHECK(ndr_pull_CFFILE(ndr, NDR_SCALARS, &r->cffiles[cntr_cffiles_0]));
416 : }
417 8 : NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cffiles_0, 0);
418 : #if 0
419 : NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->cfdata_count));
420 : #else
421 8 : r->cfdata_count = ndr_count_cfdata(r);
422 : #endif
423 8 : size_cfdata_0 = r->cfdata_count;
424 8 : NDR_PULL_ALLOC_N(ndr, r->cfdata, size_cfdata_0);
425 8 : _mem_save_cfdata_0 = NDR_PULL_GET_MEM_CTX(ndr);
426 8 : NDR_PULL_SET_MEM_CTX(ndr, r->cfdata, 0);
427 16 : for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (size_cffolders_0); cntr_cffolders_0++) {
428 8 : NDR_CHECK(ndr_pull_folder_cfdata(ndr,
429 : r->cfdata + processed_cfdata,
430 : r->cffolders[cntr_cffolders_0].typeCompress,
431 : r->cffolders[cntr_cffolders_0].cCFData));
432 8 : processed_cfdata += r->cffolders[cntr_cffolders_0].cCFData;
433 : }
434 8 : NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cfdata_0, 0);
435 8 : NDR_CHECK(ndr_pull_trailer_align(ndr, 4));
436 : }
437 8 : if (ndr_flags & NDR_BUFFERS) {
438 8 : }
439 8 : ndr->flags = _flags_save_STRUCT;
440 : }
441 8 : return NDR_ERR_SUCCESS;
442 : }
|