xref: /minix3/crypto/external/bsd/netpgp/dist/src/lib/compress.c (revision ebfedea0ce5bbe81e252ddf32d732e40fb633fae)
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