]> git.saurik.com Git - apple/libsecurity_codesigning.git/blobdiff - lib/codedirectory.cpp
libsecurity_codesigning-55037.15.tar.gz
[apple/libsecurity_codesigning.git] / lib / codedirectory.cpp
index 5ce2ff6a9ae21c6a7ee357124568e0874326b05c..0bcb980192b6cc4a746533cdddd8c8094a1b220a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -25,7 +25,8 @@
 // codedirectory - format and operations for code signing "code directory" structures
 //
 #include "codedirectory.h"
-#include "CSCommon.h"
+#include "csutilities.h"
+#include "CSCommonPriv.h"
 
 using namespace UnixPlusPlus;
 
@@ -34,6 +35,18 @@ namespace Security {
 namespace CodeSigning {
 
 
+//
+// Highest understood special slot in this CodeDirectory.
+//
+CodeDirectory::SpecialSlot CodeDirectory::maxSpecialSlot() const
+{
+       SpecialSlot slot = this->nSpecialSlots;
+       if (slot > cdSlotMax)
+               slot = cdSlotMax;
+       return slot;
+}
+
+
 //
 // Canonical filesystem names for select slot numbers.
 // These are variously used for filenames, extended attribute names, etc.
@@ -75,6 +88,8 @@ unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
                return cdComponentPerArchitecture; // raw
        case cdEntitlementSlot:
                return cdComponentIsBlob; // global
+       case cdIdentificationSlot:
+               return cdComponentPerArchitecture; // raw
        default:
                return 0; // global, raw
        }
@@ -92,26 +107,58 @@ const char * const CodeDirectory::debugSlotName[] = {
        "info",
        "requirements",
        "resources",
-       "application"
+       "application",
+       "entitlement"
 };
 #endif //NDEBUG
 
 
 //
-// Check the version of this CodeDirectory for basic sanity.
+// Check a CodeDirectory for basic integrity. This should ensure that the
+// version is understood by our code, and that the internal structure
+// (offsets etc.) is intact. In particular, it must make sure that no offsets
+// point outside the CodeDirectory.
 // Throws if the directory is corrupted or out of versioning bounds.
 // Returns if the version is usable (perhaps with degraded features due to
 // compatibility hacks).
 //
-void CodeDirectory::checkVersion() const
+// Note: There are some things we don't bother checking because they won't
+// cause crashes, and will just be flagged as nonsense later. For example,
+// a Bad Guy could overlap the identifier and hash fields, which is nonsense
+// but not dangerous.
+//
+void CodeDirectory::checkIntegrity() const
 {
+       // check version for support
        if (!this->validateBlob())
                MacOSError::throwMe(errSecCSSignatureInvalid);  // busted
        if (version > compatibilityLimit)
                MacOSError::throwMe(errSecCSSignatureUnsupported);      // too new - no clue
+       if (version < earliestVersion)
+               MacOSError::throwMe(errSecCSSignatureUnsupported);      // too old - can't support
        if (version > currentVersion)
                secdebug("codedir", "%p version 0x%x newer than current 0x%x",
                        this, uint32_t(version), currentVersion);
+       
+       // now check interior offsets for validity
+       if (!stringAt(identOffset))
+               MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
+       if (!contains(hashOffset - uint64_t(hashSize) * nSpecialSlots, hashSize * (uint64_t(nSpecialSlots) + nCodeSlots)))
+               MacOSError::throwMe(errSecCSSignatureFailed); // hash array out of blob range
+       if (const Scatter *scatter = this->scatterVector()) {
+               // the optional scatter vector is terminated with an element having (count == 0)
+               unsigned int pagesConsumed = 0;
+               while (scatter->count) {
+                       if (!contains(scatter, sizeof(Scatter)))
+                               MacOSError::throwMe(errSecCSSignatureFailed);
+                       pagesConsumed += scatter->count;
+                       scatter++;
+               }
+               if (!contains(scatter, sizeof(Scatter)))                        // (even sentinel must be in range)
+                       MacOSError::throwMe(errSecCSSignatureFailed);
+               if (!contains((*this)[pagesConsumed-1], hashSize))      // referenced too many main hash slots
+                       MacOSError::throwMe(errSecCSSignatureFailed);
+       }
 }
 
 
@@ -121,9 +168,10 @@ void CodeDirectory::checkVersion() const
 bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const
 {
        secdebug("codedir", "%p validating slot %d", this, int(slot));
-       Hash::Byte digest[Hash::digestLength];
-       hash(data, length, digest);
-       return memcmp(digest, (*this)[slot], Hash::digestLength) == 0;
+       MakeHash<CodeDirectory> hasher(this);
+       Hashing::Byte digest[hasher->digestLength()];
+       generateHash(hasher, data, length, digest);
+       return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
 }
 
 
@@ -133,9 +181,10 @@ bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) con
 //
 bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
 {
-       Hash::Digest digest;
-       hash(fd, digest, length);
-       return memcmp(digest, (*this)[slot], Hash::digestLength) == 0;
+       MakeHash<CodeDirectory> hasher(this);
+       Hashing::Byte digest[hasher->digestLength()];
+       generateHash(hasher, fd, digest, length);
+       return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
 }
 
 
@@ -147,8 +196,8 @@ bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
 bool CodeDirectory::slotIsPresent(Slot slot) const
 {
        if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
-               const Hash::Byte *digest = (*this)[slot];
-               for (unsigned n = 0; n < Hash::digestLength; n++)
+               const Hashing::Byte *digest = (*this)[slot];
+               for (unsigned n = 0; n < hashSize; n++)
                        if (digest[n])
                                return true;    // non-zero digest => present
        }
@@ -156,6 +205,27 @@ bool CodeDirectory::slotIsPresent(Slot slot) const
 }
 
 
+//
+// Given a hash type code, create an appropriate subclass of DynamicHash
+// and return it. The caller owns the object and  must delete it when done.
+// This function never returns NULL. It throws if the hashType is unsuupported,
+// or if there's an error creating the hasher.
+//
+DynamicHash *CodeDirectory::hashFor(HashAlgorithm hashType)
+{
+       CCDigestAlg alg;
+       switch (hashType) {
+       case kSecCodeSignatureHashSHA1:                                         alg = kCCDigestSHA1; break;
+       case kSecCodeSignatureHashSHA256:                                       alg = kCCDigestSHA256; break;
+       case kSecCodeSignatureHashPrestandardSkein160x256:      alg = kCCDigestSkein160; break;
+       case kSecCodeSignatureHashPrestandardSkein256x512:      alg = kCCDigestSkein256; break;
+       default:
+               MacOSError::throwMe(errSecCSSignatureUnsupported);
+       }
+       return new CCHashInstance(alg);
+}
+
+
 //
 // Hash the next limit bytes of a file and return the digest.
 // If the file is shorter, hash as much as you can.
@@ -163,48 +233,34 @@ bool CodeDirectory::slotIsPresent(Slot slot) const
 // Return how many bytes were actually hashed.
 // Throw on any errors.
 //
-size_t CodeDirectory::hash(FileDesc fd, Hash::Byte *digest, size_t limit)
+size_t CodeDirectory::generateHash(DynamicHash *hasher, FileDesc fd, Hashing::Byte *digest, size_t limit)
 {
-       IFDEBUG(size_t hpos = fd.position());
-       IFDEBUG(size_t hlimit = limit);
-       unsigned char buffer[4096];
-       Hash hash;
-       size_t total = 0;
-       for (;;) {
-               size_t size = sizeof(buffer);
-               if (limit && limit < size)
-                       size = limit;
-               size_t got = fd.read(buffer, size);
-               total += got;
-               if (fd.atEnd())
-                       break;
-               hash(buffer, got);
-               if (limit && (limit -= got) == 0)
-                       break;
-       }
-       hash.finish(digest);
-       secdebug("cdhash", "fd %d %zd@0x%zx => %2x.%2x.%2x...",
-               fd.fd(), hpos, hlimit, digest[0], digest[1], digest[2]);
-       return total;
+       size_t size = hashFileData(fd, hasher, limit);
+       hasher->finish(digest);
+       return size;
 }
 
 
 //
 // Ditto, but hash a memory buffer instead.
 //
-size_t CodeDirectory::hash(const void *data, size_t length, Hash::Byte *digest)
+size_t CodeDirectory::generateHash(DynamicHash *hasher, const void *data, size_t length, Hashing::Byte *digest)
 {
-       Hash hash;
-       hash(data, length);
-       hash.finish(digest);
+       hasher->update(data, length);
+       hasher->finish(digest);
        return length;
 }
 
 
+}      // CodeSigning
+}      // Security
+
+
 //
-// Canonical text form for user-settable code directory flags
+// Canonical text form for user-settable code directory flags.
+// Note: This table is actually exported from Security.framework.
 //
-const CodeDirectory::FlagItem CodeDirectory::flagItems[] = {
+const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[] = {
        { "host",               kSecCodeSignatureHost,                  true },
        { "adhoc",              kSecCodeSignatureAdhoc,                 false },
        { "hard",               kSecCodeSignatureForceHard,             true },
@@ -212,33 +268,3 @@ const CodeDirectory::FlagItem CodeDirectory::flagItems[] = {
        { "expires",    kSecCodeSignatureForceExpiration, true },
        { NULL }
 };
-
-
-//
-// Parse a canonical text description of code flags, in the form
-//     flag,...,flag
-// where each flag can be a prefix of a known flag name.
-// Internally set flags are not accepted.
-//
-uint32_t CodeDirectory::textFlags(std::string text)
-{
-       uint32_t flags = 0;
-       for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
-               string word = (comma == string::npos) ? text : text.substr(0, comma);
-               const CodeDirectory::FlagItem *item;
-               for (item = CodeDirectory::flagItems; item->name; item++)
-                       if (item->external && !strncmp(word.c_str(), item->name, word.size())) {
-                               flags |= item->value;
-                               break;
-                       }
-               if (!item)      // not found
-                       MacOSError::throwMe(errSecCSInvalidFlags);
-               if (comma == string::npos)      // last word
-                       break;
-       }
-       return flags;
-}
-
-
-}      // CodeSigning
-}      // Security