summaryrefslogtreecommitdiff
path: root/librpc/ndr/ndr_cab.c
diff options
context:
space:
mode:
Diffstat (limited to 'librpc/ndr/ndr_cab.c')
-rw-r--r--librpc/ndr/ndr_cab.c252
1 files changed, 240 insertions, 12 deletions
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));