xar++.cpp
  1  /*
  2   * Copyright (c) 2011-2012 Apple Inc. All Rights Reserved.
  3   * 
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  //
 25  // xar++ - interface to XAR-format archive files
 26  //
 27  #include "xar++.h"
 28  #include "notarization.h"
 29  #include <security_utilities/cfutilities.h>
 30  #include <Security/Security.h>
 31  
 32  
 33  namespace Security {
 34  namespace CodeSigning {
 35  
 36  
 37  Xar::Xar(const char *path)
 38  {
 39  	mXar = 0;
 40  	mSigCMS = 0;
 41  	mSigClassic = 0;
 42  	if (path)
 43  		open(path);
 44  }
 45  
 46  void Xar::open(const char *path)
 47  {
 48  	if ((mXar = ::xar_open(path, READ)) == NULL)
 49  	    return;
 50  
 51  	mPath = std::string(path);
 52      
 53  	xar_signature_t sig = ::xar_signature_first(mXar);
 54  	// read signatures until we find a CMS signature
 55  	while (sig && mSigCMS == NULL) {
 56  		const char *type = ::xar_signature_type(sig);
 57  		if (strcmp(type, "CMS") == 0) {
 58  			mSigCMS = sig;
 59  		} else if (strcmp(type, "RSA") == 0) {
 60  			mSigClassic = sig;
 61  		}
 62  		sig = ::xar_signature_next(sig);
 63  	}
 64  }
 65  
 66  Xar::~Xar()
 67  {
 68  	if (mXar)
 69  		::xar_close(mXar);
 70  }
 71  
 72  static CFArrayRef copyCertChainFromSignature(xar_signature_t sig)
 73  {
 74  	unsigned count = xar_signature_get_x509certificate_count(sig);
 75  	CFRef<CFMutableArrayRef> certs = makeCFMutableArray(0);
 76  	for (unsigned ix = 0; ix < count; ix++) {
 77  		const uint8_t *data;
 78  		uint32_t length;
 79  		if (xar_signature_get_x509certificate_data(sig, ix, &data, &length) == 0) {
 80  			CFTempData cdata(data, length);
 81  			CFRef<SecCertificateRef> cert = SecCertificateCreateWithData(NULL, cdata);
 82  			CFArrayAppendValue(certs, cert.get());
 83  		}
 84  	}
 85  	return certs.yield();
 86  }
 87  
 88  CFArrayRef Xar::copyCertChain()
 89  {
 90  	if (mSigCMS)
 91  		return copyCertChainFromSignature(mSigCMS);
 92  	else if (mSigClassic)
 93  		return copyCertChainFromSignature(mSigClassic);
 94  	return NULL;
 95  }
 96  
 97  void Xar::registerStapledNotarization()
 98  {
 99  	registerStapledTicketInPackage(mPath);
100  }
101  
102  CFDataRef Xar::createPackageChecksum()
103  {
104  	xar_signature_t sig = NULL;
105  
106  	// Always prefer a CMS signature to a class signature and return early
107  	// if no appropriate signature has been found.
108  	if (mSigCMS) {
109  		sig = mSigCMS;
110  	} else if (mSigClassic) {
111  		sig = mSigClassic;
112  	} else {
113  		return NULL;
114  	}
115  
116  	// Extract the signed data from the xar, which is actually just the checksum
117  	// we use as an identifying hash.
118  	uint8_t *data = NULL;
119  	uint32_t length;
120  	if (xar_signature_copy_signed_data(sig, &data, &length, NULL, NULL, NULL) != 0) {
121  		secerror("Unable to extract package hash for package: %s", mPath.c_str());
122  		return NULL;
123  	}
124  
125  	// xar_signature_copy_signed_data returns malloc'd data that can be used without copying
126  	// but must be free'd properly later.
127  	return makeCFDataMalloc(data, length);
128  }
129  
130  SecCSDigestAlgorithm Xar::checksumDigestAlgorithm()
131  {
132  	int32_t error = 0;
133  	const char* value = NULL;
134  	unsigned long size = 0;
135  
136  	if (mXar == NULL) {
137  		secerror("Evaluating checksum digest on bad xar: %s", mPath.c_str());
138  		return kSecCodeSignatureNoHash;
139  	}
140  
141  	error = xar_prop_get((xar_file_t)mXar, "checksum/size", &value);
142  	if (error == -1) {
143  		secerror("Unable to extract package checksum size: %s", mPath.c_str());
144  		return kSecCodeSignatureNoHash;
145  	}
146  
147  	size = strtoul(value, NULL, 10);
148  	switch (size) {
149  		case CC_SHA1_DIGEST_LENGTH:
150  			return kSecCodeSignatureHashSHA1;
151  		case CC_SHA256_DIGEST_LENGTH:
152  			return kSecCodeSignatureHashSHA256;
153  		case CC_SHA512_DIGEST_LENGTH:
154  			return kSecCodeSignatureHashSHA512;
155  		case CC_MD5_DIGEST_LENGTH:
156  		default:
157  			return kSecCodeSignatureNoHash;
158  	}
159  }
160  
161  } // end namespace CodeSigning
162  } // end namespace Security