]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/machorep.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / machorep.cpp
index de4b43a4a099d02b68cf2c2e0c3a4797544c226c..02e7faa8bae1baf03095e2fbe53a462a135aab93 100644 (file)
 #include "machorep.h"
 #include "StaticCode.h"
 #include "reqmaker.h"
+#include <security_utilities/logging.h>
+#include <security_utilities/cfmunge.h>
+#include <security_utilities/casts.h>
+
 
 
 namespace Security {
@@ -105,38 +109,32 @@ Universal *MachORep::mainExecutableImage()
 void MachORep::prepareForSigning(SigningContext &context)
 {
        if (context.digestAlgorithms().empty()) {
-               MachO *macho = mainExecutableImage()->architecture();
-               if (const version_min_command *version = macho->findMinVersion()) {
-                       uint32_t limit = 0;
-                       switch (macho->flip(version->cmd)) {
-                       case LC_VERSION_MIN_MACOSX:
+        auto_ptr<MachO> macho(mainExecutableImage()->architecture());
+               
+               uint32_t limit = 0;
+               switch (macho->platform()) {
+                       case 0:
+                               // If we don't know the platform, we stay agile.
+                               return;
+                       case PLATFORM_MACOS:
+                               // 10.11.4 had first proper sha256 support.
                                limit = (10 << 16 | 11 << 8 | 4 << 0);
                                break;
-#if 0 /* need updated libMIS before we can do this switch */
-                       case LC_VERSION_MIN_IPHONEOS:
-                               limit = (9 << 16 | 3 << 8);
-                               break;
-                       case LC_VERSION_MIN_WATCHOS:
-                               limit = (2 << 16 | 2 << 8);
-                               break;
-                       case LC_VERSION_MIN_TVOS:
-                               limit = (9 << 16 | 2 << 8);
+                       case PLATFORM_TVOS:
+                       case PLATFORM_IOS:
+                               // iOS 11 and tvOS 11 had first proper sha256 support.
+                               limit = (11 << 16 | 0 << 8 | 0 << 0);
                                break;
+                       case PLATFORM_WATCHOS:
+                               // We stay agile on the watch right now.
+                               return;
                        default:
+                               // All other platforms are assumed to be new and support SHA256.
                                break;
-#else
-            case LC_VERSION_MIN_IPHONEOS:
-            case LC_VERSION_MIN_WATCHOS:
-            case LC_VERSION_MIN_TVOS:
-                return;
-            default:
-                break;
-#endif
-                       }
-                       if (macho->flip(version->version) >= limit) {
-                               // young enough not to need SHA-1 legacy support
-                               context.setDigestAlgorithm(kSecCodeSignatureHashSHA256);
-                       }
+               }
+               if (macho->minVersion() >= limit) {
+                       // young enough not to need SHA-1 legacy support
+                       context.setDigestAlgorithm(kSecCodeSignatureHashSHA256);
                }
        }
 }
@@ -149,6 +147,68 @@ size_t MachORep::signingBase()
 {
        return mainExecutableImage()->archOffset();
 }
+       
+size_t MachORep::signingLimit()
+{
+       auto_ptr<MachO> macho(mExecutable->architecture());
+       return macho->signingExtent();
+}
+
+bool MachORep::needsExecSeg(const MachO& macho) {
+       uint32_t platform = macho.platform();
+       // Everything embedded gets an exec segment.
+       return platform != 0 && platform != PLATFORM_MACOS;
+}
+
+size_t MachORep::execSegBase(const Architecture *arch)
+{
+       auto_ptr<MachO> macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture());
+
+       if (!needsExecSeg(*macho)) {
+               return 0;
+       }
+
+       segment_command const * const text_cmd = macho->findSegment("__TEXT");
+
+       if (text_cmd == NULL) {
+               return 0;
+       }
+
+       size_t off = 0;
+
+       if (macho->is64()) {
+               off = int_cast<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->fileoff);
+       } else {
+               off = text_cmd->fileoff;
+       }
+
+       return off;
+}
+
+size_t MachORep::execSegLimit(const Architecture *arch)
+{
+       auto_ptr<MachO> macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture());
+
+       if (!needsExecSeg(*macho)) {
+               return 0;
+       }
+
+       segment_command const * const text_cmd = macho->findSegment("__TEXT");
+
+       if (text_cmd == NULL) {
+               return 0;
+       }
+
+       size_t size = 0;
+
+       if (macho->is64()) {
+               size = int_cast<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->filesize);
+       } else {
+               size = text_cmd->filesize;
+       }
+
+       return size;
+}
 
 
 //
@@ -205,6 +265,21 @@ CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
        }
 }
 
+//
+// Retrieve all components, used for signature editing.
+//
+EditableDiskRep::RawComponentMap MachORep::createRawComponents()
+{
+       EditableDiskRep::RawComponentMap  blobMap;
+       const EmbeddedSignatureBlob &blobs = *signingData();
+       
+       for (unsigned int i = 0; i < blobs.count(); ++i) {
+               CodeDirectory::Slot slot = blobs.type(i);
+               const BlobCore *blob = blobs.blob(i);
+               blobMap[slot] = blobs.blobData(slot, blob);
+       }
+       return blobMap;
+}
 
 // Retrieve a component from the embedded signature SuperBlob (if present).
 // This reads the entire signing SuperBlob when first called for an executable,
@@ -215,6 +290,18 @@ CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
 // calls wouldn't be slower in the end.
 //
 CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
+{
+       if (signingData()) {
+               return signingData()->component(slot);
+       }
+       
+       // not found
+       return NULL;
+}
+       
+       
+
+EmbeddedSignatureBlob *MachORep::signingData()
 {
        if (!mSigningData) {            // fetch and cache
                auto_ptr<MachO> macho(mainExecutableImage()->architecture());
@@ -223,21 +310,17 @@ CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
                                size_t offset = macho->flip(cs->dataoff);
                                size_t length = macho->flip(cs->datasize);
                                if ((mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length))) {
-                                       secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
-                                               mSigningData->length(), mSigningData->count(),
-                                               mainExecutablePath().c_str(), macho->architecture().name());
+                                       secinfo("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
+                                                       mSigningData->length(), mSigningData->count(),
+                                                       mainExecutablePath().c_str(), macho->architecture().name());
                                } else {
-                                       secdebug("machorep", "failed to read signing bytes from %s(%s)",
-                                               mainExecutablePath().c_str(), macho->architecture().name());
+                                       secinfo("machorep", "failed to read signing bytes from %s(%s)",
+                                                       mainExecutablePath().c_str(), macho->architecture().name());
                                        MacOSError::throwMe(errSecCSSignatureInvalid);
                                }
                        }
        }
-       if (mSigningData)
-               return mSigningData->component(slot);
-       
-       // not found
-       return NULL;
+       return mSigningData;
 }
 
 
@@ -259,7 +342,7 @@ CFDataRef MachORep::infoPlist()
                        }
                }
        } catch (...) {
-               secdebug("machorep", "exception reading embedded Info.plist");
+               secinfo("machorep", "exception reading embedded Info.plist");
        }
        return info.yield();
 }
@@ -306,6 +389,43 @@ void MachORep::flush()
        mExecutable = new Universal(fd(), offset, length);
 }
 
+CFDictionaryRef MachORep::diskRepInformation()
+{
+    auto_ptr<MachO> macho (mainExecutableImage()->architecture());
+    CFRef<CFDictionaryRef> info;
+
+       uint32_t platform = 0;
+       uint32_t minVersion = 0;
+       uint32_t sdkVersion = 0;
+       
+    if (macho->version(&platform, &minVersion, &sdkVersion)) {
+
+               /* These keys replace the old kSecCodeInfoDiskRepOSPlatform, kSecCodeInfoDiskRepOSVersionMin
+                * and kSecCodeInfoDiskRepOSSDKVersion. The keys were renamed because we changed what value
+                * "platform" represents: For the old key, the actual load command (e.g. LC_VERSION_MIN_MACOSX)
+                * was returned; for the new key, we return one of the PLATFORM_* values used by LC_BUILD_VERSION.
+                *
+                * The keys are private and undocumented, and maintaining a translation table between the old and
+                * new domain would provide little value at high cost, but we do remove the old keys to make
+                * the change obvious.
+                */
+               
+        info.take(cfmake<CFMutableDictionaryRef>("{%O = %d,%O = %d,%O = %d}",
+                                              kSecCodeInfoDiskRepVersionPlatform, platform,
+                                              kSecCodeInfoDiskRepVersionMin, minVersion,
+                                              kSecCodeInfoDiskRepVersionSDK, sdkVersion));
+
+        if (platform == PLATFORM_MACOS && sdkVersion < (10 << 16 | 9 << 8))
+        {
+            info.take(cfmake<CFMutableDictionaryRef>("{+%O, %O = 'OS X SDK version before 10.9 does not support Library Validation'}",
+                                                  info.get(),
+                                                  kSecCodeInfoDiskRepNoLibraryValidation));
+        }
+    }
+
+    return info.yield();
+}
+
 
 //
 // Return a recommended unique identifier.
@@ -359,7 +479,7 @@ Requirement *MachORep::libraryRequirements(const Architecture *arch, const Signi
                        size_t length = macho->flip(ldep->datasize);
                        if (LibraryDependencyBlob *deplist = LibraryDependencyBlob::readBlob(macho->fd(), macho->offset() + offset, length)) {
                                try {
-                                       secdebug("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)",
+                                       secinfo("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)",
                                                deplist->length(), deplist->count(),
                                                mainExecutablePath().c_str(), macho->architecture().name());
                                        unsigned count = deplist->count();
@@ -378,13 +498,13 @@ Requirement *MachORep::libraryRequirements(const Architecture *arch, const Signi
                                                                MacOSError::check(SecRequirementCopyData(areq, kSecCSDefaultFlags, &reqData.aref()));
                                                                req = Requirement::specific((const BlobCore *)CFDataGetBytePtr(reqData));
                                                        } else {
-                                                               secdebug("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n);
+                                                               secinfo("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n);
                                                                continue;
                                                        }
                                                        chain.add();
                                                        maker.copy(req);
                                                } else
-                                                       secdebug("machorep", "missing DR info for library index %d", n);
+                                                       secinfo("machorep", "missing DR info for library index %d", n);
                                        }
                                        ::free(deplist);
                                } catch (...) {
@@ -414,18 +534,11 @@ size_t MachORep::pageSize(const SigningContext &)
 //
 void MachORep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
 {
-       DiskRep::strictValidate(cd, tolerated, flags);
+       SingleDiskRep::strictValidate(cd, tolerated, flags);
        
        // if the constructor found suspicious issues, fail a struct validation now
        if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end())
                MacOSError::throwMe(errSecCSBadMainExecutable);
-       
-       // the signature's code extent must be what we would have picked (no funny hand editing)
-       if (cd) {
-               auto_ptr<MachO> macho(mExecutable->architecture());
-               if (cd->signingLimit() != macho->signingExtent())
-                       MacOSError::throwMe(errSecCSSignatureInvalid);
-       }
 }
 
 
@@ -447,6 +560,7 @@ DiskRep::Writer *MachORep::writer()
 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
 {
        assert(false);
+    Syslog::notice("code signing internal error: trying to write Mach-O component directly");
        MacOSError::throwMe(errSecCSInternalError);
 }