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