1/* 2 * Copyright (c) 2006-2014 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// CodeSigner - SecCodeSigner API objects 26// 27#include "CodeSigner.h" 28#include "signer.h" 29#include "csdatabase.h" 30#include "drmaker.h" 31#include "csutilities.h" 32#include <security_utilities/unix++.h> 33#include <security_utilities/unixchild.h> 34#include <Security/SecCertificate.h> 35#include <Security/SecCertificatePriv.h> 36#include <vector> 37 38namespace Security { 39 40__SEC_CFTYPE(SecIdentity) 41 42namespace CodeSigning { 43 44using namespace UnixPlusPlus; 45 46 47// 48// A helper for parsing out a CFDictionary signing-data specification 49// 50class SecCodeSigner::Parser : CFDictionary { 51public: 52 Parser(SecCodeSigner &signer, CFDictionaryRef parameters); 53 54 bool getBool(CFStringRef key) const 55 { 56 if (CFBooleanRef flag = get<CFBooleanRef>(key)) 57 return flag == kCFBooleanTrue; 58 else 59 return false; 60 } 61}; 62 63 64// 65// Construct a SecCodeSigner 66// 67SecCodeSigner::SecCodeSigner(SecCSFlags flags) 68 : mOpFlags(flags), mDigestAlgorithm(kSecCodeSignatureDefaultDigestAlgorithm) 69{ 70} 71 72 73// 74// Clean up a SecCodeSigner 75// 76SecCodeSigner::~SecCodeSigner() throw() 77try { 78} catch (...) { 79 return; 80} 81 82 83// 84// Parse an input parameter dictionary and set ready-to-use parameters 85// 86void SecCodeSigner::parameters(CFDictionaryRef paramDict) 87{ 88 Parser(*this, paramDict); 89 if (!valid()) 90 MacOSError::throwMe(errSecCSInvalidObjectRef); 91} 92 93// 94// Retrieve the team ID from the signing certificate if and only if 95// it is an apple developer signing cert 96// 97std::string SecCodeSigner::getTeamIDFromSigner(CFArrayRef certs) 98{ 99 if (mSigner && mSigner != SecIdentityRef(kCFNull)) { 100 CFRef<SecCertificateRef> signerCert; 101 MacOSError::check(SecIdentityCopyCertificate(mSigner, &signerCert.aref())); 102 103 /* Make sure the certificate looks like an Apple certificate, because we do not 104 extract the team ID from a non Apple certificate */ 105 if (SecStaticCode::isAppleDeveloperCert(certs)) { 106 CFRef<CFStringRef> teamIDFromCert; 107 108 MacOSError::check(SecCertificateCopySubjectComponent(signerCert.get(), &CSSMOID_OrganizationalUnitName, &teamIDFromCert.aref())); 109 110 if (teamIDFromCert) 111 return cfString(teamIDFromCert); 112 } 113 } 114 115 return ""; 116} 117 118// 119// Roughly check for validity. 120// This isn't thorough; it just sees if if looks like we've set up the object appropriately. 121// 122bool SecCodeSigner::valid() const 123{ 124 if (mOpFlags & kSecCSRemoveSignature) 125 return true; 126 return mSigner; 127} 128 129 130// 131// Sign code 132// 133void SecCodeSigner::sign(SecStaticCode *code, SecCSFlags flags) 134{ 135 code->setValidationFlags(flags); 136 if (code->isSigned() && (flags & kSecCSSignPreserveSignature)) 137 return; 138 Signer operation(*this, code); 139 if ((flags | mOpFlags) & kSecCSRemoveSignature) { 140 secdebug("signer", "%p will remove signature from %p", this, code); 141 operation.remove(flags); 142 } else { 143 if (!valid()) 144 MacOSError::throwMe(errSecCSInvalidObjectRef); 145 secdebug("signer", "%p will sign %p (flags 0x%x)", this, code, flags); 146 operation.sign(flags); 147 } 148 code->resetValidity(); 149} 150 151 152// 153// ReturnDetachedSignature is called by writers or editors that try to return 154// detached signature data (rather than annotate the target). 155// 156void SecCodeSigner::returnDetachedSignature(BlobCore *blob, Signer &signer) 157{ 158 assert(mDetached); 159 if (CFGetTypeID(mDetached) == CFURLGetTypeID()) { 160 // URL to destination file 161 AutoFileDesc fd(cfString(CFURLRef(mDetached.get())), O_WRONLY | O_CREAT | O_TRUNC); 162 fd.writeAll(*blob); 163 } else if (CFGetTypeID(mDetached) == CFDataGetTypeID()) { 164 CFDataAppendBytes(CFMutableDataRef(mDetached.get()), 165 (const UInt8 *)blob, blob->length()); 166 } else if (CFGetTypeID(mDetached) == CFNullGetTypeID()) { 167 SignatureDatabaseWriter db; 168 db.storeCode(blob, signer.path().c_str()); 169 } else 170 assert(false); 171} 172 173 174// 175// Our DiskRep::signingContext methods communicate with the signing subsystem 176// in terms those callers can easily understand. 177// 178string SecCodeSigner::sdkPath(const std::string &path) const 179{ 180 assert(path[0] == '/'); // need absolute path here 181 if (mSDKRoot) 182 return cfString(mSDKRoot) + path; 183 else 184 return path; 185} 186 187bool SecCodeSigner::isAdhoc() const 188{ 189 return mSigner == SecIdentityRef(kCFNull); 190} 191 192SecCSFlags SecCodeSigner::signingFlags() const 193{ 194 return mOpFlags; 195} 196 197 198// 199// The actual parsing operation is done in the Parser class. 200// 201// Note that we need to copy or retain all incoming data. The caller has no requirement 202// to keep the parameters dictionary around. 203// 204SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters) 205 : CFDictionary(parameters, errSecCSBadDictionaryFormat) 206{ 207 // the signer may be an identity or null 208 state.mSigner = SecIdentityRef(get<CFTypeRef>(kSecCodeSignerIdentity)); 209 if (state.mSigner) 210 if (CFGetTypeID(state.mSigner) != SecIdentityGetTypeID() && !CFEqual(state.mSigner, kCFNull)) 211 MacOSError::throwMe(errSecCSInvalidObjectRef); 212 213 // the flags need some augmentation 214 if (CFNumberRef flags = get<CFNumberRef>(kSecCodeSignerFlags)) { 215 state.mCdFlagsGiven = true; 216 state.mCdFlags = cfNumber<uint32_t>(flags); 217 } else 218 state.mCdFlagsGiven = false; 219 220 // digest algorithms are specified as a numeric code 221 if (CFNumberRef digestAlgorithm = get<CFNumberRef>(kSecCodeSignerDigestAlgorithm)) 222 state.mDigestAlgorithm = cfNumber<unsigned int>(digestAlgorithm); 223 224 if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize"))) 225 state.mCMSSize = cfNumber<size_t>(cmsSize); 226 else 227 state.mCMSSize = 9000; // likely big enough 228 229 // metadata preservation options 230 if (CFNumberRef preserve = get<CFNumberRef>(kSecCodeSignerPreserveMetadata)) { 231 state.mPreserveMetadata = cfNumber<uint32_t>(preserve); 232 } else 233 state.mPreserveMetadata = 0; 234 235 236 // signing time can be a CFDateRef or null 237 if (CFTypeRef time = get<CFTypeRef>(kSecCodeSignerSigningTime)) { 238 if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull) 239 state.mSigningTime = CFDateRef(time); 240 else 241 MacOSError::throwMe(errSecCSInvalidObjectRef); 242 } 243 244 if (CFStringRef ident = get<CFStringRef>(kSecCodeSignerIdentifier)) 245 state.mIdentifier = cfString(ident); 246 247 if (CFStringRef teamid = get<CFStringRef>(kSecCodeSignerTeamIdentifier)) 248 state.mTeamID = cfString(teamid); 249 250 if (CFStringRef prefix = get<CFStringRef>(kSecCodeSignerIdentifierPrefix)) 251 state.mIdentifierPrefix = cfString(prefix); 252 253 // Requirements can be binary or string (to be compiled). 254 // We must pass them along to the signer for possible text substitution 255 if (CFTypeRef reqs = get<CFTypeRef>(kSecCodeSignerRequirements)) { 256 if (CFGetTypeID(reqs) == CFDataGetTypeID() || CFGetTypeID(reqs) == CFStringGetTypeID()) 257 state.mRequirements = reqs; 258 else 259 MacOSError::throwMe(errSecCSInvalidObjectRef); 260 } else 261 state.mRequirements = NULL; 262 263 state.mNoMachO = getBool(CFSTR("no-macho")); 264 265 state.mPageSize = get<CFNumberRef>(kSecCodeSignerPageSize); 266 267 // detached can be (destination) file URL or (mutable) Data to be appended-to 268 if ((state.mDetached = get<CFTypeRef>(kSecCodeSignerDetached))) { 269 CFTypeID type = CFGetTypeID(state.mDetached); 270 if (type != CFURLGetTypeID() && type != CFDataGetTypeID() && type != CFNullGetTypeID()) 271 MacOSError::throwMe(errSecCSInvalidObjectRef); 272 } 273 274 state.mDryRun = getBool(kSecCodeSignerDryRun); 275 276 state.mResourceRules = get<CFDictionaryRef>(kSecCodeSignerResourceRules); 277 278 state.mApplicationData = get<CFDataRef>(kSecCodeSignerApplicationData); 279 state.mEntitlementData = get<CFDataRef>(kSecCodeSignerEntitlements); 280 281 state.mSDKRoot = get<CFURLRef>(kSecCodeSignerSDKRoot); 282 283 if (CFBooleanRef timestampRequest = get<CFBooleanRef>(kSecCodeSignerRequireTimestamp)) { 284 state.mWantTimeStamp = timestampRequest == kCFBooleanTrue; 285 } else { // pick default 286 state.mWantTimeStamp = false; 287 if (state.mSigner && state.mSigner != SecIdentityRef(kCFNull)) { 288 CFRef<SecCertificateRef> signerCert; 289 MacOSError::check(SecIdentityCopyCertificate(state.mSigner, &signerCert.aref())); 290 if (certificateHasField(signerCert, devIdLeafMarkerOID)) 291 state.mWantTimeStamp = true; 292 } 293 } 294 state.mTimestampAuthentication = get<SecIdentityRef>(kSecCodeSignerTimestampAuthentication); 295 state.mTimestampService = get<CFURLRef>(kSecCodeSignerTimestampServer); 296 state.mNoTimeStampCerts = getBool(kSecCodeSignerTimestampOmitCertificates); 297} 298 299 300} // end namespace CodeSigning 301} // end namespace Security 302