+
+/*
+ * cs_validate_codedirectory
+ *
+ * Validate that pointers inside the code directory to make sure that
+ * all offsets and lengths are constrained within the buffer.
+ *
+ * Parameters: cd Pointer to code directory buffer
+ * length Length of buffer
+ *
+ * Returns: 0 Success
+ * EBADEXEC Invalid code signature
+ */
+
+static int
+cs_validate_codedirectory(const CS_CodeDirectory *cd, size_t length)
+{
+
+ if (length < sizeof(*cd))
+ return EBADEXEC;
+ if (ntohl(cd->magic) != CSMAGIC_CODEDIRECTORY)
+ return EBADEXEC;
+ if (cd->hashSize != SHA1_RESULTLEN)
+ return EBADEXEC;
+ if (cd->pageSize != PAGE_SHIFT)
+ return EBADEXEC;
+ if (cd->hashType != CS_HASHTYPE_SHA1)
+ return EBADEXEC;
+
+ if (length < ntohl(cd->hashOffset))
+ return EBADEXEC;
+
+ /* check that nSpecialSlots fits in the buffer in front of hashOffset */
+ if (ntohl(cd->hashOffset) / SHA1_RESULTLEN < ntohl(cd->nSpecialSlots))
+ return EBADEXEC;
+
+ /* check that codeslots fits in the buffer */
+ if ((length - ntohl(cd->hashOffset)) / SHA1_RESULTLEN < ntohl(cd->nCodeSlots))
+ return EBADEXEC;
+
+ if (ntohl(cd->version) >= CS_SUPPORTSSCATTER && cd->scatterOffset) {
+
+ if (length < ntohl(cd->scatterOffset))
+ return EBADEXEC;
+
+ SC_Scatter *scatter = (SC_Scatter *)
+ (((uint8_t *)cd) + ntohl(cd->scatterOffset));
+ uint32_t nPages = 0;
+
+ /*
+ * Check each scatter buffer, since we don't know the
+ * length of the scatter buffer array, we have to
+ * check each entry.
+ */
+ while(1) {
+ /* check that the end of each scatter buffer in within the length */
+ if (((const uint8_t *)scatter) + sizeof(scatter[0]) > (const uint8_t *)cd + length)
+ return EBADEXEC;
+ uint32_t scount = ntohl(scatter->count);
+ if (scount == 0)
+ break;
+ if (nPages + scount < nPages)
+ return EBADEXEC;
+ nPages += scount;
+ scatter++;
+
+ /* XXX check that basees doesn't overlap */
+ /* XXX check that targetOffset doesn't overlap */
+ }
+#if 0 /* rdar://12579439 */
+ if (nPages != ntohl(cd->nCodeSlots))
+ return EBADEXEC;
+#endif
+ }
+
+ if (length < ntohl(cd->identOffset))
+ return EBADEXEC;
+
+ /* identifier is NUL terminated string */
+ if (cd->identOffset) {
+ uint8_t *ptr = (uint8_t *)cd + ntohl(cd->identOffset);
+ if (memchr(ptr, 0, length - ntohl(cd->identOffset)) == NULL)
+ return EBADEXEC;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+cs_validate_blob(const CS_GenericBlob *blob, size_t length)
+{
+ if (length < sizeof(CS_GenericBlob) || length < ntohl(blob->length))
+ return EBADEXEC;
+ return 0;
+}
+
+/*
+ * cs_validate_csblob
+ *
+ * Validate that superblob/embedded code directory to make sure that
+ * all internal pointers are valid.
+ *
+ * Will validate both a superblob csblob and a "raw" code directory.
+ *
+ *
+ * Parameters: buffer Pointer to code signature
+ * length Length of buffer
+ * rcd returns pointer to code directory
+ *
+ * Returns: 0 Success
+ * EBADEXEC Invalid code signature
+ */
+
+static int
+cs_validate_csblob(const uint8_t *addr, size_t length,
+ const CS_CodeDirectory **rcd)
+{
+ const CS_GenericBlob *blob = (const CS_GenericBlob *)(void *)addr;
+ int error;
+
+ *rcd = NULL;
+
+ error = cs_validate_blob(blob, length);
+ if (error)
+ return error;
+
+ length = ntohl(blob->length);
+
+ if (ntohl(blob->magic) == CSMAGIC_EMBEDDED_SIGNATURE) {
+ const CS_SuperBlob *sb = (const CS_SuperBlob *)blob;
+ uint32_t n, count = ntohl(sb->count);
+
+ if (length < sizeof(CS_SuperBlob))
+ return EBADEXEC;
+
+ /* check that the array of BlobIndex fits in the rest of the data */
+ if ((length - sizeof(CS_SuperBlob)) / sizeof(CS_BlobIndex) < count)
+ return EBADEXEC;
+
+ /* now check each BlobIndex */
+ for (n = 0; n < count; n++) {
+ const CS_BlobIndex *blobIndex = &sb->index[n];
+ if (length < ntohl(blobIndex->offset))
+ return EBADEXEC;
+
+ const CS_GenericBlob *subBlob =
+ (const CS_GenericBlob *)(void *)(addr + ntohl(blobIndex->offset));
+
+ size_t subLength = length - ntohl(blobIndex->offset);
+
+ if ((error = cs_validate_blob(subBlob, subLength)) != 0)
+ return error;
+ subLength = ntohl(subBlob->length);
+
+ /* extra validation for CDs, that is also returned */
+ if (ntohl(blobIndex->type) == CSSLOT_CODEDIRECTORY) {
+ const CS_CodeDirectory *cd = (const CS_CodeDirectory *)subBlob;
+ if ((error = cs_validate_codedirectory(cd, subLength)) != 0)
+ return error;
+ *rcd = cd;
+ }
+ }
+
+ } else if (ntohl(blob->magic) == CSMAGIC_CODEDIRECTORY) {
+
+ if ((error = cs_validate_codedirectory((const CS_CodeDirectory *)(void *)addr, length)) != 0)
+ return error;
+ *rcd = (const CS_CodeDirectory *)blob;
+ } else {
+ return EBADEXEC;
+ }
+
+ if (*rcd == NULL)
+ return EBADEXEC;
+
+ return 0;
+}
+
+/*
+ * cs_find_blob_bytes
+ *
+ * Find an blob from the superblob/code directory. The blob must have
+ * been been validated by cs_validate_csblob() before calling
+ * this. Use cs_find_blob() instead.
+ *
+ * Will also find a "raw" code directory if its stored as well as
+ * searching the superblob.
+ *
+ * Parameters: buffer Pointer to code signature
+ * length Length of buffer
+ * type type of blob to find
+ * magic the magic number for that blob
+ *
+ * Returns: pointer Success
+ * NULL Buffer not found
+ */
+
+static const CS_GenericBlob *
+cs_find_blob_bytes(const uint8_t *addr, size_t length, uint32_t type, uint32_t magic)
+{
+ const CS_GenericBlob *blob = (const CS_GenericBlob *)(void *)addr;
+
+ if (ntohl(blob->magic) == CSMAGIC_EMBEDDED_SIGNATURE) {
+ const CS_SuperBlob *sb = (const CS_SuperBlob *)blob;
+ size_t n, count = ntohl(sb->count);
+
+ for (n = 0; n < count; n++) {
+ if (ntohl(sb->index[n].type) != type)
+ continue;
+ uint32_t offset = ntohl(sb->index[n].offset);
+ if (length - sizeof(const CS_GenericBlob) < offset)
+ return NULL;
+ blob = (const CS_GenericBlob *)(void *)(addr + offset);
+ if (ntohl(blob->magic) != magic)
+ continue;
+ return blob;
+ }
+ } else if (type == CSSLOT_CODEDIRECTORY
+ && ntohl(blob->magic) == CSMAGIC_CODEDIRECTORY
+ && magic == CSMAGIC_CODEDIRECTORY)
+ return blob;
+ return NULL;
+}
+
+
+static const CS_GenericBlob *
+cs_find_blob(struct cs_blob *csblob, uint32_t type, uint32_t magic)
+{
+ if ((csblob->csb_flags & CS_VALID) == 0)
+ return NULL;
+ return cs_find_blob_bytes((const uint8_t *)csblob->csb_mem_kaddr, csblob->csb_mem_size, type, magic);
+}
+
+static const uint8_t *
+cs_find_special_slot(const CS_CodeDirectory *cd, uint32_t slot)
+{
+ /* there is no zero special slot since that is the first code slot */
+ if (ntohl(cd->nSpecialSlots) < slot || slot == 0)
+ return NULL;
+
+ return ((const uint8_t *)cd + ntohl(cd->hashOffset) - (SHA1_RESULTLEN * slot));
+}
+