Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : libndr compression support
5 :
6 : Copyright (C) Stefan Metzmacher 2005
7 : Copyright (C) Matthieu Suiche 2008
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "includes.h"
24 : #include "../lib/compression/lzxpress.h"
25 : #include "librpc/ndr/libndr.h"
26 : #include "../librpc/ndr/ndr_compression.h"
27 : #include <zlib.h>
28 :
29 : struct ndr_compression_state {
30 : enum ndr_compression_alg type;
31 : union {
32 : struct {
33 : struct z_stream_s *z;
34 : uint8_t *dict;
35 : size_t dict_size;
36 : } mszip;
37 : } alg;
38 : };
39 :
40 0 : static voidpf ndr_zlib_alloc(voidpf opaque, uInt items, uInt size)
41 : {
42 0 : return talloc_zero_size(opaque, items * size);
43 : }
44 :
45 0 : static void ndr_zlib_free(voidpf opaque, voidpf address)
46 : {
47 0 : talloc_free(address);
48 0 : }
49 :
50 0 : static enum ndr_err_code ndr_pull_compression_mszip_cab_chunk(struct ndr_pull *ndrpull,
51 : struct ndr_push *ndrpush,
52 : struct ndr_compression_state *state,
53 : ssize_t decompressed_len,
54 : ssize_t compressed_len)
55 : {
56 : DATA_BLOB comp_chunk;
57 : uint32_t comp_chunk_offset;
58 : uint32_t comp_chunk_size;
59 : DATA_BLOB plain_chunk;
60 : uint32_t plain_chunk_offset;
61 : uint32_t plain_chunk_size;
62 0 : z_stream *z = state->alg.mszip.z;
63 : int z_ret;
64 :
65 0 : plain_chunk_size = decompressed_len;
66 :
67 0 : if (plain_chunk_size > 0x00008000) {
68 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
69 : "Bad MSZIP CAB plain chunk size %08X > 0x00008000 (PULL)",
70 : plain_chunk_size);
71 : }
72 :
73 :
74 0 : comp_chunk_size = compressed_len;
75 :
76 0 : DEBUG(9,("MSZIP CAB plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
77 : plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
78 :
79 0 : comp_chunk_offset = ndrpull->offset;
80 0 : NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
81 0 : comp_chunk.length = comp_chunk_size;
82 0 : comp_chunk.data = ndrpull->data + comp_chunk_offset;
83 :
84 0 : plain_chunk_offset = ndrpush->offset;
85 0 : NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
86 0 : plain_chunk.length = plain_chunk_size;
87 0 : plain_chunk.data = ndrpush->data + plain_chunk_offset;
88 :
89 0 : if (comp_chunk.length < 2) {
90 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
91 : "Bad MSZIP CAB comp chunk size %u < 2 (PULL)",
92 : (unsigned int)comp_chunk.length);
93 : }
94 : /* CK = Chris Kirmse, official Microsoft purloiner */
95 0 : if (comp_chunk.data[0] != 'C' ||
96 0 : comp_chunk.data[1] != 'K') {
97 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
98 : "Bad MSZIP CAB invalid prefix [%c%c] != [CK]",
99 : comp_chunk.data[0], comp_chunk.data[1]);
100 : }
101 :
102 : /*
103 : * This is a MSZIP block. It is actually using the deflate
104 : * algorithm which can be decompressed by zlib. zlib will try
105 : * to decompress as much as it can in each run. If we provide
106 : * all the input and enough room for the uncompressed output,
107 : * one call is enough. It will loop over all the sub-blocks
108 : * that make up a deflate block.
109 : *
110 : * See corresponding push function for more info.
111 : */
112 :
113 0 : z->next_in = comp_chunk.data + 2;
114 0 : z->avail_in = comp_chunk.length - 2;
115 0 : z->next_out = plain_chunk.data;
116 0 : z->avail_out = plain_chunk.length;
117 :
118 : /*
119 : * Each MSZIP CDATA contains a complete deflate stream
120 : * i.e. the stream starts and ends in the CFDATA but the
121 : * _dictionary_ is shared between all CFDATA of a CFFOLDER.
122 : *
123 : * When decompressing, the initial dictionary of the first
124 : * CDATA is empty. All other CFDATA use the previous CFDATA
125 : * uncompressed output as dictionary.
126 : */
127 :
128 0 : if (state->alg.mszip.dict_size) {
129 0 : z_ret = inflateSetDictionary(z, state->alg.mszip.dict, state->alg.mszip.dict_size);
130 0 : if (z_ret != Z_OK) {
131 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
132 : "zlib inflateSetDictionary error %s (%d) %s (PULL)",
133 : zError(z_ret), z_ret, z->msg);
134 : }
135 : }
136 :
137 0 : z_ret = inflate(z, Z_FINISH);
138 0 : if (z_ret == Z_OK) {
139 : /*
140 : * Z_OK here means there was no error but the stream
141 : * hasn't been fully decompressed because there was
142 : * not enough room for the output, which should not
143 : * happen
144 : */
145 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
146 : "zlib inflate error not enough space for output (PULL)");
147 : }
148 0 : if (z_ret != Z_STREAM_END) {
149 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
150 : "zlib inflate error %s (%d) %s (PULL)", zError(z_ret), z_ret, z->msg);
151 : }
152 :
153 0 : if (z->total_out < plain_chunk.length) {
154 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
155 : "zlib uncompressed output is smaller than expected (%lu < %zu) (PULL)",
156 : z->total_out, plain_chunk.length);
157 : }
158 :
159 : /*
160 : * Keep a copy of the output to set as dictionary for the
161 : * next decompression call.
162 : *
163 : * The input pointer seems to be still valid between calls, so
164 : * we can just store that instead of copying the memory over
165 : * the dict temp buffer.
166 : */
167 0 : state->alg.mszip.dict = plain_chunk.data;
168 0 : state->alg.mszip.dict_size = plain_chunk.length;
169 :
170 0 : z_ret = inflateReset(z);
171 0 : if (z_ret != Z_OK) {
172 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
173 : "zlib inflateReset error %s (%d) %s (PULL)",
174 : zError(z_ret), z_ret, z->msg);
175 : }
176 :
177 0 : return NDR_ERR_SUCCESS;
178 : }
179 :
180 0 : static enum ndr_err_code ndr_push_compression_mszip_cab_chunk(struct ndr_push *ndrpush,
181 : struct ndr_pull *ndrpull,
182 : struct ndr_compression_state *state)
183 : {
184 : DATA_BLOB comp_chunk;
185 : uint32_t comp_chunk_size;
186 : DATA_BLOB plain_chunk;
187 : uint32_t plain_chunk_size;
188 : uint32_t plain_chunk_offset;
189 0 : uint32_t max_plain_size = 0x00008000;
190 : /*
191 : * The maximum compressed size of each MSZIP block is 32k + 12 bytes
192 : * header size.
193 : */
194 0 : uint32_t max_comp_size = 0x00008000 + 12;
195 : int z_ret;
196 : z_stream *z;
197 :
198 0 : if (ndrpull->data_size <= ndrpull->offset) {
199 0 : return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
200 : "strange NDR pull size and offset (integer overflow?)");
201 :
202 : }
203 :
204 0 : plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
205 0 : plain_chunk_offset = ndrpull->offset;
206 0 : NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
207 :
208 0 : plain_chunk.data = ndrpull->data + plain_chunk_offset;
209 0 : plain_chunk.length = plain_chunk_size;
210 :
211 0 : NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
212 :
213 0 : comp_chunk.data = ndrpush->data + ndrpush->offset;
214 0 : comp_chunk.length = max_comp_size;
215 :
216 : /* CK = Chris Kirmse, official Microsoft purloiner */
217 0 : comp_chunk.data[0] = 'C';
218 0 : comp_chunk.data[1] = 'K';
219 :
220 0 : z = state->alg.mszip.z;
221 0 : z->next_in = plain_chunk.data;
222 0 : z->avail_in = plain_chunk.length;
223 0 : z->total_in = 0;
224 :
225 0 : z->next_out = comp_chunk.data + 2;
226 0 : z->avail_out = comp_chunk.length;
227 0 : z->total_out = 0;
228 :
229 : /*
230 : * See pull function for explanations of the MSZIP format.
231 : *
232 : * The CFDATA block contains a full deflate stream. Each stream
233 : * uses the uncompressed input of the previous CFDATA in the
234 : * same CFFOLDER as a dictionary for the compression.
235 : */
236 :
237 0 : if (state->alg.mszip.dict_size) {
238 0 : z_ret = deflateSetDictionary(z, state->alg.mszip.dict, state->alg.mszip.dict_size);
239 0 : if (z_ret != Z_OK) {
240 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
241 : "zlib deflateSetDictionary error %s (%d) %s (PUSH)",
242 : zError(z_ret), z_ret, z->msg);
243 : }
244 : }
245 :
246 : /*
247 : * Z_FINISH should make deflate process all of the input in
248 : * one call. If the stream is not finished there was an error
249 : * e.g. not enough room to store the compressed output.
250 : */
251 0 : z_ret = deflate(z, Z_FINISH);
252 0 : if (z_ret != Z_STREAM_END) {
253 0 : return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
254 : "zlib deflate error %s (%d) %s (PUSH)",
255 : zError(z_ret), z_ret, z->msg);
256 : }
257 :
258 0 : if (z->avail_in) {
259 0 : return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
260 : "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
261 : z->avail_in);
262 : }
263 :
264 0 : comp_chunk_size = 2 + z->total_out;
265 0 : if (comp_chunk_size < z->total_out) {
266 0 : return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
267 : "strange NDR push compressed size (integer overflow?)");
268 : }
269 :
270 0 : z_ret = deflateReset(z);
271 0 : if (z_ret != Z_OK) {
272 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
273 : "zlib deflateReset error %s (%d) %s (PUSH)",
274 : zError(z_ret), z_ret, z->msg);
275 : }
276 :
277 0 : if (plain_chunk.length > talloc_array_length(state->alg.mszip.dict)) {
278 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
279 : "zlib dict buffer is too big (PUSH)");
280 : }
281 :
282 : /*
283 : * Keep a copy of the input to set as dictionary for the next
284 : * compression call.
285 : *
286 : * Ideally we would just store the input pointer and length
287 : * without copying but the memory gets invalidated between the
288 : * calls, so we just copy to a dedicated buffer we now is
289 : * still going to been valid for the lifetime of the
290 : * compressions state object.
291 : */
292 0 : memcpy(state->alg.mszip.dict, plain_chunk.data, plain_chunk.length);
293 0 : state->alg.mszip.dict_size = plain_chunk.length;
294 :
295 0 : DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
296 : (unsigned int)plain_chunk.length,
297 : (unsigned int)plain_chunk.length,
298 : comp_chunk_size, comp_chunk_size));
299 :
300 0 : ndrpush->offset += comp_chunk_size;
301 0 : return NDR_ERR_SUCCESS;
302 : }
303 :
304 :
305 0 : static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull,
306 : struct ndr_push *ndrpush,
307 : z_stream *z,
308 : bool *last)
309 : {
310 : DATA_BLOB comp_chunk;
311 : uint32_t comp_chunk_offset;
312 : uint32_t comp_chunk_size;
313 : DATA_BLOB plain_chunk;
314 : uint32_t plain_chunk_offset;
315 : uint32_t plain_chunk_size;
316 : int z_ret;
317 :
318 0 : NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
319 0 : if (plain_chunk_size > 0x00008000) {
320 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad MSZIP plain chunk size %08X > 0x00008000 (PULL)",
321 : plain_chunk_size);
322 : }
323 :
324 0 : NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
325 :
326 0 : DEBUG(9,("MSZIP plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
327 : plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
328 :
329 0 : comp_chunk_offset = ndrpull->offset;
330 0 : NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
331 0 : comp_chunk.length = comp_chunk_size;
332 0 : comp_chunk.data = ndrpull->data + comp_chunk_offset;
333 :
334 0 : plain_chunk_offset = ndrpush->offset;
335 0 : NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
336 0 : plain_chunk.length = plain_chunk_size;
337 0 : plain_chunk.data = ndrpush->data + plain_chunk_offset;
338 :
339 0 : if (comp_chunk.length < 2) {
340 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
341 : "Bad MSZIP comp chunk size %u < 2 (PULL)",
342 : (unsigned int)comp_chunk.length);
343 : }
344 : /* CK = Chris Kirmse, official Microsoft purloiner */
345 0 : if (comp_chunk.data[0] != 'C' ||
346 0 : comp_chunk.data[1] != 'K') {
347 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
348 : "Bad MSZIP invalid prefix [%c%c] != [CK]",
349 : comp_chunk.data[0], comp_chunk.data[1]);
350 : }
351 :
352 0 : z->next_in = comp_chunk.data + 2;
353 0 : z->avail_in = comp_chunk.length -2;
354 0 : z->total_in = 0;
355 :
356 0 : z->next_out = plain_chunk.data;
357 0 : z->avail_out = plain_chunk.length;
358 0 : z->total_out = 0;
359 :
360 0 : if (!z->opaque) {
361 : /* the first time we need to intialize completely */
362 0 : z->zalloc = ndr_zlib_alloc;
363 0 : z->zfree = ndr_zlib_free;
364 0 : z->opaque = ndrpull;
365 :
366 0 : z_ret = inflateInit2(z, -MAX_WBITS);
367 0 : if (z_ret != Z_OK) {
368 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
369 : "Bad inflateInit2 error %s(%d) (PULL)",
370 : zError(z_ret), z_ret);
371 :
372 : }
373 : }
374 :
375 : /* call inflate untill we get Z_STREAM_END or an error */
376 : while (true) {
377 0 : z_ret = inflate(z, Z_BLOCK);
378 0 : if (z_ret != Z_OK) break;
379 : }
380 :
381 0 : if (z_ret != Z_STREAM_END) {
382 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
383 : "Bad inflate(Z_BLOCK) error %s(%d) (PULL)",
384 : zError(z_ret), z_ret);
385 : }
386 :
387 0 : if (z->avail_in) {
388 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
389 : "MSZIP not all avail_in[%u] bytes consumed (PULL)",
390 : z->avail_in);
391 : }
392 :
393 0 : if (z->avail_out) {
394 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
395 : "MSZIP not all avail_out[%u] bytes consumed (PULL)",
396 : z->avail_out);
397 : }
398 :
399 0 : if ((plain_chunk_size < 0x00008000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
400 : /* this is the last chunk */
401 0 : *last = true;
402 : }
403 :
404 0 : z_ret = inflateReset(z);
405 0 : if (z_ret != Z_OK) {
406 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
407 : "Bad inflateReset error %s(%d) (PULL)",
408 : zError(z_ret), z_ret);
409 : }
410 :
411 0 : z_ret = inflateSetDictionary(z, plain_chunk.data, plain_chunk.length);
412 0 : if (z_ret != Z_OK) {
413 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
414 : "Bad inflateSetDictionary error %s(%d) (PULL)",
415 : zError(z_ret), z_ret);
416 : }
417 :
418 0 : return NDR_ERR_SUCCESS;
419 : }
420 :
421 0 : static enum ndr_err_code ndr_push_compression_mszip_chunk(struct ndr_push *ndrpush,
422 : struct ndr_pull *ndrpull,
423 : z_stream *z,
424 : bool *last)
425 : {
426 : DATA_BLOB comp_chunk;
427 : uint32_t comp_chunk_size;
428 : uint32_t comp_chunk_size_offset;
429 : DATA_BLOB plain_chunk;
430 : uint32_t plain_chunk_size;
431 : uint32_t plain_chunk_offset;
432 0 : uint32_t max_plain_size = 0x00008000;
433 : /*
434 : * The maximum compressed size of each MSZIP block is 32k + 12 bytes
435 : * header size.
436 : */
437 0 : uint32_t max_comp_size = 0x00008000 + 12;
438 : uint32_t tmp_offset;
439 : int z_ret;
440 :
441 0 : plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
442 0 : plain_chunk_offset = ndrpull->offset;
443 0 : NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
444 :
445 0 : plain_chunk.data = ndrpull->data + plain_chunk_offset;
446 0 : plain_chunk.length = plain_chunk_size;
447 :
448 0 : if (plain_chunk_size < max_plain_size) {
449 0 : *last = true;
450 : }
451 :
452 0 : NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, plain_chunk_size));
453 0 : comp_chunk_size_offset = ndrpush->offset;
454 0 : NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, 0xFEFEFEFE));
455 :
456 0 : NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
457 :
458 0 : comp_chunk.data = ndrpush->data + ndrpush->offset;
459 0 : comp_chunk.length = max_comp_size;
460 :
461 : /* CK = Chris Kirmse, official Microsoft purloiner */
462 0 : comp_chunk.data[0] = 'C';
463 0 : comp_chunk.data[1] = 'K';
464 :
465 0 : z->next_in = plain_chunk.data;
466 0 : z->avail_in = plain_chunk.length;
467 0 : z->total_in = 0;
468 :
469 0 : z->next_out = comp_chunk.data + 2;
470 0 : z->avail_out = comp_chunk.length;
471 0 : z->total_out = 0;
472 :
473 0 : if (!z->opaque) {
474 : /* the first time we need to intialize completely */
475 0 : z->zalloc = ndr_zlib_alloc;
476 0 : z->zfree = ndr_zlib_free;
477 0 : z->opaque = ndrpull;
478 :
479 : /* TODO: find how to trigger the same parameters windows uses */
480 0 : z_ret = deflateInit2(z,
481 : Z_DEFAULT_COMPRESSION,
482 : Z_DEFLATED,
483 : -MAX_WBITS,
484 : 8, /* memLevel */
485 : Z_DEFAULT_STRATEGY);
486 0 : if (z_ret != Z_OK) {
487 0 : return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
488 : "Bad deflateInit2 error %s(%d) (PUSH)",
489 : zError(z_ret), z_ret);
490 :
491 : }
492 : }
493 :
494 : /* call deflate untill we get Z_STREAM_END or an error */
495 : while (true) {
496 0 : z_ret = deflate(z, Z_FINISH);
497 0 : if (z_ret != Z_OK) break;
498 : }
499 0 : if (z_ret != Z_STREAM_END) {
500 0 : return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
501 : "Bad delate(Z_BLOCK) error %s(%d) (PUSH)",
502 : zError(z_ret), z_ret);
503 : }
504 :
505 0 : if (z->avail_in) {
506 0 : return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
507 : "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
508 : z->avail_in);
509 : }
510 :
511 0 : comp_chunk_size = 2 + z->total_out;
512 :
513 0 : z_ret = deflateReset(z);
514 0 : if (z_ret != Z_OK) {
515 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
516 : "Bad deflateReset error %s(%d) (PULL)",
517 : zError(z_ret), z_ret);
518 : }
519 :
520 0 : z_ret = deflateSetDictionary(z, plain_chunk.data, plain_chunk.length);
521 0 : if (z_ret != Z_OK) {
522 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
523 : "Bad deflateSetDictionary error %s(%d) (PULL)",
524 : zError(z_ret), z_ret);
525 : }
526 :
527 0 : tmp_offset = ndrpush->offset;
528 0 : ndrpush->offset = comp_chunk_size_offset;
529 0 : NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, comp_chunk_size));
530 0 : ndrpush->offset = tmp_offset;
531 :
532 0 : DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
533 : (unsigned int)plain_chunk.length,
534 : (unsigned int)plain_chunk.length,
535 : comp_chunk_size, comp_chunk_size));
536 :
537 0 : ndrpush->offset += comp_chunk_size;
538 0 : return NDR_ERR_SUCCESS;
539 : }
540 :
541 0 : static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
542 : struct ndr_push *ndrpush,
543 : bool *last)
544 : {
545 : DATA_BLOB comp_chunk;
546 : DATA_BLOB plain_chunk;
547 : uint32_t comp_chunk_offset;
548 : uint32_t plain_chunk_offset;
549 : uint32_t comp_chunk_size;
550 : uint32_t plain_chunk_size;
551 : ssize_t ret;
552 :
553 0 : NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
554 0 : if (plain_chunk_size > 0x00010000) {
555 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)",
556 : plain_chunk_size);
557 : }
558 :
559 0 : NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
560 :
561 0 : comp_chunk_offset = ndrpull->offset;
562 0 : NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
563 0 : comp_chunk.length = comp_chunk_size;
564 0 : comp_chunk.data = ndrpull->data + comp_chunk_offset;
565 :
566 0 : plain_chunk_offset = ndrpush->offset;
567 0 : NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
568 0 : plain_chunk.length = plain_chunk_size;
569 0 : plain_chunk.data = ndrpush->data + plain_chunk_offset;
570 :
571 0 : DEBUG(9,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
572 : plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
573 :
574 : /* Uncompressing the buffer using LZ Xpress algorithm */
575 0 : ret = lzxpress_decompress(comp_chunk.data,
576 0 : comp_chunk.length,
577 : plain_chunk.data,
578 0 : plain_chunk.length);
579 0 : if (ret < 0) {
580 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
581 : "XPRESS lzxpress_decompress() returned %d\n",
582 : (int)ret);
583 : }
584 0 : plain_chunk.length = ret;
585 :
586 0 : if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
587 : /* this is the last chunk */
588 0 : *last = true;
589 : }
590 :
591 0 : return NDR_ERR_SUCCESS;
592 : }
593 :
594 0 : static enum ndr_err_code ndr_push_compression_xpress_chunk(struct ndr_push *ndrpush,
595 : struct ndr_pull *ndrpull,
596 : bool *last)
597 : {
598 : DATA_BLOB comp_chunk;
599 : uint32_t comp_chunk_size_offset;
600 : DATA_BLOB plain_chunk;
601 : uint32_t plain_chunk_size;
602 : uint32_t plain_chunk_offset;
603 0 : uint32_t max_plain_size = 0x00010000;
604 0 : uint32_t max_comp_size = 0x00020000 + 2; /* TODO: use the correct value here */
605 : uint32_t tmp_offset;
606 : ssize_t ret;
607 :
608 0 : plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
609 0 : plain_chunk_offset = ndrpull->offset;
610 0 : NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
611 :
612 0 : plain_chunk.data = ndrpull->data + plain_chunk_offset;
613 0 : plain_chunk.length = plain_chunk_size;
614 :
615 0 : if (plain_chunk_size < max_plain_size) {
616 0 : *last = true;
617 : }
618 :
619 0 : NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, plain_chunk_size));
620 0 : comp_chunk_size_offset = ndrpush->offset;
621 0 : NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, 0xFEFEFEFE));
622 :
623 0 : NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
624 :
625 0 : comp_chunk.data = ndrpush->data + ndrpush->offset;
626 0 : comp_chunk.length = max_comp_size;
627 :
628 : /* Compressing the buffer using LZ Xpress algorithm */
629 0 : ret = lzxpress_compress(plain_chunk.data,
630 0 : plain_chunk.length,
631 : comp_chunk.data,
632 0 : comp_chunk.length);
633 0 : if (ret < 0) {
634 0 : return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
635 : "XPRESS lzxpress_compress() returned %d\n",
636 : (int)ret);
637 : }
638 0 : comp_chunk.length = ret;
639 :
640 0 : tmp_offset = ndrpush->offset;
641 0 : ndrpush->offset = comp_chunk_size_offset;
642 0 : NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, comp_chunk.length));
643 0 : ndrpush->offset = tmp_offset;
644 :
645 0 : ndrpush->offset += comp_chunk.length;
646 0 : return NDR_ERR_SUCCESS;
647 : }
648 :
649 : /*
650 : handle compressed subcontext buffers, which in midl land are user-marshalled, but
651 : we use magic in pidl to make them easier to cope with
652 : */
653 0 : enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr,
654 : struct ndr_pull **_comndr,
655 : enum ndr_compression_alg compression_alg,
656 : ssize_t decompressed_len,
657 : ssize_t compressed_len)
658 : {
659 : struct ndr_push *ndrpush;
660 : struct ndr_pull *comndr;
661 : DATA_BLOB uncompressed;
662 0 : bool last = false;
663 : z_stream z;
664 :
665 0 : ndrpush = ndr_push_init_ctx(subndr);
666 0 : NDR_ERR_HAVE_NO_MEMORY(ndrpush);
667 :
668 0 : switch (compression_alg) {
669 0 : case NDR_COMPRESSION_MSZIP_CAB:
670 0 : NDR_CHECK(ndr_pull_compression_mszip_cab_chunk(subndr, ndrpush,
671 : subndr->cstate,
672 : decompressed_len,
673 : compressed_len));
674 0 : break;
675 0 : case NDR_COMPRESSION_MSZIP:
676 0 : ZERO_STRUCT(z);
677 0 : while (!last) {
678 0 : NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
679 : }
680 0 : break;
681 :
682 0 : case NDR_COMPRESSION_XPRESS:
683 0 : while (!last) {
684 0 : NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
685 : }
686 0 : break;
687 :
688 0 : default:
689 0 : return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)",
690 : compression_alg);
691 : }
692 :
693 0 : uncompressed = ndr_push_blob(ndrpush);
694 0 : if (uncompressed.length != decompressed_len) {
695 0 : return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
696 : "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
697 : (int)uncompressed.length,
698 : (int)decompressed_len,
699 : (int)decompressed_len);
700 : }
701 :
702 0 : comndr = talloc_zero(subndr, struct ndr_pull);
703 0 : NDR_ERR_HAVE_NO_MEMORY(comndr);
704 0 : comndr->flags = subndr->flags;
705 0 : comndr->current_mem_ctx = subndr->current_mem_ctx;
706 :
707 0 : comndr->data = uncompressed.data;
708 0 : comndr->data_size = uncompressed.length;
709 0 : comndr->offset = 0;
710 :
711 0 : *_comndr = comndr;
712 0 : return NDR_ERR_SUCCESS;
713 : }
714 :
715 0 : enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
716 : struct ndr_pull *comndr,
717 : enum ndr_compression_alg compression_alg,
718 : ssize_t decompressed_len)
719 : {
720 0 : return NDR_ERR_SUCCESS;
721 : }
722 :
723 : /*
724 : push a compressed subcontext
725 : */
726 0 : enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
727 : struct ndr_push **_uncomndr,
728 : enum ndr_compression_alg compression_alg,
729 : ssize_t decompressed_len)
730 : {
731 : struct ndr_push *uncomndr;
732 :
733 0 : switch (compression_alg) {
734 0 : case NDR_COMPRESSION_MSZIP_CAB:
735 : case NDR_COMPRESSION_MSZIP:
736 : case NDR_COMPRESSION_XPRESS:
737 0 : break;
738 0 : default:
739 0 : return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
740 : "Bad compression algorithm %d (PUSH)",
741 : compression_alg);
742 : }
743 :
744 0 : uncomndr = ndr_push_init_ctx(subndr);
745 0 : NDR_ERR_HAVE_NO_MEMORY(uncomndr);
746 0 : uncomndr->flags = subndr->flags;
747 :
748 0 : *_uncomndr = uncomndr;
749 0 : return NDR_ERR_SUCCESS;
750 : }
751 :
752 : /*
753 : push a compressed subcontext
754 : */
755 0 : enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
756 : struct ndr_push *uncomndr,
757 : enum ndr_compression_alg compression_alg,
758 : ssize_t decompressed_len)
759 : {
760 : struct ndr_pull *ndrpull;
761 0 : bool last = false;
762 : z_stream z;
763 :
764 0 : ndrpull = talloc_zero(uncomndr, struct ndr_pull);
765 0 : NDR_ERR_HAVE_NO_MEMORY(ndrpull);
766 0 : ndrpull->flags = uncomndr->flags;
767 0 : ndrpull->data = uncomndr->data;
768 0 : ndrpull->data_size = uncomndr->offset;
769 0 : ndrpull->offset = 0;
770 :
771 0 : switch (compression_alg) {
772 0 : case NDR_COMPRESSION_MSZIP_CAB:
773 0 : NDR_CHECK(ndr_push_compression_mszip_cab_chunk(subndr, ndrpull, subndr->cstate));
774 0 : break;
775 :
776 0 : case NDR_COMPRESSION_MSZIP:
777 0 : ZERO_STRUCT(z);
778 0 : while (!last) {
779 0 : NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
780 : }
781 0 : break;
782 :
783 0 : case NDR_COMPRESSION_XPRESS:
784 0 : while (!last) {
785 0 : NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
786 : }
787 0 : break;
788 :
789 0 : default:
790 0 : return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)",
791 : compression_alg);
792 : }
793 :
794 0 : talloc_free(uncomndr);
795 0 : return NDR_ERR_SUCCESS;
796 : }
797 :
798 0 : static enum ndr_err_code generic_mszip_init(TALLOC_CTX *mem_ctx,
799 : struct ndr_compression_state *state)
800 : {
801 0 : z_stream *z = talloc_zero(mem_ctx, z_stream);
802 0 : NDR_ERR_HAVE_NO_MEMORY(z);
803 :
804 0 : z->zalloc = ndr_zlib_alloc;
805 0 : z->zfree = ndr_zlib_free;
806 0 : z->opaque = mem_ctx;
807 :
808 0 : state->alg.mszip.z = z;
809 0 : state->alg.mszip.dict_size = 0;
810 : /* pre-alloc dictionary */
811 0 : state->alg.mszip.dict = talloc_array(mem_ctx, uint8_t, 0x8000);
812 0 : NDR_ERR_HAVE_NO_MEMORY(state->alg.mszip.dict);
813 :
814 0 : return NDR_ERR_SUCCESS;
815 : }
816 :
817 0 : static void generic_mszip_free(struct ndr_compression_state *state)
818 : {
819 0 : if (state == NULL) {
820 0 : return;
821 : }
822 :
823 0 : TALLOC_FREE(state->alg.mszip.z);
824 0 : TALLOC_FREE(state->alg.mszip.dict);
825 : }
826 :
827 :
828 0 : enum ndr_err_code ndr_pull_compression_state_init(struct ndr_pull *ndr,
829 : enum ndr_compression_alg compression_alg,
830 : struct ndr_compression_state **state)
831 : {
832 : struct ndr_compression_state *s;
833 : int z_ret;
834 :
835 0 : s = talloc_zero(ndr, struct ndr_compression_state);
836 0 : NDR_ERR_HAVE_NO_MEMORY(s);
837 0 : s->type = compression_alg;
838 :
839 0 : switch (compression_alg) {
840 0 : case NDR_COMPRESSION_MSZIP:
841 : case NDR_COMPRESSION_XPRESS:
842 0 : break;
843 0 : case NDR_COMPRESSION_MSZIP_CAB:
844 0 : NDR_CHECK(generic_mszip_init(ndr, s));
845 0 : z_ret = inflateInit2(s->alg.mszip.z, -MAX_WBITS);
846 0 : if (z_ret != Z_OK) {
847 0 : return ndr_pull_error(ndr, NDR_ERR_COMPRESSION,
848 : "zlib inflateinit2 error %s (%d) %s (PULL)",
849 : zError(z_ret), z_ret, s->alg.mszip.z->msg);
850 : }
851 0 : break;
852 0 : default:
853 0 : return ndr_pull_error(ndr, NDR_ERR_COMPRESSION,
854 : "Bad compression algorithm %d (PULL)",
855 : compression_alg);
856 : break;
857 : }
858 :
859 0 : *state = s;
860 :
861 0 : return NDR_ERR_SUCCESS;
862 : }
863 :
864 0 : void ndr_pull_compression_state_free(struct ndr_compression_state *state)
865 : {
866 0 : if (state == NULL) {
867 0 : return;
868 : }
869 :
870 0 : switch (state->type) {
871 0 : case NDR_COMPRESSION_MSZIP:
872 : case NDR_COMPRESSION_XPRESS:
873 0 : break;
874 0 : case NDR_COMPRESSION_MSZIP_CAB:
875 0 : generic_mszip_free(state);
876 0 : break;
877 0 : default:
878 0 : break;
879 : }
880 0 : TALLOC_FREE(state);
881 : }
882 :
883 0 : enum ndr_err_code ndr_push_compression_state_init(struct ndr_push *ndr,
884 : enum ndr_compression_alg compression_alg,
885 : struct ndr_compression_state **state)
886 : {
887 : struct ndr_compression_state *s;
888 : int z_ret;
889 :
890 0 : s = talloc_zero(ndr, struct ndr_compression_state);
891 0 : NDR_ERR_HAVE_NO_MEMORY(s);
892 0 : s->type = compression_alg;
893 :
894 0 : switch (compression_alg) {
895 0 : case NDR_COMPRESSION_XPRESS:
896 : case NDR_COMPRESSION_MSZIP:
897 0 : break;
898 0 : case NDR_COMPRESSION_MSZIP_CAB:
899 0 : NDR_CHECK(generic_mszip_init(ndr, s));
900 0 : z_ret = deflateInit2(s->alg.mszip.z,
901 : Z_DEFAULT_COMPRESSION,
902 : Z_DEFLATED,
903 : -MAX_WBITS,
904 : 8, /* memLevel */
905 : Z_DEFAULT_STRATEGY);
906 0 : if (z_ret != Z_OK) {
907 0 : return ndr_push_error(ndr, NDR_ERR_COMPRESSION,
908 : "zlib inflateinit2 error %s (%d) %s (PUSH)",
909 : zError(z_ret), z_ret, s->alg.mszip.z->msg);
910 : }
911 0 : break;
912 0 : default:
913 0 : return ndr_push_error(ndr, NDR_ERR_COMPRESSION,
914 : "Bad compression algorithm %d (PUSH)",
915 : compression_alg);
916 : break;
917 : }
918 :
919 0 : *state = s;
920 :
921 0 : return NDR_ERR_SUCCESS;
922 : }
923 :
924 0 : void ndr_push_compression_state_free(struct ndr_compression_state *state)
925 : {
926 0 : if (state == NULL) {
927 0 : return;
928 : }
929 :
930 0 : switch (state->type) {
931 0 : case NDR_COMPRESSION_MSZIP:
932 : case NDR_COMPRESSION_XPRESS:
933 0 : break;
934 0 : case NDR_COMPRESSION_MSZIP_CAB:
935 0 : generic_mszip_free(state);
936 0 : break;
937 0 : default:
938 0 : break;
939 : }
940 0 : TALLOC_FREE(state);
941 : }
|