]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/signer.cpp
Security-58286.41.2.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / signer.cpp
index 387f68b5535f1968b9b158527f4ef912863e1ae1..fb9e75671f244bf934c4f1dfb89cdde7eeaf76bf 100644 (file)
@@ -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<uint32_t>(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<CFMutableDictionaryRef> result = makeCFMutableDictionary();
        
        CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
@@ -352,7 +353,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
                                        target[len] = '\0';
                                        seal.take(cfmake<CFMutableDictionaryRef>("{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<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
-                       Dumper::dump(code->designatedRequirement()).c_str(),
-                       code->cdHash());
+               if (CFDataRef hash = code->cdHash())
+                       return cfmake<CFMutableDictionaryRef>("{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<DiskRep::Writer> writer = rep->writer();
        auto_ptr<ArchEditor> editor(state.mDetached
                ? static_cast<ArchEditor *>(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,14 +558,17 @@ void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
        }
 
        // write out all CodeDirectories
-       cdSet.populate(writer);
-       writer->flush();
+       if (!state.mDryRun)
+               cdSet.populate(writer);
 
        CFRef<CFArrayRef> hashes = cdSet.hashBag();
        CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashes.get());
        CFRef<CFDataRef> hashBag = makeCFData(hashDict.get());
        CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashBag);
        writer->signature(signature);
+       
+       // commit to storage
+       writer->flush();
 }
 
 
@@ -573,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);
@@ -581,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<CFDataRef> data = rep->component(cdInfoSlot))
                builder.specialSlot(cdInfoSlot, data);
@@ -594,13 +613,18 @@ 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<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
                builder.specialSlot(cdRepSpecificSlot, repSpecific);
        
        writer.addDiscretionary(builder);
        
-       if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0) {
+#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<Endian<uint32_t> > slotVector;
@@ -615,6 +639,7 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
                writer.component(cdTopDirectorySlot, cfSlotVector);
                builder.specialSlot(cdTopDirectorySlot, cfSlotVector);
        }
+#endif
 }
 
        
@@ -640,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) {
@@ -751,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<const EntitlementBlob *>(CFDataGetBytePtr(entitlements));
+
+       if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) {
+               return 0;
+       }
+
+       try {
+               CFRef<CFDictionaryRef> 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