2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // CodeSigner - SecCodeSigner API objects
27 #include "CodeSigner.h"
29 #include "csdatabase.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>
41 __SEC_CFTYPE(SecIdentity
)
43 namespace CodeSigning
{
45 using namespace UnixPlusPlus
;
49 // A helper for parsing out a CFDictionary signing-data specification
51 class SecCodeSigner::Parser
: CFDictionary
{
53 Parser(SecCodeSigner
&signer
, CFDictionaryRef parameters
);
55 bool getBool(CFStringRef key
) const
57 if (CFBooleanRef flag
= get
<CFBooleanRef
>(key
))
58 return flag
== kCFBooleanTrue
;
63 uint32_t parseRuntimeVersion(std::string
& runtime
)
66 char* cursor
= const_cast<char*>(runtime
.c_str());
67 char* end
= cursor
+ runtime
.length();
70 int component_shift
= 16;
72 // x should convert to 0x00XX0000
73 // x.y should convert to 0x00XXYY00
74 // x.y.z should covert to 0x00XXYYZZ
75 // 0, 0.0, and 0.0.0 are rejected
76 // anything else should be rejected
77 while (cursor
< end
) {
80 component
= strtol(cursor
, &nxt
, 10);
83 (component
< 0 || component
> UINT8_MAX
)) {
84 secdebug("signer", "Runtime version: %s is invalid", runtime
.c_str());
85 MacOSError::throwMe(errSecCSInvalidRuntimeVersion
);
87 version
|= (component
& 0xff) << component_shift
;
94 if (*nxt
!= '.' || component_shift
< 0 || (nxt
+ 1) == end
) {
95 // Catch a trailing "."
96 secdebug("signer", "Runtime version: %s is invalid", runtime
.c_str());
97 MacOSError::throwMe(errSecCSInvalidRuntimeVersion
);
103 secdebug("signer","Runtime version: %s is a version of zero", runtime
.c_str());
104 MacOSError::throwMe(errSecCSInvalidRuntimeVersion
);
113 // Construct a SecCodeSigner
115 SecCodeSigner::SecCodeSigner(SecCSFlags flags
)
116 : mOpFlags(flags
), mLimitedAsync(NULL
), mRuntimeVersionOverride(0)
122 // Clean up a SecCodeSigner
124 SecCodeSigner::~SecCodeSigner() throw()
126 delete mLimitedAsync
;
133 // Parse an input parameter dictionary and set ready-to-use parameters
135 void SecCodeSigner::parameters(CFDictionaryRef paramDict
)
137 Parser(*this, paramDict
);
139 MacOSError::throwMe(errSecCSInvalidObjectRef
);
143 // Retrieve the team ID from the signing certificate if and only if
144 // it is an apple developer signing cert
146 std::string
SecCodeSigner::getTeamIDFromSigner(CFArrayRef certs
)
148 if (mSigner
&& mSigner
!= SecIdentityRef(kCFNull
)) {
149 CFRef
<SecCertificateRef
> signerCert
;
150 MacOSError::check(SecIdentityCopyCertificate(mSigner
, &signerCert
.aref()));
152 /* Make sure the certificate looks like an Apple certificate, because we do not
153 extract the team ID from a non Apple certificate */
154 if (SecStaticCode::isAppleDeveloperCert(certs
)) {
155 CFRef
<CFStringRef
> teamIDFromCert
;
157 MacOSError::check(SecCertificateCopySubjectComponent(signerCert
.get(), &CSSMOID_OrganizationalUnitName
, &teamIDFromCert
.aref()));
160 return cfString(teamIDFromCert
);
168 // Roughly check for validity.
169 // This isn't thorough; it just sees if if looks like we've set up the object appropriately.
171 bool SecCodeSigner::valid() const
173 if (mOpFlags
& (kSecCSRemoveSignature
| kSecCSEditSignature
)) {
183 void SecCodeSigner::sign(SecStaticCode
*code
, SecCSFlags flags
)
185 code
->setValidationFlags(flags
);
186 if (code
->isSigned() && (flags
& kSecCSSignPreserveSignature
))
188 Signer
operation(*this, code
);
189 if ((flags
| mOpFlags
) & kSecCSRemoveSignature
) {
190 secinfo("signer", "%p will remove signature from %p", this, code
);
191 operation
.remove(flags
);
192 } else if ((flags
| mOpFlags
) & kSecCSEditSignature
) {
193 secinfo("signer", "%p will edit signature of %p", this, code
);
194 operation
.edit(flags
);
197 MacOSError::throwMe(errSecCSInvalidObjectRef
);
198 secinfo("signer", "%p will sign %p (flags 0x%x)", this, code
, flags
);
199 operation
.sign(flags
);
201 code
->resetValidity();
206 // ReturnDetachedSignature is called by writers or editors that try to return
207 // detached signature data (rather than annotate the target).
209 void SecCodeSigner::returnDetachedSignature(BlobCore
*blob
, Signer
&signer
)
212 if (CFGetTypeID(mDetached
) == CFURLGetTypeID()) {
213 // URL to destination file
214 AutoFileDesc
fd(cfString(CFURLRef(mDetached
.get())), O_WRONLY
| O_CREAT
| O_TRUNC
);
216 } else if (CFGetTypeID(mDetached
) == CFDataGetTypeID()) {
217 CFDataAppendBytes(CFMutableDataRef(mDetached
.get()),
218 (const UInt8
*)blob
, blob
->length());
219 } else if (CFGetTypeID(mDetached
) == CFNullGetTypeID()) {
220 SignatureDatabaseWriter db
;
221 db
.storeCode(blob
, signer
.path().c_str());
228 // The actual parsing operation is done in the Parser class.
230 // Note that we need to copy or retain all incoming data. The caller has no requirement
231 // to keep the parameters dictionary around.
233 SecCodeSigner::Parser::Parser(SecCodeSigner
&state
, CFDictionaryRef parameters
)
234 : CFDictionary(parameters
, errSecCSBadDictionaryFormat
)
236 CFNumberRef editCpuType
= get
<CFNumberRef
>(kSecCodeSignerEditCpuType
);
237 CFNumberRef editCpuSubtype
= get
<CFNumberRef
>(kSecCodeSignerEditCpuSubtype
);
238 if (editCpuType
!= NULL
&& editCpuSubtype
!= NULL
) {
239 state
.mEditArch
= Architecture(cfNumber
<uint32_t>(editCpuType
),
240 cfNumber
<uint32_t>(editCpuSubtype
));
243 state
.mEditCMS
= get
<CFDataRef
>(kSecCodeSignerEditCMS
);
245 state
.mDryRun
= getBool(kSecCodeSignerDryRun
);
247 state
.mSDKRoot
= get
<CFURLRef
>(kSecCodeSignerSDKRoot
);
249 state
.mPreserveAFSC
= getBool(kSecCodeSignerPreserveAFSC
);
251 if (state
.mOpFlags
& kSecCSEditSignature
) {
253 /* Everything below this point is irrelevant for
254 * Signature Editing, which does not create any
255 * parts of the signature, only replaces them.
259 // the signer may be an identity or null
260 state
.mSigner
= SecIdentityRef(get
<CFTypeRef
>(kSecCodeSignerIdentity
));
262 if (CFGetTypeID(state
.mSigner
) != SecIdentityGetTypeID() && !CFEqual(state
.mSigner
, kCFNull
))
263 MacOSError::throwMe(errSecCSInvalidObjectRef
);
265 // the flags need some augmentation
266 if (CFNumberRef flags
= get
<CFNumberRef
>(kSecCodeSignerFlags
)) {
267 state
.mCdFlagsGiven
= true;
268 state
.mCdFlags
= cfNumber
<uint32_t>(flags
);
270 state
.mCdFlagsGiven
= false;
272 // digest algorithms are specified as a numeric code
273 if (CFCopyRef
<CFTypeRef
> digestAlgorithms
= get
<CFTypeRef
>(kSecCodeSignerDigestAlgorithm
)) {
274 CFRef
<CFArrayRef
> array
= cfArrayize(digestAlgorithms
);
275 CFToVector
<CodeDirectory::HashAlgorithm
, CFNumberRef
, cfNumber
<CodeDirectory::HashAlgorithm
> > digests(array
);
276 std::copy(digests
.begin(), digests
.end(), std::inserter(state
.mDigestAlgorithms
, state
.mDigestAlgorithms
.begin()));
279 if (CFNumberRef cmsSize
= get
<CFNumberRef
>(CFSTR("cmssize")))
280 state
.mCMSSize
= cfNumber
<size_t>(cmsSize
);
282 state
.mCMSSize
= 18000; // big enough for now, not forever.
284 // metadata preservation options
285 if (CFNumberRef preserve
= get
<CFNumberRef
>(kSecCodeSignerPreserveMetadata
)) {
286 state
.mPreserveMetadata
= cfNumber
<uint32_t>(preserve
);
288 state
.mPreserveMetadata
= 0;
290 // signing time can be a CFDateRef or null
291 if (CFTypeRef time
= get
<CFTypeRef
>(kSecCodeSignerSigningTime
)) {
292 if (CFGetTypeID(time
) == CFDateGetTypeID() || time
== kCFNull
)
293 state
.mSigningTime
= CFDateRef(time
);
295 MacOSError::throwMe(errSecCSInvalidObjectRef
);
298 if (CFStringRef ident
= get
<CFStringRef
>(kSecCodeSignerIdentifier
))
299 state
.mIdentifier
= cfString(ident
);
301 if (CFStringRef teamid
= get
<CFStringRef
>(kSecCodeSignerTeamIdentifier
))
302 state
.mTeamID
= cfString(teamid
);
304 if (CFNumberRef platform
= get
<CFNumberRef
>(kSecCodeSignerPlatformIdentifier
)) {
305 int64_t ident
= cfNumber
<int64_t>(platform
);
306 if (ident
< 0 || ident
> maxPlatform
) // overflow
307 MacOSError::throwMe(errSecCSInvalidPlatform
);
308 state
.mPlatform
= ident
;
311 if (CFStringRef prefix
= get
<CFStringRef
>(kSecCodeSignerIdentifierPrefix
))
312 state
.mIdentifierPrefix
= cfString(prefix
);
314 // Requirements can be binary or string (to be compiled).
315 // We must pass them along to the signer for possible text substitution
316 if (CFTypeRef reqs
= get
<CFTypeRef
>(kSecCodeSignerRequirements
)) {
317 if (CFGetTypeID(reqs
) == CFDataGetTypeID() || CFGetTypeID(reqs
) == CFStringGetTypeID())
318 state
.mRequirements
= reqs
;
320 MacOSError::throwMe(errSecCSInvalidObjectRef
);
322 state
.mRequirements
= NULL
;
324 state
.mNoMachO
= getBool(CFSTR("no-macho"));
326 state
.mPageSize
= get
<CFNumberRef
>(kSecCodeSignerPageSize
);
328 // detached can be (destination) file URL or (mutable) Data to be appended-to
329 if ((state
.mDetached
= get
<CFTypeRef
>(kSecCodeSignerDetached
))) {
330 CFTypeID type
= CFGetTypeID(state
.mDetached
);
331 if (type
!= CFURLGetTypeID() && type
!= CFDataGetTypeID() && type
!= CFNullGetTypeID())
332 MacOSError::throwMe(errSecCSInvalidObjectRef
);
335 state
.mResourceRules
= get
<CFDictionaryRef
>(kSecCodeSignerResourceRules
);
337 state
.mApplicationData
= get
<CFDataRef
>(kSecCodeSignerApplicationData
);
338 state
.mEntitlementData
= get
<CFDataRef
>(kSecCodeSignerEntitlements
);
340 if (CFBooleanRef timestampRequest
= get
<CFBooleanRef
>(kSecCodeSignerRequireTimestamp
)) {
341 state
.mWantTimeStamp
= timestampRequest
== kCFBooleanTrue
;
342 } else { // pick default
343 state
.mWantTimeStamp
= false;
344 if (state
.mSigner
&& state
.mSigner
!= SecIdentityRef(kCFNull
)) {
345 CFRef
<SecCertificateRef
> signerCert
;
346 MacOSError::check(SecIdentityCopyCertificate(state
.mSigner
, &signerCert
.aref()));
347 if (certificateHasField(signerCert
, devIdLeafMarkerOID
))
348 state
.mWantTimeStamp
= true;
351 state
.mTimestampAuthentication
= get
<SecIdentityRef
>(kSecCodeSignerTimestampAuthentication
);
352 state
.mTimestampService
= get
<CFURLRef
>(kSecCodeSignerTimestampServer
);
353 state
.mNoTimeStampCerts
= getBool(kSecCodeSignerTimestampOmitCertificates
);
355 if (CFStringRef runtimeVersionOverride
= get
<CFStringRef
>(kSecCodeSignerRuntimeVersion
)) {
356 std::string runtime
= cfString(runtimeVersionOverride
);
357 if (runtime
.empty()) {
358 MacOSError::throwMe(errSecCSInvalidRuntimeVersion
);
360 state
.mRuntimeVersionOverride
= parseRuntimeVersion(runtime
);
363 // Don't add the adhoc flag, even if no signer identity was specified.
364 // Useful for editing in the CMS at a later point.
365 state
.mOmitAdhocFlag
= getBool(kSecCodeSignerOmitAdhocFlag
);
369 } // end namespace CodeSigning
370 } // end namespace Security