| /********************************************************************************/ |
| /* */ |
| /* IMA Routines */ |
| /* Written by Ken Goldman */ |
| /* IBM Thomas J. Watson Research Center */ |
| /* */ |
| /* (c) Copyright IBM Corporation 2016 - 2019. */ |
| /* */ |
| /* 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 names of the IBM Corporation 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 */ |
| /* HOLDER 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. */ |
| /********************************************************************************/ |
| |
| /* imalib is a set of utility functions to handle IMA (Integrity Measurement Architecture) event |
| logs. |
| |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| |
| #ifdef TPM_POSIX |
| #include <arpa/inet.h> |
| #endif |
| |
| #ifdef TPM_WINDOWS |
| #include <winsock2.h> |
| #endif |
| |
| #include <openssl/x509.h> |
| #include <openssl/bio.h> |
| |
| #include <ibmtss/TPM_Types.h> |
| #include <ibmtss/tsscryptoh.h> |
| #include <ibmtss/tssmarshal.h> |
| #include <ibmtss/tssprint.h> |
| #include <ibmtss/tsserror.h> |
| |
| #include "imalib.h" |
| |
| #define IMA_PARSE_FUNCTIONS_MAX 128 |
| |
| static uint32_t IMA_Uint32_Convert(const uint8_t *stream, |
| int littleEndian); |
| static uint32_t IMA_Strn2cpy(char *dest, const uint8_t *src, |
| size_t destLength, size_t srcLength); |
| static void IMA_Event_ParseName(ImaEvent *imaEvent); |
| |
| static uint32_t IMA_TemplateData_ReadFile(ImaEvent *imaEvent, |
| int *endOfFile, |
| FILE *inFile, |
| int littleEndian); |
| static uint32_t IMA_TemplateDataIma_ReadFile(ImaEvent *imaEvent, |
| int *endOfFile, |
| FILE *inFile, |
| int littleEndian); |
| |
| /* callback to parse a template data field */ |
| |
| typedef uint32_t (*TemplateDataParseFunction_t)(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| static uint32_t IMA_TemplateName_Parse(TemplateDataParseFunction_t templateDataParseFunctions[], |
| size_t templateDataParseFunctionsSize, |
| ImaEvent *imaEvent); |
| static uint32_t |
| IMA_TemplateName_ParseCustom(TemplateDataParseFunction_t templateDataParseFunctions[], |
| size_t templateDataParseFunctionsSize, |
| ImaEvent *imaEvent); |
| static uint32_t IMA_ParseD(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| static uint32_t IMA_ParseDNG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| static uint32_t IMA_ParseNNG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| static uint32_t IMA_ParseSIG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| static uint32_t IMA_ParseDMODSIG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| static uint32_t IMA_ParseMODSIG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| static uint32_t IMA_ParseBUF(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian); |
| |
| extern int tssUtilsVerbose; |
| |
| /* IMA_Event_Init() initializes the ImaEvent structure so that IMA_Event_Free() is safe. |
| |
| */ |
| |
| void IMA_Event_Init(ImaEvent *imaEvent) |
| { |
| if (imaEvent != NULL) { |
| imaEvent->nameInt = IMA_UNSUPPORTED; |
| imaEvent->template_data = NULL; |
| } |
| return; |
| } |
| |
| /* IMA_Event_Free() frees any memory allocated for the ImaEvent structure. |
| |
| */ |
| |
| void IMA_Event_Free(ImaEvent *imaEvent) |
| { |
| if (imaEvent != NULL) { |
| free(imaEvent->template_data); |
| imaEvent->template_data = NULL; |
| } |
| return; |
| } |
| |
| /* IMA_Event_Trace() traces the ImaEvent structure. |
| |
| If traceTemplate is FALSE, template data is not traced. This handles the case where template |
| data is not unmarshaled. |
| |
| */ |
| |
| void IMA_Event_Trace(ImaEvent *imaEvent, int traceTemplate) |
| { |
| printf("IMA_Event_Trace: PCR index %u\n", imaEvent->pcrIndex); |
| TSS_PrintAll("IMA_Event_Trace: hash", |
| imaEvent->digest, sizeof(((ImaEvent *)NULL)->digest)); |
| |
| printf("IMA_Event_Trace: name length %u\n", imaEvent->name_len); |
| printf("IMA_Event_Trace: name %s\n", imaEvent->name); |
| printf("IMA_Event_Trace: name integer %u\n", imaEvent->nameInt); |
| printf("IMA_Event_Trace: template data length %u\n", imaEvent->template_data_len); |
| /* in some use cases, the template_data field is not populated. In those cases, do not trace |
| it. */ |
| if (traceTemplate) { |
| TSS_PrintAll("IMA_Event_Trace: template data", |
| imaEvent->template_data, imaEvent->template_data_len); |
| } |
| return; |
| } |
| |
| /* IMA_Event_ParseName() parses the Template Name and sets the nameInt field */ |
| |
| static void IMA_Event_ParseName(ImaEvent *imaEvent) |
| { |
| if (strcmp(imaEvent->name, "ima-ng") == 0) { |
| imaEvent->nameInt = IMA_FORMAT_IMA_NG; |
| } |
| else if (strcmp(imaEvent->name, "ima-sig") == 0) { |
| imaEvent->nameInt = IMA_FORMAT_IMA_SIG; |
| } |
| else if (strcmp(imaEvent->name, "ima") == 0) { |
| imaEvent->nameInt = IMA_FORMAT_IMA; |
| } |
| else if (strcmp(imaEvent->name, "ima-modsig") == 0) { |
| imaEvent->nameInt = IMA_FORMAT_MODSIG; |
| } |
| else if (strcmp(imaEvent->name, "ima-buf") == 0) { |
| imaEvent->nameInt = IMA_FORMAT_BUF; |
| } |
| /* the template data parser currently supports only these formats. */ |
| else { |
| imaEvent->nameInt = IMA_UNSUPPORTED; |
| } |
| return; |
| } |
| |
| void IMA_TemplateData_Init(ImaTemplateData *imaTemplateData) |
| { |
| imaTemplateData->imaTemplateDNG.hashLength = 0; |
| imaTemplateData->imaTemplateDNG.fileDataHashLength = 0; |
| imaTemplateData->imaTemplateNNG.fileNameLength = 0; |
| imaTemplateData->imaTemplateNNG.fileName[0] = '\0'; |
| imaTemplateData->imaTemplateSIG.sigLength = 0; |
| imaTemplateData->imaTemplateSIG.sigHeaderLength = 0; |
| imaTemplateData->imaTemplateSIG.signatureSize = 0; |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashLength = 0; |
| imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength = 0; |
| imaTemplateData->imaTemplateMODSIG.modSigLength = 0; |
| imaTemplateData->imaTemplateBUF.bufLength = 0; |
| return; |
| } |
| |
| /* IMA_TemplateData_Trace() traces the ImaTemplateData structure. |
| |
| nameInt maps to the template name. |
| |
| */ |
| |
| void IMA_TemplateData_Trace(ImaTemplateData *imaTemplateData, |
| unsigned int nameInt) |
| { |
| nameInt = nameInt; /* obsolete now that custom templates are supported */ |
| /* d-ng */ |
| printf("IMA_TemplateData_Trace: DNG hashLength %u\n", imaTemplateData->imaTemplateDNG.hashLength); |
| printf("IMA_TemplateData_Trace: DNG hashAlg %s\n", imaTemplateData->imaTemplateDNG.hashAlg); |
| TSS_PrintAll("IMA_Template_Trace: DNG file data hash", |
| imaTemplateData->imaTemplateDNG.fileDataHash, |
| imaTemplateData->imaTemplateDNG.fileDataHashLength); |
| /* n-ng */ |
| printf("IMA_TemplateData_Trace: NNG fileNameLength %u\n", |
| imaTemplateData->imaTemplateNNG.fileNameLength); |
| if (imaTemplateData->imaTemplateNNG.fileNameLength > 0) { |
| printf("IMA_TemplateData_Trace: NNG fileName %s\n", imaTemplateData->imaTemplateNNG.fileName); |
| } |
| /* sig */ |
| printf("IMA_TemplateData_Trace: SIG sigLength %u\n", imaTemplateData->imaTemplateSIG.sigLength); |
| if (imaTemplateData->imaTemplateSIG.sigLength != 0) { |
| TSS_PrintAll("IMA_TemplateData_Trace: sigHeader", |
| imaTemplateData->imaTemplateSIG.sigHeader, |
| imaTemplateData->imaTemplateSIG.sigHeaderLength); |
| printf("IMA_TemplateData_Trace: SIG signatureSize %u\n", |
| imaTemplateData->imaTemplateSIG.signatureSize); |
| TSS_PrintAll("IMA_TemplateData_Trace: SIG signature", |
| imaTemplateData->imaTemplateSIG.signature, |
| imaTemplateData->imaTemplateSIG.signatureSize); |
| } |
| /* d-modsig */ |
| printf("IMA_TemplateData_Trace: DMODSIG dModSigHashLength %u\n", |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashLength); |
| if (imaTemplateData->imaTemplateDMODSIG.dModSigHashLength != 0) { |
| printf("IMA_TemplateData_Trace: DMODSIG dModSigHashAlg %s\n", |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashAlg); |
| TSS_PrintAll("IMA_Template_Trace: DMODSIG file data hash", |
| imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHash, |
| imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength); |
| } |
| /* modsig */ |
| printf("IMA_TemplateData_Trace: MODSIG modSigLength %u\n", |
| imaTemplateData->imaTemplateMODSIG.modSigLength); |
| if (imaTemplateData->imaTemplateMODSIG.modSigLength != 0) { |
| TSS_PrintAll("IMA_TemplateData_Trace: MODSIG modSigData", |
| imaTemplateData->imaTemplateMODSIG.modSigData, |
| imaTemplateData->imaTemplateMODSIG.modSigLength); |
| #ifndef TPM_TSS_MBEDTLS |
| { |
| PKCS7 *pkcs7 = NULL; |
| unsigned char *tmpData = NULL; |
| /* tmp pointer because d2i moves the pointer */ |
| tmpData = imaTemplateData->imaTemplateMODSIG.modSigData; |
| pkcs7 = d2i_PKCS7(NULL, /* freed @1 */ |
| (const unsigned char **)&tmpData, |
| imaTemplateData->imaTemplateMODSIG.modSigLength); |
| if (pkcs7 != NULL) { |
| BIO *bio = NULL; |
| bio = BIO_new_fd(fileno(stdout), BIO_NOCLOSE); /* freed @2 */ |
| if (bio != NULL) { |
| PKCS7_print_ctx(bio, pkcs7, 4, NULL); |
| BIO_free(bio); /* @2 */ |
| } |
| else { |
| printf("IMA_TemplateData_Trace: MODSIG Could not create BIO for PKCS7\n"); |
| } |
| PKCS7_free(pkcs7); /* @1 */ |
| } |
| else { |
| printf("IMA_TemplateData_Trace: MODSIG Could not trace modSigData as PKCS7\n"); |
| } |
| } |
| #endif /* TPM_TSS_MBEDTLS */ |
| } |
| /* buf */ |
| printf("IMA_TemplateData_Trace: BUF bufLength %u\n", imaTemplateData->imaTemplateBUF.bufLength); |
| if (imaTemplateData->imaTemplateBUF.bufLength != 0) { |
| TSS_PrintAll("IMA_TemplateData_Trace: BUF bufData", |
| imaTemplateData->imaTemplateBUF.bufData, imaTemplateData->imaTemplateBUF.bufLength); |
| #ifndef TPM_TSS_MBEDTLS |
| if ((strcmp((const char *)imaTemplateData->imaTemplateNNG.fileName, ".builtin_trusted_keys") == 0) || |
| (strcmp((const char *)imaTemplateData->imaTemplateNNG.fileName, ".ima") == 0)) { |
| { |
| X509 *x509 = NULL; |
| unsigned char *tmpData = NULL; |
| /* tmp pointer because d2i moves the pointer */ |
| tmpData = imaTemplateData->imaTemplateBUF.bufData; |
| x509 = d2i_X509(NULL, /* freed @1 */ |
| (const unsigned char **)&tmpData, |
| imaTemplateData->imaTemplateBUF.bufLength); |
| if (x509 != NULL) { |
| X509_print_fp(stdout, x509); |
| X509_free(x509); /* @1 */ |
| } |
| else { |
| printf("IMA_TemplateData_Trace: BUF Could not trace bufData as X509\n"); |
| } |
| } |
| |
| } |
| #endif /* TPM_TSS_MBEDTLS */ |
| } |
| return; |
| } |
| |
| /* IMA_Event_ReadFile() reads one IMA event from a file. |
| |
| It currently supports these template formats: ima, ima-ng, ima-sig. |
| |
| This is typically used at the client, reading from the pseudofile. |
| */ |
| |
| uint32_t IMA_Event_ReadFile(ImaEvent *imaEvent, /* freed by caller */ |
| int *endOfFile, |
| FILE *inFile, |
| int littleEndian) |
| { |
| int rc = 0; |
| size_t readSize; |
| *endOfFile = FALSE; |
| |
| imaEvent->template_data = NULL; /* for free */ |
| |
| /* read the IMA PCR index */ |
| if ((rc == 0) && !(*endOfFile)) { |
| readSize = fread(&(imaEvent->pcrIndex), |
| sizeof(((ImaEvent *)NULL)->pcrIndex), 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_Event_ReadFile: could not read pcrIndex, returned %lu\n", |
| (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| /* PCR index endian convert */ |
| if ((rc == 0) && !(*endOfFile)) { |
| imaEvent->pcrIndex = IMA_Uint32_Convert((uint8_t *)&imaEvent->pcrIndex, littleEndian); |
| /* range check the PCR index */ |
| if (imaEvent->pcrIndex >= IMPLEMENTATION_PCR) { |
| printf("ERROR: IMA_Event_ReadFile: PCR index %u %08x out of range\n", |
| imaEvent->pcrIndex, imaEvent->pcrIndex); |
| rc = TSS_RC_BAD_PROPERTY_VALUE; |
| } |
| } |
| /* read the IMA digest, this is hard coded to SHA-1 */ |
| if ((rc == 0) && !(*endOfFile)) { |
| readSize = fread(&(imaEvent->digest), |
| sizeof(((ImaEvent *)NULL)->digest), 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_Event_ReadFile: could not read digest, returned %lu\n", |
| (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| /* read the IMA name length */ |
| if ((rc == 0) && !(*endOfFile)) { |
| readSize = fread(&(imaEvent->name_len), |
| sizeof(((ImaEvent *)NULL)->name_len), 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_Event_ReadFile: could not read name_len, returned %lu\n", |
| (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| imaEvent->name_len = IMA_Uint32_Convert((uint8_t *)&imaEvent->name_len, littleEndian); |
| } |
| /* bounds check the name length, leave a byte for the nul terminator */ |
| if ((rc == 0) && !(*endOfFile)) { |
| if (imaEvent->name_len > (sizeof(((ImaEvent *)NULL)->name)) -1) { |
| printf("ERROR: IMA_Event_ReadFile: template name length too big: %u\n", |
| imaEvent->name_len); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| /* read the template name */ |
| if ((rc == 0) && !(*endOfFile)) { |
| /* nul terminate first */ |
| memset(imaEvent->name, 0, sizeof(((ImaEvent *)NULL)->name)); |
| readSize = fread(&(imaEvent->name), |
| imaEvent->name_len, 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_Event_ReadFile: could not read template name, returned %lu\n", |
| (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| /* record the template name as an int */ |
| if ((rc == 0) && !(*endOfFile)) { |
| IMA_Event_ParseName(imaEvent); |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| if (imaEvent->nameInt != IMA_FORMAT_IMA) { /* standard format */ |
| rc = IMA_TemplateData_ReadFile(imaEvent, endOfFile, inFile, littleEndian); |
| } |
| else { /* unique 'ima' format */ |
| rc = IMA_TemplateDataIma_ReadFile(imaEvent, endOfFile, inFile, littleEndian); |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_TemplateData_ReadFile() reads the template data as a pure array. It handles the normal case |
| of template data length plus template data. |
| */ |
| |
| static uint32_t IMA_TemplateData_ReadFile(ImaEvent *imaEvent, /* freed by caller */ |
| int *endOfFile, |
| FILE *inFile, |
| int littleEndian) |
| { |
| int rc = 0; |
| size_t readSize; |
| |
| /* read template data length */ |
| if ((rc == 0) && !(*endOfFile)) { |
| readSize = fread(&(imaEvent->template_data_len), |
| sizeof(((ImaEvent *)NULL)->template_data_len ), 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_TemplateData_ReadFile: could not read template_data_len, " |
| " returned %lu\n", (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| imaEvent->template_data_len = |
| IMA_Uint32_Convert((uint8_t *)&imaEvent->template_data_len, |
| littleEndian); |
| } |
| /* bounds check the template data length */ |
| if ((rc == 0) && !(*endOfFile)) { |
| if (imaEvent->template_data_len > TCG_TEMPLATE_DATA_LEN_MAX) { |
| printf("ERROR: IMA_TemplateData_ReadFile: template data length too big: %u\n", |
| imaEvent->template_data_len); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| imaEvent->template_data = malloc(imaEvent->template_data_len); |
| if (imaEvent->template_data == NULL) { |
| printf("ERROR: IMA_TemplateData_ReadFile: " |
| "could not allocate template data, size %u\n", |
| imaEvent->template_data_len); |
| rc = TSS_RC_OUT_OF_MEMORY; |
| } |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| readSize = fread(imaEvent->template_data, |
| imaEvent->template_data_len, 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_Event_ReadFile: could not read template_data, " |
| "returned %lu\n", (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_TemplateDataIma_ReadFile() reads the template data. It handles the special case of the |
| template name 'ima', which does not have a template data length. 'ima' has a 20 byte file data |
| hash, a 4 byte file name length, and a file name. |
| */ |
| |
| static uint32_t IMA_TemplateDataIma_ReadFile(ImaEvent *imaEvent, /* freed by caller */ |
| int *endOfFile, |
| FILE *inFile, |
| int littleEndian) |
| { |
| int rc = 0; |
| size_t readSize; |
| uint8_t fileDataHash[SHA1_DIGEST_SIZE]; /* IMA hard coded to SHA-1 */ |
| uint32_t fileNameLengthIbo; /* ima log byte order */ |
| uint32_t fileNameLength; /* host byte order */ |
| |
| /* read the fileDataHash digest, this is hard coded to SHA-1 */ |
| if ((rc == 0) && !(*endOfFile)) { |
| readSize = fread(&fileDataHash, |
| sizeof(fileDataHash), 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_TemplateDataIma_ReadFile: " |
| "could not read fileDataHash, returned %lu\n", |
| (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| /* read the IMA name length */ |
| if ((rc == 0) && !(*endOfFile)) { |
| readSize = fread(&fileNameLengthIbo, |
| sizeof(fileNameLength), 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_TemplateDataIma_ReadFile: " |
| "could not read fileNameLength, returned %lu\n", |
| (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| fileNameLength = IMA_Uint32_Convert((uint8_t *)&fileNameLengthIbo, littleEndian); |
| /* should check for addition overflowing a uint32_t */ |
| if (fileNameLength > (0xffffffff - (uint32_t)(sizeof(fileDataHash) + sizeof(fileNameLength)))) { |
| printf("ERROR: IMA_TemplateDataIma_ReadFile: file name length too big: %u\n", |
| fileNameLength); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| /* addition is safe because of above check */ |
| imaEvent->template_data_len = sizeof(fileDataHash) + sizeof(fileNameLength) + fileNameLength; |
| } |
| /* bounds check the template data length */ |
| if ((rc == 0) && !(*endOfFile)) { |
| if (imaEvent->template_data_len > TCG_TEMPLATE_DATA_LEN_MAX) { |
| printf("ERROR: IMA_TemplateDataIma_ReadFile: template data length too big: %u\n", |
| imaEvent->template_data_len); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| if ((rc == 0) && !(*endOfFile)) { |
| imaEvent->template_data = malloc(imaEvent->template_data_len); |
| if (imaEvent->template_data == NULL) { |
| printf("ERROR: IMA_TemplateData_ReadFile: " |
| "could not allocate template data, size %u\n", |
| imaEvent->template_data_len); |
| rc = TSS_RC_OUT_OF_MEMORY; |
| } |
| } |
| /* copy results to template_data */ |
| if ((rc == 0) && !(*endOfFile)) { |
| /* copy file data hash */ |
| memcpy(imaEvent->template_data, fileDataHash, sizeof(fileDataHash)); |
| /* copy file name length */ |
| memcpy(imaEvent->template_data + sizeof(fileDataHash), |
| &fileNameLength, sizeof(fileNameLength)); |
| /* read and copy the file name */ |
| readSize = fread(imaEvent->template_data + sizeof(fileDataHash) + sizeof(fileNameLength), |
| fileNameLength, 1, inFile); |
| if (readSize != 1) { |
| if (feof(inFile)) { |
| *endOfFile = TRUE; |
| } |
| else { |
| printf("ERROR: IMA_TemplateDataIma_ReadFile: " |
| "could not read fileNameLength, returned %lu\n", |
| (unsigned long)readSize); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_Event_ReadBuffer() reads one IMA event from a buffer. |
| |
| This is typically used at the server, reading from a client connection. |
| |
| Although the raw IMA event log 'ima' template does not have a template data length, this function |
| at the server assumes it has been inserted by the client. |
| |
| If getTemplate is TRUE, the template data is copied to a malloced imaEvent->template_data. If |
| FALSE, template data is skipped. FALSE is used for the first pass, where the template data is not |
| needed until the hash is validated. |
| |
| */ |
| |
| uint32_t IMA_Event_ReadBuffer(ImaEvent *imaEvent, /* freed by caller */ |
| size_t *length, |
| uint8_t **buffer, |
| int *endOfBuffer, |
| int littleEndian, |
| int getTemplate) |
| { |
| int rc = 0; |
| |
| imaEvent->template_data = NULL; /* for free */ |
| if (*length == 0) { |
| *endOfBuffer = 1; |
| } |
| else { |
| /* read the IMA pcr index */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_Event_ReadBuffer: buffer too small for PCR index\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaEvent->pcrIndex = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* sanity check the PCR index */ |
| if (rc == 0) { |
| if (imaEvent->pcrIndex != IMA_PCR) { |
| printf("ERROR: IMA_Event_ReadBuffer: PCR index %u not PCR %u\n", |
| IMA_PCR, imaEvent->pcrIndex); |
| rc = TSS_RC_BAD_PROPERTY_VALUE; |
| } |
| } |
| /* read the IMA digest, this is hard coded to SHA-1 */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(((ImaEvent *)NULL)->digest)) { |
| printf("ERROR: IMA_Event_ReadBuffer: buffer too small for IMA digest\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaEvent->digest), *buffer, sizeof(((ImaEvent *)NULL)->digest)); |
| *buffer += sizeof(((ImaEvent *)NULL)->digest); |
| *length -= sizeof(((ImaEvent *)NULL)->digest); |
| } |
| } |
| /* read the IMA name length */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_Event_ReadBuffer: " |
| "buffer too small for IMA template name length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaEvent->name_len = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* read the template name */ |
| if (rc == 0) { |
| /* bounds check the name length */ |
| if (imaEvent->name_len > TCG_EVENT_NAME_LEN_MAX) { |
| printf("ERROR: IMA_Event_ReadBuffer: Error, template name length too big: %u\n", |
| imaEvent->name_len); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else if (*length < imaEvent->name_len) { |
| printf("ERROR: IMA_Event_ReadBuffer: buffer too small for template name\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| /* nul terminate first */ |
| memset(imaEvent->name, 0, sizeof(((ImaEvent *)NULL)->name)); |
| memcpy(&(imaEvent->name), *buffer, imaEvent->name_len); |
| *buffer += imaEvent->name_len; |
| *length -= imaEvent->name_len; |
| } |
| } |
| /* record the template name as an int */ |
| if (rc == 0) { |
| IMA_Event_ParseName(imaEvent); |
| } |
| /* read the template data length */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_Event_ReadBuffer: buffer too small for template data length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaEvent->template_data_len = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* allocate for the template data */ |
| if (rc == 0) { |
| if (getTemplate) { |
| /* bounds check the template data length */ |
| if (imaEvent->template_data_len > TCG_TEMPLATE_DATA_LEN_MAX) { |
| printf("ERROR: IMA_Event_ReadBuffer: template data length too big: %u\n", |
| imaEvent->template_data_len); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else if (*length < imaEvent->template_data_len) { |
| printf("ERROR: IMA_Event_ReadBuffer: buffer too small for template data\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| if (rc == 0) { |
| imaEvent->template_data = malloc(imaEvent->template_data_len); |
| if (imaEvent->template_data == NULL) { |
| printf("ERROR: IMA_Event_ReadBuffer: " |
| "could not allocate template data, size %u\n", |
| imaEvent->template_data_len); |
| rc = TSS_RC_OUT_OF_MEMORY; |
| } |
| } |
| if (rc == 0) { |
| memcpy(imaEvent->template_data, *buffer, imaEvent->template_data_len); |
| } |
| } |
| } |
| /* move the buffer even if getTemplate is false */ |
| if (rc == 0) { |
| *buffer += imaEvent->template_data_len; |
| *length -= imaEvent->template_data_len; |
| } |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_TemplateName_Parse() parses the template name and registers the template data callbacks */ |
| |
| static uint32_t IMA_TemplateName_Parse(TemplateDataParseFunction_t templateDataParseFunctions[], |
| size_t templateDataParseFunctionsSize, |
| ImaEvent *imaEvent) |
| { |
| uint32_t rc = 0; |
| size_t i; |
| |
| /* initialize all the function pointers to NULL */ |
| for (i = 0 ; (rc == 0) && (i < templateDataParseFunctionsSize) ; i++) { |
| templateDataParseFunctions[i] = NULL; |
| } |
| /* parse the name into the callback structure */ |
| if (rc == 0) { |
| switch (imaEvent->nameInt) { |
| /* these are the pre-defined formats */ |
| case IMA_FORMAT_IMA_NG: |
| /* d-ng | n-ng */ |
| templateDataParseFunctions[0] = (TemplateDataParseFunction_t)IMA_ParseDNG; |
| templateDataParseFunctions[1] = (TemplateDataParseFunction_t)IMA_ParseNNG; |
| break; |
| case IMA_FORMAT_IMA_SIG: |
| /* d-ng | n-ng | sig */ |
| templateDataParseFunctions[0] = (TemplateDataParseFunction_t)IMA_ParseDNG; |
| templateDataParseFunctions[1] = (TemplateDataParseFunction_t)IMA_ParseNNG; |
| templateDataParseFunctions[2] = (TemplateDataParseFunction_t)IMA_ParseSIG; |
| break; |
| case IMA_FORMAT_IMA: |
| templateDataParseFunctions[0] = (TemplateDataParseFunction_t)IMA_ParseD; |
| templateDataParseFunctions[1] = (TemplateDataParseFunction_t)IMA_ParseNNG; |
| break; |
| case IMA_FORMAT_MODSIG: |
| /* d-ng | n-ng | sig | d-modsig | modsig */ |
| templateDataParseFunctions[0] = (TemplateDataParseFunction_t)IMA_ParseDNG; |
| templateDataParseFunctions[1] = (TemplateDataParseFunction_t)IMA_ParseNNG; |
| templateDataParseFunctions[2] = (TemplateDataParseFunction_t)IMA_ParseSIG; |
| templateDataParseFunctions[3] = (TemplateDataParseFunction_t)IMA_ParseDMODSIG; |
| templateDataParseFunctions[4] = (TemplateDataParseFunction_t)IMA_ParseMODSIG; |
| break; |
| case IMA_FORMAT_BUF: |
| /* d-ng | n-ng | buf */ |
| templateDataParseFunctions[0] = (TemplateDataParseFunction_t)IMA_ParseDNG; |
| templateDataParseFunctions[1] = (TemplateDataParseFunction_t)IMA_ParseNNG; |
| templateDataParseFunctions[2] = (TemplateDataParseFunction_t)IMA_ParseBUF; |
| break; |
| /* these are potentially the custom templates */ |
| default: |
| rc = IMA_TemplateName_ParseCustom(templateDataParseFunctions, |
| templateDataParseFunctionsSize, |
| imaEvent); |
| } |
| } |
| return rc; |
| } |
| |
| /* the mapping between a format string and the template data parse function */ |
| |
| typedef struct { |
| const char *formatString; |
| TemplateDataParseFunction_t parseFunction; |
| } ImaFormatMap; |
| |
| static ImaFormatMap imaFormatMap[] = { |
| {"d", (TemplateDataParseFunction_t)IMA_ParseD}, |
| {"n", (TemplateDataParseFunction_t)IMA_ParseNNG}, |
| {"d-ng", (TemplateDataParseFunction_t)IMA_ParseDNG}, |
| {"n-ng", (TemplateDataParseFunction_t)IMA_ParseNNG}, |
| {"sig", (TemplateDataParseFunction_t)IMA_ParseSIG}, |
| {"d-modsig", (TemplateDataParseFunction_t)IMA_ParseDMODSIG}, |
| {"modsig", (TemplateDataParseFunction_t)IMA_ParseMODSIG}, |
| {"buf", (TemplateDataParseFunction_t)IMA_ParseBUF} |
| }; |
| |
| static uint32_t |
| IMA_TemplateName_ParseCustom(TemplateDataParseFunction_t templateDataParseFunctions[], |
| size_t templateDataParseFunctionsSize, |
| ImaEvent *imaEvent) |
| { |
| uint32_t rc = 0; |
| size_t i; /* index into templateDataParseFunctions table */ |
| size_t j; /* index into imaFormatMap table */ |
| char *startName; |
| char *endName; |
| char templateName[TCG_EVENT_NAME_LEN_MAX + 1]; /* one | separated item with nul */ |
| |
| /* parse the custom templates */ |
| strcpy(templateName, imaEvent->name); /* modify'able */ |
| startName = templateName; |
| |
| for (i = 0 ; (rc == 0) && (i < templateDataParseFunctionsSize) ; i++) { |
| endName = strchr(startName, '|'); |
| if (endName != NULL) { /* found a | character */ |
| *endName = '\0'; /* nul terminate the next format string */ |
| } |
| printf("item %lu : %s\n", (unsigned long)i, startName); |
| /* search the table for the format string */ |
| for (j = 0 ; j < (sizeof(imaFormatMap) / sizeof(ImaFormatMap)) ; j++) { |
| int irc; |
| irc = strcmp(startName, imaFormatMap[j].formatString); |
| if (irc == 0) { |
| templateDataParseFunctions[i] = imaFormatMap[j].parseFunction; |
| } |
| } |
| /* if no format string found */ |
| if (templateDataParseFunctions[i] == NULL) { |
| printf("ERROR: IMA_TemplateName_ParseCustom: unknown format string %s\n", |
| startName); |
| rc = TSS_RC_BAD_PROPERTY_VALUE; |
| } |
| /* if found an item, move the pointer */ |
| if (rc == 0) { |
| startName = endName + 1; |
| } |
| if (endName == NULL) { /* no | character, last entry */ |
| break; |
| } |
| } |
| return rc; |
| } |
| |
| /* |
| template data callbacks |
| */ |
| |
| /* IMA_ParseD() parses a d : digest (no length or algorithm) */ |
| |
| static uint32_t IMA_ParseD(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| littleEndian = littleEndian; /* unised */ |
| /* fileDataHash */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < SHA1_DIGEST_SIZE) { |
| printf("ERROR: IMA_ParseD: buffer too small for file data hash\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaTemplateData->imaTemplateDNG.fileDataHashLength = SHA1_DIGEST_SIZE; |
| memcpy(&(imaTemplateData->imaTemplateDNG.fileDataHash), *buffer, SHA1_DIGEST_SIZE); |
| *buffer += SHA1_DIGEST_SIZE; |
| *length -= SHA1_DIGEST_SIZE; |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_ParseDNG parses a d-ng : hash length + hash algorithm string + digest |
| |
| The digest is a file data hash. |
| */ |
| |
| static uint32_t IMA_ParseDNG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| size_t hashAlgSize; |
| /* read the hash length, algorithm + hash */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_ParseDNG: buffer too small for hash length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaTemplateData->imaTemplateDNG.hashLength = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* read the hash algorithm, nul terminated string */ |
| if (rc == 0) { |
| /* NUL terminate first */ |
| memset(imaTemplateData->imaTemplateDNG.hashAlg, 0, |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateDNG.hashAlg)); |
| rc = IMA_Strn2cpy(imaTemplateData->imaTemplateDNG.hashAlg, *buffer, |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateDNG.hashAlg), /* destLength */ |
| imaTemplateData->imaTemplateDNG.hashLength); /* srcLength */ |
| if (rc != 0) { |
| printf("ERROR: IMA_ParseDNG: buffer too small for hash algorithm\n" |
| "\tor hash algorithm exceeds maximum size\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| hashAlgSize = strlen(imaTemplateData->imaTemplateDNG.hashAlg) + 1; |
| *buffer += hashAlgSize; |
| *length -= hashAlgSize; |
| } |
| } |
| /* fileDataHashLength */ |
| if (rc == 0) { |
| if (strcmp(imaTemplateData->imaTemplateDNG.hashAlg, "sha1:") == 0) { |
| imaTemplateData->imaTemplateDNG.fileDataHashLength = SHA1_DIGEST_SIZE; |
| imaTemplateData->imaTemplateDNG.hashAlgId = TPM_ALG_SHA1; |
| } |
| else if (strcmp(imaTemplateData->imaTemplateDNG.hashAlg, "sha256:") == 0) { |
| imaTemplateData->imaTemplateDNG.fileDataHashLength = SHA256_DIGEST_SIZE; |
| imaTemplateData->imaTemplateDNG.hashAlgId = TPM_ALG_SHA256; |
| } |
| else { |
| printf("ERROR: IMA_ParseDNG: Unknown file data hash algorithm: %s\n", |
| imaTemplateData->imaTemplateDNG.hashAlg); |
| rc = TSS_RC_BAD_HASH_ALGORITHM; |
| } |
| } |
| /* consistency check hashLength vs contents */ |
| if (rc == 0) { |
| if ((hashAlgSize + imaTemplateData->imaTemplateDNG.fileDataHashLength) != |
| imaTemplateData->imaTemplateDNG.hashLength) { |
| printf("ERROR: IMA_ParseDNG: " |
| "hashLength %u inconsistent with hashAlgSize %lu and fileDataHashLength %u\n", |
| imaTemplateData->imaTemplateDNG.hashLength, (unsigned long)hashAlgSize, |
| imaTemplateData->imaTemplateDNG.fileDataHashLength); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| /* fileDataHash */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < imaTemplateData->imaTemplateDNG.fileDataHashLength) { |
| printf("ERROR: IMA_ParseDNG: buffer too small for file data hash\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else if (imaTemplateData->imaTemplateDNG.fileDataHashLength > |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateDNG.fileDataHash)) { |
| printf("ERROR: IMA_ParseDNG: " |
| "file data hash length exceeds maximum size\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaTemplateData->imaTemplateDNG.fileDataHash), *buffer, |
| imaTemplateData->imaTemplateDNG.fileDataHashLength); |
| *buffer += imaTemplateData->imaTemplateDNG.fileDataHashLength; |
| *length -= imaTemplateData->imaTemplateDNG.fileDataHashLength; |
| /* FIXME remove */ |
| TSS_PrintAll("IMA_ParseDNG: file data hash", |
| imaTemplateData->imaTemplateDNG.fileDataHash, |
| imaTemplateData->imaTemplateDNG.fileDataHashLength); |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_ParseNNG() parses a n-ng : length + filename */ |
| |
| static uint32_t IMA_ParseNNG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| /* fileNameLength (length includes the nul terminator) */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_ParseNNG: buffer too small for file name length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaTemplateData->imaTemplateNNG.fileNameLength = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* fileName */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < imaTemplateData->imaTemplateNNG.fileNameLength) { |
| printf("ERROR: IMA_ParseNNG: buffer too small for file name\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| /* leave one byte for the nul terminator */ |
| else if (imaTemplateData->imaTemplateNNG.fileNameLength > |
| (sizeof(imaTemplateData->imaTemplateNNG.fileName)-1)) { |
| printf("ERROR: IMA_ParseNNG: file name length exceeds maximum size\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaTemplateData->imaTemplateNNG.fileName), *buffer, |
| imaTemplateData->imaTemplateNNG.fileNameLength); |
| /* ima template does not nul terminate the file name */ |
| imaTemplateData->imaTemplateNNG.fileName[imaTemplateData->imaTemplateNNG.fileNameLength] = '\0'; |
| *buffer += imaTemplateData->imaTemplateNNG.fileNameLength; |
| *length -= imaTemplateData->imaTemplateNNG.fileNameLength; |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_ParseSIG() parses a sig : signature header + signature */ |
| |
| static uint32_t IMA_ParseSIG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| /* sigLength */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_ParseSIG: " |
| "buffer too small for signature length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaTemplateData->imaTemplateSIG.sigLength = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| /* FIXME remove */ |
| printf("IMA_ParseSIG: sigLength %u\n", imaTemplateData->imaTemplateSIG.sigLength); |
| } |
| } |
| /* sigHeader - only parsed if its length is not zero */ |
| if (imaTemplateData->imaTemplateSIG.sigLength != 0) { |
| if (rc == 0) { |
| imaTemplateData->imaTemplateSIG.sigHeaderLength = |
| sizeof((ImaTemplateData *)NULL)->imaTemplateSIG.sigHeader; |
| /* bounds check the length */ |
| if (*length < imaTemplateData->imaTemplateSIG.sigHeaderLength) { |
| printf("ERROR: IMA_ParseSIG: " |
| "buffer too small for signature header\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaTemplateData->imaTemplateSIG.sigHeader), *buffer, |
| imaTemplateData->imaTemplateSIG.sigHeaderLength); |
| *buffer += imaTemplateData->imaTemplateSIG.sigHeaderLength; |
| *length -= imaTemplateData->imaTemplateSIG.sigHeaderLength; |
| } |
| } |
| /* get signature length from last two bytes */ |
| if (rc == 0) { |
| /* magic number for offset: type(1) version(1) hash alg (1) pubkey id (4) */ |
| imaTemplateData->imaTemplateSIG.signatureSize = |
| ntohs(*(uint16_t *)(imaTemplateData->imaTemplateSIG.sigHeader + 7)); |
| } |
| /* consistency check signature header contents */ |
| if (rc == 0) { |
| int goodHashAlgo = (((imaTemplateData->imaTemplateSIG.sigHeader[2] == HASH_ALGO_SHA1) && |
| (imaTemplateData->imaTemplateDNG.hashAlgId == TPM_ALG_SHA1)) || |
| ((imaTemplateData->imaTemplateSIG.sigHeader[2] == HASH_ALGO_SHA256) && |
| (imaTemplateData->imaTemplateDNG.hashAlgId == TPM_ALG_SHA256))); |
| int goodSigSize = ((imaTemplateData->imaTemplateSIG.signatureSize == 128) || |
| (imaTemplateData->imaTemplateSIG.signatureSize == 256)); |
| /* xattr type */ |
| if ( |
| (imaTemplateData->imaTemplateSIG.sigHeader[0] != EVM_IMA_XATTR_DIGSIG) || /* [0] type */ |
| (imaTemplateData->imaTemplateSIG.sigHeader[1] != 2) || /* [1] version */ |
| !goodHashAlgo || /* [2] hash algorithm */ |
| /* [3]-[6] are the public key fingerprint. Any value is legal. */ |
| !goodSigSize /* [7][8] sig size */ |
| ) { |
| printf("ERROR: IMA_ParseSIG: invalid sigHeader\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| /* signature */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < imaTemplateData->imaTemplateSIG.signatureSize) { |
| printf("ERROR: IMA_ParseSIG: " |
| "buffer too small for signature \n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| /* sanity check the signatureSize against the sigLength */ |
| else if (imaTemplateData->imaTemplateSIG.sigLength != |
| (sizeof((ImaTemplateData *)NULL)->imaTemplateSIG.sigHeader + |
| imaTemplateData->imaTemplateSIG.signatureSize)) { |
| printf("ERROR: IMA_ParseSIG: " |
| "sigLength inconsistent with signatureSize\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaTemplateData->imaTemplateSIG.signature), *buffer, |
| imaTemplateData->imaTemplateSIG.signatureSize); |
| *buffer += imaTemplateData->imaTemplateSIG.signatureSize; |
| *length -= imaTemplateData->imaTemplateSIG.signatureSize; |
| /* FIXME remove */ |
| TSS_PrintAll("IMA_ParseSIG: file data hash", |
| imaTemplateData->imaTemplateSIG.signature, |
| imaTemplateData->imaTemplateSIG.signatureSize); |
| |
| } |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_ParseDMODSIG parses a d-ng : hash length + hash algorithm string + digest |
| |
| The digest is a file data hash omitting the appended modsig signature. |
| |
| NOTE: This is currently thre same as IMA_ParseDNG but may have different processing in the |
| future. |
| */ |
| |
| static uint32_t IMA_ParseDMODSIG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| size_t hashAlgSize; |
| |
| /* read the hash length, algorithm + hash */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_ParseDMODSIG: buffer too small for hash length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashLength = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* FIXME is zero length an error? */ |
| if (imaTemplateData->imaTemplateDMODSIG.dModSigHashLength != 0) { |
| |
| /* read the hash algorithm, nul terminated string */ |
| if (rc == 0) { |
| /* NUL terminate first */ |
| memset(imaTemplateData->imaTemplateDMODSIG.dModSigHashAlg, 0, |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateDMODSIG.dModSigHashAlgId)); |
| rc = IMA_Strn2cpy(imaTemplateData->imaTemplateDMODSIG.dModSigHashAlg, *buffer, |
| /* destLength */ |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateDMODSIG.dModSigHashAlg), |
| /* srcLength */ |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashLength); |
| if (rc != 0) { |
| printf("ERROR: IMA_ParseDMODSIG: buffer too small for hash algorithm\n" |
| "\tor hash algorithm exceeds maximum size\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| hashAlgSize = strlen(imaTemplateData->imaTemplateDMODSIG.dModSigHashAlg) + 1; |
| *buffer += hashAlgSize; |
| *length -= hashAlgSize; |
| } |
| } |
| /* dModSigFileDataHashLength */ |
| if (rc == 0) { |
| if (strcmp(imaTemplateData->imaTemplateDMODSIG.dModSigHashAlg, "sha1:") == 0) { |
| imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength = SHA1_DIGEST_SIZE; |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashAlgId = TPM_ALG_SHA1; |
| } |
| else if (strcmp(imaTemplateData->imaTemplateDMODSIG.dModSigHashAlg, "sha256:") == 0) { |
| imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength = SHA256_DIGEST_SIZE; |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashAlgId = TPM_ALG_SHA256; |
| } |
| else { |
| printf("ERROR: IMA_ParseDMODSIG: Unknown file data hash algorithm: %s\n", |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashAlg); |
| rc = TSS_RC_BAD_HASH_ALGORITHM; |
| } |
| } |
| /* consistency check dModSigFileDataHashLength vs contents */ |
| if (rc == 0) { |
| if ((hashAlgSize + imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength) != |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashLength) { |
| printf("ERROR: IMA_ParseDMODSIG: " |
| "dModSigFileDataHashLength %u inconsistent with hashAlgSize %lu " |
| "and dModSigFileDataHashLength %u\n", |
| imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength, |
| (unsigned long)hashAlgSize, |
| imaTemplateData->imaTemplateDMODSIG.dModSigHashLength); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| /* dModSigFileDataHashLength */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength ) { |
| printf("ERROR: IMA_ParseDMODSIG: buffer too small for file data hash\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else if (imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength > |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateDMODSIG.dModSigFileDataHash)) { |
| printf("ERROR: IMA_ParseDMODSIG: " |
| "file data hash length exceeds maximum size\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHash), |
| *buffer, imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength); |
| *buffer += imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength ; |
| *length -= imaTemplateData->imaTemplateDMODSIG.dModSigFileDataHashLength ; |
| } |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_ParseMODSIG parses a modsig : 4 byte length + DER encoded CMS document, RFC 5652 */ |
| |
| static uint32_t IMA_ParseMODSIG(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| |
| /* read the length */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_ParseMODSIG: buffer too small for length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaTemplateData->imaTemplateMODSIG.modSigLength = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* read the DER */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < imaTemplateData->imaTemplateMODSIG.modSigLength) { |
| printf("ERROR: IMA_ParseMODSIG: buffer too small for modSig data\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else if (imaTemplateData->imaTemplateMODSIG.modSigLength > |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateMODSIG.modSigData)) { |
| printf("ERROR: IMA_ParseMODSIG: " |
| "modSigData length exceeds maximum size\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaTemplateData->imaTemplateMODSIG.modSigData), *buffer, |
| imaTemplateData->imaTemplateMODSIG.modSigLength); |
| *buffer += imaTemplateData->imaTemplateMODSIG.modSigLength; |
| *length -= imaTemplateData->imaTemplateMODSIG.modSigLength; |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_ParseBUF parses a modsig : 4 byte length + DER encoded CMS document, RFC 5652 */ |
| |
| static uint32_t IMA_ParseBUF(ImaTemplateData *imaTemplateData, |
| uint8_t **buffer, |
| size_t *length, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| |
| /* FIXME factor reading a 4 byte length plus data stream */ |
| /* read the length */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < sizeof(uint32_t)) { |
| printf("ERROR: IMA_ParseBUF: buffer too small for length\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| imaTemplateData->imaTemplateBUF.bufLength = IMA_Uint32_Convert(*buffer, littleEndian); |
| *buffer += sizeof(uint32_t); |
| *length -= sizeof(uint32_t); |
| } |
| } |
| /* read the DER */ |
| if (rc == 0) { |
| /* bounds check the length */ |
| if (*length < imaTemplateData->imaTemplateBUF.bufLength) { |
| printf("ERROR: IMA_ParseBUF: buffer too small for buf data\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else if (imaTemplateData->imaTemplateBUF.bufLength > |
| sizeof(((ImaTemplateData *)NULL)->imaTemplateBUF.bufData)) { |
| printf("ERROR: IMA_ParseBUF: " |
| "bufData length exceeds maximum size\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| else { |
| memcpy(&(imaTemplateData->imaTemplateBUF.bufData), *buffer, |
| imaTemplateData->imaTemplateBUF.bufLength); |
| *buffer += imaTemplateData->imaTemplateBUF.bufLength; |
| *length -= imaTemplateData->imaTemplateBUF.bufLength; |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_TemplateData_ReadBuffer() unmarshals the template data fields from the template data byte |
| array. |
| |
| */ |
| |
| uint32_t IMA_TemplateData_ReadBuffer(ImaTemplateData *imaTemplateData, |
| ImaEvent *imaEvent, |
| int littleEndian) |
| { |
| uint32_t rc = 0; |
| size_t length = imaEvent->template_data_len; |
| uint8_t *buffer = imaEvent->template_data; |
| TemplateDataParseFunction_t templateDataParseFunctions[IMA_PARSE_FUNCTIONS_MAX]; |
| size_t i; |
| |
| /* initialize all fields, since not all fields are included in all templates */ |
| if (rc == 0) { |
| IMA_TemplateData_Init(imaTemplateData); |
| } |
| if (rc == 0) { |
| rc = IMA_TemplateName_Parse(templateDataParseFunctions, IMA_PARSE_FUNCTIONS_MAX, |
| imaEvent); |
| } |
| for (i = 0 ; (rc == 0) && (templateDataParseFunctions[i] != NULL) ; i++) { |
| rc = templateDataParseFunctions[i](imaTemplateData, &buffer, &length, littleEndian); |
| } |
| /* length should now be zero */ |
| if (rc == 0) { |
| if (length != 0) { |
| printf("ERROR: IMA_TemplateData_ReadBuffer: " |
| "buffer too large (bytes remaining after unmarshaling)\n"); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_Event_Write() writes an event line to a binary file outFile. |
| |
| The write is always big endian, network byte order. |
| */ |
| |
| uint32_t IMA_Event_Write(ImaEvent *imaEvent, |
| FILE *outFile) |
| { |
| int rc = 0; |
| size_t writeSize; |
| uint32_t nbo32; /* network byte order */ |
| |
| if (rc == 0) { |
| /* do the endian conversion */ |
| nbo32 = htonl(imaEvent->pcrIndex); |
| /* write the IMA pcr index */ |
| writeSize = fwrite(&nbo32, sizeof(uint32_t), 1, outFile); |
| if (writeSize != 1) { |
| printf("ERROR: IMA_Event_Write: could not write pcrIndex, returned %lu\n", |
| (unsigned long)writeSize); |
| rc = TSS_RC_FILE_WRITE; |
| } |
| } |
| /* write the IMA digest, name length */ |
| if (rc == 0) { |
| writeSize = fwrite(&(imaEvent->digest), sizeof(((ImaEvent *)NULL)->digest), 1, outFile); |
| if (writeSize != 1) { |
| printf("ERROR: IMA_Event_Write: could not write digest, returned %lu\n", |
| (unsigned long)writeSize); |
| rc = TSS_RC_FILE_WRITE; |
| } |
| } |
| /* write the IMA name length */ |
| if (rc == 0) { |
| /* do the endian conversion */ |
| nbo32 = htonl(imaEvent->name_len); |
| /* write the IMA name length */ |
| writeSize = fwrite(&nbo32, sizeof(uint32_t), 1, outFile); |
| if (writeSize != 1) { |
| printf("ERROR: IMA_Event_Write: could not write name length, returned %lu\n", |
| (unsigned long)writeSize); |
| rc = TSS_RC_FILE_WRITE; |
| } |
| } |
| /* write the name */ |
| if (rc == 0) { |
| writeSize = fwrite(&(imaEvent->name), imaEvent->name_len, 1, outFile); |
| if (writeSize != 1) { |
| printf("ERROR: IMA_Event_Write: could not write name, returned %lu\n", |
| (unsigned long)writeSize); |
| rc = TSS_RC_FILE_WRITE; |
| } |
| } |
| /* write the template data length */ |
| if (rc == 0) { |
| /* do the endian conversion */ |
| nbo32 = htonl(imaEvent->template_data_len); |
| /* write the IMA template data length */ |
| writeSize = fwrite(&nbo32, sizeof(uint32_t), 1, outFile); |
| if (writeSize != 1) { |
| printf("ERROR: IMA_Event_Write: could not template data length , returned %lu\n", |
| (unsigned long)writeSize); |
| rc = TSS_RC_FILE_WRITE; |
| } |
| } |
| /* write the template data */ |
| if (rc == 0) { |
| writeSize = fwrite(&(imaEvent->template_data), imaEvent->template_data_len, 1, outFile); |
| if (writeSize != 1) { |
| printf("ERROR: IMA_Event_Write: could not write template data, returned %lu\n", |
| (unsigned long)writeSize); |
| rc = TSS_RC_FILE_WRITE; |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_Extend() extends the event into the imaPcr. |
| |
| An IMA quirk is that, if the event is all zero, all ones is extended into the SHA-1 bank. Since |
| the SHA-256 bank currently gets the SHA-1 value zero extended, it will get 20 ff's and 12 00's. |
| |
| halg indicates whether to calculate the digest for the SHA-1 or SHA-256 PCR bank. The IMA event |
| log itself is always SHA-1. |
| |
| This function assumes that the same hash algorithm / PCR bank is used for all calls. |
| */ |
| |
| uint32_t IMA_Extend(TPMT_HA *imapcr, |
| ImaEvent *imaEvent, |
| TPMI_ALG_HASH hashAlg) |
| { |
| uint32_t rc = 0; |
| uint16_t digestSize; |
| uint16_t zeroPad; |
| int notAllZero; |
| unsigned char zeroDigest[SHA256_DIGEST_SIZE]; |
| unsigned char oneDigest[SHA256_DIGEST_SIZE]; |
| |
| /* FIXME sanity check TPM_IMA_PCR imaEvent->pcrIndex */ |
| |
| /* extend based on the previous IMA PCR value */ |
| if (rc == 0) { |
| memset(zeroDigest, 0, SHA256_DIGEST_SIZE); |
| memset(oneDigest, 0xff, SHA256_DIGEST_SIZE); |
| if (hashAlg == TPM_ALG_SHA1) { |
| digestSize = SHA1_DIGEST_SIZE; |
| zeroPad = 0; |
| } |
| else if (hashAlg == TPM_ALG_SHA256) { |
| digestSize = SHA256_DIGEST_SIZE; |
| /* pad the SHA-1 event with zeros for the SHA-256 bank */ |
| zeroPad = SHA256_DIGEST_SIZE - SHA1_DIGEST_SIZE; |
| } |
| else { |
| printf("ERROR: IMA_Extend: Unsupported hash algorithm: %04x\n", hashAlg); |
| rc = TSS_RC_BAD_HASH_ALGORITHM; |
| } |
| } |
| if (rc == 0) { |
| notAllZero = memcmp(imaEvent->digest, zeroDigest, SHA1_DIGEST_SIZE); |
| imapcr->hashAlg = hashAlg; |
| #if 1 |
| TSS_PrintAll("IMA_Extend: Start PCR", (uint8_t *)&imapcr->digest, digestSize); |
| TSS_PrintAll("IMA_Extend: SHA-256 Pad", zeroDigest, zeroPad); |
| #endif |
| if (notAllZero) { |
| TSS_PrintAll("IMA_Extend: Extend", (uint8_t *)&imaEvent->digest, SHA1_DIGEST_SIZE); |
| rc = TSS_Hash_Generate(imapcr, |
| digestSize, (uint8_t *)&imapcr->digest, |
| SHA1_DIGEST_SIZE, &imaEvent->digest, |
| /* SHA-1 PCR extend gets zero padded */ |
| zeroPad, zeroDigest, |
| 0, NULL); |
| #if 1 |
| TSS_PrintAll("IMA_Extend: notAllZero End PCR", |
| (uint8_t *)&imapcr->digest, digestSize); |
| #endif |
| } |
| /* IMA has a quirk where, when it places all all zero digest into the measurement log, it |
| extends all ones into IMA PCR */ |
| else { |
| TSS_PrintAll("IMA_Extend: Extend", (uint8_t *)oneDigest, SHA1_DIGEST_SIZE); |
| rc = TSS_Hash_Generate(imapcr, |
| digestSize, (uint8_t *)&imapcr->digest, |
| SHA1_DIGEST_SIZE, oneDigest, |
| /* SHA-1 gets zero padded */ |
| zeroPad, zeroDigest, |
| 0, NULL); |
| #if 1 |
| TSS_PrintAll("IMA_Extend: allZero End PCR", |
| (uint8_t *)&imapcr->digest, digestSize); |
| #endif |
| } |
| } |
| if (rc != 0) { |
| printf("ERROR: IMA_Extend: could not extend imapcr, rc %08x\n", rc); |
| } |
| return rc; |
| } |
| |
| /* IMA_VerifyImaDigest() verifies the IMA digest against the hash of the template data. |
| |
| This handles the SHA-1 IMA event log. |
| */ |
| |
| uint32_t IMA_VerifyImaDigest(uint32_t *badEvent, /* TRUE if hash does not match */ |
| ImaEvent *imaEvent, /* the current IMA event being processed */ |
| int eventNum) /* the current IMA event number being processed */ |
| { |
| uint32_t rc = 0; |
| int irc; |
| TPMT_HA calculatedImaDigest; |
| |
| /* calculate the hash of the template data */ |
| if (rc == 0) { |
| calculatedImaDigest.hashAlg = TPM_ALG_SHA1; |
| /* standard case, hash of entire template data */ |
| if (imaEvent->nameInt != IMA_FORMAT_IMA) { |
| rc = TSS_Hash_Generate(&calculatedImaDigest, |
| imaEvent->template_data_len, imaEvent->template_data, |
| 0, NULL); |
| } |
| /* special case of "ima" template, hash of File Data Hash || File Name padded with zeros to |
| 256 bytes */ |
| else { |
| ImaTemplateData imaTemplateData; |
| int zeroPadLength; |
| uint8_t zeroPad[256]; |
| if (rc == 0) { |
| rc = IMA_TemplateData_ReadBuffer(&imaTemplateData, |
| imaEvent, |
| TRUE); /* FIXME littleEndian */ |
| } |
| if (rc == 0) { |
| if (imaTemplateData.imaTemplateNNG.fileNameLength > sizeof(zeroPad)) { |
| printf("ERROR: IMA_VerifyImaDigest: ima template file name length %lu > %lu\n", |
| (unsigned long)imaTemplateData.imaTemplateNNG.fileNameLength, |
| (unsigned long)sizeof(zeroPad)); |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| } |
| if (rc == 0) { |
| memset(zeroPad, 0, sizeof(zeroPad)); |
| /* subtract safe after above length check */ |
| zeroPadLength = sizeof(zeroPad) - imaTemplateData.imaTemplateNNG.fileNameLength; |
| } |
| if (rc == 0) { |
| rc = TSS_Hash_Generate(&calculatedImaDigest, |
| SHA1_DIGEST_SIZE, &imaTemplateData.imaTemplateDNG.fileDataHash, |
| imaTemplateData.imaTemplateNNG.fileNameLength, |
| &imaTemplateData.imaTemplateNNG.fileName, |
| zeroPadLength, zeroPad, |
| 0, NULL); |
| } |
| } |
| } |
| /* compare the calculated hash to the event digest received from the client */ |
| if (rc == 0) { |
| if (tssUtilsVerbose) TSS_PrintAll("IMA_VerifyImaDigest: Received IMA digest", |
| imaEvent->digest, SHA1_DIGEST_SIZE); |
| if (tssUtilsVerbose) TSS_PrintAll("IMA_VerifyImaDigest: Calculated IMA digest", |
| (uint8_t *)&calculatedImaDigest.digest, SHA1_DIGEST_SIZE); |
| |
| irc = memcmp(imaEvent->digest, &calculatedImaDigest.digest, SHA1_DIGEST_SIZE); |
| if (irc == 0) { |
| if (tssUtilsVerbose) printf("IMA_VerifyImaDigest: IMA digest verified, event %u\n", eventNum); |
| *badEvent = FALSE; |
| } |
| else { |
| printf("ERROR: IMA_VerifyImaDigest: IMA digest did not verify, event %u\n", |
| eventNum); |
| *badEvent = TRUE; |
| } |
| } |
| return rc; |
| } |
| |
| /* IMA_Uint32_Convert() converts a uint8_t (from an input stream) to host byte order |
| */ |
| |
| static uint32_t IMA_Uint32_Convert(const uint8_t *stream, |
| int littleEndian) |
| { |
| uint32_t out = 0; |
| |
| /* little endian input */ |
| if (littleEndian) { |
| out = (stream[0] << 0) | |
| (stream[1] << 8) | |
| (stream[2] << 16) | |
| (stream[3] << 24); |
| } |
| /* big endian input */ |
| else { |
| out = (stream[0] << 24) | |
| (stream[1] << 16) | |
| (stream[2] << 8) | |
| (stream[3] << 0); |
| } |
| return out; |
| } |
| |
| /* IMA_Strn2cpy() copies src to dest, including a NUL terminator |
| |
| It checks that src is nul terminated within srcLength bytes. |
| It checks that src fits into dest within destLength bytes |
| |
| Returns error if either the src is not nul terminated or will not fit in dest. |
| */ |
| |
| static uint32_t IMA_Strn2cpy(char *dest, const uint8_t *src, |
| size_t destLength, size_t srcLength) |
| { |
| uint32_t rc = 0; |
| int done = 0; |
| |
| while ((destLength > 0) && (srcLength > 0)) { |
| *dest = *src; |
| if (*dest == '\0') { |
| done = 1; |
| break; |
| } |
| else { |
| dest++; |
| src++; |
| destLength--; |
| srcLength--; |
| } |
| } |
| if (!done) { |
| rc = TSS_RC_INSUFFICIENT_BUFFER; |
| } |
| return rc; |
| } |
| |
| /* IMA_Event_Marshal() marshals an ImaEvent structure */ |
| |
| TPM_RC IMA_Event_Marshal(ImaEvent *source, |
| uint16_t *written, uint8_t **buffer, uint32_t *size) |
| { |
| TPM_RC rc = 0; |
| |
| if (rc == 0) { |
| rc = TSS_UINT32_Marshalu(&source->pcrIndex, written, buffer, size); |
| } |
| if (rc == 0) { |
| rc = TSS_Array_Marshalu(source->digest, SHA1_DIGEST_SIZE, written, buffer, size); |
| } |
| if (rc == 0) { |
| rc = TSS_UINT32_Marshalu(&source->name_len, written, buffer, size); |
| } |
| if (rc == 0) { |
| rc = TSS_Array_Marshalu((uint8_t *)source->name, source->name_len, written, buffer, size); |
| } |
| if (rc == 0) { |
| rc = TSS_UINT32_Marshalu(&source->template_data_len, written, buffer, size); |
| } |
| if (rc == 0) { |
| rc = TSS_Array_Marshalu(source->template_data, source->template_data_len, |
| written, buffer, size); |
| } |
| return rc; |
| } |
| |
| /* IMA_Event_PcrExtend() extends PCR digests with the digest from the ImaEvent event log |
| entry. |
| |
| Bank 0 is SHA-1. Bank 1 is SHA-256. |
| |
| The function supports all PCRs, even though the PCRs are limited in practice. |
| |
| */ |
| |
| uint32_t IMA_Event_PcrExtend(TPMT_HA pcrs[IMA_PCR_BANKS][IMPLEMENTATION_PCR], |
| ImaEvent *imaEvent) |
| { |
| TPM_RC rc = 0; |
| uint8_t eventData[SHA256_DIGEST_SIZE]; |
| |
| /* validate PCR number */ |
| if (rc == 0) { |
| if (imaEvent->pcrIndex >= IMPLEMENTATION_PCR) { |
| printf("ERROR: IMA_Event_PcrExtend: PCR number %u %08x out of range\n", |
| imaEvent->pcrIndex, imaEvent->pcrIndex); |
| rc = TSS_RC_BAD_PROPERTY; |
| } |
| } |
| /* process each event hash algorithm */ |
| if (rc == 0) { |
| unsigned char zeroDigest[SHA1_DIGEST_SIZE]; |
| int notAllZero; |
| memset(zeroDigest, 0, SHA1_DIGEST_SIZE); |
| notAllZero = memcmp(imaEvent->digest, zeroDigest, SHA1_DIGEST_SIZE); |
| /* for the SHA-256 zero extend */ |
| memset(eventData, 0, SHA256_DIGEST_SIZE); |
| |
| /* IMA has a quirk where some measurements store a zero digest in the event log, but |
| extend ones into PCR 10 */ |
| if (notAllZero) { |
| memcpy(eventData, imaEvent->digest, SHA1_DIGEST_SIZE); |
| } |
| else { |
| memset(eventData, 0xff, SHA1_DIGEST_SIZE); |
| } |
| } |
| /* SHA-1 */ |
| if (rc == 0) { |
| rc = TSS_Hash_Generate(&pcrs[0][imaEvent->pcrIndex], |
| SHA1_DIGEST_SIZE, |
| (uint8_t *)&pcrs[0][imaEvent->pcrIndex].digest, |
| SHA1_DIGEST_SIZE, |
| eventData, |
| 0, NULL); |
| } |
| /* SHA-256 */ |
| if (rc == 0) { |
| rc = TSS_Hash_Generate(&pcrs[1][imaEvent->pcrIndex], |
| SHA256_DIGEST_SIZE, |
| (uint8_t *)&pcrs[1][imaEvent->pcrIndex].digest, |
| SHA256_DIGEST_SIZE, |
| eventData, |
| 0, NULL); |
| } |
| return rc; |
| } |
| |
| #if 0 |
| /* IMA_Event_ToString() converts the ImaEvent structure to a hexascii string, big endian. */ |
| |
| uint32_t IMA_Event_ToString(char **eventString, /* freed by caller */ |
| ImaEvent *imaEvent) |
| { |
| int rc = 0; |
| size_t length; |
| |
| /* calculate size of string, from ImaEvent structure */ |
| if (rc == 0) { |
| length = ((sizeof(uint32_t) + SHA1_DIGEST_SIZE + sizeof(uint32_t) + |
| TCG_EVENT_NAME_LEN_MAX + 1 + sizeof(uint32_t) + |
| imaEvent->template_data_len) * 2) + 1; |
| } |
| if (rc == 0) { |
| *eventString = malloc(length); |
| if (*eventString == NULL) { |
| printf("ERROR: IMA_Event_ToString: error allocating %lu bytes\n", length); |
| rc = TSS_RC_OUT_OF_MEMORY; |
| } |
| } |
| if (rc == 0) { |
| memset(*eventString, '\0', length); |
| char *p = *eventString; |
| |
| sprintf(p, "%08lx", (long unsigned int)imaEvent->pcrIndex); |
| p += sizeof(uint32_t)* 2; |
| |
| Array_Print(p, NULL, imaEvent->digest, SHA1_DIGEST_SIZE); |
| p += SHA1_DIGEST_SIZE * 2; |
| |
| sprintf(p, "%08lx", (long unsigned int)imaEvent->name_len); |
| p += sizeof(uint32_t) * 2; |
| |
| Array_Print(p, NULL, FALSE, (uint8_t *)imaEvent->name, imaEvent->name_len); |
| p += imaEvent->name_len * 2; |
| |
| sprintf(p, "%08lx", (long unsigned int)imaEvent->template_data_len); |
| p += sizeof(uint32_t) * 2; |
| |
| Array_Print(p, NULL, FALSE, imaEvent->template_data, imaEvent->template_data_len); |
| p += imaEvent->template_data_len * 2; |
| /* printf("IMA_Event_ToString: result\n:%s:\n", *eventString); */ |
| } |
| return rc; |
| } |
| |
| #endif |
| |