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
, "%02d/%02d/%04d", 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);
99 ul
|= (((uint32_t)(*pb
++)) << 8);
101 ul
|= (uint32_t)(*pb
++);
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
),
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
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
,
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
;
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
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,
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
;
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
;
188 case CF_COMPRESS_LZX
:
190 * we have not yet worked out the details of LZX
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
;
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
,
244 SIVAL(ndr
->data
, csum_offset
, csum
);
247 ndr_push_compression_state_free(ndr
->cstate
);
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
) {
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
]));
274 NDR_CHECK(ndr_push_uint32(ndr
, NDR_SCALARS
, ndr_count_cfdata(r
)));
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
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
,
318 enum cf_compress_type cab_ctype
,
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
));
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
,
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
;
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
,
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
;
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
));
377 return NDR_ERR_BAD_SWITCH
;
381 ndr_pull_compression_state_free(ndr
->cstate
);
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);
422 NDR_CHECK(ndr_pull_uint32(ndr
, NDR_SCALARS
, &r
->cfdata_count
));
424 r
->cfdata_count
= ndr_count_cfdata(r
);
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
;