X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/641423b6670d8656d5daeaf988e7d307fb6c1ebc..3f0f0d49a9b6c2c6d459239f5926d59314cdeacf:/OSX/libsecurity_codesigning/lib/signer.cpp diff --git a/OSX/libsecurity_codesigning/lib/signer.cpp b/OSX/libsecurity_codesigning/lib/signer.cpp index b0e14edb..fb9e7567 100644 --- a/OSX/libsecurity_codesigning/lib/signer.cpp +++ b/OSX/libsecurity_codesigning/lib/signer.cpp @@ -127,7 +127,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) { // make sure the rep passes strict validation if (strict) - rep->strictValidate(NULL, MacOSErrorSet(), flags); + 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 @@ -149,9 +149,9 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) identifier = state.mIdentifierPrefix + identifier; 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)) { @@ -173,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) { @@ -182,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; @@ -193,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) @@ -263,15 +263,16 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) } // 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; } @@ -301,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"); @@ -352,7 +353,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase target[len] = '\0'; seal.take(cfmake("{symlink=%s}", target)); } else { - seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms())); + seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight)); } if (ruleFlags & ResourceBuilder::optional) CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue); @@ -392,11 +393,11 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase 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); } } }); @@ -419,9 +420,11 @@ CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path 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())); } @@ -437,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->digestAlgorithms(), 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); @@ -455,14 +459,18 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context MacOSError::throwMe(errSecCSBadLVArch); } } - + + 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); 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(), unsigned(digestAlgorithms().size()-1)); + arch.source->offset(), arch.source->signingExtent(), + mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)), + unsigned(digestAlgorithms().size()-1)); }); } @@ -510,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(); + } } @@ -526,12 +535,16 @@ void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context (new DetachedBlobWriter(*this)) : rep->writer(); 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(), unsigned(digestAlgorithms().size()-1)); + 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) @@ -545,7 +558,8 @@ void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context } // write out all CodeDirectories - cdSet.populate(writer); + if (!state.mDryRun) + cdSet.populate(writer); CFRef hashes = cdSet.hashBag(); CFTemp hashDict("{cdhashes=%O}", hashes.get()); @@ -575,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, size_t length, unsigned alternateDigestCount) + 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); @@ -583,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); @@ -596,6 +613,10 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W 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); @@ -644,9 +665,10 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, CFDa 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) { @@ -755,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