VERSION: Bump version up to 4.8.0rc1...
[Samba.git] / librpc / ndr / ndr_cab.c
blob837ed253065f9795320d7583aa06f51a71440435
1 /*
2 Unix SMB/CIFS implementation.
4 routines for marshalling/unmarshalling cab structures
6 Copyright (C) Guenther Deschner 2016
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.
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.
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/>.
22 #include "includes.h"
23 #include "librpc/gen_ndr/ndr_cab.h"
24 #include "librpc/ndr/ndr_compression.h"
26 #define OFFSET_OF_FOLDER_COFFCABSTART(folder) (36 /* cfheader size */ + (size_t)(folder)*8)
28 _PUBLIC_ void ndr_print_cf_time(struct ndr_print *ndr, const char *name, const struct cf_time *r)
30 uint8_t hour = 0, minute = 0, seconds = 0;
31 char *s;
32 if (r == NULL) { ndr_print_null(ndr); return; }
33 hour = r->time >> 11;
34 minute = (r->time >> 5) & 0x3f;
35 seconds = (r->time << 1) & 0x3e;
36 s = talloc_asprintf(ndr, "%02d:%02d:%02d", hour, minute, seconds);
37 if (s == NULL) { return; }
38 ndr_print_string(ndr, "time", s);
39 talloc_free(s);
42 _PUBLIC_ void ndr_print_cf_date(struct ndr_print *ndr, const char *name, const struct cf_date *r)
44 uint16_t year = 0;
45 uint8_t month = 0, day = 0;
46 char *s;
47 if (r == NULL) { ndr_print_null(ndr); return; }
48 year = (r->date >> 9);
49 year += 1980;
50 month = (r->date >> 5 & 0xf);
51 day = (r->date & 0x1f);
52 s = talloc_asprintf(ndr, "%02d/%02d/%04d", day, month, year);
53 if (s == NULL) { return; }
54 ndr_print_string(ndr, "date", s);
55 talloc_free(s);
58 uint32_t ndr_count_cfdata(const struct cab_file *r)
60 uint32_t count = 0, i;
62 for (i = 0; i < r->cfheader.cFolders; i++) {
63 if (count + r->cffolders[i].cCFData < count) {
64 /* Integer wrap. */
65 return 0;
67 count += r->cffolders[i].cCFData;
70 return count;
73 static uint32_t ndr_cab_compute_checksum(uint8_t *data, uint32_t length, uint32_t seed)
75 int num_ulong;
76 uint32_t checksum;
77 uint8_t *pb;
78 uint32_t ul;
80 num_ulong = length / 4;
81 checksum = seed;
82 pb = data;
84 while (num_ulong-- > 0) {
85 ul = (uint32_t)(*pb++);
86 ul |= (((uint32_t)(*pb++)) << 8);
87 ul |= (((uint32_t)(*pb++)) << 16);
88 ul |= (((uint32_t)(*pb++)) << 24);
90 checksum ^= ul;
93 ul = 0;
95 switch (length % 4) {
96 case 3:
97 ul |= (((uint32_t)(*pb++)) << 16);
98 case 2:
99 ul |= (((uint32_t)(*pb++)) << 8);
100 case 1:
101 ul |= (uint32_t)(*pb++);
102 default:
103 break;
106 checksum ^= ul;
108 return checksum;
111 uint32_t ndr_cab_generate_checksum(const struct CFDATA *r)
113 uint32_t csumPartial;
115 csumPartial = ndr_cab_compute_checksum(&r->ab.data[0], r->cbData, 0);
117 return ndr_cab_compute_checksum((uint8_t *)discard_const(&r->cbData),
118 sizeof(r->cbData) + sizeof(r->cbUncomp),
119 csumPartial);
122 /* Push all CFDATA of a folder.
124 * This works on a folder level because compression type is set per
125 * folder, and a compression state can be shared between CFDATA of the
126 * same folder.
128 * This is not a regular NDR func as we pass the compression type and
129 * the number of CFDATA as extra arguments
131 static enum ndr_err_code ndr_push_folder_cfdata(struct ndr_push *ndr,
132 const struct CFDATA *r,
133 enum cf_compress_type cab_ctype,
134 size_t num_cfdata)
136 size_t i;
137 enum ndr_compression_alg ndr_ctype = 0;
139 ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
141 if (cab_ctype == CF_COMPRESS_MSZIP) {
142 ndr_ctype = NDR_COMPRESSION_MSZIP_CAB;
143 NDR_CHECK(ndr_push_compression_state_init(ndr, ndr_ctype, &ndr->cstate));
146 for (i = 0; i < num_cfdata; i++, r++) {
147 uint32_t compressed_length = 0;
148 uint32_t csum, csumPartial;
149 size_t compressed_offset, csum_offset, data_offset;
151 if (!r->ab.data) {
152 return ndr_push_error(ndr, NDR_ERR_LENGTH,
153 "NULL uncompressed data blob");
155 if (r->ab.length != r->cbUncomp) {
156 return ndr_push_error(ndr, NDR_ERR_LENGTH,
157 "Uncompressed data blob size != uncompressed data size field");
161 * checksum is a function of the size fields
162 * and the potentially compressed data bytes,
163 * which haven't been compressed yet so
164 * remember offset, write zeroes, fill out
165 * later
167 csum_offset = ndr->offset;
168 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
171 * similarly, we don't know the compressed
172 * size yet, remember offset, write zeros,
173 * fill out later
175 compressed_offset = ndr->offset;
176 NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 0));
177 NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, r->cbUncomp));
179 data_offset = ndr->offset;
181 switch (cab_ctype) {
182 case CF_COMPRESS_NONE:
183 /* just copy the data */
184 NDR_PUSH_NEED_BYTES(ndr, r->ab.length);
185 NDR_CHECK(ndr_push_bytes(ndr, r->ab.data, r->ab.length));
186 compressed_length = r->ab.length;
187 break;
188 case CF_COMPRESS_LZX:
190 * we have not yet worked out the details of LZX
191 * compression
193 return NDR_ERR_COMPRESSION;
195 case CF_COMPRESS_MSZIP: {
196 struct ndr_push *push_sub, *push_compress;
198 /* compress via subcontext */
199 NDR_CHECK(ndr_push_subcontext_start(ndr, &push_sub, 0, -1));
200 push_sub->cstate = ndr->cstate;
201 NDR_CHECK(ndr_push_compression_start(push_sub, &push_compress, ndr_ctype, -1));
202 ndr_set_flags(&push_compress->flags, LIBNDR_FLAG_REMAINING);
203 NDR_CHECK(ndr_push_DATA_BLOB(push_compress, NDR_SCALARS, r->ab));
204 NDR_CHECK(ndr_push_compression_end(push_sub, push_compress, ndr_ctype, -1));
205 NDR_CHECK(ndr_push_subcontext_end(ndr, push_sub, 0, -1));
206 compressed_length = push_sub->offset;
208 break;
210 default:
211 return NDR_ERR_BAD_SWITCH;
214 /* we can now write the compressed size and the checksum */
215 SSVAL(ndr->data, compressed_offset, compressed_length);
218 * Create checksum over compressed data.
220 * The 8 bytes are the header size.
222 * We have already have written the checksum and set it to zero,
223 * earlier. So we know that after the checksum end the value
224 * for the compressed length comes the blob data.
226 * NDR already did all the checks for integer wraps.
228 csumPartial = ndr_cab_compute_checksum(&ndr->data[data_offset],
229 compressed_length, 0);
232 * Checksum over header (compressed and uncompressed length).
234 * The first 4 bytes are the checksum size.
235 * The second 4 bytes are the size of the compressed and
236 * uncompressed length fields.
238 * NDR already did all the checks for integer wraps.
240 csum = ndr_cab_compute_checksum(&ndr->data[compressed_offset],
241 data_offset - compressed_offset,
242 csumPartial);
244 SIVAL(ndr->data, csum_offset, csum);
247 ndr_push_compression_state_free(ndr->cstate);
248 ndr->cstate = NULL;
250 return NDR_ERR_SUCCESS;
253 _PUBLIC_ enum ndr_err_code ndr_push_cab_file(struct ndr_push *ndr, int ndr_flags, const struct cab_file *r)
255 uint32_t cntr_cffolders_0;
256 uint32_t cntr_cffiles_0;
257 size_t processed_cfdata = 0;
259 uint32_t _flags_save_STRUCT = ndr->flags;
260 ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
261 NDR_PUSH_CHECK_FLAGS(ndr, ndr_flags);
263 if (ndr_flags & NDR_SCALARS) {
264 uint32_t i;
265 NDR_CHECK(ndr_push_align(ndr, 4));
266 NDR_CHECK(ndr_push_CFHEADER(ndr, NDR_SCALARS, &r->cfheader));
267 for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (r->cfheader.cFolders); cntr_cffolders_0++) {
268 NDR_CHECK(ndr_push_CFFOLDER(ndr, NDR_SCALARS, &r->cffolders[cntr_cffolders_0]));
270 for (cntr_cffiles_0 = 0; cntr_cffiles_0 < (r->cfheader.cFiles); cntr_cffiles_0++) {
271 NDR_CHECK(ndr_push_CFFILE(ndr, NDR_SCALARS, &r->cffiles[cntr_cffiles_0]));
273 #if 0
274 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_count_cfdata(r)));
275 #endif
277 /* write in the folder header the offset of its first data block */
278 for (i = 0; i < r->cfheader.cFolders; i++) {
279 size_t off = OFFSET_OF_FOLDER_COFFCABSTART(i);
280 /* check that the offset we want to
281 * write to is always inside our
282 * current push buffer
284 if (off >= ndr->offset) {
285 return ndr_push_error(ndr, NDR_ERR_OFFSET,
286 "trying to write past current push buffer size");
288 SIVAL(ndr->data, off, ndr->offset);
289 NDR_CHECK(ndr_push_folder_cfdata(ndr, r->cfdata + processed_cfdata, r->cffolders[i].typeCompress, r->cffolders[i].cCFData));
290 processed_cfdata += r->cffolders[i].cCFData;
292 NDR_CHECK(ndr_push_trailer_align(ndr, 4));
294 if (ndr_flags & NDR_BUFFERS) {
296 ndr->flags = _flags_save_STRUCT;
300 /* write total file size in header */
301 SIVAL(ndr->data, 8, ndr->offset);
303 return NDR_ERR_SUCCESS;
307 /* Pull all CFDATA of a folder.
309 * This works on a folder level because compression type is set per
310 * folder, and a compression state can be shared between CFDATA of the
311 * same folder.
313 * This is not a regular NDR func as we pass the compression type and
314 * the number of CFDATA as extra arguments
316 static enum ndr_err_code ndr_pull_folder_cfdata(struct ndr_pull *ndr,
317 struct CFDATA *r,
318 enum cf_compress_type cab_ctype,
319 size_t num_cfdata)
321 size_t i;
322 enum ndr_compression_alg ndr_ctype = 0;
324 if (cab_ctype == CF_COMPRESS_MSZIP) {
325 ndr_ctype = NDR_COMPRESSION_MSZIP_CAB;
326 NDR_CHECK(ndr_pull_compression_state_init(ndr, NDR_COMPRESSION_MSZIP_CAB, &ndr->cstate));
329 for (i = 0; i < num_cfdata; i++, r++) {
330 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->csum));
331 NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbData));
332 NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbUncomp));
334 switch (cab_ctype) {
335 case CF_COMPRESS_NONE:
336 /* just copy the data */
337 NDR_PULL_NEED_BYTES(ndr, r->cbUncomp);
338 r->ab = data_blob_talloc(ndr->current_mem_ctx,
339 ndr->data+ndr->offset,
340 r->cbUncomp);
341 if (r->ab.data == NULL) {
342 return ndr_pull_error(ndr, NDR_ERR_ALLOC,
343 "failed to allocate buffer for uncompressed CFDATA block");
345 ndr->offset += r->cbUncomp;
346 break;
348 case CF_COMPRESS_LZX:
349 /* just copy the data (LZX decompression not implemented yet) */
350 NDR_PULL_NEED_BYTES(ndr, r->cbData);
351 r->ab = data_blob_talloc(ndr->current_mem_ctx,
352 ndr->data+ndr->offset,
353 r->cbData);
354 if (r->ab.data == NULL) {
355 return ndr_pull_error(ndr, NDR_ERR_ALLOC,
356 "failed to allocate buffer for LZX-compressed CFDATA block");
358 ndr->offset += r->cbData;
359 break;
361 case CF_COMPRESS_MSZIP: {
362 struct ndr_pull *pull_sub, *pull_compress;
363 NDR_PULL_NEED_BYTES(ndr, r->cbData);
364 /* decompress via subcontext */
365 NDR_CHECK(ndr_pull_subcontext_start(ndr, &pull_sub, 0, r->cbData));
366 pull_sub->cstate = ndr->cstate;
367 NDR_CHECK(ndr_pull_compression_start(pull_sub, &pull_compress,
368 ndr_ctype, r->cbUncomp, r->cbData));
369 ndr_set_flags(&pull_compress->flags, LIBNDR_FLAG_REMAINING);
370 NDR_CHECK(ndr_pull_DATA_BLOB(pull_compress, NDR_SCALARS, &r->ab));
371 NDR_CHECK(ndr_pull_compression_end(pull_sub, pull_compress, ndr_ctype, r->cbUncomp));
372 NDR_CHECK(ndr_pull_subcontext_end(ndr, pull_sub, 0, r->cbData));
374 break;
376 default:
377 return NDR_ERR_BAD_SWITCH;
381 ndr_pull_compression_state_free(ndr->cstate);
382 ndr->cstate = NULL;
384 return NDR_ERR_SUCCESS;
387 _PUBLIC_ enum ndr_err_code ndr_pull_cab_file(struct ndr_pull *ndr, int ndr_flags, struct cab_file *r)
389 uint32_t size_cffolders_0 = 0;
390 uint32_t cntr_cffolders_0;
391 TALLOC_CTX *_mem_save_cffolders_0 = NULL;
392 uint32_t size_cffiles_0 = 0;
393 uint32_t cntr_cffiles_0;
394 TALLOC_CTX *_mem_save_cffiles_0 = NULL;
395 uint32_t size_cfdata_0 = 0;
396 size_t processed_cfdata = 0;
397 TALLOC_CTX *_mem_save_cfdata_0 = NULL;
399 uint32_t _flags_save_STRUCT = ndr->flags;
400 ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
401 NDR_PULL_CHECK_FLAGS(ndr, ndr_flags);
402 if (ndr_flags & NDR_SCALARS) {
403 NDR_CHECK(ndr_pull_align(ndr, 4));
404 NDR_CHECK(ndr_pull_CFHEADER(ndr, NDR_SCALARS, &r->cfheader));
405 size_cffolders_0 = r->cfheader.cFolders;
406 NDR_PULL_ALLOC_N(ndr, r->cffolders, size_cffolders_0);
407 _mem_save_cffolders_0 = NDR_PULL_GET_MEM_CTX(ndr);
408 NDR_PULL_SET_MEM_CTX(ndr, r->cffolders, 0);
409 for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (size_cffolders_0); cntr_cffolders_0++) {
410 NDR_CHECK(ndr_pull_CFFOLDER(ndr, NDR_SCALARS, &r->cffolders[cntr_cffolders_0]));
412 NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cffolders_0, 0);
413 size_cffiles_0 = r->cfheader.cFiles;
414 NDR_PULL_ALLOC_N(ndr, r->cffiles, size_cffiles_0);
415 _mem_save_cffiles_0 = NDR_PULL_GET_MEM_CTX(ndr);
416 NDR_PULL_SET_MEM_CTX(ndr, r->cffiles, 0);
417 for (cntr_cffiles_0 = 0; cntr_cffiles_0 < (size_cffiles_0); cntr_cffiles_0++) {
418 NDR_CHECK(ndr_pull_CFFILE(ndr, NDR_SCALARS, &r->cffiles[cntr_cffiles_0]));
420 NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cffiles_0, 0);
421 #if 0
422 NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->cfdata_count));
423 #else
424 r->cfdata_count = ndr_count_cfdata(r);
425 #endif
426 size_cfdata_0 = r->cfdata_count;
427 NDR_PULL_ALLOC_N(ndr, r->cfdata, size_cfdata_0);
428 _mem_save_cfdata_0 = NDR_PULL_GET_MEM_CTX(ndr);
429 NDR_PULL_SET_MEM_CTX(ndr, r->cfdata, 0);
430 for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (size_cffolders_0); cntr_cffolders_0++) {
431 NDR_CHECK(ndr_pull_folder_cfdata(ndr,
432 r->cfdata + processed_cfdata,
433 r->cffolders[cntr_cffolders_0].typeCompress,
434 r->cffolders[cntr_cffolders_0].cCFData));
435 processed_cfdata += r->cffolders[cntr_cffolders_0].cCFData;
437 NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cfdata_0, 0);
438 NDR_CHECK(ndr_pull_trailer_align(ndr, 4));
440 if (ndr_flags & NDR_BUFFERS) {
442 ndr->flags = _flags_save_STRUCT;
444 return NDR_ERR_SUCCESS;