diff options
Diffstat (limited to 'security/tf_daemon/lib_manifest2.c')
-rw-r--r-- | security/tf_daemon/lib_manifest2.c | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/security/tf_daemon/lib_manifest2.c b/security/tf_daemon/lib_manifest2.c new file mode 100644 index 0000000..558b821 --- /dev/null +++ b/security/tf_daemon/lib_manifest2.c @@ -0,0 +1,678 @@ +/** + * Copyright(c) 2011 Trusted Logic. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Trusted Logic nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "lib_manifest2.h" +#include <string.h> + +#define CHAR_CR (uint8_t)0x0D +#define CHAR_LF (uint8_t)0x0A +#define CHAR_TAB (uint8_t)0x09 + +#ifdef LIB_TOOL_IMPLEMENTATION +#include "exos_trace.h" +#define LOG_ERROR(pContext, msg, ...) log_error("%s - line %d: " msg, pContext->pManifestName, pContext->nLine, __VA_ARGS__) +static void log_error(const char* msg, ...) +{ + va_list arg_list; + va_start(arg_list, msg); + exosTraceVPrintf("LIB_MANIFEST2", EXOS_TRACE_ORG_APPLI, K_PRINT_ERROR_LOG, msg, &arg_list); + va_end(arg_list); +} +#else +/* No error messages on the target */ +#ifdef __SYMBIAN32__ +#define LOG_ERROR(pContext...) +#else +#define LOG_ERROR(...) +#endif +#endif + +void libManifest2InitContext( + LIB_MANIFEST2_CONTEXT* pContext) +{ + pContext->nOffset = 0; + pContext->nLine = 1; + pContext->nSectionStartOffset = 0; +} + + +#define CHARACTER_NAME_FIRST 1 +#define CHARACTER_NAME_SUBSEQUENT 2 +#define CHARACTER_SECTION_NAME 3 + +static bool static_checkCharacter(uint8_t x, uint32_t nType) +{ + /* [A-Za-z0-9] is acceptable for everyone */ + if (x >= (uint8_t)'a' && x <= (uint8_t)'z') + { + return true; + } + if (x >=(uint8_t)'A' && x <= (uint8_t)'Z') + { + return true; + } + if (x >= (uint8_t)'0' && x <= (uint8_t)'9') + { + return true; + } + if (nType == CHARACTER_NAME_FIRST) + { + return false; + } + /* Subsequent property name or section name characters can be [_.-] */ + if (x == (uint8_t)'_' || x == (uint8_t)'.' || x == (uint8_t)'-') + { + return true; + } + if (nType == CHARACTER_NAME_SUBSEQUENT) + { + return false; + } + /* Space is also allowed in section names */ + if (x == (uint8_t)' ') + { + return true; + } + return false; +} + +static bool static_sectionNameEqualCaseInsensitive( + uint8_t* pName1, + uint32_t nName1Length, + uint8_t* pName2, + uint32_t nName2Length) +{ + uint32_t i; + if (nName1Length != nName2Length) + { + return false; + } + for (i = 0; i < nName1Length; i++) + { + uint8_t x1 = pName1[i]; + uint8_t x2 = pName2[i]; + + /* This code assumes the characters have been checked before */ + + if ((x1 & ~0x20) != (x2 & ~0x20)) + { + return false; + } + } + return true; +} + +static S_RESULT static_libManifest2GetNextItemInternal( + LIB_MANIFEST2_CONTEXT* pContext, + OUT uint8_t** ppName, + OUT uint32_t* pNameLength, + OUT uint8_t** ppValue, + OUT uint32_t* pValueLength) +{ + S_RESULT nResult = S_ERROR_BAD_FORMAT; + uint8_t* pCurrent = pContext->pManifestContent + pContext->nOffset; + uint8_t* pEnd = pContext->pManifestContent + pContext->nManifestLength; + uint8_t* pLastNonWhitespaceChar; + uint32_t nCurrentSequenceCount; + uint32_t nCurrentChar; + + if (pContext->nType != LIB_MANIFEST2_TYPE_COMPILED) + { + /* Skip leading BOM if we're at the start */ + if (pCurrent == pContext->pManifestContent) + { + /* We're at the start. Skip leading BOM if present */ + /* Note that the UTF-8 encoding of the BOM marker is EF BB BF */ + if (pContext->nManifestLength >= 3 + && pCurrent[0] == 0xEF + && pCurrent[1] == 0xBB + && pCurrent[2] == 0xBF) + { + pCurrent += 3; + } + } + /* Skip comments and newlines */ + while (pCurrent < pEnd) + { + if (*pCurrent == (uint8_t)'#') + { + /* This is the start of a comment. Skip until end of line or end of file */ + pCurrent++; + while (pCurrent < pEnd && *pCurrent != CHAR_LF && *pCurrent != CHAR_CR) + { + if (*pCurrent == 0) + { + LOG_ERROR(pContext, "NUL character forbidden"); + goto error; + } + pCurrent++; + } + } + else if (*pCurrent == CHAR_CR) + { + /* Check if a LF follows */ + pCurrent++; + if (pCurrent < pEnd && *pCurrent == CHAR_LF) + { + pCurrent++; + } + pContext->nLine++; + } + else if (*pCurrent == CHAR_LF) + { + pCurrent++; + pContext->nLine++; + } + else if (*pCurrent == ' ' || *pCurrent == '\t') + { + /* this is the start of a all-whitespace line */ + /* NOTE: this is not allowed by the current spec: spec update needed */ + pCurrent++; + while (pCurrent < pEnd) + { + if (*pCurrent == CHAR_LF || *pCurrent == CHAR_CR) + { + /* End-of-line reached */ + break; + } + if (! (*pCurrent == ' ' || *pCurrent == '\t')) + { + LOG_ERROR(pContext, "A line starting with whitespaces must contain only whitespaces. Illegal character: 0x%02X", *pCurrent); + goto error; + } + pCurrent++; + } + } + else + { + break; + } + } + } + + if (pCurrent >= pEnd) + { + /* No more properties */ + nResult = S_ERROR_ITEM_NOT_FOUND; + goto error; + } + + if (pContext->nType == LIB_MANIFEST2_TYPE_SOURCE_WITH_SECTIONS) + { + if (*pCurrent == '[') + { + /* This is a section descriptor */ + pCurrent++; + *ppName = pCurrent; + *ppValue = NULL; + *pValueLength = 0; + while (true) + { + if (pCurrent >= pEnd) + { + LOG_ERROR(pContext, "EOF reached within a section name"); + goto error; + } + if (*pCurrent == ']') + { + /* End of section name */ + *pNameLength = pCurrent - *ppName; + pCurrent++; + + /* Skip spaces and tabs. Note that this is a deviation from the current spec + (see SWIS). Spec must be updated */ + while (pCurrent < pEnd) + { + if (*pCurrent == ' ' || *pCurrent == '\t') + { + pCurrent++; + } + else if (*pCurrent == CHAR_CR || *pCurrent == CHAR_LF) + { + /* End of line */ + break; + } + else + { + LOG_ERROR(pContext, "Non-space character follows a sectino header: 0x02X", *pCurrent); + } + } + pContext->nOffset = pCurrent - pContext->pManifestContent; + pContext->nSectionStartOffset = pContext->nOffset; + return S_SUCCESS; + } + /* Check section name character */ + if (!static_checkCharacter(*pCurrent, CHARACTER_SECTION_NAME)) + { + LOG_ERROR(pContext, "Invalid character for a section name: 0x%02X", *pCurrent); + goto error; + } + pCurrent++; + } + } + + if (pContext->nSectionStartOffset == 0) + { + /* No section has been found yet. This is a bad format */ + LOG_ERROR(pContext, "Property found outside any section"); + goto error; + } + } + + *ppName = pCurrent; + + /* Check first character of name is in [A-Za-z0-9] */ + if (!static_checkCharacter(*pCurrent, CHARACTER_NAME_FIRST)) + { + LOG_ERROR(pContext, "Invalid first character for a property name: 0x%02X", *pCurrent); + goto error; + } + pCurrent++; + pLastNonWhitespaceChar = pCurrent; + while (true) + { + if (pCurrent == pEnd) + { + LOG_ERROR(pContext, "EOF reached within a property name"); + goto error; + } + if (*pCurrent == ':') + { + /* Colon reached */ + break; + } + if (pContext->nType != LIB_MANIFEST2_TYPE_COMPILED) + { + /* In source manifest, allow space characters before the colon. + This is a deviation from the spec. Spec must be updated */ + if (*pCurrent == ' ' || *pCurrent == '\t') + { + pCurrent++; + continue; + } + } + if (!static_checkCharacter(*pCurrent, CHARACTER_NAME_SUBSEQUENT)) + { + LOG_ERROR(pContext, "Invalid character for a property name: 0x%02X", *pCurrent); + goto error; + } + if (pContext->nType != LIB_MANIFEST2_TYPE_COMPILED) + { + /* Even in a source manifest, property name cannot contain spaces! */ + if (pCurrent != pLastNonWhitespaceChar) + { + LOG_ERROR(pContext, "Property name cannot contain spaces"); + goto error; + } + } + pCurrent++; + pLastNonWhitespaceChar = pCurrent; + } + *pNameLength = pLastNonWhitespaceChar - *ppName; + pCurrent++; + /* Skip spaces and tabs on the right of the colon */ + while (pCurrent < pEnd && (*pCurrent == ' ' || *pCurrent == '\t')) + { + pCurrent++; + } + *ppValue = pCurrent; + pLastNonWhitespaceChar = pCurrent-1; + + nCurrentSequenceCount = 0; + nCurrentChar = 0; + + while (pCurrent < pEnd) + { + uint32_t x; + x = *pCurrent; + if ((x & 0x80) == 0) + { + if (nCurrentSequenceCount != 0) + { + /* We were expecting a 10xxxxxx byte: ill-formed UTF-8 */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + else if (x == 0) + { + /* The null character is forbidden */ + LOG_ERROR(pContext, "NUL character forbidden"); + goto error; + } + /* We have a well-formed Unicode character */ + nCurrentChar = x; + } + else if ((x & 0xC0) == 0xC0) + { + /* Start of a sequence */ + if (nCurrentSequenceCount != 0) + { + /* We were expecting a 10xxxxxx byte: ill-formed UTF-8 */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + else if ((x & 0xE0) == 0xC0) + { + /* 1 byte follows */ + nCurrentChar = x & 0x1F; + nCurrentSequenceCount = 1; + if ((x & 0x1E) == 0) + { + /* Illegal UTF-8: overlong encoding of character in the [0x00-0x7F] range + (must use 1-byte encoding, not a 2-byte encoding) */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + } + else if ((x & 0xF0) == 0xE0) + { + /* 2 bytes follow */ + nCurrentChar = x & 0x0F; + nCurrentSequenceCount = 2; + } + else if ((x & 0xF8) == 0xF0) + { + /* 3 bytes follow */ + nCurrentChar = x & 0x07; + nCurrentSequenceCount = 3; + } + else + { + /* Illegal start of sequence */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + } + else if ((x & 0xC0) == 0x80) + { + /* Continuation byte */ + if (nCurrentSequenceCount == 0) + { + /* We were expecting a sequence start, not a continuation byte */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + else + { + if (nCurrentSequenceCount == 2) + { + /* We're in a 3-byte sequence, check that we're not using an overlong sequence */ + if (nCurrentChar == 0 && (x & 0x20) == 0) + { + /* The character starts with at least 5 zero bits, so has fewer than 11 bits. It should + have used a 2-byte sequence, not a 3-byte sequence */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + } + else if (nCurrentSequenceCount == 3) + { + if (nCurrentChar == 0 && (x & 0x30) == 0) + { + /* The character starts with at least 5 zero bits, so has fewer than 16 bits. It should + have used a 3-byte sequence, not a 4-byte sequence */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + } + nCurrentSequenceCount--; + nCurrentChar = (nCurrentChar << 6) | (x & 0x3F); + } + } + else + { + /* Illegal byte */ + LOG_ERROR(pContext, "Invalid UTF-8 sequence"); + goto error; + } + if (nCurrentSequenceCount == 0) + { + /* nCurrentChar contains the current Unicode character */ + /* check character */ + if ((nCurrentChar >= 0xD800 && nCurrentChar < 0xE000) || nCurrentChar >= 0x110000) + { + /* Illegal code point */ + LOG_ERROR(pContext, "Invalid UTF-8 code point 0x%X", nCurrentChar); + goto error; + } + + if (*pCurrent == CHAR_CR) + { + if (pContext->nType == LIB_MANIFEST2_TYPE_COMPILED) + { + /* Check if a LF follows */ + pCurrent++; + if (pCurrent < pEnd && *pCurrent == CHAR_LF) + { + pCurrent++; + } + pContext->nLine++; + } + goto end; + } + else if (*pCurrent == CHAR_LF) + { + if (pContext->nType == LIB_MANIFEST2_TYPE_COMPILED) + { + pCurrent++; + pContext->nLine++; + } + goto end; + } + } + if (*pCurrent != ' ' && *pCurrent != CHAR_TAB) + { + /* It's a non-whitespace char */ + pLastNonWhitespaceChar = pCurrent; + } + pCurrent++; + } + + /* Hit the end of the manifest; Check that we're not in the middle of a sequence */ + if (nCurrentSequenceCount != 0) + { + LOG_ERROR(pContext, "File ends in the middle of an UTF-8 sequence"); + goto error; + } + +end: + + *pValueLength = pLastNonWhitespaceChar - *ppValue + 1; + pContext->nOffset = pCurrent - pContext->pManifestContent; + + return S_SUCCESS; + +error: + *ppName = NULL; + *pNameLength = 0; + *ppValue = NULL; + *pValueLength = 0; + return nResult; +} + +S_RESULT libManifest2GetNextItem( + LIB_MANIFEST2_CONTEXT* pContext, + OUT uint8_t** ppName, + OUT uint32_t* pNameLength, + OUT uint8_t** ppValue, + OUT uint32_t* pValueLength) +{ + if (pContext->nType == LIB_MANIFEST2_TYPE_COMPILED) + { + /* Don't check for duplicates in binary manifests */ + return static_libManifest2GetNextItemInternal( + pContext, + ppName, + pNameLength, + ppValue, + pValueLength); + } + else + { + uint32_t nOriginalOffset = pContext->nOffset; + uint32_t nOffset; + uint32_t nLine; + uint32_t nSectionStartOffset; + S_RESULT nResult; + uint8_t* pDupName; + uint32_t nDupNameLength; + uint8_t* pDupValue; + uint32_t nDupValueLength; + + /* First get the item */ + nResult = static_libManifest2GetNextItemInternal( + pContext, + ppName, + pNameLength, + ppValue, + pValueLength); + if (nResult != S_SUCCESS) + { + return nResult; + } + /* Save the state of the parser */ + nOffset = pContext->nOffset; + nLine = pContext->nLine; + nSectionStartOffset = pContext->nSectionStartOffset; + if (pContext->nType == LIB_MANIFEST2_TYPE_SOURCE) + { + pContext->nOffset = 0; + } + else if (*ppValue == NULL) + { + /* The item was a section header. Iterate on all section headers and + check for duplicates */ + pContext->nOffset = 0; + } + else + { + if (nSectionStartOffset == 0) + { + LOG_ERROR(pContext, "Property definition outside any section"); + goto bad_format; + } + /* Iterate only on the properties in the section */ + pContext->nOffset = nSectionStartOffset; + } + while (pContext->nOffset < nOriginalOffset) + { + static_libManifest2GetNextItemInternal( + pContext, + &pDupName, + &nDupNameLength, + &pDupValue, + &nDupValueLength); + if (pContext->nType == LIB_MANIFEST2_TYPE_SOURCE_WITH_SECTIONS && *ppValue == NULL) + { + /* Check for duplicate section names */ + if (pDupValue == NULL + && + static_sectionNameEqualCaseInsensitive( + *ppName, + *pNameLength, + pDupName, + nDupNameLength)) + { + pContext->nOffset = nOffset; + pContext->nLine = nLine; + pContext->nSectionStartOffset = nSectionStartOffset; + LOG_ERROR(pContext, "Duplicate section %.*s", nDupNameLength, pDupName); + goto bad_format; + } + } + else + { + /* Check for duplicate property name */ + if (nDupNameLength == *pNameLength && + memcmp(pDupName, *ppName, nDupNameLength) == 0) + { + /* Duplicated property */ + pContext->nOffset = nOffset; + pContext->nLine = nLine; + pContext->nSectionStartOffset = nSectionStartOffset; + LOG_ERROR(pContext,"Duplicate property %.*s", nDupNameLength, pDupName); + goto bad_format; + } + } + } + /* Everything's fine. restore context and exit */ + /* Restore the context */ + pContext->nOffset = nOffset; + pContext->nLine = nLine; + pContext->nSectionStartOffset = nSectionStartOffset; + + return S_SUCCESS; +bad_format: + *ppName = NULL; + *pNameLength = 0; + *ppValue = NULL; + *pValueLength = 0; + return S_ERROR_BAD_FORMAT; + } +} + + +S_RESULT libManifest2CheckFormat( + LIB_MANIFEST2_CONTEXT* pContext, + uint32_t* pnItemCount) +{ + uint32_t nPropertyCount = 0; + uint8_t* pName; + uint32_t nNameLength; + uint8_t* pValue; + uint32_t nValueLength; + S_RESULT nResult; + + pContext->nOffset = 0; + pContext->nLine = 1; + pContext->nSectionStartOffset = 0; + + while (true) + { + nResult = libManifest2GetNextItem( + pContext, + &pName, + &nNameLength, + &pValue, + &nValueLength); + if (nResult == S_ERROR_ITEM_NOT_FOUND) + { + if (pnItemCount != NULL) + { + *pnItemCount = nPropertyCount; + } + return S_SUCCESS; + } + if (nResult != S_SUCCESS) + { + return nResult; + } + nPropertyCount++; + } +} |