]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/bundlediskrep.cpp
Security-59306.61.1.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / bundlediskrep.cpp
index c7bd784bcc834ef004176d6af786d8ecd40a8210..7c4909702644b1685db09b6dac96f61e0f53336b 100644 (file)
@@ -23,6 +23,7 @@
 #include "bundlediskrep.h"
 #include "filediskrep.h"
 #include "dirscanner.h"
+#include "notarization.h"
 #include <CoreFoundation/CFBundlePriv.h>
 #include <CoreFoundation/CFURLAccess.h>
 #include <CoreFoundation/CFBundlePriv.h>
@@ -47,11 +48,12 @@ static std::string findDistFile(const std::string &directory);
 // We make a CFBundleRef immediately, but everything else is lazy
 //
 BundleDiskRep::BundleDiskRep(const char *path, const Context *ctx)
-       : mBundle(_CFBundleCreateUnique(NULL, CFTempURL(path)))
+       : mBundle(_CFBundleCreateUnique(NULL, CFTempURL(path))), forcePlatform(false)
 {
        if (!mBundle)
                MacOSError::throwMe(errSecCSBadBundleFormat);
        setup(ctx);
+       forcePlatform = mExecRep->appleInternalForcePlatform();
        CODESIGN_DISKREP_CREATE_BUNDLE_PATH(this, (char*)path, (void*)ctx, mExecRep);
 }
 
@@ -59,6 +61,7 @@ BundleDiskRep::BundleDiskRep(CFBundleRef ref, const Context *ctx)
 {
        mBundle = ref;          // retains
        setup(ctx);
+       forcePlatform = mExecRep->appleInternalForcePlatform();
        CODESIGN_DISKREP_CREATE_BUNDLE_REF(this, ref, (void*)ctx, mExecRep);
 }
 
@@ -87,7 +90,7 @@ void BundleDiskRep::setup(const Context *ctx)
        mInstallerPackage = false;      // default
        mAppLike = false;                       // pessimism first
        bool appDisqualified = false; // found reason to disqualify as app
-       
+
        // capture the path of the main executable before descending into a specific version
        CFRef<CFURLRef> mainExecBefore = CFBundleCopyExecutableURL(mBundle);
        CFRef<CFURLRef> infoPlistBefore = _CFBundleCopyInfoPlistURL(mBundle);
@@ -360,6 +363,45 @@ CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot)
        }
 }
 
+BundleDiskRep::RawComponentMap BundleDiskRep::createRawComponents()
+{
+       RawComponentMap map;
+
+       /* Those are the slots known to BundleDiskReps.
+        * Unlike e.g. MachOReps, we cannot handle unknown slots,
+        * as we won't know their slot <-> filename mapping.
+        */
+       int const slots[] = {
+               cdCodeDirectorySlot, cdSignatureSlot, cdResourceDirSlot,
+               cdTopDirectorySlot, cdEntitlementSlot, cdEntitlementDERSlot,
+               cdRepSpecificSlot};
+       
+       for (int slot = 0; slot < (int)(sizeof(slots)/sizeof(slots[0])); ++slot) {
+               /* Here, we only handle metaData slots, i.e. slots that
+                * are explicit files in the _CodeSignature directory.
+                * Main executable slots (if the main executable is a
+                * EditableDiskRep) are handled when editing the
+                * main executable's rep explicitly.
+                * There is also an Info.plist slot, which is not a
+                * real part of the code signature.
+                */
+               CFRef<CFDataRef> data = metaData(slot);
+               
+               if (data) {
+                       map[slot] = data;
+               }
+       }
+       
+       for (CodeDirectory::Slot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; ++slot) {
+               CFRef<CFDataRef> data = metaData(slot);
+               
+               if (data) {
+                       map[slot] = data;
+               }
+       }
+       
+       return map;
+}
 
 // Check that all components of this BundleDiskRep come from either the main
 // executable or the _CodeSignature directory (not mix-and-match).
@@ -447,6 +489,16 @@ size_t BundleDiskRep::signingLimit()
        return mExecRep->signingLimit();
 }
 
+size_t BundleDiskRep::execSegBase(const Architecture *arch)
+{
+       return mExecRep->execSegBase(arch);
+}
+
+size_t BundleDiskRep::execSegLimit(const Architecture *arch)
+{
+       return mExecRep->execSegLimit(arch);
+}
+
 string BundleDiskRep::format()
 {
        return mFormat;
@@ -461,6 +513,7 @@ CFArrayRef BundleDiskRep::modifiedFiles()
        checkModifiedFile(files, cdResourceDirSlot);
        checkModifiedFile(files, cdTopDirectorySlot);
        checkModifiedFile(files, cdEntitlementSlot);
+       checkModifiedFile(files, cdEntitlementDERSlot);
        checkModifiedFile(files, cdRepSpecificSlot);
        for (CodeDirectory::Slot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; ++slot)
                checkModifiedFile(files, slot);
@@ -565,41 +618,73 @@ CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &ctx)
                        "'^.*' = #T"                                                            // everything is a resource
                        "'^Info\\.plist$' = {omit=#T,weight=10}"        // explicitly exclude this for backward compatibility
                "}}");
-       
-       // new (V2) executable bundle rules
-       return cfmake<CFDictionaryRef>("{"                                      // *** the new (V2) world ***
-               "rules={"                                                                               // old (V1; legacy) version
-                       "'^version.plist$' = #T"                                        // include version.plist
-                       "%s = #T"                                                                       // include Resources
-                       "%s = {optional=#T, weight=1000}"                       // make localizations optional
-                       "%s = {weight=1010}"                                            // ... except for Base.lproj which really isn't optional at all
-                       "%s = {omit=#T, weight=1100}"                           // exclude all locversion.plist files
-               "},rules2={"
-                       "'^.*' = #T"                                                            // include everything as a resource, with the following exceptions
-                       "'^[^/]+$' = {nested=#T, weight=10}"            // files directly in Contents
-                       "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=10}" // dynamic repositories
-                       "'.*\\.dSYM($|/)' = {weight=11}"                        // but allow dSYM directories in code locations (parallel to their code)
-                       "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}" // ignore .DS_Store files
-                       "'^Info\\.plist$' = {omit=#T, weight=20}"       // excluded automatically now, but old systems need to be told
-                       "'^version\\.plist$' = {weight=20}"                     // include version.plist as resource
-                       "'^embedded\\.provisionprofile$' = {weight=20}" // include embedded.provisionprofile as resource
-                       "'^PkgInfo$' = {omit=#T, weight=20}"            // traditionally not included
-                       "%s = {weight=20}"                                                      // Resources override default nested (widgets)
-                       "%s = {optional=#T, weight=1000}"                       // make localizations optional
-                       "%s = {weight=1010}"                                            // ... except for Base.lproj which really isn't optional at all
-                       "%s = {omit=#T, weight=1100}"                           // exclude all locversion.plist files
-               "}}",
-                       
-               (string("^") + resources).c_str(),
-               (string("^") + resources + ".*\\.lproj/").c_str(),
-               (string("^") + resources + "Base\\.lproj/").c_str(),
-               (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str(),
-                       
-               (string("^") + resources).c_str(),
-               (string("^") + resources + ".*\\.lproj/").c_str(),
-               (string("^") + resources + "Base\\.lproj/").c_str(),
-               (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str()
-       );
+
+    // new (V2) executable bundle rules
+    if (!resources.empty()) {
+        return cfmake<CFDictionaryRef>("{"                    // *** the new (V2) world ***
+            "rules={"                                        // old (V1; legacy) version
+                "'^version.plist$' = #T"                    // include version.plist
+                "%s = #T"                                    // include Resources
+                "%s = {optional=#T, weight=1000}"            // make localizations optional
+                "%s = {weight=1010}"                                           // ... except for Base.lproj which really isn't optional at all
+                "%s = {omit=#T, weight=1100}"                // exclude all locversion.plist files
+            "},rules2={"
+                "'^.*' = #T"                                // include everything as a resource, with the following exceptions
+                "'^[^/]+$' = {nested=#T, weight=10}"        // files directly in Contents
+                "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=10}" // dynamic repositories
+                "'.*\\.dSYM($|/)' = {weight=11}"            // but allow dSYM directories in code locations (parallel to their code)
+                "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}"    // ignore .DS_Store files
+                "'^Info\\.plist$' = {omit=#T, weight=20}"    // excluded automatically now, but old systems need to be told
+                "'^version\\.plist$' = {weight=20}"            // include version.plist as resource
+                "'^embedded\\.provisionprofile$' = {weight=20}"    // include embedded.provisionprofile as resource
+                "'^PkgInfo$' = {omit=#T, weight=20}"        // traditionally not included
+                "%s = {weight=20}"                            // Resources override default nested (widgets)
+                "%s = {optional=#T, weight=1000}"            // make localizations optional
+                "%s = {weight=1010}"                                           // ... except for Base.lproj which really isn't optional at all
+                "%s = {omit=#T, weight=1100}"                // exclude all locversion.plist files
+            "}}",
+
+            (string("^") + resources).c_str(),
+            (string("^") + resources + ".*\\.lproj/").c_str(),
+            (string("^") + resources + "Base\\.lproj/").c_str(),
+            (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str(),
+
+            (string("^") + resources).c_str(),
+            (string("^") + resources + ".*\\.lproj/").c_str(),
+            (string("^") + resources + "Base\\.lproj/").c_str(),
+            (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str()
+        );
+    } else {
+        /* This is a bundle format without a Resources directory, which means we need to omit
+         * Resources-directory specific rules that would create conflicts. */
+
+        /* We also declare that flat bundles do not use any nested code rules.
+         * Embedded, where flat bundles are used, currently does not support them,
+         * and we have no plans for allowing the replacement of nested code there.
+         * Should anyone actually intend to use nested code rules in a flat
+         * bundle, they are free to create their own rules. */
+        
+         return cfmake<CFDictionaryRef>("{"                    // *** the new (V2) world ***
+            "rules={"                                        // old (V1; legacy) version
+                "'^version.plist$' = #T"                    // include version.plist
+                "'^.*' = #T"                                    // include Resources
+                "'^.*\\.lproj/' = {optional=#T, weight=1000}"            // make localizations optional
+                "'^Base\\.lproj/' = {weight=1010}"                                             // ... except for Base.lproj which really isn't optional at all
+                "'^.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}"  // exclude all locversion.plist files
+            "},rules2={"
+                "'^.*' = #T"                                // include everything as a resource, with the following exceptions
+                "'.*\\.dSYM($|/)' = {weight=11}"            // but allow dSYM directories in code locations (parallel to their code)
+                "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}"    // ignore .DS_Store files
+                "'^Info\\.plist$' = {omit=#T, weight=20}"    // excluded automatically now, but old systems need to be told
+                "'^version\\.plist$' = {weight=20}"            // include version.plist as resource
+                "'^embedded\\.provisionprofile$' = {weight=20}"    // include embedded.provisionprofile as resource
+                "'^PkgInfo$' = {omit=#T, weight=20}"        // traditionally not included
+                "'^.*\\.lproj/' = {optional=#T, weight=1000}"            // make localizations optional
+                "'^Base\\.lproj/' = {weight=1010}"                                             // ... except for Base.lproj which really isn't optional at all
+                "'^.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}"                // exclude all locversion.plist files
+            "}}"
+        );
+   }
 }
 
 
@@ -632,6 +717,14 @@ size_t BundleDiskRep::pageSize(const SigningContext &ctx)
 // Takes an array of CFNumbers of errors to tolerate.
 //
 void BundleDiskRep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
+{
+       strictValidateStructure(cd, tolerated, flags);
+       
+       // now strict-check the main executable (which won't be an app-like object)
+       mExecRep->strictValidate(cd, tolerated, flags & ~kSecCSRestrictToAppLike);
+}
+
+void BundleDiskRep::strictValidateStructure(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
 {
        // scan our metadirectory (_CodeSignature) for unwanted guests
        if (!(flags & kSecCSQuickCheck))
@@ -651,9 +744,6 @@ void BundleDiskRep::strictValidate(const CodeDirectory* cd, const ToleratedError
                if (!mAppLike)
                        if (tolerated.find(kSecCSRestrictToAppLike) == tolerated.end())
                                MacOSError::throwMe(errSecCSNotAppLike);
-       
-       // now strict-check the main executable (which won't be an app-like object)
-       mExecRep->strictValidate(cd, tolerated, flags & ~kSecCSRestrictToAppLike);
 }
 
 void BundleDiskRep::recordStrictError(OSStatus error)
@@ -786,8 +876,48 @@ void BundleDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef
                if (const char *name = CodeDirectory::canonicalSlotName(slot)) {
                        rep->createMeta();
                        string path = rep->metaPath(name);
+
+#if TARGET_OS_OSX
+                       // determine AFSC status if we are told to preserve compression
+                       bool conductCompression = false;
+                       cmpInfo cInfo;
+                       if (this->getPreserveAFSC()) {
+                               struct stat statBuffer;
+                               if (stat(path.c_str(), &statBuffer) == 0) {
+                                       if (queryCompressionInfo(path.c_str(), &cInfo) == 0) {
+                                               if (cInfo.compressionType != 0 && cInfo.compressedSize > 0) {
+                                                       conductCompression = true;
+                                               }
+                                       }
+                               }
+                       }
+#endif
+
                        AutoFileDesc fd(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
                        fd.writeAll(CFDataGetBytePtr(data), CFDataGetLength(data));
+                       fd.close();
+
+#if TARGET_OS_OSX
+                       // if the original file was compressed, compress the new file after move
+                       if (conductCompression) {
+                               CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                               CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType);
+                               CFDictionarySetValue(options, kAFSCCompressionTypes, val);
+                               CFRelease(val);
+
+                               CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options);
+
+                               if (!CompressFile(compressionQueue, path.c_str(), NULL)) {
+                                       secinfo("bundlediskrep", "%p Failed to queue compression of file %s", this, path.c_str());
+                                       MacOSError::throwMe(errSecCSInternalError);
+                               }
+
+                               FinishCompressionAndCleanUp(compressionQueue);
+                               compressionQueue = NULL;
+                               CFRelease(options);
+                       }
+#endif
+
                        mWrittenFiles.insert(name);
                } else
                        MacOSError::throwMe(errSecCSBadBundleFormat);
@@ -827,8 +957,7 @@ void BundleDiskRep::Writer::flush()
        execWriter->flush();
        purgeMetaDirectory();
 }
-       
-       
+
 // purge _CodeSignature of all left-over files from any previous signature
 void BundleDiskRep::Writer::purgeMetaDirectory()
 {
@@ -845,6 +974,11 @@ void BundleDiskRep::Writer::purgeMetaDirectory()
        
 }
 
+void BundleDiskRep::registerStapledTicket()
+{
+       string root = cfStringRelease(copyCanonicalPath());
+       registerStapledTicketInBundle(root);
+}
 
 } // end namespace CodeSigning
 } // end namespace Security