1 /*- 2 * Copyright (c) 2009 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Alistair Crooks (agc@NetBSD.org) 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 /* 30 * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) 31 * All rights reserved. 32 * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted 33 * their moral rights under the UK Copyright Design and Patents Act 1988 to 34 * be recorded as the authors of this copyright work. 35 * 36 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 37 * use this file except in compliance with the License. 38 * 39 * You may obtain a copy of the License at 40 * http://www.apache.org/licenses/LICENSE-2.0 41 * 42 * Unless required by applicable law or agreed to in writing, software 43 * distributed under the License is distributed on an "AS IS" BASIS, 44 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 * 46 * See the License for the specific language governing permissions and 47 * limitations under the License. 48 */ 49 50 /** \file 51 */ 52 #include "config.h" 53 54 #ifdef HAVE_SYS_CDEFS_H 55 #include <sys/cdefs.h> 56 #endif 57 58 #if defined(__NetBSD__) 59 __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved."); 60 __RCSID("$NetBSD: compress.c,v 1.23 2012/03/05 02:20:18 christos Exp $"); 61 #endif 62 63 #ifdef HAVE_ZLIB_H 64 #include <zlib.h> 65 #endif 66 67 #ifdef HAVE_BZLIB_H 68 #include <bzlib.h> 69 #endif 70 71 #include <string.h> 72 73 #include "packet-parse.h" 74 #include "errors.h" 75 #include "netpgpdefs.h" 76 #include "crypto.h" 77 #include "memory.h" 78 #include "writer.h" 79 80 #define DECOMPRESS_BUFFER 1024 81 82 typedef struct { 83 pgp_compression_type_t type; 84 pgp_region_t *region; 85 uint8_t in[DECOMPRESS_BUFFER]; 86 uint8_t out[DECOMPRESS_BUFFER]; 87 z_stream zstream;/* ZIP and ZLIB */ 88 size_t offset; 89 int inflate_ret; 90 } z_decompress_t; 91 92 #ifdef HAVE_BZLIB_H 93 typedef struct { 94 pgp_compression_type_t type; 95 pgp_region_t *region; 96 char in[DECOMPRESS_BUFFER]; 97 char out[DECOMPRESS_BUFFER]; 98 bz_stream bzstream; /* BZIP2 */ 99 size_t offset; 100 int inflate_ret; 101 } bz_decompress_t; 102 #endif 103 104 typedef struct { 105 z_stream stream; 106 uint8_t *src; 107 uint8_t *dst; 108 } compress_t; 109 110 /* 111 * \todo remove code duplication between this and 112 * bzip2_compressed_data_reader 113 */ 114 static int zlib_compressed_data_reader(pgp_stream_t * stream,void * dest,size_t length,pgp_error_t ** errors,pgp_reader_t * readinfo,pgp_cbdata_t * cbinfo)115 zlib_compressed_data_reader(pgp_stream_t *stream, void *dest, size_t length, 116 pgp_error_t **errors, 117 pgp_reader_t *readinfo, 118 pgp_cbdata_t *cbinfo) 119 { 120 z_decompress_t *z = pgp_reader_get_arg(readinfo); 121 size_t len; 122 size_t cc; 123 char *cdest = dest; 124 125 if (z->type != PGP_C_ZIP && z->type != PGP_C_ZLIB) { 126 (void) fprintf(stderr, 127 "zlib_compressed_data_reader: weird type %d\n", 128 z->type); 129 return 0; 130 } 131 132 if (z->inflate_ret == Z_STREAM_END && 133 z->zstream.next_out == &z->out[z->offset]) { 134 return 0; 135 } 136 if (pgp_get_debug_level(__FILE__)) { 137 (void) fprintf(stderr, 138 "zlib_compressed_data_reader: length %" PRIsize "d\n", 139 length); 140 } 141 for (cc = 0 ; cc < length ; cc += len) { 142 if (&z->out[z->offset] == z->zstream.next_out) { 143 int ret; 144 145 z->zstream.next_out = z->out; 146 z->zstream.avail_out = sizeof(z->out); 147 z->offset = 0; 148 if (z->zstream.avail_in == 0) { 149 unsigned n = z->region->length; 150 151 if (!z->region->indeterminate) { 152 n -= z->region->readc; 153 if (n > sizeof(z->in)) { 154 n = sizeof(z->in); 155 } 156 } else { 157 n = sizeof(z->in); 158 } 159 if (!pgp_stacked_limited_read(stream, z->in, n, 160 z->region, 161 errors, readinfo, cbinfo)) { 162 return -1; 163 } 164 165 z->zstream.next_in = z->in; 166 z->zstream.avail_in = (z->region->indeterminate) ? 167 z->region->last_read : n; 168 } 169 ret = inflate(&z->zstream, Z_SYNC_FLUSH); 170 if (ret == Z_STREAM_END) { 171 if (!z->region->indeterminate && 172 z->region->readc != z->region->length) { 173 PGP_ERROR_1(cbinfo->errors, 174 PGP_E_P_DECOMPRESSION_ERROR, 175 "%s", 176 "Compressed stream ended before packet end."); 177 } 178 } else if (ret != Z_OK) { 179 (void) fprintf(stderr, "ret=%d\n", ret); 180 PGP_ERROR_1(cbinfo->errors, 181 PGP_E_P_DECOMPRESSION_ERROR, "%s", 182 z->zstream.msg); 183 } 184 z->inflate_ret = ret; 185 } 186 if (z->zstream.next_out <= &z->out[z->offset]) { 187 (void) fprintf(stderr, "Out of memory in buffer\n"); 188 return 0; 189 } 190 len = (size_t)(z->zstream.next_out - &z->out[z->offset]); 191 if (len > length) { 192 len = length; 193 } 194 (void) memcpy(&cdest[cc], &z->out[z->offset], len); 195 z->offset += len; 196 } 197 198 return (int)length; 199 } 200 201 #ifdef HAVE_BZLIB_H 202 /* \todo remove code duplication between this and zlib_compressed_data_reader */ 203 static int bzip2_compressed_data_reader(pgp_stream_t * stream,void * dest,size_t length,pgp_error_t ** errors,pgp_reader_t * readinfo,pgp_cbdata_t * cbinfo)204 bzip2_compressed_data_reader(pgp_stream_t *stream, void *dest, size_t length, 205 pgp_error_t **errors, 206 pgp_reader_t *readinfo, 207 pgp_cbdata_t *cbinfo) 208 { 209 bz_decompress_t *bz = pgp_reader_get_arg(readinfo); 210 size_t len; 211 size_t cc; 212 char *cdest = dest; 213 214 if (bz->type != PGP_C_BZIP2) { 215 (void) fprintf(stderr, "Weird type %d\n", bz->type); 216 return 0; 217 } 218 if (bz->inflate_ret == BZ_STREAM_END && 219 bz->bzstream.next_out == &bz->out[bz->offset]) { 220 return 0; 221 } 222 for (cc = 0 ; cc < length ; cc += len) { 223 if (&bz->out[bz->offset] == bz->bzstream.next_out) { 224 int ret; 225 226 bz->bzstream.next_out = (char *) bz->out; 227 bz->bzstream.avail_out = sizeof(bz->out); 228 bz->offset = 0; 229 if (bz->bzstream.avail_in == 0) { 230 unsigned n = bz->region->length; 231 232 if (!bz->region->indeterminate) { 233 n -= bz->region->readc; 234 if (n > sizeof(bz->in)) 235 n = sizeof(bz->in); 236 } else 237 n = sizeof(bz->in); 238 239 if (!pgp_stacked_limited_read(stream, 240 (uint8_t *) bz->in, 241 n, bz->region, 242 errors, readinfo, cbinfo)) 243 return -1; 244 245 bz->bzstream.next_in = bz->in; 246 bz->bzstream.avail_in = 247 (bz->region->indeterminate) ? 248 bz->region->last_read : n; 249 } 250 ret = BZ2_bzDecompress(&bz->bzstream); 251 if (ret == BZ_STREAM_END) { 252 if (!bz->region->indeterminate && 253 bz->region->readc != bz->region->length) 254 PGP_ERROR_1(cbinfo->errors, 255 PGP_E_P_DECOMPRESSION_ERROR, 256 "%s", 257 "Compressed stream ended before packet end."); 258 } else if (ret != BZ_OK) { 259 PGP_ERROR_1(cbinfo->errors, 260 PGP_E_P_DECOMPRESSION_ERROR, 261 "Invalid return %d from BZ2_bzDecompress", ret); 262 } 263 bz->inflate_ret = ret; 264 } 265 if (bz->bzstream.next_out <= &bz->out[bz->offset]) { 266 (void) fprintf(stderr, "Out of bz memroy\n"); 267 return 0; 268 } 269 len = (size_t)(bz->bzstream.next_out - &bz->out[bz->offset]); 270 if (len > length) { 271 len = length; 272 } 273 (void) memcpy(&cdest[cc], &bz->out[bz->offset], len); 274 bz->offset += len; 275 } 276 277 return (int)length; 278 } 279 #endif 280 281 /** 282 * \ingroup Core_Compress 283 * 284 * \param *region Pointer to a region 285 * \param *stream How to parse 286 * \param type Which compression type to expect 287 */ 288 289 int pgp_decompress(pgp_region_t * region,pgp_stream_t * stream,pgp_compression_type_t type)290 pgp_decompress(pgp_region_t *region, pgp_stream_t *stream, 291 pgp_compression_type_t type) 292 { 293 z_decompress_t z; 294 #ifdef HAVE_BZLIB_H 295 bz_decompress_t bz; 296 #endif 297 const int printerrors = 1; 298 int ret; 299 300 switch (type) { 301 case PGP_C_ZIP: 302 case PGP_C_ZLIB: 303 (void) memset(&z, 0x0, sizeof(z)); 304 305 z.region = region; 306 z.offset = 0; 307 z.type = type; 308 309 z.zstream.next_in = Z_NULL; 310 z.zstream.avail_in = 0; 311 z.zstream.next_out = z.out; 312 z.zstream.zalloc = Z_NULL; 313 z.zstream.zfree = Z_NULL; 314 z.zstream.opaque = Z_NULL; 315 316 break; 317 318 #ifdef HAVE_BZLIB_H 319 case PGP_C_BZIP2: 320 (void) memset(&bz, 0x0, sizeof(bz)); 321 322 bz.region = region; 323 bz.offset = 0; 324 bz.type = type; 325 326 bz.bzstream.next_in = NULL; 327 bz.bzstream.avail_in = 0; 328 bz.bzstream.next_out = bz.out; 329 bz.bzstream.bzalloc = NULL; 330 bz.bzstream.bzfree = NULL; 331 bz.bzstream.opaque = NULL; 332 #endif 333 334 break; 335 336 default: 337 PGP_ERROR_1(&stream->errors, 338 PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG, 339 "Compression algorithm %d is not yet supported", type); 340 return 0; 341 } 342 343 switch (type) { 344 case PGP_C_ZIP: 345 /* LINTED */ /* this is a lint problem in zlib.h header */ 346 ret = (int)inflateInit2(&z.zstream, -15); 347 break; 348 349 case PGP_C_ZLIB: 350 /* LINTED */ /* this is a lint problem in zlib.h header */ 351 ret = (int)inflateInit(&z.zstream); 352 break; 353 354 #ifdef HAVE_BZLIB_H 355 case PGP_C_BZIP2: 356 ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0); 357 break; 358 #endif 359 360 default: 361 PGP_ERROR_1(&stream->errors, 362 PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG, 363 "Compression algorithm %d is not yet supported", type); 364 return 0; 365 } 366 367 switch (type) { 368 case PGP_C_ZIP: 369 case PGP_C_ZLIB: 370 if (ret != Z_OK) { 371 PGP_ERROR_1(&stream->errors, 372 PGP_E_P_DECOMPRESSION_ERROR, 373 "Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret); 374 return 0; 375 } 376 pgp_reader_push(stream, zlib_compressed_data_reader, 377 NULL, &z); 378 break; 379 380 #ifdef HAVE_BZLIB_H 381 case PGP_C_BZIP2: 382 if (ret != BZ_OK) { 383 PGP_ERROR_1(&stream->errors, 384 PGP_E_P_DECOMPRESSION_ERROR, 385 "Cannot initialise BZIP2 stream for decompression: error=%d", ret); 386 return 0; 387 } 388 pgp_reader_push(stream, bzip2_compressed_data_reader, 389 NULL, &bz); 390 break; 391 #endif 392 393 default: 394 PGP_ERROR_1(&stream->errors, 395 PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG, 396 "Compression algorithm %d is not yet supported", type); 397 return 0; 398 } 399 400 ret = pgp_parse(stream, !printerrors); 401 402 pgp_reader_pop(stream); 403 404 return ret; 405 } 406 407 /** 408 \ingroup Core_WritePackets 409 \brief Writes Compressed packet 410 \param data Data to write out 411 \param len Length of data 412 \param output Write settings 413 \return 1 if OK; else 0 414 */ 415 416 unsigned pgp_writez(pgp_output_t * out,const uint8_t * data,const unsigned len)417 pgp_writez(pgp_output_t *out, const uint8_t *data, const unsigned len) 418 { 419 compress_t *zip; 420 size_t sz_in; 421 size_t sz_out; 422 int ret; 423 int r = 0; 424 425 /* compress the data */ 426 const int level = Z_DEFAULT_COMPRESSION; /* \todo allow varying 427 * levels */ 428 429 if ((zip = calloc(1, sizeof(*zip))) == NULL) { 430 (void) fprintf(stderr, "pgp_writez: bad alloc\n"); 431 return 0; 432 } 433 zip->stream.zalloc = Z_NULL; 434 zip->stream.zfree = Z_NULL; 435 zip->stream.opaque = NULL; 436 437 /* all other fields set to zero by use of calloc */ 438 439 /* LINTED */ /* this is a lint problem in zlib.h header */ 440 if ((int)deflateInit(&zip->stream, level) != Z_OK) { 441 (void) fprintf(stderr, "pgp_writez: can't initialise\n"); 442 return 0; 443 } 444 /* do necessary transformation */ 445 /* copy input to maintain const'ness of src */ 446 if (zip->src != NULL || zip->dst != NULL) { 447 (void) fprintf(stderr, "pgp_writez: non-null streams\n"); 448 return 0; 449 } 450 451 sz_in = len * sizeof(uint8_t); 452 sz_out = ((101 * sz_in) / 100) + 12; /* from zlib webpage */ 453 if ((zip->src = calloc(1, sz_in)) == NULL) { 454 free(zip); 455 (void) fprintf(stderr, "pgp_writez: bad alloc2\n"); 456 return 0; 457 } 458 if ((zip->dst = calloc(1, sz_out)) == NULL) { 459 free(zip->src); 460 free(zip); 461 (void) fprintf(stderr, "pgp_writez: bad alloc3\n"); 462 return 0; 463 } 464 (void) memcpy(zip->src, data, len); 465 466 /* setup stream */ 467 zip->stream.next_in = zip->src; 468 zip->stream.avail_in = (unsigned)sz_in; 469 zip->stream.total_in = 0; 470 471 zip->stream.next_out = zip->dst; 472 zip->stream.avail_out = (unsigned)sz_out; 473 zip->stream.total_out = 0; 474 475 do { 476 r = deflate(&zip->stream, Z_FINISH); 477 } while (r != Z_STREAM_END); 478 479 /* write it out */ 480 ret = pgp_write_ptag(out, PGP_PTAG_CT_COMPRESSED) && 481 pgp_write_length(out, (unsigned)(zip->stream.total_out + 1))&& 482 pgp_write_scalar(out, PGP_C_ZLIB, 1) && 483 pgp_write(out, zip->dst, (unsigned)zip->stream.total_out); 484 485 free(zip->src); 486 free(zip->dst); 487 free(zip); 488 return ret; 489 } 490