From c60ad394fa0da0d8dfbd9f3336b422f26bed8378 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Tue, 23 May 2017 12:09:28 +0200 Subject: librpc/ndr: add MSZIP compression for cabinet files Signed-off-by: Aurelien Aptel Reviewed-by: Guenther Deschner Reviewed-by: Jeremy Allison --- librpc/ndr/ndr_cab.c | 252 ++++++++++++++++++++++++++++++++++++++-- librpc/ndr/ndr_compression.c | 266 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 506 insertions(+), 12 deletions(-) (limited to 'librpc') diff --git a/librpc/ndr/ndr_cab.c b/librpc/ndr/ndr_cab.c index efd337d5daa..837ed253065 100644 --- a/librpc/ndr/ndr_cab.c +++ b/librpc/ndr/ndr_cab.c @@ -21,6 +21,9 @@ #include "includes.h" #include "librpc/gen_ndr/ndr_cab.h" +#include "librpc/ndr/ndr_compression.h" + +#define OFFSET_OF_FOLDER_COFFCABSTART(folder) (36 /* cfheader size */ + (size_t)(folder)*8) _PUBLIC_ void ndr_print_cf_time(struct ndr_print *ndr, const char *name, const struct cf_time *r) { @@ -116,35 +119,175 @@ uint32_t ndr_cab_generate_checksum(const struct CFDATA *r) csumPartial); } +/* Push all CFDATA of a folder. + * + * This works on a folder level because compression type is set per + * folder, and a compression state can be shared between CFDATA of the + * same folder. + * + * This is not a regular NDR func as we pass the compression type and + * the number of CFDATA as extra arguments + */ +static enum ndr_err_code ndr_push_folder_cfdata(struct ndr_push *ndr, + const struct CFDATA *r, + enum cf_compress_type cab_ctype, + size_t num_cfdata) +{ + size_t i; + enum ndr_compression_alg ndr_ctype = 0; + + ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN); + + if (cab_ctype == CF_COMPRESS_MSZIP) { + ndr_ctype = NDR_COMPRESSION_MSZIP_CAB; + NDR_CHECK(ndr_push_compression_state_init(ndr, ndr_ctype, &ndr->cstate)); + } + + for (i = 0; i < num_cfdata; i++, r++) { + uint32_t compressed_length = 0; + uint32_t csum, csumPartial; + size_t compressed_offset, csum_offset, data_offset; + + if (!r->ab.data) { + return ndr_push_error(ndr, NDR_ERR_LENGTH, + "NULL uncompressed data blob"); + } + if (r->ab.length != r->cbUncomp) { + return ndr_push_error(ndr, NDR_ERR_LENGTH, + "Uncompressed data blob size != uncompressed data size field"); + } + + /* + * checksum is a function of the size fields + * and the potentially compressed data bytes, + * which haven't been compressed yet so + * remember offset, write zeroes, fill out + * later + */ + csum_offset = ndr->offset; + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0)); + + /* + * similarly, we don't know the compressed + * size yet, remember offset, write zeros, + * fill out later + */ + compressed_offset = ndr->offset; + NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 0)); + NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, r->cbUncomp)); + + data_offset = ndr->offset; + + switch (cab_ctype) { + case CF_COMPRESS_NONE: + /* just copy the data */ + NDR_PUSH_NEED_BYTES(ndr, r->ab.length); + NDR_CHECK(ndr_push_bytes(ndr, r->ab.data, r->ab.length)); + compressed_length = r->ab.length; + break; + case CF_COMPRESS_LZX: + /* + * we have not yet worked out the details of LZX + * compression + */ + return NDR_ERR_COMPRESSION; + + case CF_COMPRESS_MSZIP: { + struct ndr_push *push_sub, *push_compress; + + /* compress via subcontext */ + NDR_CHECK(ndr_push_subcontext_start(ndr, &push_sub, 0, -1)); + push_sub->cstate = ndr->cstate; + NDR_CHECK(ndr_push_compression_start(push_sub, &push_compress, ndr_ctype, -1)); + ndr_set_flags(&push_compress->flags, LIBNDR_FLAG_REMAINING); + NDR_CHECK(ndr_push_DATA_BLOB(push_compress, NDR_SCALARS, r->ab)); + NDR_CHECK(ndr_push_compression_end(push_sub, push_compress, ndr_ctype, -1)); + NDR_CHECK(ndr_push_subcontext_end(ndr, push_sub, 0, -1)); + compressed_length = push_sub->offset; + + break; + } + default: + return NDR_ERR_BAD_SWITCH; + } + + /* we can now write the compressed size and the checksum */ + SSVAL(ndr->data, compressed_offset, compressed_length); + + /* + * Create checksum over compressed data. + * + * The 8 bytes are the header size. + * + * We have already have written the checksum and set it to zero, + * earlier. So we know that after the checksum end the value + * for the compressed length comes the blob data. + * + * NDR already did all the checks for integer wraps. + */ + csumPartial = ndr_cab_compute_checksum(&ndr->data[data_offset], + compressed_length, 0); + + /* + * Checksum over header (compressed and uncompressed length). + * + * The first 4 bytes are the checksum size. + * The second 4 bytes are the size of the compressed and + * uncompressed length fields. + * + * NDR already did all the checks for integer wraps. + */ + csum = ndr_cab_compute_checksum(&ndr->data[compressed_offset], + data_offset - compressed_offset, + csumPartial); + + SIVAL(ndr->data, csum_offset, csum); + } + + ndr_push_compression_state_free(ndr->cstate); + ndr->cstate = NULL; + + return NDR_ERR_SUCCESS; +} + _PUBLIC_ enum ndr_err_code ndr_push_cab_file(struct ndr_push *ndr, int ndr_flags, const struct cab_file *r) { uint32_t cntr_cffolders_0; uint32_t cntr_cffiles_0; - uint32_t cntr_cfdata_0; + size_t processed_cfdata = 0; { uint32_t _flags_save_STRUCT = ndr->flags; ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN); NDR_PUSH_CHECK_FLAGS(ndr, ndr_flags); + if (ndr_flags & NDR_SCALARS) { - uint32_t next_offset = 0; + uint32_t i; NDR_CHECK(ndr_push_align(ndr, 4)); NDR_CHECK(ndr_push_CFHEADER(ndr, NDR_SCALARS, &r->cfheader)); for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (r->cfheader.cFolders); cntr_cffolders_0++) { NDR_CHECK(ndr_push_CFFOLDER(ndr, NDR_SCALARS, &r->cffolders[cntr_cffolders_0])); } for (cntr_cffiles_0 = 0; cntr_cffiles_0 < (r->cfheader.cFiles); cntr_cffiles_0++) { - uint32_t offset = ndr->offset + 4; NDR_CHECK(ndr_push_CFFILE(ndr, NDR_SCALARS, &r->cffiles[cntr_cffiles_0])); - if (cntr_cffiles_0 > 0) { - next_offset += r->cffiles[cntr_cffiles_0 - 1].cbFile; - } - SIVAL(ndr->data, offset, next_offset); } #if 0 NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_count_cfdata(r))); #endif - for (cntr_cfdata_0 = 0; cntr_cfdata_0 < (ndr_count_cfdata(r)); cntr_cfdata_0++) { - NDR_CHECK(ndr_push_CFDATA(ndr, NDR_SCALARS, &r->cfdata[cntr_cfdata_0])); + + /* write in the folder header the offset of its first data block */ + for (i = 0; i < r->cfheader.cFolders; i++) { + size_t off = OFFSET_OF_FOLDER_COFFCABSTART(i); + /* check that the offset we want to + * write to is always inside our + * current push buffer + */ + if (off >= ndr->offset) { + return ndr_push_error(ndr, NDR_ERR_OFFSET, + "trying to write past current push buffer size"); + } + SIVAL(ndr->data, off, ndr->offset); + NDR_CHECK(ndr_push_folder_cfdata(ndr, r->cfdata + processed_cfdata, r->cffolders[i].typeCompress, r->cffolders[i].cCFData)); + processed_cfdata += r->cffolders[i].cCFData; } NDR_CHECK(ndr_push_trailer_align(ndr, 4)); } @@ -160,6 +303,87 @@ _PUBLIC_ enum ndr_err_code ndr_push_cab_file(struct ndr_push *ndr, int ndr_flags return NDR_ERR_SUCCESS; } + +/* Pull all CFDATA of a folder. + * + * This works on a folder level because compression type is set per + * folder, and a compression state can be shared between CFDATA of the + * same folder. + * + * This is not a regular NDR func as we pass the compression type and + * the number of CFDATA as extra arguments + */ +static enum ndr_err_code ndr_pull_folder_cfdata(struct ndr_pull *ndr, + struct CFDATA *r, + enum cf_compress_type cab_ctype, + size_t num_cfdata) +{ + size_t i; + enum ndr_compression_alg ndr_ctype = 0; + + if (cab_ctype == CF_COMPRESS_MSZIP) { + ndr_ctype = NDR_COMPRESSION_MSZIP_CAB; + NDR_CHECK(ndr_pull_compression_state_init(ndr, NDR_COMPRESSION_MSZIP_CAB, &ndr->cstate)); + } + + for (i = 0; i < num_cfdata; i++, r++) { + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->csum)); + NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbData)); + NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbUncomp)); + + switch (cab_ctype) { + case CF_COMPRESS_NONE: + /* just copy the data */ + NDR_PULL_NEED_BYTES(ndr, r->cbUncomp); + r->ab = data_blob_talloc(ndr->current_mem_ctx, + ndr->data+ndr->offset, + r->cbUncomp); + if (r->ab.data == NULL) { + return ndr_pull_error(ndr, NDR_ERR_ALLOC, + "failed to allocate buffer for uncompressed CFDATA block"); + } + ndr->offset += r->cbUncomp; + break; + + case CF_COMPRESS_LZX: + /* just copy the data (LZX decompression not implemented yet) */ + NDR_PULL_NEED_BYTES(ndr, r->cbData); + r->ab = data_blob_talloc(ndr->current_mem_ctx, + ndr->data+ndr->offset, + r->cbData); + if (r->ab.data == NULL) { + return ndr_pull_error(ndr, NDR_ERR_ALLOC, + "failed to allocate buffer for LZX-compressed CFDATA block"); + } + ndr->offset += r->cbData; + break; + + case CF_COMPRESS_MSZIP: { + struct ndr_pull *pull_sub, *pull_compress; + NDR_PULL_NEED_BYTES(ndr, r->cbData); + /* decompress via subcontext */ + NDR_CHECK(ndr_pull_subcontext_start(ndr, &pull_sub, 0, r->cbData)); + pull_sub->cstate = ndr->cstate; + NDR_CHECK(ndr_pull_compression_start(pull_sub, &pull_compress, + ndr_ctype, r->cbUncomp, r->cbData)); + ndr_set_flags(&pull_compress->flags, LIBNDR_FLAG_REMAINING); + NDR_CHECK(ndr_pull_DATA_BLOB(pull_compress, NDR_SCALARS, &r->ab)); + NDR_CHECK(ndr_pull_compression_end(pull_sub, pull_compress, ndr_ctype, r->cbUncomp)); + NDR_CHECK(ndr_pull_subcontext_end(ndr, pull_sub, 0, r->cbData)); + + break; + } + default: + return NDR_ERR_BAD_SWITCH; + } + } + + ndr_pull_compression_state_free(ndr->cstate); + ndr->cstate = NULL; + + return NDR_ERR_SUCCESS; +} + _PUBLIC_ enum ndr_err_code ndr_pull_cab_file(struct ndr_pull *ndr, int ndr_flags, struct cab_file *r) { uint32_t size_cffolders_0 = 0; @@ -169,7 +393,7 @@ _PUBLIC_ enum ndr_err_code ndr_pull_cab_file(struct ndr_pull *ndr, int ndr_flags uint32_t cntr_cffiles_0; TALLOC_CTX *_mem_save_cffiles_0 = NULL; uint32_t size_cfdata_0 = 0; - uint32_t cntr_cfdata_0; + size_t processed_cfdata = 0; TALLOC_CTX *_mem_save_cfdata_0 = NULL; { uint32_t _flags_save_STRUCT = ndr->flags; @@ -203,8 +427,12 @@ _PUBLIC_ enum ndr_err_code ndr_pull_cab_file(struct ndr_pull *ndr, int ndr_flags NDR_PULL_ALLOC_N(ndr, r->cfdata, size_cfdata_0); _mem_save_cfdata_0 = NDR_PULL_GET_MEM_CTX(ndr); NDR_PULL_SET_MEM_CTX(ndr, r->cfdata, 0); - for (cntr_cfdata_0 = 0; cntr_cfdata_0 < (size_cfdata_0); cntr_cfdata_0++) { - NDR_CHECK(ndr_pull_CFDATA(ndr, NDR_SCALARS, &r->cfdata[cntr_cfdata_0])); + for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (size_cffolders_0); cntr_cffolders_0++) { + NDR_CHECK(ndr_pull_folder_cfdata(ndr, + r->cfdata + processed_cfdata, + r->cffolders[cntr_cffolders_0].typeCompress, + r->cffolders[cntr_cffolders_0].cCFData)); + processed_cfdata += r->cffolders[cntr_cffolders_0].cCFData; } NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cfdata_0, 0); NDR_CHECK(ndr_pull_trailer_align(ndr, 4)); diff --git a/librpc/ndr/ndr_compression.c b/librpc/ndr/ndr_compression.c index f6185b2ac86..bdce4317a92 100644 --- a/librpc/ndr/ndr_compression.c +++ b/librpc/ndr/ndr_compression.c @@ -47,6 +47,261 @@ static void ndr_zlib_free(voidpf opaque, voidpf address) talloc_free(address); } +static enum ndr_err_code ndr_pull_compression_mszip_cab_chunk(struct ndr_pull *ndrpull, + struct ndr_push *ndrpush, + struct ndr_compression_state *state, + ssize_t decompressed_len, + ssize_t compressed_len) +{ + DATA_BLOB comp_chunk; + uint32_t comp_chunk_offset; + uint32_t comp_chunk_size; + DATA_BLOB plain_chunk; + uint32_t plain_chunk_offset; + uint32_t plain_chunk_size; + z_stream *z = state->mszip.z; + int z_ret; + + plain_chunk_size = decompressed_len; + + if (plain_chunk_size > 0x00008000) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "Bad MSZIP CAB plain chunk size %08X > 0x00008000 (PULL)", + plain_chunk_size); + } + + + comp_chunk_size = compressed_len; + + DEBUG(9,("MSZIP CAB plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n", + plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size)); + + comp_chunk_offset = ndrpull->offset; + NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size)); + comp_chunk.length = comp_chunk_size; + comp_chunk.data = ndrpull->data + comp_chunk_offset; + + plain_chunk_offset = ndrpush->offset; + NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size)); + plain_chunk.length = plain_chunk_size; + plain_chunk.data = ndrpush->data + plain_chunk_offset; + + if (comp_chunk.length < 2) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "Bad MSZIP CAB comp chunk size %u < 2 (PULL)", + (unsigned int)comp_chunk.length); + } + /* CK = Chris Kirmse, official Microsoft purloiner */ + if (comp_chunk.data[0] != 'C' || + comp_chunk.data[1] != 'K') { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "Bad MSZIP CAB invalid prefix [%c%c] != [CK]", + comp_chunk.data[0], comp_chunk.data[1]); + } + + /* + * This is a MSZIP block. It is actually using the deflate + * algorithm which can be decompressed by zlib. zlib will try + * to decompress as much as it can in each run. If we provide + * all the input and enough room for the uncompressed output, + * one call is enough. It will loop over all the sub-blocks + * that make up a deflate block. + * + * See corresponding push function for more info. + */ + + z->next_in = comp_chunk.data + 2; + z->avail_in = comp_chunk.length - 2; + z->next_out = plain_chunk.data; + z->avail_out = plain_chunk.length; + + /* + * Each MSZIP CDATA contains a complete deflate stream + * i.e. the stream starts and ends in the CFDATA but the + * _dictionnary_ is shared between all CFDATA of a CFFOLDER. + * + * When decompressing, the initial dictionnary of the first + * CDATA is empty. All other CFDATA use the previous CFDATA + * uncompressed output as dictionnary. + */ + + if (state->mszip.dict_size) { + z_ret = inflateSetDictionary(z, state->mszip.dict, state->mszip.dict_size); + if (z_ret != Z_OK) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib inflateSetDictionary error %s (%d) %s (PULL)", + zError(z_ret), z_ret, z->msg); + } + } + + z_ret = inflate(z, Z_FINISH); + if (z_ret == Z_OK) { + /* + * Z_OK here means there was no error but the stream + * hasn't been fully decompressed because there was + * not enough room for the output, which should not + * happen + */ + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib inflate error not enough space for ouput (PULL)"); + } + if (z_ret != Z_STREAM_END) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib inflate error %s (%d) %s (PULL)", zError(z_ret), z_ret, z->msg); + } + + if (z->total_out < plain_chunk.length) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib uncompressed output is smaller than expected (%lu < %zu) (PULL)", + z->total_out, plain_chunk.length); + } + + /* + * Keep a copy of the output to set as dictionnary for the + * next decompression call. + * + * The input pointer seems to be still valid between calls, so + * we can just store that instead of copying the memory over + * the dict temp buffer. + */ + state->mszip.dict = plain_chunk.data; + state->mszip.dict_size = plain_chunk.length; + + z_ret = inflateReset(z); + if (z_ret != Z_OK) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib inflateReset error %s (%d) %s (PULL)", + zError(z_ret), z_ret, z->msg); + } + + return NDR_ERR_SUCCESS; +} + +static enum ndr_err_code ndr_push_compression_mszip_cab_chunk(struct ndr_push *ndrpush, + struct ndr_pull *ndrpull, + struct ndr_compression_state *state) +{ + DATA_BLOB comp_chunk; + uint32_t comp_chunk_size; + DATA_BLOB plain_chunk; + uint32_t plain_chunk_size; + uint32_t plain_chunk_offset; + uint32_t max_plain_size = 0x00008000; + /* + * The maximum compressed size of each MSZIP block is 32k + 12 bytes + * header size. + */ + uint32_t max_comp_size = 0x00008000 + 12; + int z_ret; + z_stream *z; + + if (ndrpull->data_size <= ndrpull->offset) { + return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, + "strange NDR pull size and offset (integer overflow?)"); + + } + + plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset); + plain_chunk_offset = ndrpull->offset; + NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size)); + + plain_chunk.data = ndrpull->data + plain_chunk_offset; + plain_chunk.length = plain_chunk_size; + + NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size)); + + comp_chunk.data = ndrpush->data + ndrpush->offset; + comp_chunk.length = max_comp_size; + + /* CK = Chris Kirmse, official Microsoft purloiner */ + comp_chunk.data[0] = 'C'; + comp_chunk.data[1] = 'K'; + + z = state->mszip.z; + z->next_in = plain_chunk.data; + z->avail_in = plain_chunk.length; + z->total_in = 0; + + z->next_out = comp_chunk.data + 2; + z->avail_out = comp_chunk.length; + z->total_out = 0; + + /* + * See pull function for explanations of the MSZIP format. + * + * The CFDATA block contains a full deflate stream. Each stream + * uses the uncompressed input of the previous CFDATA in the + * same CFFOLDER as a dictionnary for the compression. + */ + + if (state->mszip.dict_size) { + z_ret = deflateSetDictionary(z, state->mszip.dict, state->mszip.dict_size); + if (z_ret != Z_OK) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib deflateSetDictionary error %s (%d) %s (PUSH)", + zError(z_ret), z_ret, z->msg); + } + } + + /* + * Z_FINISH should make deflate process all of the input in + * one call. If the stream is not finished there was an error + * e.g. not enough room to store the compressed output. + */ + z_ret = deflate(z, Z_FINISH); + if (z_ret != Z_STREAM_END) { + return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, + "zlib deflate error %s (%d) %s (PUSH)", + zError(z_ret), z_ret, z->msg); + } + + if (z->avail_in) { + return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, + "MSZIP not all avail_in[%u] bytes consumed (PUSH)", + z->avail_in); + } + + comp_chunk_size = 2 + z->total_out; + if (comp_chunk_size < z->total_out) { + return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION, + "strange NDR push compressed size (integer overflow?)"); + } + + z_ret = deflateReset(z); + if (z_ret != Z_OK) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib deflateReset error %s (%d) %s (PUSH)", + zError(z_ret), z_ret, z->msg); + } + + if (plain_chunk.length > talloc_array_length(state->mszip.dict)) { + return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, + "zlib dict buffer is too big (PUSH)"); + } + + /* + * Keep a copy of the input to set as dictionnary for the next + * compression call. + * + * Ideally we would just store the input pointer and length + * without copying but the memory gets invalidated between the + * calls, so we just copy to a dedicated buffer we now is + * still going to been valid for the lifetime of the + * compressions state object. + */ + memcpy(state->mszip.dict, plain_chunk.data, plain_chunk.length); + state->mszip.dict_size = plain_chunk.length; + + DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n", + (unsigned int)plain_chunk.length, + (unsigned int)plain_chunk.length, + comp_chunk_size, comp_chunk_size)); + + ndrpush->offset += comp_chunk_size; + return NDR_ERR_SUCCESS; +} + + static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull, struct ndr_push *ndrpush, z_stream *z, @@ -411,6 +666,12 @@ enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr, NDR_ERR_HAVE_NO_MEMORY(ndrpush); switch (compression_alg) { + case NDR_COMPRESSION_MSZIP_CAB: + NDR_CHECK(ndr_pull_compression_mszip_cab_chunk(subndr, ndrpush, + subndr->cstate, + decompressed_len, + compressed_len)); + break; case NDR_COMPRESSION_MSZIP: ZERO_STRUCT(z); while (!last) { @@ -470,6 +731,7 @@ enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr, struct ndr_push *uncomndr; switch (compression_alg) { + case NDR_COMPRESSION_MSZIP_CAB: case NDR_COMPRESSION_MSZIP: case NDR_COMPRESSION_XPRESS: break; @@ -507,6 +769,10 @@ enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr, ndrpull->offset = 0; switch (compression_alg) { + case NDR_COMPRESSION_MSZIP_CAB: + NDR_CHECK(ndr_push_compression_mszip_cab_chunk(subndr, ndrpull, subndr->cstate)); + break; + case NDR_COMPRESSION_MSZIP: ZERO_STRUCT(z); while (!last) { -- cgit v1.2.1