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
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
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
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
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