/*
- * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
// 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;
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.
return cdComponentPerArchitecture; // raw
case cdEntitlementSlot:
return cdComponentIsBlob; // global
+ case cdIdentificationSlot:
+ return cdComponentPerArchitecture; // raw
default:
return 0; // global, raw
}
"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);
+ }
}
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;
}
//
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;
}
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
}
}
+//
+// 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.
// 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 },
{ "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