X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..3f0f0d49a9b6c2c6d459239f5926d59314cdeacf:/OSX/libsecurity_codesigning/lib/signer.cpp diff --git a/OSX/libsecurity_codesigning/lib/signer.cpp b/OSX/libsecurity_codesigning/lib/signer.cpp index daa2dac7..fb9e7567 100644 --- a/OSX/libsecurity_codesigning/lib/signer.cpp +++ b/OSX/libsecurity_codesigning/lib/signer.cpp @@ -58,10 +58,22 @@ void SecCodeSigner::Signer::sign(SecCSFlags flags) PreSigningContext context(*this); + considerTeamID(context); + + if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { + signMachO(fat, context); + } else { + signArchitectureAgnostic(context); + } +} + + +void SecCodeSigner::Signer::considerTeamID(const PreSigningContext& context) +{ /* 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 */ @@ -81,14 +93,8 @@ void SecCodeSigner::Signer::sign(SecCSFlags flags) MacOSError::throwMe(errSecCSInvalidFlags); } } - - if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { - signMachO(fat, context); - } else { - signArchitectureAgnostic(context); - } } - + // // Remove any existing code signature from code @@ -102,7 +108,7 @@ void SecCodeSigner::Signer::remove(SecCSFlags flags) rep = code->diskRep(); if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { // architecture-sensitive removal - MachOEditor editor(rep->writer(), *fat, kSecCodeSignatureNoHash, rep->mainExecutablePath()); + MachOEditor editor(rep->writer(), *fat, digestAlgorithms(), rep->mainExecutablePath()); editor.allocate(); // create copy editor.commit(); // commit change } else { @@ -121,7 +127,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) { // make sure the rep passes strict validation if (strict) - rep->strictValidate(NULL, MacOSErrorSet()); + rep->strictValidate(NULL, MacOSErrorSet(), flags | (kSecCSQuickCheck|kSecCSRestrictSidebandData)); // initialize progress/cancellation state code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter @@ -138,14 +144,14 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier)) identifier = code->identifier(); if (identifier.empty()) { - identifier = rep->recommendedIdentifier(state); + identifier = rep->recommendedIdentifier(*this); if (identifier.find('.') == string::npos) identifier = state.mIdentifierPrefix + identifier; - if (identifier.find('.') == string::npos && state.isAdhoc()) + if (identifier.find('.') == string::npos && isAdhoc()) identifier = identifier + "-" + uniqueName(); - secdebug("signer", "using default identifier=%s", identifier.c_str()); + secinfo("signer", "using default identifier=%s", identifier.c_str()); } else - secdebug("signer", "using explicit identifier=%s", identifier.c_str()); + secinfo("signer", "using explicit identifier=%s", identifier.c_str()); teamID = state.mTeamID; if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) { @@ -154,6 +160,11 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) teamID = c_id; } + // Digest algorithms: explicit or preserved. Subject to diskRep defaults or final default later. + hashAlgorithms = state.mDigestAlgorithms; + if (hashAlgorithms.empty() && (inherit & kSecCodeSignerPreserveDigestAlgorithm)) + hashAlgorithms = code->hashAlgorithms(); + entitlements = state.mEntitlementData; if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements)) entitlements = code->component(cdEntitlementSlot); @@ -162,7 +173,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) bool haveCdFlags = false; if (!haveCdFlags && state.mCdFlagsGiven) { cdFlags = state.mCdFlags; - secdebug("signer", "using explicit cdFlags=0x%x", cdFlags); + secinfo("signer", "using explicit cdFlags=0x%x", cdFlags); haveCdFlags = true; } if (!haveCdFlags) { @@ -171,10 +182,10 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) { if (CFGetTypeID(csflags) == CFNumberGetTypeID()) { cdFlags = cfNumber(CFNumberRef(csflags)); - secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags); + secinfo("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); + secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags); } else MacOSError::throwMe(errSecCSBadDictionaryFormat); haveCdFlags = true; @@ -182,7 +193,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) } if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) { cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc; - secdebug("signer", "using inherited cdFlags=0x%x", cdFlags); + secinfo("signer", "using inherited cdFlags=0x%x", cdFlags); haveCdFlags = true; } if (!haveCdFlags) @@ -211,9 +222,11 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) // prepare the resource directory, if any string rpath = rep->resourcesRootPath(); + string rrpath; + CFCopyRef resourceRules; if (!rpath.empty()) { // explicitly given resource rules always win - CFCopyRef resourceRules = state.mResourceRules; + resourceRules = state.mResourceRules; // inherited rules come next (overriding embedded ones!) if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules)) @@ -240,35 +253,44 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) // finally, ask the DiskRep for its default if (!resourceRules) - resourceRules.take(rep->defaultResourceRules(state)); + resourceRules.take(rep->defaultResourceRules(*this)); // 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); + rrpath = rpath; + if (signingFlags() & kSecCSSignBundleRoot) + rrpath = cfStringRelease(rep->copyCanonicalPath()); } // screen and set the signing time - CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); if (state.mSigningTime == CFDateRef(kCFNull)) { - signingTime = 0; // no time at all + emitSigningTime = false; // no time at all } else if (!state.mSigningTime) { - signingTime = now; // default + emitSigningTime = true; + signingTime = 0; // wall clock, established later } else { CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime); - if (time > now) // not allowed to post-date a signature + if (time > CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature MacOSError::throwMe(errSecCSBadDictionaryFormat); + emitSigningTime = true; signingTime = time; } - pagesize = state.mPageSize ? cfNumber(state.mPageSize) : rep->pageSize(state); - - // Timestamping setup - CFRef mTSAuth; // identity for client-side authentication to the Timestamp server + pagesize = state.mPageSize ? cfNumber(state.mPageSize) : rep->pageSize(*this); + + // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults. + rep->prepareForSigning(*this); + + // apply some defaults after diskRep intervention + if (hashAlgorithms.empty()) { // default to SHA256 + SHA-1 + hashAlgorithms.insert(kSecCodeSignatureHashSHA1); + hashAlgorithms.insert(kSecCodeSignatureHashSHA256); + } + + // build the resource directory (once and for all, using the digests determined above) + if (!rpath.empty()) { + buildResources(rrpath, rpath, resourceRules); + } } @@ -280,7 +302,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase { typedef ResourceBuilder::Rule Rule; - secdebug("codesign", "start building resource directory"); + secinfo("codesign", "start building resource directory"); __block CFRef result = makeCFMutableDictionary(); CFDictionaryRef rules = cfget(rulesDict, "rules"); @@ -295,7 +317,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase } CFDictionaryRef files2 = NULL; - if (!(state.signingFlags() & kSecCSSignV1)) { + if (!(signingFlags() & kSecCSSignV1)) { CFCopyRef rules2 = cfget(rulesDict, "rules2"); if (!rules2) { // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules). @@ -311,7 +333,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase // build the modern (V2) resource seal __block CFRef files = makeCFMutableDictionary(); CFMutableDictionaryRef filesRef = files.get(); // (into block) - ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet()); + ResourceBuilder resourceBuilder(root, relBase, rules2, strict, MacOSErrorSet()); ResourceBuilder &resources = resourceBuilder; // (into block) rep->adjustResources(resources); @@ -331,8 +353,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase target[len] = '\0'; seal.take(cfmake("{symlink=%s}", target)); } else { - seal.take(cfmake("{hash=%O}", - CFRef(resources.hashFile(accpath.c_str())).get())); + seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight)); } if (ruleFlags & ResourceBuilder::optional) CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue); @@ -352,10 +373,10 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase } CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case - if (!(state.signingFlags() & kSecCSSignNoV1)) { + if (!(signingFlags() & kSecCSSignNoV1)) { // build the legacy (V1) resource seal __block CFRef files = makeCFMutableDictionary(); - ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet()); + ResourceBuilder resourceBuilder(root, relBase, rules, strict, MacOSErrorSet()); ResourceBuilder &resources = resourceBuilder; rep->adjustResources(resources); // DiskRep-specific adjustments resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) { @@ -369,14 +390,14 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash"))); } if (!hash) - hash.take(resources.hashFile(ent->fts_accpath)); + hash.take(resources.hashFile(ent->fts_accpath, kSecCodeSignatureHashSHA1)); if (ruleFlags == 0) { // default case - plain hash cfadd(files, "{%s=%O}", relpath.c_str(), hash.get()); - secdebug("csresource", "%s added simple (rule %p)", relpath.c_str(), rule); + secinfo("csresource", "%s added simple (rule %p)", relpath.c_str(), rule); } else { // more complicated - use a sub-dictionary cfadd(files, "{%s={hash=%O,optional=%B}}", relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional); - secdebug("csresource", "%s added complex (rule %p)", relpath.c_str(), rule); + secinfo("csresource", "%s added complex (rule %p)", relpath.c_str(), rule); } } }); @@ -396,12 +417,14 @@ CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path // sign nested code and collect nesting information try { SecPointer code = new SecStaticCode(DiskRep::bestGuess(path)); - if (state.signingFlags() & kSecCSSignNestedCode) - this->state.sign(code, state.signingFlags()); + if (signingFlags() & kSecCSSignNestedCode) + this->state.sign(code, signingFlags()); std::string dr = Dumper::dump(code->designatedRequirement()); - return cfmake("{requirement=%s,cdhash=%O}", - Dumper::dump(code->designatedRequirement()).c_str(), - code->cdHash()); + if (CFDataRef hash = code->cdHash()) + return cfmake("{requirement=%s,cdhash=%O}", + Dumper::dump(code->designatedRequirement()).c_str(), + hash); + MacOSError::throwMe(errSecCSUnsigned); } catch (const CommonError &err) { CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase())); } @@ -417,9 +440,10 @@ CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context) { // Mach-O executable at the core - perform multi-architecture signing + RefPointer writer = rep->writer(); auto_ptr editor(state.mDetached ? static_cast(new BlobEditor(*fat, *this)) - : new MachOEditor(rep->writer(), *fat, this->digestAlgorithm(), rep->mainExecutablePath())); + : new MachOEditor(writer, *fat, this->digestAlgorithms(), rep->mainExecutablePath())); assert(editor->count() > 0); if (!editor->attribute(writerNoGlobal)) // can store architecture-common components populate(*editor); @@ -435,12 +459,20 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context MacOSError::throwMe(errSecCSBadLVArch); } } - - arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, state), context); + + bool mainBinary = arch.source.get()->type() == MH_EXECUTE; + + arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), 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()); + for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) { + arch.eachDigest(^(CodeDirectory::Builder& builder) { + populate(builder, arch, arch.ireqs, + arch.source->offset(), arch.source->signingExtent(), + mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)), + unsigned(digestAlgorithms().size()-1)); + }); + } // add identification blob (made from this architecture) only if we're making a detached signature if (state.mDetached) { @@ -450,8 +482,11 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context } // prepare SuperBlob size estimate - size_t cdSize = arch.cdbuilder.size(CodeDirectory::currentVersion); - arch.blobSize = arch.size(cdSize, state.mCMSSize, 0); + __block std::vector sizes; + arch.eachDigest(^(CodeDirectory::Builder& builder){ + sizes.push_back(builder.size(CodeDirectory::currentVersion)); + }); + arch.blobSize = arch.size(sizes, state.mCMSSize, 0); } editor->allocate(); @@ -461,12 +496,19 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context MachOEditor::Arch &arch = *it->second; editor->reset(arch); - // finish CodeDirectory (off new binary) and sign it - CodeDirectory *cd = arch.cdbuilder.build(); - CFRef signature = signCodeDirectory(cd); + // finish CodeDirectories (off new binary) and sign it + __block CodeDirectorySet cdSet; + arch.eachDigest(^(CodeDirectory::Builder &builder) { + CodeDirectory *cd = builder.build(); + cdSet.add(cd); + }); + CFRef hashes = cdSet.hashBag(); + CFTemp hashDict("{cdhashes=%O}", hashes.get()); + CFRef hashBag = makeCFData(hashDict.get()); + CFRef signature = signCodeDirectory(cdSet.primary(), hashBag); // complete the SuperBlob - arch.add(cdCodeDirectorySlot, cd); // takes ownership + cdSet.populate(&arch); arch.add(cdSignatureSlot, BlobWrapper::alloc( CFDataGetBytePtr(signature), CFDataGetLength(signature))); if (!state.mDryRun) { @@ -476,8 +518,9 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context } // done: write edit copy back over the original - if (!state.mDryRun) + if (!state.mDryRun) { editor->commit(); + } } @@ -490,26 +533,42 @@ void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context // non-Mach-O executable - single-instance signing RefPointer 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()); + + CodeDirectorySet cdSet; + + for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) { + CodeDirectory::Builder builder(*type); + InternalRequirements ireqs; + ireqs(requirements, rep->defaultRequirements(NULL, *this), context); + populate(*writer); + populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(), + false, // only machOs can currently be main binaries + rep->execSegBase(NULL), rep->execSegLimit(NULL), + unsigned(digestAlgorithms().size()-1)); + + CodeDirectory *cd = builder.build(); + if (!state.mDryRun) + cdSet.add(cd); + } // add identification blob (made from this architecture) only if we're making a detached signature if (state.mDetached) { CFRef identification = rep->identification(); writer->component(cdIdentificationSlot, identification); - } - - CodeDirectory *cd = builder.build(); - CFRef signature = signCodeDirectory(cd); - if (!state.mDryRun) { - writer->codeDirectory(cd); - writer->signature(signature); - writer->flush(); } - ::free(cd); + + // write out all CodeDirectories + if (!state.mDryRun) + cdSet.populate(writer); + + CFRef hashes = cdSet.hashBag(); + CFTemp hashDict("{cdhashes=%O}", hashes.get()); + CFRef hashBag = makeCFData(hashDict.get()); + CFRef signature = signCodeDirectory(cdSet.primary(), hashBag); + writer->signature(signature); + + // commit to storage + writer->flush(); } @@ -530,7 +589,9 @@ void SecCodeSigner::Signer::populate(DiskRep::Writer &writer) // 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 */) + InternalRequirements &ireqs, size_t offset, size_t length, + bool mainBinary, size_t execSegBase, size_t execSegLimit, + unsigned alternateDigestCount) { // fill the CodeDirectory builder.executable(rep->mainExecutablePath(), pagesize, offset, length); @@ -538,6 +599,7 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W builder.identifier(identifier); builder.teamID(teamID); builder.platform(state.mPlatform); + builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0); if (CFRef data = rep->component(cdInfoSlot)) builder.specialSlot(cdInfoSlot, data); @@ -548,24 +610,45 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W } 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); + + if (mainBinary) { + builder.addExecSegFlags(entitlementsToExecSegFlags(entitlements)); + } } + if (CFRef repSpecific = rep->component(cdRepSpecificSlot)) + builder.specialSlot(cdRepSpecificSlot, repSpecific); writer.addDiscretionary(builder); + +#if 0 // rdar://problem/25720754 + if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) { + // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any) + // (but not for opaque or V1 construction, which must remain bit-for-bit compatible) + std::vector > slotVector; + slotVector.push_back(cdCodeDirectorySlot); // mandatory + std::set filledSlots = builder.filledSpecialSlots(); + filledSlots.insert(cdTopDirectorySlot); // will be added below + copy(filledSlots.begin(), filledSlots.end(), back_inserter(slotVector)); + for (unsigned n = 0; n < alternateDigestCount; n++) + slotVector.push_back(cdAlternateCodeDirectorySlots + n); + slotVector.push_back(cdSignatureSlot); + CFTempData cfSlotVector(&slotVector[0], slotVector.size() * sizeof(slotVector[0])); + writer.component(cdTopDirectorySlot, cfSlotVector); + builder.specialSlot(cdTopDirectorySlot, cfSlotVector); + } +#endif } + #include // // Generate the CMS signature for a (finished) CodeDirectory. // -CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd) +CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, CFDataRef hashBag) { assert(state.mSigner); CFRef defaultTSContext = NULL; @@ -582,15 +665,20 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd) CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256); MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true)); - if (signingTime) { + if (emitSigningTime) { MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime)); - MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime)); + CFAbsoluteTime time = signingTime ? signingTime : CFAbsoluteTimeGetCurrent(); + MacOSError::check(CMSEncoderSetSigningTime(cms, time)); + } + + if (hashBag) { + MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility)); + MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashBag)); } MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length())); // Set up to call Timestamp server if requested - if (state.mWantTimeStamp) { CFRef error = NULL; @@ -603,16 +691,40 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd) CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService); if (state.mNoTimeStampCerts) CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue); - } + } - CmsMessageSetTSAContext(cms, defaultTSContext); + CmsMessageSetTSAContext(cms, defaultTSContext); } - + CFDataRef signature; MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature)); return signature; } + + +// +// Our DiskRep::signingContext methods communicate with the signing subsystem +// in terms those callers can easily understand. +// +string SecCodeSigner::Signer::sdkPath(const std::string &path) const +{ + assert(path[0] == '/'); // need absolute path here + if (state.mSDKRoot) + return cfString(state.mSDKRoot) + path; + else + return path; +} + +bool SecCodeSigner::Signer::isAdhoc() const +{ + return state.mSigner == SecIdentityRef(kCFNull); +} + +SecCSFlags SecCodeSigner::Signer::signingFlags() const +{ + return state.mOpFlags; +} // @@ -665,6 +777,49 @@ std::string SecCodeSigner::Signer::uniqueName() const return result; } +bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict, CFStringRef key) { + CFBooleanRef entValue = (CFBooleanRef)CFDictionaryGetValue(entDict, key); + + if (entValue == NULL || CFGetTypeID(entValue) != CFBooleanGetTypeID()) { + return false; + } + + return CFBooleanGetValue(entValue); +} + +uint64_t SecCodeSigner::Signer::entitlementsToExecSegFlags(CFDataRef entitlements) +{ + if (!entitlements) { + return 0; + } + + const EntitlementBlob *blob = reinterpret_cast(CFDataGetBytePtr(entitlements)); + + if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) { + return 0; + } + + try { + CFRef entDict = blob->entitlements(); + + uint64_t flags = 0; + + flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0; + flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0; + flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0; + flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0; + flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0; + flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0; + flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0; + + return flags; + + } catch (const CommonError &err) { + // Not fatal. + secwarning("failed to parse entitlements: %s", err.what()); + return 0; + } +} } // end namespace CodeSigning } // end namespace Security