--- /dev/null
+/*
+ * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// signer - Signing operation supervisor and controller
+//
+#include "signer.h"
+#include "resources.h"
+#include "signerutils.h"
+#include "SecCodeSigner.h"
+#include <Security/SecIdentity.h>
+#include <Security/CMSEncoder.h>
+#include <Security/CMSPrivate.h>
+#include <Security/CSCommonPriv.h>
+#include <CoreFoundation/CFBundlePriv.h>
+#include "resources.h"
+#include "machorep.h"
+#include "reqparser.h"
+#include "reqdumper.h"
+#include "csutilities.h"
+#include <security_utilities/unix++.h>
+#include <security_utilities/unixchild.h>
+#include <security_utilities/cfmunge.h>
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// Sign some code.
+//
+void SecCodeSigner::Signer::sign(SecCSFlags flags)
+{
+ rep = code->diskRep()->base();
+ this->prepare(flags);
+
+ PreSigningContext context(*this);
+
+ /* If an explicit teamID was passed in it must be
+ the same as what came from the cert */
+ std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs);
+
+ if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) {
+ /* If preserving the team identifier, teamID is set previously when the
+ code object is still available */
+ if (!teamIDFromCert.empty() && teamID != teamIDFromCert)
+ MacOSError::throwMe(errSecCSInvalidFlags);
+ } else {
+ if (teamIDFromCert.empty()) {
+ /* state.mTeamID is an explicitly passed teamID */
+ teamID = state.mTeamID;
+ } else if (state.mTeamID.empty() || (state.mTeamID == teamIDFromCert)) {
+ /* If there was no explicit team ID set, or the explicit team ID matches
+ what is in the cert, use the team ID from the certificate */
+ teamID = teamIDFromCert;
+ } else {
+ /* The caller passed in an explicit team ID that does not match what is
+ in the signing cert, which is an invalid usage */
+ MacOSError::throwMe(errSecCSInvalidFlags);
+ }
+ }
+
+ if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
+ signMachO(fat, context);
+ } else {
+ signArchitectureAgnostic(context);
+ }
+}
+
+
+//
+// Remove any existing code signature from code
+//
+void SecCodeSigner::Signer::remove(SecCSFlags flags)
+{
+ // can't remove a detached signature
+ if (state.mDetached)
+ MacOSError::throwMe(errSecCSNotSupported);
+
+ rep = code->diskRep();
+ if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
+ // architecture-sensitive removal
+ MachOEditor editor(rep->writer(), *fat, kSecCodeSignatureNoHash, rep->mainExecutablePath());
+ editor.allocate(); // create copy
+ editor.commit(); // commit change
+ } else {
+ // architecture-agnostic removal
+ RefPointer<DiskRep::Writer> writer = rep->writer();
+ writer->remove();
+ writer->flush();
+ }
+}
+
+
+//
+// Contemplate the object-to-be-signed and set up the Signer state accordingly.
+//
+void SecCodeSigner::Signer::prepare(SecCSFlags flags)
+{
+ // make sure the rep passes strict validation
+ if (strict)
+ rep->strictValidate(MacOSErrorSet());
+
+ // initialize progress/cancellation state
+ code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
+
+ // get the Info.plist out of the rep for some creative defaulting
+ CFRef<CFDictionaryRef> infoDict;
+ if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
+ infoDict.take(makeCFDictionaryFrom(infoData));
+
+ uint32_t inherit = code->isSigned() ? state.mPreserveMetadata : 0;
+
+ // work out the canonical identifier
+ identifier = state.mIdentifier;
+ if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
+ identifier = code->identifier();
+ if (identifier.empty()) {
+ identifier = rep->recommendedIdentifier(state);
+ if (identifier.find('.') == string::npos)
+ identifier = state.mIdentifierPrefix + identifier;
+ if (identifier.find('.') == string::npos && state.isAdhoc())
+ identifier = identifier + "-" + uniqueName();
+ secdebug("signer", "using default identifier=%s", identifier.c_str());
+ } else
+ secdebug("signer", "using explicit identifier=%s", identifier.c_str());
+
+ teamID = state.mTeamID;
+ if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) {
+ const char *c_id = code->teamID();
+ if (c_id)
+ teamID = c_id;
+ }
+
+ entitlements = state.mEntitlementData;
+ if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
+ entitlements = code->component(cdEntitlementSlot);
+
+ // work out the CodeDirectory flags word
+ bool haveCdFlags = false;
+ if (!haveCdFlags && state.mCdFlagsGiven) {
+ cdFlags = state.mCdFlags;
+ secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
+ haveCdFlags = true;
+ }
+ if (!haveCdFlags) {
+ cdFlags = 0;
+ if (infoDict)
+ if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) {
+ if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
+ cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
+ secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
+ } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
+ cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
+ secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
+ } else
+ MacOSError::throwMe(errSecCSBadDictionaryFormat);
+ haveCdFlags = true;
+ }
+ }
+ if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) {
+ cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc;
+ secdebug("signer", "using inherited cdFlags=0x%x", cdFlags);
+ haveCdFlags = true;
+ }
+ if (!haveCdFlags)
+ cdFlags = 0;
+ if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
+ cdFlags |= kSecCodeSignatureAdhoc; // ... so note that
+
+ // prepare the internal requirements input
+ if (state.mRequirements) {
+ if (CFGetTypeID(state.mRequirements) == CFDataGetTypeID()) { // binary form
+ const Requirements *rp = (const Requirements *)CFDataGetBytePtr(state.mRequirements.as<CFDataRef>());
+ if (!rp->validateBlob())
+ MacOSError::throwMe(errSecCSReqInvalid);
+ requirements = rp->clone();
+ } else if (CFGetTypeID(state.mRequirements) == CFStringGetTypeID()) { // text form
+ CFRef<CFMutableStringRef> reqText = CFStringCreateMutableCopy(NULL, 0, state.mRequirements.as<CFStringRef>());
+ // substitute $ variable tokens
+ CFRange range = { 0, CFStringGetLength(reqText) };
+ CFStringFindAndReplace(reqText, CFSTR("$self.identifier"), CFTempString(identifier), range, 0);
+ requirements = parseRequirements(cfString(reqText));
+ } else
+ MacOSError::throwMe(errSecCSInvalidObjectRef);
+ } else if (inherit & kSecCodeSignerPreserveRequirements)
+ if (const Requirements *rp = code->internalRequirements())
+ requirements = rp->clone();
+
+ // prepare the resource directory, if any
+ string rpath = rep->resourcesRootPath();
+ if (!rpath.empty()) {
+ // explicitly given resource rules always win
+ CFCopyRef<CFDictionaryRef> resourceRules = state.mResourceRules;
+
+ // inherited rules come next (overriding embedded ones!)
+ if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
+ if (CFDictionaryRef oldRules = code->resourceDictionary(false))
+ resourceRules = oldRules;
+
+ // embedded resource rules come next
+ if (!resourceRules && infoDict)
+ if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey)) {
+ if (CFGetTypeID(spec) == CFStringGetTypeID())
+ if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec))))
+ if (CFDictionaryRef dict = makeCFDictionaryFrom(data))
+ resourceRules.take(dict);
+ if (!resourceRules) // embedded rules present but unacceptable
+ MacOSError::throwMe(errSecCSResourceRulesInvalid);
+ }
+
+ // if we got one from anywhere (but the defaults), sanity-check it
+ if (resourceRules) {
+ CFTypeRef rules = CFDictionaryGetValue(resourceRules, CFSTR("rules"));
+ if (!rules || CFGetTypeID(rules) != CFDictionaryGetTypeID())
+ MacOSError::throwMe(errSecCSResourceRulesInvalid);
+ }
+
+ // finally, ask the DiskRep for its default
+ if (!resourceRules)
+ resourceRules.take(rep->defaultResourceRules(state));
+
+ // resource root can optionally be the canonical bundle path,
+ // but sealed resource paths are always relative to rpath
+ string root = rpath;
+ if (state.signingFlags() & kSecCSSignBundleRoot)
+ root = cfStringRelease(rep->copyCanonicalPath());
+
+ // build the resource directory
+ buildResources(root, rpath, resourceRules);
+ }
+
+ // screen and set the signing time
+ CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
+ if (state.mSigningTime == CFDateRef(kCFNull)) {
+ signingTime = 0; // no time at all
+ } else if (!state.mSigningTime) {
+ signingTime = now; // default
+ } else {
+ CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
+ if (time > now) // not allowed to post-date a signature
+ MacOSError::throwMe(errSecCSBadDictionaryFormat);
+ signingTime = time;
+ }
+
+ pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(state);
+
+ // Timestamping setup
+ CFRef<SecIdentityRef> mTSAuth; // identity for client-side authentication to the Timestamp server
+}
+
+
+//
+// Collect the resource seal for a program.
+// This includes both sealed resources and information about nested code.
+//
+void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict)
+{
+ typedef ResourceBuilder::Rule Rule;
+
+ secdebug("codesign", "start building resource directory");
+ __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
+
+ CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
+ assert(rules);
+
+ CFDictionaryRef files2 = NULL;
+ if (!(state.signingFlags() & kSecCSSignV1)) {
+ CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
+ if (!rules2) {
+ // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules).
+ // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
+ rules2 = cfmake<CFDictionaryRef>("{+%O"
+ "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
+ "}", rules);
+ }
+ // build the modern (V2) resource seal
+ __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
+ ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet());
+ ResourceBuilder &resources = resourceBuilder; // (into block)
+ rep->adjustResources(resources);
+ resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, Rule *rule) {
+ CFRef<CFMutableDictionaryRef> seal;
+ if (ruleFlags & ResourceBuilder::nested) {
+ seal.take(signNested(ent, relpath));
+ } else if (ent->fts_info == FTS_SL) {
+ char target[PATH_MAX];
+ ssize_t len = ::readlink(ent->fts_accpath, target, sizeof(target)-1);
+ if (len < 0)
+ UnixError::check(-1);
+ target[len] = '\0';
+ seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
+ } else {
+ seal.take(cfmake<CFMutableDictionaryRef>("{hash=%O}",
+ CFRef<CFDataRef>(resources.hashFile(ent->fts_accpath)).get()));
+ }
+ if (ruleFlags & ResourceBuilder::optional)
+ CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
+ CFTypeRef hash;
+ if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form
+ CFDictionaryAddValue(files, CFTempString(relpath).get(), hash);
+ else
+ CFDictionaryAddValue(files, CFTempString(relpath).get(), seal.get());
+ code->reportProgress();
+ });
+ CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules());
+ files2 = files;
+ CFDictionaryAddValue(result, CFSTR("files2"), files2);
+ }
+
+ CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case
+ if (!(state.signingFlags() & kSecCSSignNoV1)) {
+ // build the legacy (V1) resource seal
+ __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
+ ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet());
+ ResourceBuilder &resources = resourceBuilder;
+ rep->adjustResources(resources); // DiskRep-specific adjustments
+ resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, Rule *rule) {
+ if (ent->fts_info == FTS_F) {
+ CFRef<CFDataRef> hash;
+ if (files2) // try to get the hash from a previously-made version
+ if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) {
+ if (CFGetTypeID(seal) == CFDataGetTypeID())
+ hash = CFDataRef(seal);
+ else
+ hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
+ }
+ if (!hash)
+ hash.take(resources.hashFile(ent->fts_accpath));
+ if (ruleFlags == 0) { // default case - plain hash
+ cfadd(files, "{%s=%O}", relpath, hash.get());
+ secdebug("csresource", "%s added simple (rule %p)", relpath, rule);
+ } else { // more complicated - use a sub-dictionary
+ cfadd(files, "{%s={hash=%O,optional=%B}}",
+ relpath, hash.get(), ruleFlags & ResourceBuilder::optional);
+ secdebug("csresource", "%s added complex (rule %p)", relpath, rule);
+ }
+ }
+ });
+ CFDictionaryAddValue(result, CFSTR("files"), files.get());
+ }
+
+ resourceDirectory = result.get();
+ resourceDictData.take(makeCFData(resourceDirectory.get()));
+}
+
+
+//
+// Deal with one piece of nested code
+//
+CFMutableDictionaryRef SecCodeSigner::Signer::signNested(FTSENT *ent, const char *relpath)
+{
+ // sign nested code and collect nesting information
+ try {
+ SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(ent->fts_path));
+ if (state.signingFlags() & kSecCSSignNestedCode)
+ this->state.sign(code, state.signingFlags());
+ std::string dr = Dumper::dump(code->designatedRequirement());
+ return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
+ Dumper::dump(code->designatedRequirement()).c_str(),
+ code->cdHash());
+ } catch (const CommonError &err) {
+ CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
+ }
+}
+
+
+//
+// Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
+// Note that this will deal just fine with non-fat Mach-O binaries, but it will
+// treat them as architectural binaries containing (only) one architecture - that
+// interpretation is courtesy of the Universal/MachO support classes.
+//
+void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
+{
+ // Mach-O executable at the core - perform multi-architecture signing
+ auto_ptr<ArchEditor> editor(state.mDetached
+ ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
+ : new MachOEditor(rep->writer(), *fat, this->digestAlgorithm(), rep->mainExecutablePath()));
+ assert(editor->count() > 0);
+ if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
+ populate(*editor);
+
+ // pass 1: prepare signature blobs and calculate sizes
+ for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
+ MachOEditor::Arch &arch = *it->second;
+ arch.source.reset(fat->architecture(it->first));
+
+ // library validation is not compatible with i386
+ if (arch.architecture.cpuType() == CPU_TYPE_I386) {
+ if (cdFlags & kSecCodeSignatureLibraryValidation) {
+ MacOSError::throwMe(errSecCSBadLVArch);
+ }
+ }
+
+ arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, state), context);
+ if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
+ populate(arch);
+ populate(arch.cdbuilder, arch, arch.ireqs,
+ arch.source->offset(), arch.source->signingExtent());
+
+ // add identification blob (made from this architecture) only if we're making a detached signature
+ if (state.mDetached) {
+ CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
+ arch.add(cdIdentificationSlot, BlobWrapper::alloc(
+ CFDataGetBytePtr(identification), CFDataGetLength(identification)));
+ }
+
+ // prepare SuperBlob size estimate
+ size_t cdSize = arch.cdbuilder.size(CodeDirectory::currentVersion);
+ arch.blobSize = arch.size(cdSize, state.mCMSSize, 0);
+ }
+
+ editor->allocate();
+
+ // pass 2: Finish and generate signatures, and write them
+ for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
+ MachOEditor::Arch &arch = *it->second;
+ editor->reset(arch);
+
+ // finish CodeDirectory (off new binary) and sign it
+ CodeDirectory *cd = arch.cdbuilder.build();
+ CFRef<CFDataRef> signature = signCodeDirectory(cd);
+
+ // complete the SuperBlob
+ arch.add(cdCodeDirectorySlot, cd); // takes ownership
+ arch.add(cdSignatureSlot, BlobWrapper::alloc(
+ CFDataGetBytePtr(signature), CFDataGetLength(signature)));
+ if (!state.mDryRun) {
+ EmbeddedSignatureBlob *blob = arch.make();
+ editor->write(arch, blob); // takes ownership of blob
+ }
+ }
+
+ // done: write edit copy back over the original
+ if (!state.mDryRun)
+ editor->commit();
+}
+
+
+//
+// Sign a binary that has no notion of architecture.
+// That currently means anything that isn't Mach-O format.
+//
+void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context)
+{
+ // non-Mach-O executable - single-instance signing
+ RefPointer<DiskRep::Writer> writer = state.mDetached ?
+ (new DetachedBlobWriter(*this)) : rep->writer();
+ CodeDirectory::Builder builder(state.mDigestAlgorithm);
+ InternalRequirements ireqs;
+ ireqs(requirements, rep->defaultRequirements(NULL, state), context);
+ populate(*writer);
+ populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
+
+ // add identification blob (made from this architecture) only if we're making a detached signature
+ if (state.mDetached) {
+ CFRef<CFDataRef> identification = rep->identification();
+ writer->component(cdIdentificationSlot, identification);
+ }
+
+ CodeDirectory *cd = builder.build();
+ CFRef<CFDataRef> signature = signCodeDirectory(cd);
+ if (!state.mDryRun) {
+ writer->codeDirectory(cd);
+ writer->signature(signature);
+ writer->flush();
+ }
+ ::free(cd);
+}
+
+
+//
+// Global populate - send components to destination buffers ONCE
+//
+void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
+{
+ if (resourceDirectory && !state.mDryRun)
+ writer.component(cdResourceDirSlot, resourceDictData);
+}
+
+
+//
+// Per-architecture populate - send components to per-architecture buffers
+// and populate the CodeDirectory for an architecture. In architecture-agnostic
+// signing operations, the non-architectural binary is considered one (arbitrary) architecture
+// for the purposes of this call.
+//
+void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
+ InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */)
+{
+ // fill the CodeDirectory
+ builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
+ builder.flags(cdFlags);
+ builder.identifier(identifier);
+ builder.teamID(teamID);
+
+ if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
+ builder.specialSlot(cdInfoSlot, data);
+ if (ireqs) {
+ CFRef<CFDataRef> data = makeCFData(*ireqs);
+ writer.component(cdRequirementsSlot, data);
+ builder.specialSlot(cdRequirementsSlot, data);
+ }
+ if (resourceDirectory)
+ builder.specialSlot(cdResourceDirSlot, resourceDictData);
+#if NOT_YET
+ if (state.mApplicationData)
+ builder.specialSlot(cdApplicationSlot, state.mApplicationData);
+#endif
+ if (entitlements) {
+ writer.component(cdEntitlementSlot, entitlements);
+ builder.specialSlot(cdEntitlementSlot, entitlements);
+ }
+
+ writer.addDiscretionary(builder);
+}
+
+#include <security_smime/tsaSupport.h>
+
+//
+// Generate the CMS signature for a (finished) CodeDirectory.
+//
+CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
+{
+ assert(state.mSigner);
+ CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
+
+ // a null signer generates a null signature blob
+ if (state.mSigner == SecIdentityRef(kCFNull))
+ return CFDataCreate(NULL, NULL, 0);
+
+ // generate CMS signature
+ CFRef<CMSEncoderRef> cms;
+ MacOSError::check(CMSEncoderCreate(&cms.aref()));
+ MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
+ CMSEncoderAddSigners(cms, state.mSigner);
+ MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
+
+ if (signingTime) {
+ MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
+ MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
+ }
+
+ MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
+
+ // Set up to call Timestamp server if requested
+
+ if (state.mWantTimeStamp)
+ {
+ CFRef<CFErrorRef> error = NULL;
+ defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref());
+ if (error)
+ MacOSError::throwMe(errSecDataNotAvailable);
+
+ if (state.mNoTimeStampCerts || state.mTimestampService) {
+ if (state.mTimestampService)
+ CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
+ if (state.mNoTimeStampCerts)
+ CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
+ }
+
+ CmsMessageSetTSAContext(cms, defaultTSContext);
+ }
+
+ CFDataRef signature;
+ MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
+
+ return signature;
+}
+
+
+//
+// Parse a text of the form
+// flag,...,flag
+// where each flag is the canonical name of a signable CodeDirectory flag.
+// No abbreviations are allowed, and internally set flags are not accepted.
+//
+uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
+{
+ uint32_t flags = 0;
+ for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
+ string word = (comma == string::npos) ? text : text.substr(0, comma);
+ const SecCodeDirectoryFlagTable *item;
+ for (item = kSecCodeDirectoryFlagTable; item->name; item++)
+ if (item->signable && word == item->name) {
+ flags |= item->value;
+ break;
+ }
+ if (!item->name) // not found
+ MacOSError::throwMe(errSecCSInvalidFlags);
+ if (comma == string::npos) // last word
+ break;
+ }
+ return flags;
+}
+
+
+//
+// Generate a unique string from our underlying DiskRep.
+// We could get 90%+ of the uniquing benefit by just generating
+// a random string here. Instead, we pick the (hex string encoding of)
+// the source rep's unique identifier blob. For universal binaries,
+// this is the canonical local architecture, which is a bit arbitrary.
+// This provides us with a consistent unique string for all architectures
+// of a fat binary, *and* (unlike a random string) is reproducible
+// for identical inputs, even upon resigning.
+//
+std::string SecCodeSigner::Signer::uniqueName() const
+{
+ CFRef<CFDataRef> identification = rep->identification();
+ const UInt8 *ident = CFDataGetBytePtr(identification);
+ const CFIndex length = CFDataGetLength(identification);
+ string result;
+ for (CFIndex n = 0; n < length; n++) {
+ char hex[3];
+ snprintf(hex, sizeof(hex), "%02x", ident[n]);
+ result += hex;
+ }
+ return result;
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security