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