2 * Copyright (c) 2006-2010 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>
39 __SEC_CFTYPE(SecIdentity
)
41 namespace CodeSigning
{
43 using namespace UnixPlusPlus
;
47 // A helper for parsing out a CFDictionary signing-data specification
49 class SecCodeSigner::Parser
: CFDictionary
{
51 Parser(SecCodeSigner
&signer
, CFDictionaryRef parameters
);
53 bool getBool(CFStringRef key
) const
55 if (CFBooleanRef flag
= get
<CFBooleanRef
>(key
))
56 return flag
== kCFBooleanTrue
;
64 // Construct a SecCodeSigner
66 SecCodeSigner::SecCodeSigner(SecCSFlags flags
)
67 : mOpFlags(flags
), mDigestAlgorithm(kSecCodeSignatureDefaultDigestAlgorithm
)
73 // Clean up a SecCodeSigner
75 SecCodeSigner::~SecCodeSigner() throw()
83 // Parse an input parameter dictionary and set ready-to-use parameters
85 void SecCodeSigner::parameters(CFDictionaryRef paramDict
)
87 Parser(*this, paramDict
);
89 MacOSError::throwMe(errSecCSInvalidObjectRef
);
94 // Roughly check for validity.
95 // This isn't thorough; it just sees if if looks like we've set up the object appropriately.
97 bool SecCodeSigner::valid() const
99 if (mOpFlags
& kSecCSRemoveSignature
)
108 void SecCodeSigner::sign(SecStaticCode
*code
, SecCSFlags flags
)
110 if (code
->isSigned() && (flags
& kSecCSSignPreserveSignature
))
112 Signer
operation(*this, code
);
113 if ((flags
| mOpFlags
) & kSecCSRemoveSignature
) {
114 secdebug("signer", "%p will remove signature from %p", this, code
);
115 operation
.remove(flags
);
118 MacOSError::throwMe(errSecCSInvalidObjectRef
);
119 secdebug("signer", "%p will sign %p (flags 0x%x)", this, code
, flags
);
120 operation
.sign(flags
);
122 code
->resetValidity();
127 // ReturnDetachedSignature is called by writers or editors that try to return
128 // detached signature data (rather than annotate the target).
130 void SecCodeSigner::returnDetachedSignature(BlobCore
*blob
, Signer
&signer
)
133 if (CFGetTypeID(mDetached
) == CFURLGetTypeID()) {
134 // URL to destination file
135 AutoFileDesc
fd(cfString(CFURLRef(mDetached
.get())), O_WRONLY
| O_CREAT
| O_TRUNC
);
137 } else if (CFGetTypeID(mDetached
) == CFDataGetTypeID()) {
138 CFDataAppendBytes(CFMutableDataRef(mDetached
.get()),
139 (const UInt8
*)blob
, blob
->length());
140 } else if (CFGetTypeID(mDetached
) == CFNullGetTypeID()) {
141 SignatureDatabaseWriter db
;
142 db
.storeCode(blob
, signer
.path().c_str());
149 // Our DiskRep::signingContext methods communicate with the signing subsystem
150 // in terms those callers can easily understand.
152 string
SecCodeSigner::sdkPath(const std::string
&path
) const
154 assert(path
[0] == '/'); // need absolute path here
156 return cfString(mSDKRoot
) + path
;
161 bool SecCodeSigner::isAdhoc() const
163 return mSigner
== SecIdentityRef(kCFNull
);
166 SecCSFlags
SecCodeSigner::signingFlags() const
173 // The actual parsing operation is done in the Parser class.
175 // Note that we need to copy or retain all incoming data. The caller has no requirement
176 // to keep the parameters dictionary around.
178 SecCodeSigner::Parser::Parser(SecCodeSigner
&state
, CFDictionaryRef parameters
)
179 : CFDictionary(parameters
, errSecCSBadDictionaryFormat
)
181 // the signer may be an identity or null
182 state
.mSigner
= SecIdentityRef(get
<CFTypeRef
>(kSecCodeSignerIdentity
));
184 if (CFGetTypeID(state
.mSigner
) != SecIdentityGetTypeID() && !CFEqual(state
.mSigner
, kCFNull
))
185 MacOSError::throwMe(errSecCSInvalidObjectRef
);
187 // the flags need some augmentation
188 if (CFNumberRef flags
= get
<CFNumberRef
>(kSecCodeSignerFlags
)) {
189 state
.mCdFlagsGiven
= true;
190 state
.mCdFlags
= cfNumber
<uint32_t>(flags
);
192 state
.mCdFlagsGiven
= false;
194 // digest algorithms are specified as a numeric code
195 if (CFNumberRef digestAlgorithm
= get
<CFNumberRef
>(kSecCodeSignerDigestAlgorithm
))
196 state
.mDigestAlgorithm
= cfNumber
<unsigned int>(digestAlgorithm
);
198 if (CFNumberRef cmsSize
= get
<CFNumberRef
>(CFSTR("cmssize")))
199 state
.mCMSSize
= cfNumber
<size_t>(cmsSize
);
201 state
.mCMSSize
= 9000; // likely big enough
203 // metadata preservation options
204 if (CFNumberRef preserve
= get
<CFNumberRef
>(kSecCodeSignerPreserveMetadata
)) {
205 state
.mPreserveMetadata
= cfNumber
<uint32_t>(preserve
);
207 state
.mPreserveMetadata
= 0;
210 // signing time can be a CFDateRef or null
211 if (CFTypeRef time
= get
<CFTypeRef
>(kSecCodeSignerSigningTime
)) {
212 if (CFGetTypeID(time
) == CFDateGetTypeID() || time
== kCFNull
)
213 state
.mSigningTime
= CFDateRef(time
);
215 MacOSError::throwMe(errSecCSInvalidObjectRef
);
218 if (CFStringRef ident
= get
<CFStringRef
>(kSecCodeSignerIdentifier
))
219 state
.mIdentifier
= cfString(ident
);
221 if (CFStringRef prefix
= get
<CFStringRef
>(kSecCodeSignerIdentifierPrefix
))
222 state
.mIdentifierPrefix
= cfString(prefix
);
224 // Requirements can be binary or string (to be compiled).
225 // We must pass them along to the signer for possible text substitution
226 if (CFTypeRef reqs
= get
<CFTypeRef
>(kSecCodeSignerRequirements
)) {
227 if (CFGetTypeID(reqs
) == CFDataGetTypeID() || CFGetTypeID(reqs
) == CFStringGetTypeID())
228 state
.mRequirements
= reqs
;
230 MacOSError::throwMe(errSecCSInvalidObjectRef
);
232 state
.mRequirements
= NULL
;
234 state
.mNoMachO
= getBool(CFSTR("no-macho"));
236 state
.mPageSize
= get
<CFNumberRef
>(kSecCodeSignerPageSize
);
238 // detached can be (destination) file URL or (mutable) Data to be appended-to
239 if ((state
.mDetached
= get
<CFTypeRef
>(kSecCodeSignerDetached
))) {
240 CFTypeID type
= CFGetTypeID(state
.mDetached
);
241 if (type
!= CFURLGetTypeID() && type
!= CFDataGetTypeID() && type
!= CFNullGetTypeID())
242 MacOSError::throwMe(errSecCSInvalidObjectRef
);
245 state
.mDryRun
= getBool(kSecCodeSignerDryRun
);
247 state
.mResourceRules
= get
<CFDictionaryRef
>(kSecCodeSignerResourceRules
);
249 state
.mApplicationData
= get
<CFDataRef
>(kSecCodeSignerApplicationData
);
250 state
.mEntitlementData
= get
<CFDataRef
>(kSecCodeSignerEntitlements
);
252 state
.mSDKRoot
= get
<CFURLRef
>(kSecCodeSignerSDKRoot
);
254 if (CFBooleanRef timestampRequest
= get
<CFBooleanRef
>(kSecCodeSignerRequireTimestamp
)) {
255 state
.mWantTimeStamp
= timestampRequest
== kCFBooleanTrue
;
256 } else { // pick default
257 state
.mWantTimeStamp
= false;
258 if (state
.mSigner
&& state
.mSigner
!= SecIdentityRef(kCFNull
)) {
259 CFRef
<SecCertificateRef
> signerCert
;
260 MacOSError::check(SecIdentityCopyCertificate(state
.mSigner
, &signerCert
.aref()));
261 if (certificateHasField(signerCert
, devIdLeafMarkerOID
))
262 state
.mWantTimeStamp
= true;
265 state
.mTimestampAuthentication
= get
<SecIdentityRef
>(kSecCodeSignerTimestampAuthentication
);
266 state
.mTimestampService
= get
<CFURLRef
>(kSecCodeSignerTimestampServer
);
267 state
.mNoTimeStampCerts
= getBool(kSecCodeSignerTimestampOmitCertificates
);
271 } // end namespace CodeSigning
272 } // end namespace Security