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/>.
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;
32 if (r
== NULL
) { ndr_print_null(ndr
); return; }
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
);
42 _PUBLIC_
void ndr_print_cf_date(struct ndr_print
*ndr
, const char *name
, const struct cf_date
*r
)
45 uint8_t month
= 0, day
= 0;
47 if (r
== NULL
) { ndr_print_null(ndr
); return; }
48 year
= (r
->date
>> 9);
50 month
= (r
->date
>> 5 & 0xf);
51 day
= (r
->date
& 0x1f);
52 s
= talloc_asprintf(ndr
, "%02"PRIu8
"/%02"PRIu8
"/%04"PRIu16
, day
, month
, year
);
53 if (s
== NULL
) { return; }
54 ndr_print_string(ndr
, "date", 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
) {
67 count
+= r
->cffolders
[i
].cCFData
;
73 static uint32_t ndr_cab_compute_checksum(uint8_t *data
, uint32_t length
, uint32_t seed
)
80 num_ulong
= length
/ 4;
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);
97 ul
|= (((uint32_t)(*pb
++)) << 16);
100 ul
|= (((uint32_t)(*pb
++)) << 8);
103 ul
|= (uint32_t)(*pb
++);
114 /* Push all CFDATA of a folder.
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
120 * This is not a regular NDR func as we pass the compression type and
121 * the number of CFDATA as extra arguments
123 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
,
129 enum ndr_compression_alg ndr_ctype
= 0;
131 ndr_set_flags(&ndr
->flags
, LIBNDR_PRINT_ARRAY_HEX
|LIBNDR_FLAG_LITTLE_ENDIAN
|LIBNDR_FLAG_NOALIGN
);
133 if (cab_ctype
== CF_COMPRESS_MSZIP
) {
134 ndr_ctype
= NDR_COMPRESSION_MSZIP_CAB
;
135 NDR_CHECK(ndr_push_compression_state_init(ndr
, ndr_ctype
));
138 for (i
= 0; i
< num_cfdata
; i
++, r
++) {
139 uint32_t compressed_length
= 0;
140 uint32_t csum
, csumPartial
;
141 size_t compressed_offset
, csum_offset
, data_offset
;
144 return ndr_push_error(ndr
, NDR_ERR_LENGTH
,
145 "NULL uncompressed data blob");
147 if (r
->ab
.length
!= r
->cbUncomp
) {
148 return ndr_push_error(ndr
, NDR_ERR_LENGTH
,
149 "Uncompressed data blob size != uncompressed data size field");
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
159 csum_offset
= ndr
->offset
;
160 NDR_CHECK(ndr_push_uint32(ndr
, NDR_SCALARS
, 0));
163 * similarly, we don't know the compressed
164 * size yet, remember offset, write zeros,
167 compressed_offset
= ndr
->offset
;
168 NDR_CHECK(ndr_push_uint16(ndr
, NDR_SCALARS
, 0));
169 NDR_CHECK(ndr_push_uint16(ndr
, NDR_SCALARS
, r
->cbUncomp
));
171 data_offset
= ndr
->offset
;
174 case CF_COMPRESS_NONE
:
175 /* just copy the data */
176 NDR_PUSH_NEED_BYTES(ndr
, r
->ab
.length
);
177 NDR_CHECK(ndr_push_bytes(ndr
, r
->ab
.data
, r
->ab
.length
));
178 compressed_length
= r
->ab
.length
;
180 case CF_COMPRESS_LZX
:
182 * we have not yet worked out the details of LZX
185 return NDR_ERR_COMPRESSION
;
187 case CF_COMPRESS_MSZIP
: {
188 struct ndr_push
*push_sub
, *push_compress
;
190 /* compress via subcontext */
191 NDR_CHECK(ndr_push_subcontext_start(ndr
, &push_sub
, 0, -1));
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.
198 push_sub
->cstate
= ndr
->cstate
;
200 NDR_CHECK(ndr_push_compression_start(push_sub
, &push_compress
));
201 ndr_set_flags(&push_compress
->flags
, LIBNDR_FLAG_REMAINING
);
202 NDR_CHECK(ndr_push_DATA_BLOB(push_compress
, NDR_SCALARS
, r
->ab
));
203 NDR_CHECK(ndr_push_compression_end(push_sub
, push_compress
));
204 NDR_CHECK(ndr_push_subcontext_end(ndr
, push_sub
, 0, -1));
205 compressed_length
= push_sub
->offset
;
210 return NDR_ERR_BAD_SWITCH
;
213 /* we can now write the compressed size and the checksum */
214 SSVAL(ndr
->data
, compressed_offset
, compressed_length
);
217 * Create checksum over compressed data.
219 * The 8 bytes are the header size.
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.
225 * NDR already did all the checks for integer wraps.
227 csumPartial
= ndr_cab_compute_checksum(&ndr
->data
[data_offset
],
228 compressed_length
, 0);
231 * Checksum over header (compressed and uncompressed length).
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.
237 * NDR already did all the checks for integer wraps.
239 csum
= ndr_cab_compute_checksum(&ndr
->data
[compressed_offset
],
240 data_offset
- compressed_offset
,
243 SIVAL(ndr
->data
, csum_offset
, csum
);
246 TALLOC_FREE(ndr
->cstate
);
248 return NDR_ERR_SUCCESS
;
251 _PUBLIC_
enum ndr_err_code
ndr_push_cab_file(struct ndr_push
*ndr
, ndr_flags_type ndr_flags
, const struct cab_file
*r
)
253 uint32_t cntr_cffolders_0
;
254 uint32_t cntr_cffiles_0
;
255 size_t processed_cfdata
= 0;
257 libndr_flags _flags_save_STRUCT
= ndr
->flags
;
258 ndr_set_flags(&ndr
->flags
, LIBNDR_PRINT_ARRAY_HEX
|LIBNDR_FLAG_LITTLE_ENDIAN
|LIBNDR_FLAG_NOALIGN
);
259 NDR_PUSH_CHECK_FLAGS(ndr
, ndr_flags
);
261 if (ndr_flags
& NDR_SCALARS
) {
263 NDR_CHECK(ndr_push_align(ndr
, 4));
264 NDR_CHECK(ndr_push_CFHEADER(ndr
, NDR_SCALARS
, &r
->cfheader
));
265 for (cntr_cffolders_0
= 0; cntr_cffolders_0
< (r
->cfheader
.cFolders
); cntr_cffolders_0
++) {
266 NDR_CHECK(ndr_push_CFFOLDER(ndr
, NDR_SCALARS
, &r
->cffolders
[cntr_cffolders_0
]));
268 for (cntr_cffiles_0
= 0; cntr_cffiles_0
< (r
->cfheader
.cFiles
); cntr_cffiles_0
++) {
269 NDR_CHECK(ndr_push_CFFILE(ndr
, NDR_SCALARS
, &r
->cffiles
[cntr_cffiles_0
]));
272 NDR_CHECK(ndr_push_uint32(ndr
, NDR_SCALARS
, ndr_count_cfdata(r
)));
275 /* write in the folder header the offset of its first data block */
276 for (i
= 0; i
< r
->cfheader
.cFolders
; i
++) {
277 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
282 if (off
>= ndr
->offset
) {
283 return ndr_push_error(ndr
, NDR_ERR_OFFSET
,
284 "trying to write past current push buffer size");
286 SIVAL(ndr
->data
, off
, ndr
->offset
);
287 NDR_CHECK(ndr_push_folder_cfdata(ndr
, r
->cfdata
+ processed_cfdata
, r
->cffolders
[i
].typeCompress
, r
->cffolders
[i
].cCFData
));
288 processed_cfdata
+= r
->cffolders
[i
].cCFData
;
290 NDR_CHECK(ndr_push_trailer_align(ndr
, 4));
292 if (ndr_flags
& NDR_BUFFERS
) {
294 ndr
->flags
= _flags_save_STRUCT
;
298 /* write total file size in header */
299 SIVAL(ndr
->data
, 8, ndr
->offset
);
301 return NDR_ERR_SUCCESS
;
305 /* Pull all CFDATA of a folder.
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
311 * This is not a regular NDR func as we pass the compression type and
312 * the number of CFDATA as extra arguments
314 static enum ndr_err_code
ndr_pull_folder_cfdata(struct ndr_pull
*ndr
,
316 enum cf_compress_type cab_ctype
,
320 enum ndr_compression_alg ndr_ctype
= 0;
322 if (cab_ctype
== CF_COMPRESS_MSZIP
) {
323 ndr_ctype
= NDR_COMPRESSION_MSZIP_CAB
;
324 NDR_CHECK(ndr_pull_compression_state_init(ndr
, NDR_COMPRESSION_MSZIP_CAB
, &ndr
->cstate
));
327 for (i
= 0; i
< num_cfdata
; i
++, r
++) {
328 NDR_CHECK(ndr_pull_uint32(ndr
, NDR_SCALARS
, &r
->csum
));
329 NDR_CHECK(ndr_pull_uint16(ndr
, NDR_SCALARS
, &r
->cbData
));
330 NDR_CHECK(ndr_pull_uint16(ndr
, NDR_SCALARS
, &r
->cbUncomp
));
333 case CF_COMPRESS_NONE
:
334 /* just copy the data */
335 NDR_PULL_NEED_BYTES(ndr
, r
->cbUncomp
);
336 r
->ab
= data_blob_talloc(ndr
->current_mem_ctx
,
337 ndr
->data
+ndr
->offset
,
339 if (r
->ab
.data
== NULL
) {
340 return ndr_pull_error(ndr
, NDR_ERR_ALLOC
,
341 "failed to allocate buffer for uncompressed CFDATA block");
343 ndr
->offset
+= r
->cbUncomp
;
346 case CF_COMPRESS_LZX
:
347 /* just copy the data (LZX decompression not implemented yet) */
348 NDR_PULL_NEED_BYTES(ndr
, r
->cbData
);
349 r
->ab
= data_blob_talloc(ndr
->current_mem_ctx
,
350 ndr
->data
+ndr
->offset
,
352 if (r
->ab
.data
== NULL
) {
353 return ndr_pull_error(ndr
, NDR_ERR_ALLOC
,
354 "failed to allocate buffer for LZX-compressed CFDATA block");
356 ndr
->offset
+= r
->cbData
;
359 case CF_COMPRESS_MSZIP
: {
360 struct ndr_pull
*pull_sub
, *pull_compress
;
361 NDR_PULL_NEED_BYTES(ndr
, r
->cbData
);
362 /* decompress via subcontext */
363 NDR_CHECK(ndr_pull_subcontext_start(ndr
, &pull_sub
, 0, r
->cbData
));
364 pull_sub
->cstate
= ndr
->cstate
;
365 NDR_CHECK(ndr_pull_compression_start(pull_sub
, &pull_compress
,
366 ndr_ctype
, r
->cbUncomp
, r
->cbData
));
367 ndr_set_flags(&pull_compress
->flags
, LIBNDR_FLAG_REMAINING
);
368 NDR_CHECK(ndr_pull_DATA_BLOB(pull_compress
, NDR_SCALARS
, &r
->ab
));
369 NDR_CHECK(ndr_pull_compression_end(pull_sub
, pull_compress
, ndr_ctype
, r
->cbUncomp
));
370 NDR_CHECK(ndr_pull_subcontext_end(ndr
, pull_sub
, 0, r
->cbData
));
375 return NDR_ERR_BAD_SWITCH
;
379 TALLOC_FREE(ndr
->cstate
);
381 return NDR_ERR_SUCCESS
;
384 _PUBLIC_
enum ndr_err_code
ndr_pull_cab_file(struct ndr_pull
*ndr
, ndr_flags_type ndr_flags
, struct cab_file
*r
)
386 uint32_t size_cffolders_0
= 0;
387 uint32_t cntr_cffolders_0
;
388 TALLOC_CTX
*_mem_save_cffolders_0
= NULL
;
389 uint32_t size_cffiles_0
= 0;
390 uint32_t cntr_cffiles_0
;
391 TALLOC_CTX
*_mem_save_cffiles_0
= NULL
;
392 uint32_t size_cfdata_0
= 0;
393 size_t processed_cfdata
= 0;
394 TALLOC_CTX
*_mem_save_cfdata_0
= NULL
;
396 libndr_flags _flags_save_STRUCT
= ndr
->flags
;
397 ndr_set_flags(&ndr
->flags
, LIBNDR_PRINT_ARRAY_HEX
|LIBNDR_FLAG_LITTLE_ENDIAN
|LIBNDR_FLAG_NOALIGN
);
398 NDR_PULL_CHECK_FLAGS(ndr
, ndr_flags
);
399 if (ndr_flags
& NDR_SCALARS
) {
400 NDR_CHECK(ndr_pull_align(ndr
, 4));
401 NDR_CHECK(ndr_pull_CFHEADER(ndr
, NDR_SCALARS
, &r
->cfheader
));
402 size_cffolders_0
= r
->cfheader
.cFolders
;
403 NDR_PULL_ALLOC_N(ndr
, r
->cffolders
, size_cffolders_0
);
404 _mem_save_cffolders_0
= NDR_PULL_GET_MEM_CTX(ndr
);
405 NDR_PULL_SET_MEM_CTX(ndr
, r
->cffolders
, 0);
406 for (cntr_cffolders_0
= 0; cntr_cffolders_0
< (size_cffolders_0
); cntr_cffolders_0
++) {
407 NDR_CHECK(ndr_pull_CFFOLDER(ndr
, NDR_SCALARS
, &r
->cffolders
[cntr_cffolders_0
]));
409 NDR_PULL_SET_MEM_CTX(ndr
, _mem_save_cffolders_0
, 0);
410 size_cffiles_0
= r
->cfheader
.cFiles
;
411 NDR_PULL_ALLOC_N(ndr
, r
->cffiles
, size_cffiles_0
);
412 _mem_save_cffiles_0
= NDR_PULL_GET_MEM_CTX(ndr
);
413 NDR_PULL_SET_MEM_CTX(ndr
, r
->cffiles
, 0);
414 for (cntr_cffiles_0
= 0; cntr_cffiles_0
< (size_cffiles_0
); cntr_cffiles_0
++) {
415 NDR_CHECK(ndr_pull_CFFILE(ndr
, NDR_SCALARS
, &r
->cffiles
[cntr_cffiles_0
]));
417 NDR_PULL_SET_MEM_CTX(ndr
, _mem_save_cffiles_0
, 0);
419 NDR_CHECK(ndr_pull_uint32(ndr
, NDR_SCALARS
, &r
->cfdata_count
));
421 r
->cfdata_count
= ndr_count_cfdata(r
);
423 size_cfdata_0
= r
->cfdata_count
;
424 NDR_PULL_ALLOC_N(ndr
, r
->cfdata
, size_cfdata_0
);
425 _mem_save_cfdata_0
= NDR_PULL_GET_MEM_CTX(ndr
);
426 NDR_PULL_SET_MEM_CTX(ndr
, r
->cfdata
, 0);
427 for (cntr_cffolders_0
= 0; cntr_cffolders_0
< (size_cffolders_0
); cntr_cffolders_0
++) {
428 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 processed_cfdata
+= r
->cffolders
[cntr_cffolders_0
].cCFData
;
434 NDR_PULL_SET_MEM_CTX(ndr
, _mem_save_cfdata_0
, 0);
435 NDR_CHECK(ndr_pull_trailer_align(ndr
, 4));
437 if (ndr_flags
& NDR_BUFFERS
) {
439 ndr
->flags
= _flags_save_STRUCT
;
441 return NDR_ERR_SUCCESS
;