librpc/ndr: add MSZIP compression for cabinet files
authorAurelien Aptel <aaptel@suse.com>
Tue, 23 May 2017 10:09:28 +0000 (12:09 +0200)
committerJeremy Allison <jra@samba.org>
Wed, 19 Jul 2017 19:22:13 +0000 (21:22 +0200)
Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Reviewed-by: Guenther Deschner <gd@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
librpc/ndr/ndr_cab.c
librpc/ndr/ndr_compression.c
source4/torture/ndr/cabinet.c

index efd337d5daa21041ce5ff7f9caffc2b6cdcdbc17..837ed253065f9795320d7583aa06f51a71440435 100644 (file)
@@ -21,6 +21,9 @@
 
 #include "includes.h"
 #include "librpc/gen_ndr/ndr_cab.h"
 
 #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)
 {
 
 _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);
 }
 
                                        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;
 _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);
        {
                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) {
                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++) {
                        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]));
                                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
                        }
 #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));
                }
                        }
                        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;
 }
 
        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;
 _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_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;
        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);
                        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));
                        }
                        NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cfdata_0, 0);
                        NDR_CHECK(ndr_pull_trailer_align(ndr, 4));
index f6185b2ac86cd1a4ba0ec65a4cce7a646eaee930..bdce4317a928b2214566e218dec4304875cb5044 100644 (file)
@@ -47,6 +47,261 @@ static void  ndr_zlib_free(voidpf opaque, voidpf address)
        talloc_free(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,
 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) {
        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) {
        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) {
        struct ndr_push *uncomndr;
 
        switch (compression_alg) {
+       case NDR_COMPRESSION_MSZIP_CAB:
        case NDR_COMPRESSION_MSZIP:
        case NDR_COMPRESSION_XPRESS:
                break;
        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) {
        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) {
        case NDR_COMPRESSION_MSZIP:
                ZERO_STRUCT(z);
                while (!last) {
index 790f7c37f11977edd3317570f4cb6b71013b53ef..f197534bb4afe66636219880475dbc8d2de2b658 100644 (file)
@@ -4233,10 +4233,9 @@ static bool cab_file_MSZIP_check(struct torture_context *tctx,
 
        blob = data_blob(NULL, r->cfdata[0].cbUncomp);
        memset(blob.data, 'A', blob.length);
 
        blob = data_blob(NULL, r->cfdata[0].cbUncomp);
        memset(blob.data, 'A', blob.length);
-#if 0
-       /* once we have MSZIP compression working we can enable this test */
+
        torture_assert_data_blob_equal(tctx, r->cfdata[0].ab, blob, "ab");
        torture_assert_data_blob_equal(tctx, r->cfdata[0].ab, blob, "ab");
-#endif
+
        return true;
 }
 
        return true;
 }