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