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