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.21 2010/11/15 08:03:39 agc 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 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(cbinfo->errors, 174 PGP_E_P_DECOMPRESSION_ERROR, 175 "Compressed stream ended before packet end."); 176 } 177 } else if (ret != Z_OK) { 178 (void) fprintf(stderr, "ret=%d\n", ret); 179 PGP_ERROR(cbinfo->errors, 180 PGP_E_P_DECOMPRESSION_ERROR, z->zstream.msg); 181 } 182 z->inflate_ret = ret; 183 } 184 if (z->zstream.next_out <= &z->out[z->offset]) { 185 (void) fprintf(stderr, "Out of memory in buffer\n"); 186 return 0; 187 } 188 len = (size_t)(z->zstream.next_out - &z->out[z->offset]); 189 if (len > length) { 190 len = length; 191 } 192 (void) memcpy(&cdest[cc], &z->out[z->offset], len); 193 z->offset += len; 194 } 195 196 return (int)length; 197 } 198 199 #ifdef HAVE_BZLIB_H 200 /* \todo remove code duplication between this and zlib_compressed_data_reader */ 201 static int 202 bzip2_compressed_data_reader(pgp_stream_t *stream, void *dest, size_t length, 203 pgp_error_t **errors, 204 pgp_reader_t *readinfo, 205 pgp_cbdata_t *cbinfo) 206 { 207 bz_decompress_t *bz = pgp_reader_get_arg(readinfo); 208 size_t len; 209 size_t cc; 210 char *cdest = dest; 211 212 if (bz->type != PGP_C_BZIP2) { 213 (void) fprintf(stderr, "Weird type %d\n", bz->type); 214 return 0; 215 } 216 if (bz->inflate_ret == BZ_STREAM_END && 217 bz->bzstream.next_out == &bz->out[bz->offset]) { 218 return 0; 219 } 220 for (cc = 0 ; cc < length ; cc += len) { 221 if (&bz->out[bz->offset] == bz->bzstream.next_out) { 222 int ret; 223 224 bz->bzstream.next_out = (char *) bz->out; 225 bz->bzstream.avail_out = sizeof(bz->out); 226 bz->offset = 0; 227 if (bz->bzstream.avail_in == 0) { 228 unsigned n = bz->region->length; 229 230 if (!bz->region->indeterminate) { 231 n -= bz->region->readc; 232 if (n > sizeof(bz->in)) 233 n = sizeof(bz->in); 234 } else 235 n = sizeof(bz->in); 236 237 if (!pgp_stacked_limited_read(stream, 238 (uint8_t *) bz->in, 239 n, bz->region, 240 errors, readinfo, cbinfo)) 241 return -1; 242 243 bz->bzstream.next_in = bz->in; 244 bz->bzstream.avail_in = 245 (bz->region->indeterminate) ? 246 bz->region->last_read : n; 247 } 248 ret = BZ2_bzDecompress(&bz->bzstream); 249 if (ret == BZ_STREAM_END) { 250 if (!bz->region->indeterminate && 251 bz->region->readc != bz->region->length) 252 PGP_ERROR(cbinfo->errors, 253 PGP_E_P_DECOMPRESSION_ERROR, 254 "Compressed stream ended before packet end."); 255 } else if (ret != BZ_OK) { 256 PGP_ERROR_1(cbinfo->errors, 257 PGP_E_P_DECOMPRESSION_ERROR, 258 "Invalid return %d from BZ2_bzDecompress", ret); 259 } 260 bz->inflate_ret = ret; 261 } 262 if (bz->bzstream.next_out <= &bz->out[bz->offset]) { 263 (void) fprintf(stderr, "Out of bz memroy\n"); 264 return 0; 265 } 266 len = (size_t)(bz->bzstream.next_out - &bz->out[bz->offset]); 267 if (len > length) { 268 len = length; 269 } 270 (void) memcpy(&cdest[cc], &bz->out[bz->offset], len); 271 bz->offset += len; 272 } 273 274 return (int)length; 275 } 276 #endif 277 278 /** 279 * \ingroup Core_Compress 280 * 281 * \param *region Pointer to a region 282 * \param *stream How to parse 283 * \param type Which compression type to expect 284 */ 285 286 int 287 pgp_decompress(pgp_region_t *region, pgp_stream_t *stream, 288 pgp_compression_type_t type) 289 { 290 z_decompress_t z; 291 #ifdef HAVE_BZLIB_H 292 bz_decompress_t bz; 293 #endif 294 const int printerrors = 1; 295 int ret; 296 297 switch (type) { 298 case PGP_C_ZIP: 299 case PGP_C_ZLIB: 300 (void) memset(&z, 0x0, sizeof(z)); 301 302 z.region = region; 303 z.offset = 0; 304 z.type = type; 305 306 z.zstream.next_in = Z_NULL; 307 z.zstream.avail_in = 0; 308 z.zstream.next_out = z.out; 309 z.zstream.zalloc = Z_NULL; 310 z.zstream.zfree = Z_NULL; 311 z.zstream.opaque = Z_NULL; 312 313 break; 314 315 #ifdef HAVE_BZLIB_H 316 case PGP_C_BZIP2: 317 (void) memset(&bz, 0x0, sizeof(bz)); 318 319 bz.region = region; 320 bz.offset = 0; 321 bz.type = type; 322 323 bz.bzstream.next_in = NULL; 324 bz.bzstream.avail_in = 0; 325 bz.bzstream.next_out = bz.out; 326 bz.bzstream.bzalloc = NULL; 327 bz.bzstream.bzfree = NULL; 328 bz.bzstream.opaque = NULL; 329 #endif 330 331 break; 332 333 default: 334 PGP_ERROR_1(&stream->errors, 335 PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG, 336 "Compression algorithm %d is not yet supported", type); 337 return 0; 338 } 339 340 switch (type) { 341 case PGP_C_ZIP: 342 /* LINTED */ /* this is a lint problem in zlib.h header */ 343 ret = (int)inflateInit2(&z.zstream, -15); 344 break; 345 346 case PGP_C_ZLIB: 347 /* LINTED */ /* this is a lint problem in zlib.h header */ 348 ret = (int)inflateInit(&z.zstream); 349 break; 350 351 #ifdef HAVE_BZLIB_H 352 case PGP_C_BZIP2: 353 ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0); 354 break; 355 #endif 356 357 default: 358 PGP_ERROR_1(&stream->errors, 359 PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG, 360 "Compression algorithm %d is not yet supported", type); 361 return 0; 362 } 363 364 switch (type) { 365 case PGP_C_ZIP: 366 case PGP_C_ZLIB: 367 if (ret != Z_OK) { 368 PGP_ERROR_1(&stream->errors, 369 PGP_E_P_DECOMPRESSION_ERROR, 370 "Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret); 371 return 0; 372 } 373 pgp_reader_push(stream, zlib_compressed_data_reader, 374 NULL, &z); 375 break; 376 377 #ifdef HAVE_BZLIB_H 378 case PGP_C_BZIP2: 379 if (ret != BZ_OK) { 380 PGP_ERROR_1(&stream->errors, 381 PGP_E_P_DECOMPRESSION_ERROR, 382 "Cannot initialise BZIP2 stream for decompression: error=%d", ret); 383 return 0; 384 } 385 pgp_reader_push(stream, bzip2_compressed_data_reader, 386 NULL, &bz); 387 break; 388 #endif 389 390 default: 391 PGP_ERROR_1(&stream->errors, 392 PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG, 393 "Compression algorithm %d is not yet supported", type); 394 return 0; 395 } 396 397 ret = pgp_parse(stream, !printerrors); 398 399 pgp_reader_pop(stream); 400 401 return ret; 402 } 403 404 /** 405 \ingroup Core_WritePackets 406 \brief Writes Compressed packet 407 \param data Data to write out 408 \param len Length of data 409 \param output Write settings 410 \return 1 if OK; else 0 411 */ 412 413 unsigned 414 pgp_writez(pgp_output_t *out, const uint8_t *data, const unsigned len) 415 { 416 compress_t *zip; 417 size_t sz_in; 418 size_t sz_out; 419 int ret; 420 int r = 0; 421 422 /* compress the data */ 423 const int level = Z_DEFAULT_COMPRESSION; /* \todo allow varying 424 * levels */ 425 426 if ((zip = calloc(1, sizeof(*zip))) == NULL) { 427 (void) fprintf(stderr, "pgp_writez: bad alloc\n"); 428 return 0; 429 } 430 zip->stream.zalloc = Z_NULL; 431 zip->stream.zfree = Z_NULL; 432 zip->stream.opaque = NULL; 433 434 /* all other fields set to zero by use of calloc */ 435 436 /* LINTED */ /* this is a lint problem in zlib.h header */ 437 if ((int)deflateInit(&zip->stream, level) != Z_OK) { 438 (void) fprintf(stderr, "pgp_writez: can't initialise\n"); 439 return 0; 440 } 441 /* do necessary transformation */ 442 /* copy input to maintain const'ness of src */ 443 if (zip->src != NULL || zip->dst != NULL) { 444 (void) fprintf(stderr, "pgp_writez: non-null streams\n"); 445 return 0; 446 } 447 448 sz_in = len * sizeof(uint8_t); 449 sz_out = ((101 * sz_in) / 100) + 12; /* from zlib webpage */ 450 if ((zip->src = calloc(1, sz_in)) == NULL) { 451 free(zip); 452 (void) fprintf(stderr, "pgp_writez: bad alloc2\n"); 453 return 0; 454 } 455 if ((zip->dst = calloc(1, sz_out)) == NULL) { 456 free(zip->src); 457 free(zip); 458 (void) fprintf(stderr, "pgp_writez: bad alloc3\n"); 459 return 0; 460 } 461 (void) memcpy(zip->src, data, len); 462 463 /* setup stream */ 464 zip->stream.next_in = zip->src; 465 zip->stream.avail_in = (unsigned)sz_in; 466 zip->stream.total_in = 0; 467 468 zip->stream.next_out = zip->dst; 469 zip->stream.avail_out = (unsigned)sz_out; 470 zip->stream.total_out = 0; 471 472 do { 473 r = deflate(&zip->stream, Z_FINISH); 474 } while (r != Z_STREAM_END); 475 476 /* write it out */ 477 ret = pgp_write_ptag(out, PGP_PTAG_CT_COMPRESSED) && 478 pgp_write_length(out, (unsigned)(zip->stream.total_out + 1))&& 479 pgp_write_scalar(out, PGP_C_ZLIB, 1) && 480 pgp_write(out, zip->dst, (unsigned)zip->stream.total_out); 481 482 free(zip->src); 483 free(zip->dst); 484 free(zip); 485 return ret; 486 } 487