libsecurity_codesigning-55037.15.tar.gz
[apple/libsecurity_codesigning.git] / lib / signer.cpp
index 68537d92d53b74240490df944ec5911ba98aadea..92fce92b03d724efed9ea0b480d4ce8ab5c98f10 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -35,6 +35,7 @@
 #include <CoreFoundation/CFBundlePriv.h>
 #include "renum.h"
 #include "machorep.h"
+#include "csutilities.h"
 #include <security_utilities/unix++.h>
 #include <security_utilities/unixchild.h>
 #include <security_utilities/cfmunge.h>
@@ -50,10 +51,11 @@ void SecCodeSigner::Signer::sign(SecCSFlags flags)
 {
        rep = code->diskRep()->base();
        this->prepare(flags);
+       PreSigningContext context(*this);
        if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
-               signMachO(fat);
+               signMachO(fat, context);
        } else {
-               signArchitectureAgnostic();
+               signArchitectureAgnostic(context);
        }
 }
 
@@ -70,7 +72,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, rep->mainExecutablePath());
+               MachOEditor editor(rep->writer(), *fat, kSecCodeSignatureNoHash, rep->mainExecutablePath());
                editor.allocate();              // create copy
                editor.commit();                // commit change
        } else {
@@ -95,9 +97,11 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        // work out the canonical identifier
        identifier = state.mIdentifier;
        if (identifier.empty()) {
-               identifier = rep->recommendedIdentifier();
+               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());
@@ -109,7 +113,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        } else {
                cdFlags = 0;
                if (infoDict)
-                       if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags")))
+                       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);
@@ -118,6 +122,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
                                        secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
                                } else
                                        MacOSError::throwMe(errSecCSBadDictionaryFormat);
+                       }
        }
        if (state.mSigner == SecIdentityRef(kCFNull))   // ad-hoc signing requested...
                cdFlags |= kSecCodeSignatureAdhoc;      // ... so note that
@@ -141,10 +146,10 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
 
                // finally, ask the DiskRep for its default
                if (!resourceRules)
-                       resourceRules.take(rep->defaultResourceRules());
+                       resourceRules.take(rep->defaultResourceRules(state));
                
                // build the resource directory
-               ResourceBuilder resources(rpath, cfget<CFDictionaryRef>(resourceRules, "rules"));
+               ResourceBuilder resources(rpath, cfget<CFDictionaryRef>(resourceRules, "rules"), digestAlgorithm());
                rep->adjustResources(resources);        // DiskRep-specific adjustments
                CFRef<CFDictionaryRef> rdir = resources.build();
                resourceDirectory.take(CFPropertyListCreateXMLData(NULL, rdir));
@@ -163,7 +168,10 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
                signingTime = time;
        }
        
-       pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize();
+       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
 }
 
 
@@ -173,12 +181,12 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
 // 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)
+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, rep->mainExecutablePath()));
+               : new MachOEditor(rep->writer(), *fat, this->digestAlgorithm(), rep->mainExecutablePath()));
        assert(editor->count() > 0);
        if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
                populate(*editor);
@@ -187,7 +195,7 @@ void SecCodeSigner::Signer::signMachO(Universal *fat)
        for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
                MachOEditor::Arch &arch = *it->second;
                arch.source.reset(fat->architecture(it->first));
-               arch.ireqs(state.mRequirements, rep->defaultRequirements(&arch.architecture));
+               arch.ireqs(state.mRequirements, 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,
@@ -236,14 +244,14 @@ void SecCodeSigner::Signer::signMachO(Universal *fat)
 // Sign a binary that has no notion of architecture.
 // That currently means anything that isn't Mach-O format.
 //
-void SecCodeSigner::Signer::signArchitectureAgnostic()
+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;
+       CodeDirectory::Builder builder(state.mDigestAlgorithm);
        InternalRequirements ireqs;
-       ireqs(state.mRequirements, rep->defaultRequirements(NULL));
+       ireqs(state.mRequirements, rep->defaultRequirements(NULL, state), context);
        populate(*writer);
        populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
        
@@ -288,27 +296,28 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
        builder.flags(cdFlags);
        builder.identifier(identifier);
        
-       if (CFDataRef data = rep->component(cdInfoSlot))
-               builder.special(cdInfoSlot, data);
+       if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
+               builder.specialSlot(cdInfoSlot, data);
        if (ireqs) {
                CFRef<CFDataRef> data = makeCFData(*ireqs);
                writer.component(cdRequirementsSlot, data);
-               builder.special(cdRequirementsSlot, data);
+               builder.specialSlot(cdRequirementsSlot, data);
        }
        if (resourceDirectory)
-               builder.special(cdResourceDirSlot, resourceDirectory);
+               builder.specialSlot(cdResourceDirSlot, resourceDirectory);
 #if NOT_YET
        if (state.mApplicationData)
-               builder.special(cdApplicationSlot, state.mApplicationData);
+               builder.specialSlot(cdApplicationSlot, state.mApplicationData);
 #endif
        if (state.mEntitlementData) {
                writer.component(cdEntitlementSlot, state.mEntitlementData);
-               builder.special(cdEntitlementSlot, state.mEntitlementData);
+               builder.specialSlot(cdEntitlementSlot, state.mEntitlementData);
        }
        
        writer.addDiscretionary(builder);
 }
 
+#include <Security/tsaSupport.h>
 
 //
 // Generate the CMS signature for a (finished) CodeDirectory.
@@ -316,7 +325,8 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
 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);
@@ -334,8 +344,29 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
        }
        
        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;
 }
 
@@ -366,5 +397,30 @@ uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
 }
 
 
+//
+// 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 unsigned int length = CFDataGetLength(identification);
+       string result;
+       for (unsigned int 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