root/librpc/ndr/ndr_compression.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ndr_zlib_alloc
  2. ndr_zlib_free
  3. ndr_pull_compression_mszip_chunk
  4. ndr_push_compression_mszip_chunk
  5. ndr_pull_compression_xpress_chunk
  6. ndr_push_compression_xpress_chunk
  7. ndr_pull_compression_start
  8. ndr_pull_compression_end
  9. ndr_push_compression_start
  10. ndr_push_compression_end

   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 static voidpf ndr_zlib_alloc(voidpf opaque, uInt items, uInt size)
     /* [<][>][^][v][top][bottom][index][help] */
  30 {
  31         return talloc_zero_size(opaque, items * size);
  32 }
  33 
  34 static void  ndr_zlib_free(voidpf opaque, voidpf address)
     /* [<][>][^][v][top][bottom][index][help] */
  35 {
  36         talloc_free(address);
  37 }
  38 
  39 static enum ndr_err_code ndr_pull_compression_mszip_chunk(struct ndr_pull *ndrpull,
     /* [<][>][^][v][top][bottom][index][help] */
  40                                                  struct ndr_push *ndrpush,
  41                                                  z_stream *z,
  42                                                  bool *last)
  43 {
  44         DATA_BLOB comp_chunk;
  45         uint32_t comp_chunk_offset;
  46         uint32_t comp_chunk_size;
  47         DATA_BLOB plain_chunk;
  48         uint32_t plain_chunk_offset;
  49         uint32_t plain_chunk_size;
  50         int z_ret;
  51 
  52         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
  53         if (plain_chunk_size > 0x00008000) {
  54                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad MSZIP plain chunk size %08X > 0x00008000 (PULL)", 
  55                                       plain_chunk_size);
  56         }
  57 
  58         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
  59 
  60         DEBUG(9,("MSZIP plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
  61                  plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
  62 
  63         comp_chunk_offset = ndrpull->offset;
  64         NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
  65         comp_chunk.length = comp_chunk_size;
  66         comp_chunk.data = ndrpull->data + comp_chunk_offset;
  67 
  68         plain_chunk_offset = ndrpush->offset;
  69         NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
  70         plain_chunk.length = plain_chunk_size;
  71         plain_chunk.data = ndrpush->data + plain_chunk_offset;
  72 
  73         if (comp_chunk.length < 2) {
  74                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
  75                                       "Bad MSZIP comp chunk size %u < 2 (PULL)",
  76                                       (unsigned int)comp_chunk.length);
  77         }
  78         /* CK = Chris Kirmse, official Microsoft purloiner */
  79         if (comp_chunk.data[0] != 'C' ||
  80             comp_chunk.data[1] != 'K') {
  81                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
  82                                       "Bad MSZIP invalid prefix [%c%c] != [CK]",
  83                                       comp_chunk.data[0], comp_chunk.data[1]);
  84         }
  85 
  86         z->next_in      = comp_chunk.data + 2;
  87         z->avail_in     = comp_chunk.length -2;
  88         z->total_in     = 0;
  89 
  90         z->next_out     = plain_chunk.data;
  91         z->avail_out    = plain_chunk.length;
  92         z->total_out    = 0;
  93 
  94         if (!z->opaque) {
  95                 /* the first time we need to intialize completely */
  96                 z->zalloc       = ndr_zlib_alloc;
  97                 z->zfree        = ndr_zlib_free;
  98                 z->opaque       = ndrpull;
  99 
 100                 z_ret = inflateInit2(z, -15);
 101                 if (z_ret != Z_OK) {
 102                         return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 103                                               "Bad inflateInit2 error %s(%d) (PULL)",
 104                                               zError(z_ret), z_ret);
 105 
 106                 }
 107         }
 108 
 109         /* call inflate untill we get Z_STREAM_END or an error */
 110         while (true) {
 111                 z_ret = inflate(z, Z_BLOCK);
 112                 if (z_ret != Z_OK) break;
 113         }
 114 
 115         if (z_ret != Z_STREAM_END) {
 116                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 117                                       "Bad inflate(Z_BLOCK) error %s(%d) (PULL)",
 118                                       zError(z_ret), z_ret);
 119         }
 120 
 121         if (z->avail_in) {
 122                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 123                                       "MSZIP not all avail_in[%u] bytes consumed (PULL)",
 124                                       z->avail_in);
 125         }
 126 
 127         if (z->avail_out) {
 128                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 129                                       "MSZIP not all avail_out[%u] bytes consumed (PULL)",
 130                                       z->avail_out);
 131         }
 132 
 133         if ((plain_chunk_size < 0x00008000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
 134                 /* this is the last chunk */
 135                 *last = true;
 136         }
 137 
 138         z_ret = inflateReset(z);
 139         if (z_ret != Z_OK) {
 140                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 141                                       "Bad inflateReset error %s(%d) (PULL)",
 142                                       zError(z_ret), z_ret);
 143         }
 144 
 145         z_ret = inflateSetDictionary(z, plain_chunk.data, plain_chunk.length);
 146         if (z_ret != Z_OK) {
 147                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 148                                       "Bad inflateSetDictionary error %s(%d) (PULL)",
 149                                       zError(z_ret), z_ret);
 150         }
 151 
 152         return NDR_ERR_SUCCESS;
 153 }
 154 
 155 static enum ndr_err_code ndr_push_compression_mszip_chunk(struct ndr_push *ndrpush,
     /* [<][>][^][v][top][bottom][index][help] */
 156                                                           struct ndr_pull *ndrpull,
 157                                                           z_stream *z,
 158                                                           bool *last)
 159 {
 160         DATA_BLOB comp_chunk;
 161         uint32_t comp_chunk_size;
 162         uint32_t comp_chunk_size_offset;
 163         DATA_BLOB plain_chunk;
 164         uint32_t plain_chunk_size;
 165         uint32_t plain_chunk_offset;
 166         uint32_t max_plain_size = 0x00008000;
 167         uint32_t max_comp_size = 0x00008000 + 2 + 12 /*TODO: what value do we really need here?*/;
 168         uint32_t tmp_offset;
 169         int z_ret;
 170 
 171         plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
 172         plain_chunk_offset = ndrpull->offset;
 173         NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
 174 
 175         plain_chunk.data = ndrpull->data + plain_chunk_offset;
 176         plain_chunk.length = plain_chunk_size;
 177 
 178         if (plain_chunk_size < max_plain_size) {
 179                 *last = true;
 180         }
 181 
 182         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, plain_chunk_size));
 183         comp_chunk_size_offset = ndrpush->offset;
 184         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, 0xFEFEFEFE));
 185 
 186         NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
 187 
 188         comp_chunk.data = ndrpush->data + ndrpush->offset;
 189         comp_chunk.length = max_comp_size;
 190 
 191         /* CK = Chris Kirmse, official Microsoft purloiner */
 192         comp_chunk.data[0] = 'C';
 193         comp_chunk.data[1] = 'K';
 194 
 195         z->next_in      = plain_chunk.data;
 196         z->avail_in     = plain_chunk.length;
 197         z->total_in     = 0;
 198 
 199         z->next_out     = comp_chunk.data + 2;
 200         z->avail_out    = comp_chunk.length - 2;
 201         z->total_out    = 0;
 202 
 203         if (!z->opaque) {
 204                 /* the first time we need to intialize completely */
 205                 z->zalloc       = ndr_zlib_alloc;
 206                 z->zfree        = ndr_zlib_free;
 207                 z->opaque       = ndrpull;
 208 
 209                 /* TODO: find how to trigger the same parameters windows uses */
 210                 z_ret = deflateInit2(z,
 211                                      Z_DEFAULT_COMPRESSION,
 212                                      Z_DEFLATED,
 213                                      -15,
 214                                      9,
 215                                      Z_DEFAULT_STRATEGY);
 216                 if (z_ret != Z_OK) {
 217                         return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
 218                                               "Bad deflateInit2 error %s(%d) (PUSH)",
 219                                               zError(z_ret), z_ret);
 220 
 221                 }
 222         }
 223 
 224         /* call deflate untill we get Z_STREAM_END or an error */
 225         while (true) {
 226                 z_ret = deflate(z, Z_FINISH);
 227                 if (z_ret != Z_OK) break;
 228         }
 229         if (z_ret != Z_STREAM_END) {
 230                 return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
 231                                       "Bad delate(Z_BLOCK) error %s(%d) (PUSH)",
 232                                       zError(z_ret), z_ret);
 233         }
 234 
 235         if (z->avail_in) {
 236                 return ndr_push_error(ndrpush, NDR_ERR_COMPRESSION,
 237                                       "MSZIP not all avail_in[%u] bytes consumed (PUSH)",
 238                                       z->avail_in);
 239         }
 240 
 241         comp_chunk_size = 2 + z->total_out;
 242 
 243         z_ret = deflateReset(z);
 244         if (z_ret != Z_OK) {
 245                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 246                                       "Bad deflateReset error %s(%d) (PULL)",
 247                                       zError(z_ret), z_ret);
 248         }
 249 
 250         z_ret = deflateSetDictionary(z, plain_chunk.data, plain_chunk.length);
 251         if (z_ret != Z_OK) {
 252                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 253                                       "Bad deflateSetDictionary error %s(%d) (PULL)",
 254                                       zError(z_ret), z_ret);
 255         }
 256 
 257         tmp_offset = ndrpush->offset;
 258         ndrpush->offset = comp_chunk_size_offset;
 259         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, comp_chunk_size));
 260         ndrpush->offset = tmp_offset;
 261 
 262         DEBUG(9,("MSZIP comp plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
 263                  (unsigned int)plain_chunk.length,
 264                  (unsigned int)plain_chunk.length,
 265                  comp_chunk_size, comp_chunk_size));
 266 
 267         ndrpush->offset += comp_chunk_size;
 268         return NDR_ERR_SUCCESS;
 269 }
 270 
 271 static enum ndr_err_code ndr_pull_compression_xpress_chunk(struct ndr_pull *ndrpull,
     /* [<][>][^][v][top][bottom][index][help] */
 272                                                   struct ndr_push *ndrpush,
 273                                                   bool *last)
 274 {
 275         DATA_BLOB comp_chunk;
 276         DATA_BLOB plain_chunk;
 277         uint32_t comp_chunk_offset;
 278         uint32_t plain_chunk_offset;
 279         uint32_t comp_chunk_size;
 280         uint32_t plain_chunk_size;
 281         ssize_t ret;
 282 
 283         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &plain_chunk_size));
 284         if (plain_chunk_size > 0x00010000) {
 285                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION, "Bad XPRESS plain chunk size %08X > 0x00010000 (PULL)", 
 286                                       plain_chunk_size);
 287         }
 288 
 289         NDR_CHECK(ndr_pull_uint32(ndrpull, NDR_SCALARS, &comp_chunk_size));
 290 
 291         comp_chunk_offset = ndrpull->offset;
 292         NDR_CHECK(ndr_pull_advance(ndrpull, comp_chunk_size));
 293         comp_chunk.length = comp_chunk_size;
 294         comp_chunk.data = ndrpull->data + comp_chunk_offset;
 295 
 296         plain_chunk_offset = ndrpush->offset;
 297         NDR_CHECK(ndr_push_zero(ndrpush, plain_chunk_size));
 298         plain_chunk.length = plain_chunk_size;
 299         plain_chunk.data = ndrpush->data + plain_chunk_offset;
 300 
 301         DEBUG(9,("XPRESS plain_chunk_size: %08X (%u) comp_chunk_size: %08X (%u)\n",
 302                  plain_chunk_size, plain_chunk_size, comp_chunk_size, comp_chunk_size));
 303 
 304         /* Uncompressing the buffer using LZ Xpress algorithm */
 305         ret = lzxpress_decompress(comp_chunk.data,
 306                                   comp_chunk.length,
 307                                   plain_chunk.data,
 308                                   plain_chunk.length);
 309         if (ret < 0) {
 310                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 311                                       "XPRESS lzxpress_decompress() returned %d\n",
 312                                       (int)ret);
 313         }
 314         plain_chunk.length = ret;
 315 
 316         if ((plain_chunk_size < 0x00010000) || (ndrpull->offset+4 >= ndrpull->data_size)) {
 317                 /* this is the last chunk */
 318                 *last = true;
 319         }
 320 
 321         return NDR_ERR_SUCCESS;
 322 }
 323 
 324 static enum ndr_err_code ndr_push_compression_xpress_chunk(struct ndr_push *ndrpush,
     /* [<][>][^][v][top][bottom][index][help] */
 325                                                            struct ndr_pull *ndrpull,
 326                                                            bool *last)
 327 {
 328         DATA_BLOB comp_chunk;
 329         uint32_t comp_chunk_size_offset;
 330         DATA_BLOB plain_chunk;
 331         uint32_t plain_chunk_size;
 332         uint32_t plain_chunk_offset;
 333         uint32_t max_plain_size = 0x00010000;
 334         uint32_t max_comp_size = 0x00020000 + 2; /* TODO: use the correct value here */
 335         uint32_t tmp_offset;
 336         ssize_t ret;
 337 
 338         plain_chunk_size = MIN(max_plain_size, ndrpull->data_size - ndrpull->offset);
 339         plain_chunk_offset = ndrpull->offset;
 340         NDR_CHECK(ndr_pull_advance(ndrpull, plain_chunk_size));
 341 
 342         plain_chunk.data = ndrpull->data + plain_chunk_offset;
 343         plain_chunk.length = plain_chunk_size;
 344 
 345         if (plain_chunk_size < max_plain_size) {
 346                 *last = true;
 347         }
 348 
 349         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, plain_chunk_size));
 350         comp_chunk_size_offset = ndrpush->offset;
 351         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, 0xFEFEFEFE));
 352 
 353         NDR_CHECK(ndr_push_expand(ndrpush, max_comp_size));
 354 
 355         comp_chunk.data = ndrpush->data + ndrpush->offset;
 356         comp_chunk.length = max_comp_size;
 357 
 358         /* Compressing the buffer using LZ Xpress algorithm */
 359         ret = lzxpress_compress(plain_chunk.data,
 360                                 plain_chunk.length,
 361                                 comp_chunk.data,
 362                                 comp_chunk.length);
 363         if (ret < 0) {
 364                 return ndr_pull_error(ndrpull, NDR_ERR_COMPRESSION,
 365                                       "XPRESS lzxpress_compress() returned %d\n",
 366                                       (int)ret);
 367         }
 368         comp_chunk.length = ret;
 369 
 370         tmp_offset = ndrpush->offset;
 371         ndrpush->offset = comp_chunk_size_offset;
 372         NDR_CHECK(ndr_push_uint32(ndrpush, NDR_SCALARS, comp_chunk.length));
 373         ndrpush->offset = tmp_offset;
 374 
 375         ndrpush->offset += comp_chunk.length;
 376         return NDR_ERR_SUCCESS;
 377 }
 378 
 379 /*
 380   handle compressed subcontext buffers, which in midl land are user-marshalled, but
 381   we use magic in pidl to make them easier to cope with
 382 */
 383 enum ndr_err_code ndr_pull_compression_start(struct ndr_pull *subndr,
     /* [<][>][^][v][top][bottom][index][help] */
 384                                     struct ndr_pull **_comndr,
 385                                     enum ndr_compression_alg compression_alg,
 386                                     ssize_t decompressed_len)
 387 {
 388         struct ndr_push *ndrpush;
 389         struct ndr_pull *comndr;
 390         DATA_BLOB uncompressed;
 391         bool last = false;
 392         z_stream z;
 393 
 394         ndrpush = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
 395         NDR_ERR_HAVE_NO_MEMORY(ndrpush);
 396 
 397         switch (compression_alg) {
 398         case NDR_COMPRESSION_MSZIP:
 399                 ZERO_STRUCT(z);
 400                 while (!last) {
 401                         NDR_CHECK(ndr_pull_compression_mszip_chunk(subndr, ndrpush, &z, &last));
 402                 }
 403                 break;
 404 
 405         case NDR_COMPRESSION_XPRESS:
 406                 while (!last) {
 407                         NDR_CHECK(ndr_pull_compression_xpress_chunk(subndr, ndrpush, &last));
 408                 }
 409                 break;
 410 
 411         default:
 412                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PULL)",
 413                                       compression_alg);
 414         }
 415 
 416         uncompressed = ndr_push_blob(ndrpush);
 417         if (uncompressed.length != decompressed_len) {
 418                 return ndr_pull_error(subndr, NDR_ERR_COMPRESSION,
 419                                       "Bad uncompressed_len [%u] != [%u](0x%08X) (PULL)",
 420                                       (int)uncompressed.length,
 421                                       (int)decompressed_len,
 422                                       (int)decompressed_len);
 423         }
 424 
 425         comndr = talloc_zero(subndr, struct ndr_pull);
 426         NDR_ERR_HAVE_NO_MEMORY(comndr);
 427         comndr->flags           = subndr->flags;
 428         comndr->current_mem_ctx = subndr->current_mem_ctx;
 429 
 430         comndr->data            = uncompressed.data;
 431         comndr->data_size       = uncompressed.length;
 432         comndr->offset          = 0;
 433 
 434         comndr->iconv_convenience = talloc_reference(comndr, subndr->iconv_convenience);
 435 
 436         *_comndr = comndr;
 437         return NDR_ERR_SUCCESS;
 438 }
 439 
 440 enum ndr_err_code ndr_pull_compression_end(struct ndr_pull *subndr,
     /* [<][>][^][v][top][bottom][index][help] */
 441                                   struct ndr_pull *comndr,
 442                                   enum ndr_compression_alg compression_alg,
 443                                   ssize_t decompressed_len)
 444 {
 445         return NDR_ERR_SUCCESS;
 446 }
 447 
 448 /*
 449   push a compressed subcontext
 450 */
 451 enum ndr_err_code ndr_push_compression_start(struct ndr_push *subndr,
     /* [<][>][^][v][top][bottom][index][help] */
 452                                     struct ndr_push **_uncomndr,
 453                                     enum ndr_compression_alg compression_alg,
 454                                     ssize_t decompressed_len)
 455 {
 456         struct ndr_push *uncomndr;
 457 
 458         switch (compression_alg) {
 459         case NDR_COMPRESSION_MSZIP:
 460         case NDR_COMPRESSION_XPRESS:
 461                 break;
 462         default:
 463                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION,
 464                                       "Bad compression algorithm %d (PUSH)",
 465                                       compression_alg);
 466         }
 467 
 468         uncomndr = ndr_push_init_ctx(subndr, subndr->iconv_convenience);
 469         NDR_ERR_HAVE_NO_MEMORY(uncomndr);
 470         uncomndr->flags = subndr->flags;
 471 
 472         *_uncomndr = uncomndr;
 473         return NDR_ERR_SUCCESS;
 474 }
 475 
 476 /*
 477   push a compressed subcontext
 478 */
 479 enum ndr_err_code ndr_push_compression_end(struct ndr_push *subndr,
     /* [<][>][^][v][top][bottom][index][help] */
 480                                   struct ndr_push *uncomndr,
 481                                   enum ndr_compression_alg compression_alg,
 482                                   ssize_t decompressed_len)
 483 {
 484         struct ndr_pull *ndrpull;
 485         bool last = false;
 486         z_stream z;
 487 
 488         ndrpull = talloc_zero(uncomndr, struct ndr_pull);
 489         NDR_ERR_HAVE_NO_MEMORY(ndrpull);
 490         ndrpull->flags          = uncomndr->flags;
 491         ndrpull->data           = uncomndr->data;
 492         ndrpull->data_size      = uncomndr->offset;
 493         ndrpull->offset         = 0;
 494 
 495         ndrpull->iconv_convenience = talloc_reference(ndrpull, subndr->iconv_convenience);
 496 
 497         switch (compression_alg) {
 498         case NDR_COMPRESSION_MSZIP:
 499                 ZERO_STRUCT(z);
 500                 while (!last) {
 501                         NDR_CHECK(ndr_push_compression_mszip_chunk(subndr, ndrpull, &z, &last));
 502                 }
 503                 break;
 504 
 505         case NDR_COMPRESSION_XPRESS:
 506                 while (!last) {
 507                         NDR_CHECK(ndr_push_compression_xpress_chunk(subndr, ndrpull, &last));
 508                 }
 509                 break;
 510 
 511         default:
 512                 return ndr_push_error(subndr, NDR_ERR_COMPRESSION, "Bad compression algorithm %d (PUSH)", 
 513                                       compression_alg);
 514         }
 515 
 516         talloc_free(uncomndr);
 517         return NDR_ERR_SUCCESS;
 518 }

/* [<][>][^][v][top][bottom][index][help] */