1/*
2 * Copyright (c) 2005-2007 Rob Braun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Rob Braun nor the names of his contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * 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 * 03-Apr-2005
31 * DRI: Rob Braun <bbraun@synack.net>
32 */
33/*
34 * Portions Copyright 2006, Apple Computer, Inc.
35 * Christopher Ryan <ryanc@apple.com>
36*/
37
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sys/types.h>
43#include <zlib.h>
44#ifdef __APPLE__
45#include <CommonCrypto/CommonDigest.h>
46#include <CommonCrypto/CommonDigestSPI.h>
47#else
48#include <openssl/evp.h>
49#endif
50
51#include "xar.h"
52#include "hash.h"
53#include "config.h"
54#ifndef HAVE_ASPRINTF
55#include "asprintf.h"
56#endif
57
58
59#pragma mark Hash Wrapper Object
60
61#ifdef __APPLE__
62
63CCDigestRef digestRef_from_name(const char* name, unsigned int *outHashSize) {
64    CCDigestRef result = NULL;
65
66    if (NULL != outHashSize)
67        *outHashSize = 0;
68
69	if (0 == strcasecmp(name, "sha512")) {
70        result = CCDigestCreate(kCCDigestSHA512);
71        if (NULL != outHashSize)
72            *outHashSize = CC_SHA512_DIGEST_LENGTH;
73    } else if (0 == strcasecmp(name, "sha256")) {
74        result = CCDigestCreate(kCCDigestSHA256);
75        if (NULL != outHashSize)
76            *outHashSize = CC_SHA256_DIGEST_LENGTH;
77    } else if (0 == strcasecmp(name, "sha") || !strcasecmp(name, "sha1")) {
78        result = CCDigestCreate(kCCDigestSHA1);
79        if (NULL != outHashSize)
80            *outHashSize = CC_SHA1_DIGEST_LENGTH;
81    } else if (0 == strcasecmp(name, "md5")) {
82        result = CCDigestCreate(kCCDigestMD5);
83        if (NULL != outHashSize)
84            *outHashSize = CC_MD5_DIGEST_LENGTH;
85    } else if (0 == strcasecmp(name, "md2")) {
86        result = CCDigestCreate(kCCDigestMD2);
87        if (NULL != outHashSize)
88            *outHashSize = CC_MD2_DIGEST_LENGTH;
89    }
90
91    return result;
92
93}
94#endif // __APPLE__
95
96
97struct __xar_hash_t {
98	const char *digest_name;
99	void *context;
100#ifdef __APPLE__
101	CCDigestRef digest;
102#else
103	EVP_MD_CTX digest;
104	const EVP_MD *type;
105#endif
106	unsigned int length;
107};
108
109#define HASH_CTX(x) ((struct __xar_hash_t *)(x))
110
111xar_hash_t xar_hash_new(const char *digest_name, void *context) {
112	struct __xar_hash_t *hash = calloc(1, sizeof(struct __xar_hash_t));
113	if( ! hash )
114		return NULL; // errno will already be set
115
116	if( context )
117		HASH_CTX(hash)->context = context;
118
119#ifdef __APPLE__
120	HASH_CTX(hash)->digest = digestRef_from_name(digest_name, &HASH_CTX(hash)->length);
121#else
122	OpenSSL_add_all_digests();
123	HASH_CTX(hash)->type = EVP_get_digestbyname(digest_name);
124	EVP_DigestInit(&HASH_CTX(hash)->digest, HASH_CTX(hash)->type);
125#endif
126
127	HASH_CTX(hash)->digest_name = strdup(digest_name);
128
129	return hash;
130}
131
132void *xar_hash_get_context(xar_hash_t hash) {
133	return HASH_CTX(hash)->context;
134}
135
136const char *xar_hash_get_digest_name(xar_hash_t hash) {
137	return HASH_CTX(hash)->digest_name;
138}
139
140void xar_hash_update(xar_hash_t hash, void *buffer, size_t nbyte) {
141#ifdef __APPLE__
142	CCDigestUpdate(HASH_CTX(hash)->digest, buffer, nbyte);
143#else
144	EVP_DigestUpdate(&HASH_CTX(hash)->digest, buffer, nbyte);
145#endif
146}
147
148void *xar_hash_finish(xar_hash_t hash, size_t *nbyte) {
149#ifdef __APPLE__
150	void *buffer = calloc(1, CC_SHA512_DIGEST_LENGTH); // current biggest digest size  This is what OpenSSL uses
151#else
152	void *buffer = calloc(1, EVP_MAX_MD_SIZE);
153#endif
154	if( ! buffer )
155		return NULL;
156
157#ifdef __APPLE__
158	CCDigestFinal(HASH_CTX(hash)->digest, buffer);
159	CCDigestDestroy(HASH_CTX(hash)->digest);
160#else
161	EVP_DigestFinal(&HASH_CTX(hash)->digest, buffer, &HASH_CTX(hash)->length);
162#endif
163
164	*nbyte = HASH_CTX(hash)->length;
165	free((void *)HASH_CTX(hash)->digest_name);
166	free((void *)hash);
167	return buffer;
168}
169
170#undef HASH_CTX
171
172
173#pragma mark datamod
174
175struct _hash_context {
176	xar_hash_t archived;
177	xar_hash_t unarchived;
178	uint64_t count;
179};
180
181#define CONTEXT(x) ((struct _hash_context *)(*x))
182
183static char *_xar_format_hash(const unsigned char* m,unsigned int len) {
184	char *result = malloc((2*len)+1);
185	char hexValue[3];
186	unsigned int itr = 0;
187
188	result[0] = '\0';
189
190	for(itr = 0;itr < len;itr++) {
191		sprintf(hexValue,"%02x",m[itr]);
192		strncat(result,hexValue,2);
193	}
194
195	return result;
196}
197
198int32_t xar_hash_toheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
199	return xar_hash_fromheap_out(x,f,p,*in,*inlen,context);
200}
201
202int32_t xar_hash_fromheap_out(xar_t x, xar_file_t f, xar_prop_t p, void *in, size_t inlen, void **context) {
203	const char *opt;
204	xar_prop_t tmpp;
205
206	opt = NULL;
207	tmpp = xar_prop_pget(p, "extracted-checksum");
208	if( tmpp )
209		opt = xar_attr_pget(f, tmpp, "style");
210
211	if( !opt )
212		opt = xar_opt_get(x, XAR_OPT_FILECKSUM);
213
214	if( !opt || (0 == strcmp(opt, XAR_OPT_VAL_NONE) ) )
215		return 0;
216
217	if(!CONTEXT(context)) {
218		*context = calloc(1, sizeof(struct _hash_context));
219		if( ! *context )
220			return -1;
221	}
222
223	if( ! CONTEXT(context)->unarchived ) {
224		CONTEXT(context)->unarchived = xar_hash_new(opt, NULL);
225		if( ! CONTEXT(context)->unarchived ) {
226			free(*context);
227			*context = NULL;
228			return -1;
229		}
230	}
231
232	if( inlen == 0 )
233		return 0;
234
235	CONTEXT(context)->count += inlen;
236	xar_hash_update(CONTEXT(context)->unarchived, in, inlen);
237	return 0;
238}
239
240int32_t xar_hash_fromheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
241	return xar_hash_toheap_out(x,f,p,*in,*inlen,context);
242}
243
244int32_t xar_hash_toheap_out(xar_t x, xar_file_t f, xar_prop_t p, void *in, size_t inlen, void **context) {
245	const char *opt;
246	xar_prop_t tmpp;
247
248	opt = NULL;
249	tmpp = xar_prop_pget(p, "archived-checksum");
250	if( tmpp )
251		opt = xar_attr_pget(f, tmpp, "style");
252
253	if( !opt )
254		opt = xar_opt_get(x, XAR_OPT_FILECKSUM);
255
256	if( !opt || (0 == strcmp(opt, XAR_OPT_VAL_NONE) ) )
257		return 0;
258
259	if( ! CONTEXT(context) ) {
260		*context = calloc(1, sizeof(struct _hash_context));
261		if( ! *context )
262			return -1;
263	}
264
265	if( ! CONTEXT(context)->archived ) {
266		CONTEXT(context)->archived = xar_hash_new(opt, NULL);
267		if( ! CONTEXT(context)->archived ) {
268			free(*context);
269			*context = NULL;
270			return -1;
271		}
272	}
273
274	if( inlen == 0 )
275		return 0;
276
277	CONTEXT(context)->count += inlen;
278	xar_hash_update(CONTEXT(context)->archived, in, inlen);
279	return 0;
280}
281
282int32_t xar_hash_toheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
283	const char *archived_style = NULL, *unarchived_style = NULL;
284	size_t archived_length = -1, unarchived_length = -1;
285	void *archived_hash = NULL, *unarchived_hash = NULL;
286
287	if( ! CONTEXT(context) )
288		return 0;
289	else if( CONTEXT(context)->count == 0 )
290		goto DONE;
291
292	archived_style = strdup(xar_hash_get_digest_name(CONTEXT(context)->archived));
293	unarchived_style = strdup(xar_hash_get_digest_name(CONTEXT(context)->unarchived));
294
295	archived_hash = xar_hash_finish(CONTEXT(context)->archived, &archived_length);
296	unarchived_hash = xar_hash_finish(CONTEXT(context)->unarchived, &unarchived_length);
297
298	char *str;
299	xar_prop_t tmpp;
300
301	str = _xar_format_hash(archived_hash, archived_length);
302	if( f ) {
303		tmpp = xar_prop_pset(f, p, "archived-checksum", str);
304		if( tmpp )
305			xar_attr_pset(f, tmpp, "style", archived_style);
306	}
307	free(str);
308
309	str = _xar_format_hash(unarchived_hash, unarchived_length);
310	if( f ) {
311		tmpp = xar_prop_pset(f, p, "unarchived-checksum", str);
312		if( tmpp )
313			xar_attr_pset(f, tmpp, "style", unarchived_style);
314	}
315	free(str);
316
317DONE:
318	free((void *)archived_style);
319	free((void *)unarchived_style);
320
321	free(archived_hash);
322	free(unarchived_hash);
323
324	free(*context);
325	*context = NULL;
326
327	return 0;
328}
329
330int32_t xar_hash_fromheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
331	if(!CONTEXT(context))
332		return 0;
333
334	int32_t result = 0;
335    const char *archived_hash = NULL, *archived_style = NULL;
336
337	// Fetch the existing hash from the archive
338	if( CONTEXT(context)->archived ) {
339		xar_prop_t tmpp = xar_prop_pget(p, "archived-checksum");
340		if( tmpp ) {
341			archived_style = xar_attr_pget(f, tmpp, "style");
342			archived_hash = xar_prop_getvalue(tmpp);
343		}
344
345		// We have the fetched hash; now get the calculated hash
346		if( archived_hash && archived_style ) {
347			size_t calculated_length = -1;
348			const char *calculated_style = strdup(xar_hash_get_digest_name(CONTEXT(context)->archived));
349			void *calculated_buffer = xar_hash_finish(CONTEXT(context)->archived, &calculated_length);
350			char *calculated_hash = _xar_format_hash(calculated_buffer, calculated_length);
351			free(calculated_buffer);
352
353			// Compare
354			int hash_match = ( strcmp(archived_hash, calculated_hash) == 0 );
355			int style_match = (strcmp(archived_style, calculated_style) == 0 );
356
357			if( ! hash_match || ! style_match ) {
358				xar_err_new(x);
359				xar_err_set_file(x, f);
360				xar_err_set_formatted_string(x, "archived-checksum %s's do not match", archived_style);
361				xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION);
362				result = -1;
363			}
364
365			free((void *)calculated_style);
366			free(calculated_hash);
367		}
368	}
369
370	// Clean up the unarchived hash as well, if we have one
371	if( CONTEXT(context)->unarchived ) {
372		size_t length = -1;
373		void *hash = xar_hash_finish(CONTEXT(context)->unarchived, &length);
374		free(hash);
375	}
376
377	if(*context) {
378		free(*context);
379		*context = NULL;
380	}
381
382	return result;
383}
384