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);
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
, &ndr
->cstate
));
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));
192 push_sub
->cstate
= ndr
->cstate
;
193 NDR_CHECK(ndr_push_compression_start(push_sub
, &push_compress
, ndr_ctype
, -1));
194 ndr_set_flags(&push_compress
->flags
, LIBNDR_FLAG_REMAINING
);
195 NDR_CHECK(ndr_push_DATA_BLOB(push_compress
, NDR_SCALARS
, r
->ab
));
196 NDR_CHECK(ndr_push_compression_end(push_sub
, push_compress
, ndr_ctype
, -1));
197 NDR_CHECK(ndr_push_subcontext_end(ndr
, push_sub
, 0, -1));
198 compressed_length
= push_sub
->offset
;
203 return NDR_ERR_BAD_SWITCH
;
206 /* we can now write the compressed size and the checksum */
207 SSVAL(ndr
->data
, compressed_offset
, compressed_length
);
210 * Create checksum over compressed data.
212 * The 8 bytes are the header size.
214 * We have already have written the checksum and set it to zero,
215 * earlier. So we know that after the checksum end the value
216 * for the compressed length comes the blob data.
218 * NDR already did all the checks for integer wraps.
220 csumPartial
= ndr_cab_compute_checksum(&ndr
->data
[data_offset
],
221 compressed_length
, 0);
224 * Checksum over header (compressed and uncompressed length).
226 * The first 4 bytes are the checksum size.
227 * The second 4 bytes are the size of the compressed and
228 * uncompressed length fields.
230 * NDR already did all the checks for integer wraps.
232 csum
= ndr_cab_compute_checksum(&ndr
->data
[compressed_offset
],
233 data_offset
- compressed_offset
,
236 SIVAL(ndr
->data
, csum_offset
, csum
);
239 ndr_push_compression_state_free(ndr
->cstate
);
242 return NDR_ERR_SUCCESS
;
245 _PUBLIC_
enum ndr_err_code
ndr_push_cab_file(struct ndr_push
*ndr
, int ndr_flags
, const struct cab_file
*r
)
247 uint32_t cntr_cffolders_0
;
248 uint32_t cntr_cffiles_0
;
249 size_t processed_cfdata
= 0;
251 uint32_t _flags_save_STRUCT
= ndr
->flags
;
252 ndr_set_flags(&ndr
->flags
, LIBNDR_PRINT_ARRAY_HEX
|LIBNDR_FLAG_LITTLE_ENDIAN
|LIBNDR_FLAG_NOALIGN
);
253 NDR_PUSH_CHECK_FLAGS(ndr
, ndr_flags
);
255 if (ndr_flags
& NDR_SCALARS
) {
257 NDR_CHECK(ndr_push_align(ndr
, 4));
258 NDR_CHECK(ndr_push_CFHEADER(ndr
, NDR_SCALARS
, &r
->cfheader
));
259 for (cntr_cffolders_0
= 0; cntr_cffolders_0
< (r
->cfheader
.cFolders
); cntr_cffolders_0
++) {
260 NDR_CHECK(ndr_push_CFFOLDER(ndr
, NDR_SCALARS
, &r
->cffolders
[cntr_cffolders_0
]));
262 for (cntr_cffiles_0
= 0; cntr_cffiles_0
< (r
->cfheader
.cFiles
); cntr_cffiles_0
++) {
263 NDR_CHECK(ndr_push_CFFILE(ndr
, NDR_SCALARS
, &r
->cffiles
[cntr_cffiles_0
]));
266 NDR_CHECK(ndr_push_uint32(ndr
, NDR_SCALARS
, ndr_count_cfdata(r
)));
269 /* write in the folder header the offset of its first data block */
270 for (i
= 0; i
< r
->cfheader
.cFolders
; i
++) {
271 size_t off
= OFFSET_OF_FOLDER_COFFCABSTART(i
);
272 /* check that the offset we want to
273 * write to is always inside our
274 * current push buffer
276 if (off
>= ndr
->offset
) {
277 return ndr_push_error(ndr
, NDR_ERR_OFFSET
,
278 "trying to write past current push buffer size");
280 SIVAL(ndr
->data
, off
, ndr
->offset
);
281 NDR_CHECK(ndr_push_folder_cfdata(ndr
, r
->cfdata
+ processed_cfdata
, r
->cffolders
[i
].typeCompress
, r
->cffolders
[i
].cCFData
));
282 processed_cfdata
+= r
->cffolders
[i
].cCFData
;
284 NDR_CHECK(ndr_push_trailer_align(ndr
, 4));
286 if (ndr_flags
& NDR_BUFFERS
) {
288 ndr
->flags
= _flags_save_STRUCT
;
292 /* write total file size in header */
293 SIVAL(ndr
->data
, 8, ndr
->offset
);
295 return NDR_ERR_SUCCESS
;
299 /* Pull all CFDATA of a folder.
301 * This works on a folder level because compression type is set per
302 * folder, and a compression state can be shared between CFDATA of the
305 * This is not a regular NDR func as we pass the compression type and
306 * the number of CFDATA as extra arguments
308 static enum ndr_err_code
ndr_pull_folder_cfdata(struct ndr_pull
*ndr
,
310 enum cf_compress_type cab_ctype
,
314 enum ndr_compression_alg ndr_ctype
= 0;
316 if (cab_ctype
== CF_COMPRESS_MSZIP
) {
317 ndr_ctype
= NDR_COMPRESSION_MSZIP_CAB
;
318 NDR_CHECK(ndr_pull_compression_state_init(ndr
, NDR_COMPRESSION_MSZIP_CAB
, &ndr
->cstate
));
321 for (i
= 0; i
< num_cfdata
; i
++, r
++) {
322 NDR_CHECK(ndr_pull_uint32(ndr
, NDR_SCALARS
, &r
->csum
));
323 NDR_CHECK(ndr_pull_uint16(ndr
, NDR_SCALARS
, &r
->cbData
));
324 NDR_CHECK(ndr_pull_uint16(ndr
, NDR_SCALARS
, &r
->cbUncomp
));
327 case CF_COMPRESS_NONE
:
328 /* just copy the data */
329 NDR_PULL_NEED_BYTES(ndr
, r
->cbUncomp
);
330 r
->ab
= data_blob_talloc(ndr
->current_mem_ctx
,
331 ndr
->data
+ndr
->offset
,
333 if (r
->ab
.data
== NULL
) {
334 return ndr_pull_error(ndr
, NDR_ERR_ALLOC
,
335 "failed to allocate buffer for uncompressed CFDATA block");
337 ndr
->offset
+= r
->cbUncomp
;
340 case CF_COMPRESS_LZX
:
341 /* just copy the data (LZX decompression not implemented yet) */
342 NDR_PULL_NEED_BYTES(ndr
, r
->cbData
);
343 r
->ab
= data_blob_talloc(ndr
->current_mem_ctx
,
344 ndr
->data
+ndr
->offset
,
346 if (r
->ab
.data
== NULL
) {
347 return ndr_pull_error(ndr
, NDR_ERR_ALLOC
,
348 "failed to allocate buffer for LZX-compressed CFDATA block");
350 ndr
->offset
+= r
->cbData
;
353 case CF_COMPRESS_MSZIP
: {
354 struct ndr_pull
*pull_sub
, *pull_compress
;
355 NDR_PULL_NEED_BYTES(ndr
, r
->cbData
);
356 /* decompress via subcontext */
357 NDR_CHECK(ndr_pull_subcontext_start(ndr
, &pull_sub
, 0, r
->cbData
));
358 pull_sub
->cstate
= ndr
->cstate
;
359 NDR_CHECK(ndr_pull_compression_start(pull_sub
, &pull_compress
,
360 ndr_ctype
, r
->cbUncomp
, r
->cbData
));
361 ndr_set_flags(&pull_compress
->flags
, LIBNDR_FLAG_REMAINING
);
362 NDR_CHECK(ndr_pull_DATA_BLOB(pull_compress
, NDR_SCALARS
, &r
->ab
));
363 NDR_CHECK(ndr_pull_compression_end(pull_sub
, pull_compress
, ndr_ctype
, r
->cbUncomp
));
364 NDR_CHECK(ndr_pull_subcontext_end(ndr
, pull_sub
, 0, r
->cbData
));
369 return NDR_ERR_BAD_SWITCH
;
373 ndr_pull_compression_state_free(ndr
->cstate
);
376 return NDR_ERR_SUCCESS
;
379 _PUBLIC_
enum ndr_err_code
ndr_pull_cab_file(struct ndr_pull
*ndr
, int ndr_flags
, struct cab_file
*r
)
381 uint32_t size_cffolders_0
= 0;
382 uint32_t cntr_cffolders_0
;
383 TALLOC_CTX
*_mem_save_cffolders_0
= NULL
;
384 uint32_t size_cffiles_0
= 0;
385 uint32_t cntr_cffiles_0
;
386 TALLOC_CTX
*_mem_save_cffiles_0
= NULL
;
387 uint32_t size_cfdata_0
= 0;
388 size_t processed_cfdata
= 0;
389 TALLOC_CTX
*_mem_save_cfdata_0
= NULL
;
391 uint32_t _flags_save_STRUCT
= ndr
->flags
;
392 ndr_set_flags(&ndr
->flags
, LIBNDR_PRINT_ARRAY_HEX
|LIBNDR_FLAG_LITTLE_ENDIAN
|LIBNDR_FLAG_NOALIGN
);
393 NDR_PULL_CHECK_FLAGS(ndr
, ndr_flags
);
394 if (ndr_flags
& NDR_SCALARS
) {
395 NDR_CHECK(ndr_pull_align(ndr
, 4));
396 NDR_CHECK(ndr_pull_CFHEADER(ndr
, NDR_SCALARS
, &r
->cfheader
));
397 size_cffolders_0
= r
->cfheader
.cFolders
;
398 NDR_PULL_ALLOC_N(ndr
, r
->cffolders
, size_cffolders_0
);
399 _mem_save_cffolders_0
= NDR_PULL_GET_MEM_CTX(ndr
);
400 NDR_PULL_SET_MEM_CTX(ndr
, r
->cffolders
, 0);
401 for (cntr_cffolders_0
= 0; cntr_cffolders_0
< (size_cffolders_0
); cntr_cffolders_0
++) {
402 NDR_CHECK(ndr_pull_CFFOLDER(ndr
, NDR_SCALARS
, &r
->cffolders
[cntr_cffolders_0
]));
404 NDR_PULL_SET_MEM_CTX(ndr
, _mem_save_cffolders_0
, 0);
405 size_cffiles_0
= r
->cfheader
.cFiles
;
406 NDR_PULL_ALLOC_N(ndr
, r
->cffiles
, size_cffiles_0
);
407 _mem_save_cffiles_0
= NDR_PULL_GET_MEM_CTX(ndr
);
408 NDR_PULL_SET_MEM_CTX(ndr
, r
->cffiles
, 0);
409 for (cntr_cffiles_0
= 0; cntr_cffiles_0
< (size_cffiles_0
); cntr_cffiles_0
++) {
410 NDR_CHECK(ndr_pull_CFFILE(ndr
, NDR_SCALARS
, &r
->cffiles
[cntr_cffiles_0
]));
412 NDR_PULL_SET_MEM_CTX(ndr
, _mem_save_cffiles_0
, 0);
414 NDR_CHECK(ndr_pull_uint32(ndr
, NDR_SCALARS
, &r
->cfdata_count
));
416 r
->cfdata_count
= ndr_count_cfdata(r
);
418 size_cfdata_0
= r
->cfdata_count
;
419 NDR_PULL_ALLOC_N(ndr
, r
->cfdata
, size_cfdata_0
);
420 _mem_save_cfdata_0
= NDR_PULL_GET_MEM_CTX(ndr
);
421 NDR_PULL_SET_MEM_CTX(ndr
, r
->cfdata
, 0);
422 for (cntr_cffolders_0
= 0; cntr_cffolders_0
< (size_cffolders_0
); cntr_cffolders_0
++) {
423 NDR_CHECK(ndr_pull_folder_cfdata(ndr
,
424 r
->cfdata
+ processed_cfdata
,
425 r
->cffolders
[cntr_cffolders_0
].typeCompress
,
426 r
->cffolders
[cntr_cffolders_0
].cCFData
));
427 processed_cfdata
+= r
->cffolders
[cntr_cffolders_0
].cCFData
;
429 NDR_PULL_SET_MEM_CTX(ndr
, _mem_save_cfdata_0
, 0);
430 NDR_CHECK(ndr_pull_trailer_align(ndr
, 4));
432 if (ndr_flags
& NDR_BUFFERS
) {
434 ndr
->flags
= _flags_save_STRUCT
;
436 return NDR_ERR_SUCCESS
;