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 // signer - Signing operation supervisor and controller
28 #include "resources.h"
29 #include "signerutils.h"
30 #include "SecCodeSigner.h"
31 #include <Security/SecIdentity.h>
32 #include <Security/CMSEncoder.h>
33 #include <Security/CMSPrivate.h>
34 #include <Security/CSCommonPriv.h>
35 #include <CoreFoundation/CFBundlePriv.h>
38 #include "csutilities.h"
39 #include <security_utilities/unix++.h>
40 #include <security_utilities/unixchild.h>
41 #include <security_utilities/cfmunge.h>
44 namespace CodeSigning
{
50 void SecCodeSigner::Signer::sign(SecCSFlags flags
)
52 rep
= code
->diskRep()->base();
54 PreSigningContext
context(*this);
55 if (Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage()) {
56 signMachO(fat
, context
);
58 signArchitectureAgnostic(context
);
64 // Remove any existing code signature from code
66 void SecCodeSigner::Signer::remove(SecCSFlags flags
)
68 // can't remove a detached signature
70 MacOSError::throwMe(errSecCSNotSupported
);
72 rep
= code
->diskRep();
73 if (Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage()) {
74 // architecture-sensitive removal
75 MachOEditor
editor(rep
->writer(), *fat
, kSecCodeSignatureNoHash
, rep
->mainExecutablePath());
76 editor
.allocate(); // create copy
77 editor
.commit(); // commit change
79 // architecture-agnostic removal
80 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
88 // Contemplate the object-to-be-signed and set up the Signer state accordingly.
90 void SecCodeSigner::Signer::prepare(SecCSFlags flags
)
92 // get the Info.plist out of the rep for some creative defaulting
93 CFRef
<CFDictionaryRef
> infoDict
;
94 if (CFRef
<CFDataRef
> infoData
= rep
->component(cdInfoSlot
))
95 infoDict
.take(makeCFDictionaryFrom(infoData
));
97 // work out the canonical identifier
98 identifier
= state
.mIdentifier
;
99 if (identifier
.empty()) {
100 identifier
= rep
->recommendedIdentifier(state
);
101 if (identifier
.find('.') == string::npos
)
102 identifier
= state
.mIdentifierPrefix
+ identifier
;
103 if (identifier
.find('.') == string::npos
&& state
.isAdhoc())
104 identifier
= identifier
+ "-" + uniqueName();
105 secdebug("signer", "using default identifier=%s", identifier
.c_str());
107 secdebug("signer", "using explicit identifier=%s", identifier
.c_str());
109 // work out the CodeDirectory flags word
110 if (state
.mCdFlagsGiven
) {
111 cdFlags
= state
.mCdFlags
;
112 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags
);
116 if (CFTypeRef csflags
= CFDictionaryGetValue(infoDict
, CFSTR("CSFlags"))) {
117 if (CFGetTypeID(csflags
) == CFNumberGetTypeID()) {
118 cdFlags
= cfNumber
<uint32_t>(CFNumberRef(csflags
));
119 secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags
);
120 } else if (CFGetTypeID(csflags
) == CFStringGetTypeID()) {
121 cdFlags
= cdTextFlags(cfString(CFStringRef(csflags
)));
122 secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags
);
124 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
127 if (state
.mSigner
== SecIdentityRef(kCFNull
)) // ad-hoc signing requested...
128 cdFlags
|= kSecCodeSignatureAdhoc
; // ... so note that
130 // prepare the resource directory, if any
131 string rpath
= rep
->resourcesRootPath();
132 if (!rpath
.empty()) {
133 // explicitly given resource rules always win
134 CFCopyRef
<CFDictionaryRef
> resourceRules
= state
.mResourceRules
;
136 // embedded resource rules come next
137 if (!resourceRules
&& infoDict
)
138 if (CFTypeRef spec
= CFDictionaryGetValue(infoDict
, _kCFBundleResourceSpecificationKey
)) {
139 if (CFGetTypeID(spec
) == CFStringGetTypeID())
140 if (CFRef
<CFDataRef
> data
= cfLoadFile(rpath
+ "/" + cfString(CFStringRef(spec
))))
141 if (CFDictionaryRef dict
= makeCFDictionaryFrom(data
))
142 resourceRules
.take(dict
);
143 if (!resourceRules
) // embedded rules present but unacceptable
144 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
147 // finally, ask the DiskRep for its default
149 resourceRules
.take(rep
->defaultResourceRules(state
));
151 // build the resource directory
152 ResourceBuilder
resources(rpath
, cfget
<CFDictionaryRef
>(resourceRules
, "rules"), digestAlgorithm());
153 rep
->adjustResources(resources
); // DiskRep-specific adjustments
154 CFRef
<CFDictionaryRef
> rdir
= resources
.build();
155 resourceDirectory
.take(CFPropertyListCreateXMLData(NULL
, rdir
));
158 // screen and set the signing time
159 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
160 if (state
.mSigningTime
== CFDateRef(kCFNull
)) {
161 signingTime
= 0; // no time at all
162 } else if (!state
.mSigningTime
) {
163 signingTime
= now
; // default
165 CFAbsoluteTime time
= CFDateGetAbsoluteTime(state
.mSigningTime
);
166 if (time
> now
) // not allowed to post-date a signature
167 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
171 pagesize
= state
.mPageSize
? cfNumber
<size_t>(state
.mPageSize
) : rep
->pageSize(state
);
173 // Timestamping setup
174 CFRef
<SecIdentityRef
> mTSAuth
; // identity for client-side authentication to the Timestamp server
179 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
180 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
181 // treat them as architectural binaries containing (only) one architecture - that
182 // interpretation is courtesy of the Universal/MachO support classes.
184 void SecCodeSigner::Signer::signMachO(Universal
*fat
, const Requirement::Context
&context
)
186 // Mach-O executable at the core - perform multi-architecture signing
187 auto_ptr
<ArchEditor
> editor(state
.mDetached
188 ? static_cast<ArchEditor
*>(new BlobEditor(*fat
, *this))
189 : new MachOEditor(rep
->writer(), *fat
, this->digestAlgorithm(), rep
->mainExecutablePath()));
190 assert(editor
->count() > 0);
191 if (!editor
->attribute(writerNoGlobal
)) // can store architecture-common components
194 // pass 1: prepare signature blobs and calculate sizes
195 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
196 MachOEditor::Arch
&arch
= *it
->second
;
197 arch
.source
.reset(fat
->architecture(it
->first
));
198 arch
.ireqs(state
.mRequirements
, rep
->defaultRequirements(&arch
.architecture
, state
), context
);
199 if (editor
->attribute(writerNoGlobal
)) // can't store globally, add per-arch
201 populate(arch
.cdbuilder
, arch
, arch
.ireqs
,
202 arch
.source
->offset(), arch
.source
->signingExtent());
204 // add identification blob (made from this architecture) only if we're making a detached signature
205 if (state
.mDetached
) {
206 CFRef
<CFDataRef
> identification
= MachORep::identificationFor(arch
.source
.get());
207 arch
.add(cdIdentificationSlot
, BlobWrapper::alloc(
208 CFDataGetBytePtr(identification
), CFDataGetLength(identification
)));
211 // prepare SuperBlob size estimate
212 size_t cdSize
= arch
.cdbuilder
.size();
213 arch
.blobSize
= arch
.size(cdSize
, state
.mCMSSize
, 0);
218 // pass 2: Finish and generate signatures, and write them
219 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
220 MachOEditor::Arch
&arch
= *it
->second
;
223 // finish CodeDirectory (off new binary) and sign it
224 CodeDirectory
*cd
= arch
.cdbuilder
.build();
225 CFRef
<CFDataRef
> signature
= signCodeDirectory(cd
);
227 // complete the SuperBlob
228 arch
.add(cdCodeDirectorySlot
, cd
); // takes ownership
229 arch
.add(cdSignatureSlot
, BlobWrapper::alloc(
230 CFDataGetBytePtr(signature
), CFDataGetLength(signature
)));
231 if (!state
.mDryRun
) {
232 EmbeddedSignatureBlob
*blob
= arch
.make();
233 editor
->write(arch
, blob
); // takes ownership of blob
237 // done: write edit copy back over the original
244 // Sign a binary that has no notion of architecture.
245 // That currently means anything that isn't Mach-O format.
247 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
&context
)
249 // non-Mach-O executable - single-instance signing
250 RefPointer
<DiskRep::Writer
> writer
= state
.mDetached
?
251 (new DetachedBlobWriter(*this)) : rep
->writer();
252 CodeDirectory::Builder
builder(state
.mDigestAlgorithm
);
253 InternalRequirements ireqs
;
254 ireqs(state
.mRequirements
, rep
->defaultRequirements(NULL
, state
), context
);
256 populate(builder
, *writer
, ireqs
, rep
->signingBase(), rep
->signingLimit());
258 // add identification blob (made from this architecture) only if we're making a detached signature
259 if (state
.mDetached
) {
260 CFRef
<CFDataRef
> identification
= rep
->identification();
261 writer
->component(cdIdentificationSlot
, identification
);
264 CodeDirectory
*cd
= builder
.build();
265 CFRef
<CFDataRef
> signature
= signCodeDirectory(cd
);
266 if (!state
.mDryRun
) {
267 writer
->codeDirectory(cd
);
268 writer
->signature(signature
);
276 // Global populate - send components to destination buffers ONCE
278 void SecCodeSigner::Signer::populate(DiskRep::Writer
&writer
)
280 if (resourceDirectory
)
281 writer
.component(cdResourceDirSlot
, resourceDirectory
);
286 // Per-architecture populate - send components to per-architecture buffers
287 // and populate the CodeDirectory for an architecture. In architecture-agnostic
288 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
289 // for the purposes of this call.
291 void SecCodeSigner::Signer::populate(CodeDirectory::Builder
&builder
, DiskRep::Writer
&writer
,
292 InternalRequirements
&ireqs
, size_t offset
/* = 0 */, size_t length
/* = 0 */)
294 // fill the CodeDirectory
295 builder
.executable(rep
->mainExecutablePath(), pagesize
, offset
, length
);
296 builder
.flags(cdFlags
);
297 builder
.identifier(identifier
);
299 if (CFRef
<CFDataRef
> data
= rep
->component(cdInfoSlot
))
300 builder
.specialSlot(cdInfoSlot
, data
);
302 CFRef
<CFDataRef
> data
= makeCFData(*ireqs
);
303 writer
.component(cdRequirementsSlot
, data
);
304 builder
.specialSlot(cdRequirementsSlot
, data
);
306 if (resourceDirectory
)
307 builder
.specialSlot(cdResourceDirSlot
, resourceDirectory
);
309 if (state
.mApplicationData
)
310 builder
.specialSlot(cdApplicationSlot
, state
.mApplicationData
);
312 if (state
.mEntitlementData
) {
313 writer
.component(cdEntitlementSlot
, state
.mEntitlementData
);
314 builder
.specialSlot(cdEntitlementSlot
, state
.mEntitlementData
);
317 writer
.addDiscretionary(builder
);
320 #include <security_smime/tsaSupport.h>
323 // Generate the CMS signature for a (finished) CodeDirectory.
325 CFDataRef
SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory
*cd
)
327 assert(state
.mSigner
);
328 CFRef
<CFMutableDictionaryRef
> defaultTSContext
= NULL
;
330 // a null signer generates a null signature blob
331 if (state
.mSigner
== SecIdentityRef(kCFNull
))
332 return CFDataCreate(NULL
, NULL
, 0);
334 // generate CMS signature
335 CFRef
<CMSEncoderRef
> cms
;
336 MacOSError::check(CMSEncoderCreate(&cms
.aref()));
337 MacOSError::check(CMSEncoderSetCertificateChainMode(cms
, kCMSCertificateChainWithRoot
));
338 CMSEncoderAddSigners(cms
, state
.mSigner
);
339 MacOSError::check(CMSEncoderSetHasDetachedContent(cms
, true));
342 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrSigningTime
));
343 MacOSError::check(CMSEncoderSetSigningTime(cms
, signingTime
));
346 MacOSError::check(CMSEncoderUpdateContent(cms
, cd
, cd
->length()));
348 // Set up to call Timestamp server if requested
350 if (state
.mWantTimeStamp
)
352 CFRef
<CFErrorRef
> error
= NULL
;
353 defaultTSContext
= SecCmsTSAGetDefaultContext(&error
.aref());
355 MacOSError::throwMe(errSecDataNotAvailable
);
357 if (state
.mNoTimeStampCerts
|| state
.mTimestampService
) {
358 if (state
.mTimestampService
)
359 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyURL
, state
.mTimestampService
);
360 if (state
.mNoTimeStampCerts
)
361 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyNoCerts
, kCFBooleanTrue
);
364 CmsMessageSetTSAContext(cms
, defaultTSContext
);
368 MacOSError::check(CMSEncoderCopyEncodedContent(cms
, &signature
));
375 // Parse a text of the form
377 // where each flag is the canonical name of a signable CodeDirectory flag.
378 // No abbreviations are allowed, and internally set flags are not accepted.
380 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text
)
383 for (string::size_type comma
= text
.find(','); ; text
= text
.substr(comma
+1), comma
= text
.find(',')) {
384 string word
= (comma
== string::npos
) ? text
: text
.substr(0, comma
);
385 const SecCodeDirectoryFlagTable
*item
;
386 for (item
= kSecCodeDirectoryFlagTable
; item
->name
; item
++)
387 if (item
->signable
&& word
== item
->name
) {
388 flags
|= item
->value
;
391 if (!item
->name
) // not found
392 MacOSError::throwMe(errSecCSInvalidFlags
);
393 if (comma
== string::npos
) // last word
401 // Generate a unique string from our underlying DiskRep.
402 // We could get 90%+ of the uniquing benefit by just generating
403 // a random string here. Instead, we pick the (hex string encoding of)
404 // the source rep's unique identifier blob. For universal binaries,
405 // this is the canonical local architecture, which is a bit arbitrary.
406 // This provides us with a consistent unique string for all architectures
407 // of a fat binary, *and* (unlike a random string) is reproducible
408 // for identical inputs, even upon resigning.
410 std::string
SecCodeSigner::Signer::uniqueName() const
412 CFRef
<CFDataRef
> identification
= rep
->identification();
413 const UInt8
*ident
= CFDataGetBytePtr(identification
);
414 const unsigned int length
= CFDataGetLength(identification
);
416 for (unsigned int n
= 0; n
< length
; n
++) {
418 snprintf(hex
, sizeof(hex
), "%02x", ident
[n
]);
425 } // end namespace CodeSigning
426 } // end namespace Security