| /* |
| Copyright © Trustonic Limited 2013 |
| |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without modification, |
| are permitted provided that the following conditions are met: |
| |
| 1. Redistributions of source code must retain the above copyright notice, this |
| list of conditions and the following disclaimer. |
| |
| 2. 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. |
| |
| 3. Neither the name of the Trustonic Limited 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. |
| */ |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| |
| #include <libxml/parser.h> |
| #include <libxml/valid.h> |
| #include <libxml/xmlschemas.h> |
| |
| #include <mcVersionInfo.h> |
| |
| #include "logging.h" |
| #include "enrollmentservicexmlschema.h" |
| #include "xmlmessagehandler.h" |
| #include "contentmanager.h" |
| #include "provisioningengine.h" |
| #include "base64.h" |
| |
| #define ENROLLMENT_SERVICE_NS_PREFIX 0 // "mces" |
| #define ENROLLMENT_SERVICE_NAMESPACE "http://www.mcore.gi-de.com/2012/04/schema/EnrollmentService" |
| |
| #define PLATFORM_TYPES_NS_PREFIX "mcpt" |
| #define PLATFORM_TYPES_NAMESPACE "http://www.mcore.gi-de.com/2012/02/schema/MCPlatformTypes" |
| |
| #define XSD_PATH_MAX_LEN 256 |
| #define INT_BUFFER_LENGTH 11 |
| #define UNKNOWN_ID 0xFFFFFFFF |
| |
| #define EXTERNAL_MEMORY 2 |
| #define DEFAULT_MEMORY_TYPE EXTERNAL_MEMORY |
| #define DEFAULT_NUMBER_OF_INSTANCES 1 |
| #define DEFAULT_FLAGS 0 |
| |
| typedef enum |
| { |
| CMP, |
| SO_UPLOAD, |
| TLT_UPLOAD, |
| UNKNOWN_TYPE=0xFFFFFFFF |
| } commandtype_t; |
| |
| static char enrollmentServiceFullPath_[XSD_PATH_MAX_LEN]; |
| static char platformTypesFullPath_[XSD_PATH_MAX_LEN]; |
| static xmlNsPtr nameSpace_=NULL; |
| static xmlNsPtr typesNameSpace_=NULL; |
| |
| // file internal functions |
| |
| xmlDocPtr createXmlResponse() |
| { |
| xmlDocPtr docP = NULL; |
| xmlNodePtr root_node = NULL; |
| |
| docP = xmlNewDoc(BAD_CAST "1.0"); |
| root_node = xmlNewNode(nameSpace_, BAD_CAST "ContentManagementResponse"); |
| nameSpace_=xmlNewNs(root_node, (const xmlChar*) ENROLLMENT_SERVICE_NAMESPACE, (const xmlChar*) ENROLLMENT_SERVICE_NS_PREFIX ); |
| typesNameSpace_=xmlNewNs(root_node, (const xmlChar*) PLATFORM_TYPES_NAMESPACE, (const xmlChar*) PLATFORM_TYPES_NS_PREFIX ); |
| xmlSetNs(root_node, nameSpace_); |
| xmlDocSetRootElement(docP, root_node); |
| docP->standalone=1; |
| return docP; |
| } |
| |
| |
| bool addTrustletData(xmlNodePtr rootNode, bool tltBin, char* contentP) |
| { |
| xmlNodePtr trustletDataNode=xmlNewChild(rootNode, nameSpace_, BAD_CAST "trustletData", NULL); |
| if(NULL==trustletDataNode) return false; |
| char* element="encryptedKey"; |
| if(tltBin) |
| { |
| element="tltBin"; |
| } |
| |
| if(xmlNewChild(trustletDataNode, nameSpace_, BAD_CAST element, BAD_CAST contentP)==NULL ) return false; |
| return true; |
| } |
| |
| |
| char* errorCodeToString(rootpaerror_t errorCode) |
| { |
| switch(errorCode) |
| { |
| case ROOTPA_COMMAND_NOT_SUPPORTED: |
| return STRING_ROOTPA_COMMAND_NOT_SUPPORTED; |
| |
| case ROOTPA_ERROR_LOCK: |
| return STRING_ROOTPA_ERROR_LOCK; |
| |
| // |
| // this is not currently understood by SE |
| // |
| // case ROOTPA_ERROR_COMMAND_EXECUTION: |
| // return STRING_ROOTPA_ERROR_COMMAND_EXECUTION; |
| |
| case ROOTPA_ERROR_REGISTRY: |
| return STRING_ROOTPA_ERROR_REGISTRY; |
| |
| case ROOTPA_ERROR_MOBICORE_CONNECTION: |
| return STRING_ROOTPA_ERROR_MOBICORE_CONNECTION; |
| |
| case ROOTPA_ERROR_OUT_OF_MEMORY: |
| return STRING_ROOTPA_ERROR_OUT_OF_MEMORY; |
| |
| case ROOTPA_ERROR_INTERNAL: |
| return STRING_ROOTPA_ERROR_INTERNAL; |
| |
| case ROOTPA_ERROR_XML: |
| return STRING_ROOTPA_ERROR_XML; |
| |
| case ROOTPA_ERROR_REGISTRY_OBJECT_NOT_AVAILABLE: |
| return STRING_ROOTPA_ERROR_REGISTRY_OBJECT_NOT_AVAILABLE; |
| |
| } |
| LOGD("errorCodeToString: unknown error code %d", errorCode); |
| return STRING_ROOTPA_ERROR_INTERNAL; |
| } |
| |
| bool addCommandResultData(xmlNodePtr resultListNode, int id, char* commandResultP, rootpaerror_t errorCode, uint32_t errorDetail ) |
| { |
| xmlNodePtr commandResultNode=xmlNewChild(resultListNode, nameSpace_, BAD_CAST "commandResult", NULL); |
| if(NULL==commandResultNode) return false; |
| |
| bool retValue=true; |
| char intBuffer[INT_BUFFER_LENGTH]; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",(uint32_t) id); |
| if(xmlNewProp(commandResultNode, BAD_CAST "id", BAD_CAST intBuffer)==NULL) return false; |
| |
| if(commandResultP==NULL) |
| { |
| xmlNodePtr errorNode=xmlNewChild(commandResultNode, nameSpace_, BAD_CAST "resultError", NULL); |
| if( NULL==errorNode ) return false; // CommandExecutionError |
| |
| if(xmlNewProp(errorNode, BAD_CAST "errorCode", BAD_CAST errorCodeToString(errorCode))==NULL) |
| { |
| retValue=false; |
| } |
| else if(errorDetail!=0) |
| { |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",errorDetail); |
| if(xmlNewProp(errorNode, BAD_CAST "errorDetail", BAD_CAST intBuffer)==NULL) |
| { |
| retValue=false; |
| } |
| } |
| } |
| else |
| { |
| if(xmlNewChild(commandResultNode, nameSpace_, BAD_CAST "resultValue", BAD_CAST commandResultP)==NULL ) |
| { |
| retValue=false; |
| } |
| } |
| |
| return retValue; |
| } |
| |
| xmlNodePtr findFirstCommandNode(xmlDocPtr xmlDocP) |
| { |
| xmlNodePtr rootElementP = xmlDocGetRootElement(xmlDocP); |
| if(NULL==rootElementP) return NULL; |
| |
| xmlNodePtr commandsNodeP=rootElementP->children; |
| for (; commandsNodeP; commandsNodeP = commandsNodeP->next) |
| { |
| if (commandsNodeP->type == XML_ELEMENT_NODE && strcmp((char*)commandsNodeP->name, "commands")==0) |
| { |
| break; |
| } |
| } |
| if(NULL==commandsNodeP) return NULL; |
| return commandsNodeP->children; |
| } |
| |
| xmlNodePtr getNextCommand(xmlDocPtr xmlDocP, xmlNodePtr prevNode) |
| { |
| LOGD(">> getNextCommand %ld %ld", (long int) xmlDocP, (long int) prevNode); |
| xmlNodePtr firstNode; |
| if(NULL==prevNode) |
| { |
| firstNode=findFirstCommandNode(xmlDocP); |
| } |
| else |
| { |
| firstNode=prevNode->next; |
| } |
| |
| xmlNodePtr commandNode; |
| for (commandNode = firstNode; commandNode; commandNode = commandNode->next) |
| { |
| if (commandNode->type == XML_ELEMENT_NODE && strcmp((char*)commandNode->name, "command")==0) |
| { |
| break; |
| } |
| } |
| LOGD("<< getNextCommand %ld", (long int) commandNode); |
| return commandNode; |
| } |
| |
| |
| int getCommandId(xmlNodePtr commandNode) |
| { |
| xmlChar* idP=xmlGetProp(commandNode, BAD_CAST "id"); |
| if(NULL==idP) |
| { |
| return UNKNOWN_ID; |
| } |
| int id=atoi((char*)idP); |
| xmlFree(idP); |
| return id; |
| } |
| |
| commandtype_t getCommandType(xmlNodePtr commandNode) |
| { |
| if(NULL==commandNode) return UNKNOWN_TYPE; |
| xmlChar* typeP=xmlGetProp(commandNode, BAD_CAST "type"); |
| commandtype_t type=UNKNOWN_TYPE; |
| if(typeP!=NULL) |
| { |
| if(strcmp((char*)typeP,"CMP")==0) type=CMP; |
| else if(strcmp((char*)typeP,"SO_UPLOAD")==0) type=SO_UPLOAD; |
| else if(strcmp((char*)typeP,"TLT_UPLOAD")==0) type=TLT_UPLOAD; |
| xmlFree(typeP); |
| } |
| else |
| { |
| LOGE("type property does not exist"); |
| } |
| return type; |
| } |
| |
| /** |
| Note, the caller has to release the memory with xmlFree |
| */ |
| char* getCommandValue(xmlNodePtr commandNode) |
| { |
| xmlNodePtr commandValueNodeP=commandNode->children; |
| |
| for (; commandValueNodeP; commandValueNodeP = commandValueNodeP->next) |
| { |
| if (commandValueNodeP->type == XML_ELEMENT_NODE && strcmp((char*)commandValueNodeP->name, "commandValue")==0) |
| { |
| break; |
| } |
| } |
| if(NULL==commandValueNodeP) return NULL; |
| |
| return (char*) xmlNodeGetContent(commandValueNodeP); |
| } |
| |
| |
| bool getCommandIgnoreError(xmlNodePtr commandNode) |
| { |
| xmlChar* attribute=xmlGetProp(commandNode, BAD_CAST "ignoreError"); |
| bool ignoreError=false; // default value is false |
| if(NULL!=attribute) |
| { |
| if(strcmp((char*)attribute, "true")==0) |
| { |
| ignoreError=true; |
| } |
| xmlFree(attribute); |
| } |
| |
| return ignoreError; |
| } |
| |
| void getValues(xmlNodePtr commandNodeP, commandtype_t* commandTypeP, uint32_t* idP, char** commandValueP, bool* ignoreErrorP) |
| { |
| *commandTypeP=getCommandType(commandNodeP); |
| *idP=getCommandId(commandNodeP); |
| *commandValueP=(char*) getCommandValue(commandNodeP); |
| *ignoreErrorP=getCommandIgnoreError(commandNodeP); |
| } |
| |
| uint32_t extractCmpCommand(CmpMessage** cmpCommandsP, uint32_t numberOfCmpCommands, uint32_t id, char* commandValueP, bool ignoreError) |
| { |
| CmpMessage* localCommandsP=*cmpCommandsP; // localCommandsP is just to make the code a bit more readable |
| CmpMessage* tmpCommandsP=realloc(localCommandsP, sizeof(CmpMessage)*(numberOfCmpCommands+1)); |
| |
| if(tmpCommandsP!=NULL) |
| { |
| localCommandsP=tmpCommandsP; |
| *cmpCommandsP=localCommandsP; |
| |
| memset(&(localCommandsP[numberOfCmpCommands]), 0,sizeof(CmpMessage)); |
| if(commandValueP) |
| { |
| localCommandsP[numberOfCmpCommands].length= base64DecodeStringRemoveEndZero(commandValueP, |
| (char**) &(localCommandsP[numberOfCmpCommands].contentP)); |
| if(0==localCommandsP[numberOfCmpCommands].length) |
| { |
| LOGE("handleCmpCommand: base64 decoding failed"); |
| } |
| localCommandsP[numberOfCmpCommands].hdr.id=id; |
| localCommandsP[numberOfCmpCommands].hdr.ignoreError=ignoreError; |
| numberOfCmpCommands++; |
| } |
| else |
| { |
| localCommandsP[numberOfCmpCommands].hdr.ret=ROOTPA_ERROR_XML; |
| } |
| } |
| else |
| { |
| LOGE("handleCmpCommand: was not able to realloc"); |
| // In this case we can not return an error to SE unless we set some of the earlier errors. |
| if(!ignoreError) |
| { |
| free(*cmpCommandsP); |
| *cmpCommandsP=NULL; |
| numberOfCmpCommands=0; |
| } |
| } |
| return numberOfCmpCommands; |
| } |
| |
| rootpaerror_t handleCmpResponses(uint32_t maxNumberOfCmpResponses, CmpMessage* cmpResponsesP, xmlNodePtr rspResultElementP) |
| { |
| LOGD(">>handleCmpResponses %d", maxNumberOfCmpResponses); |
| rootpaerror_t ret=ROOTPA_OK; |
| uint32_t i; |
| if(cmpResponsesP == NULL) |
| { |
| if(maxNumberOfCmpResponses>0) |
| { |
| LOGE("maxNumberOfCmpResponses %d while pointer is NULL", maxNumberOfCmpResponses); |
| return ROOTPA_ERROR_INTERNAL; |
| } |
| return ROOTPA_OK; |
| } |
| |
| for(i=0; (i<maxNumberOfCmpResponses) && (ROOTPA_OK==ret); i++) |
| { |
| char* encodedResponseP=NULL; |
| if((ROOTPA_ERROR_COMMAND_EXECUTION==cmpResponsesP[i].hdr.ret || |
| ROOTPA_OK==cmpResponsesP[i].hdr.ret) && cmpResponsesP[i].contentP!=NULL) |
| { |
| encodedResponseP=base64EncodeAddEndZero((char*) cmpResponsesP[i].contentP, cmpResponsesP[i].length); |
| if(NULL==encodedResponseP) |
| { |
| LOGE("handleCmpResponses: base64 encoding failed %d", i); |
| cmpResponsesP[i].hdr.ret=ROOTPA_ERROR_INTERNAL; |
| if(cmpResponsesP[i].hdr.ignoreError==false) ret=ROOTPA_ERROR_INTERNAL; |
| } |
| } |
| |
| if( addCommandResultData(rspResultElementP, cmpResponsesP[i].hdr.id, encodedResponseP, cmpResponsesP[i].hdr.ret, cmpResponsesP[i].hdr.intRet )==false ) |
| { |
| ret=ROOTPA_ERROR_XML; |
| } |
| free(encodedResponseP); |
| |
| if(cmpResponsesP[i].hdr.ret != ROOTPA_OK && false == cmpResponsesP[i].hdr.ignoreError) |
| { |
| LOGD("handleCmpResponses, stopping since the current message (%d) has error %d and ignoreError false", i, cmpResponsesP[i].hdr.ret); |
| break; |
| } |
| } |
| LOGD("<<handleCmpResponses %d", ret); |
| return ret; |
| } |
| |
| uint32_t handleUploadCommand(commandtype_t commandType, |
| CommonMessage** uploadCommandsP, |
| uint32_t numberOfUploadCommands, |
| uint32_t id, |
| char* commandValueP, |
| bool ignoreError) |
| { |
| LOGD(">>handleUploadCommand %d %lx %lx", commandType, (long int) uploadCommandsP, (long int) *uploadCommandsP); |
| CommonMessage* localCommandsP=*uploadCommandsP; // localCommandsP is just to make the code a bit more readable |
| CommonMessage* tmpCommandsP=realloc(localCommandsP, sizeof(CommonMessage)*(numberOfUploadCommands+1)); |
| |
| if(NULL == tmpCommandsP) |
| { |
| LOGE("handleUploadCommand: was not able to realloc, returning %d", ignoreError); |
| if(!ignoreError) |
| { |
| free(*uploadCommandsP); |
| *uploadCommandsP=NULL; |
| numberOfUploadCommands=0; |
| } |
| return numberOfUploadCommands; |
| // In this case we can not return an error to SE unless we set some of the earlier errors. |
| } |
| |
| localCommandsP=tmpCommandsP; |
| *uploadCommandsP=localCommandsP; |
| memset(&(localCommandsP[numberOfUploadCommands]), 0,sizeof(CommonMessage)); |
| |
| if(NULL == commandValueP) |
| { |
| localCommandsP[numberOfUploadCommands++].ret=ROOTPA_ERROR_XML; |
| return numberOfUploadCommands; |
| } |
| |
| localCommandsP[numberOfUploadCommands].ret=ROOTPA_OK; |
| uint8_t* containerDataP = NULL; |
| int containerLength= base64DecodeStringRemoveEndZero(commandValueP, (char**) &(containerDataP)); |
| |
| if(0 == containerLength) |
| { |
| LOGE("handleUploadCommand: base64 decoding failed"); |
| localCommandsP[numberOfUploadCommands].ret=ROOTPA_ERROR_INTERNAL; |
| } |
| |
| if(TLT_UPLOAD == commandType) |
| { |
| localCommandsP[numberOfUploadCommands].ret = uploadTrustlet(containerDataP, containerLength); |
| } |
| else if (SO_UPLOAD == commandType) |
| { |
| localCommandsP[numberOfUploadCommands].ret = uploadSo(containerDataP, |
| containerLength, |
| &localCommandsP[numberOfUploadCommands].intRet); |
| } |
| else |
| { |
| LOGE("handleUploadCommand: unknown command type %d this should not have happened", commandType); |
| localCommandsP[numberOfUploadCommands].ret=ROOTPA_ERROR_INTERNAL; |
| } |
| free(containerDataP); |
| localCommandsP[numberOfUploadCommands].id=id; |
| localCommandsP[numberOfUploadCommands].ignoreError=ignoreError; |
| numberOfUploadCommands++; |
| |
| LOGD("<<handleUploadCommand %d %lx %lx", numberOfUploadCommands, (long int) uploadCommandsP, (long int) *uploadCommandsP); |
| return numberOfUploadCommands; |
| } |
| |
| rootpaerror_t handleUploadResponses(uint32_t numberOfUploadResponses, CommonMessage* uploadResponsesP, xmlNodePtr rspResultElementP) |
| { |
| LOGD(">>handleUploadResponses %d", numberOfUploadResponses); |
| rootpaerror_t ret=ROOTPA_OK; |
| char zero=0; |
| uint32_t i; |
| if(uploadResponsesP == NULL) |
| { |
| if(numberOfUploadResponses>0) |
| { |
| LOGE("numberOfUploadResponses %d while pointer is NULL", numberOfUploadResponses); |
| return ROOTPA_ERROR_INTERNAL; |
| } |
| return ROOTPA_OK; |
| } |
| |
| for(i=0; (i < numberOfUploadResponses) && (ROOTPA_OK==ret); i++) |
| { |
| char* encodedResponseP=NULL; |
| if(ROOTPA_OK == uploadResponsesP[i].ret) |
| { |
| // in success case TLT_UPLOAD and SO_UPLOAD return "0" (encoded) in the resultValue |
| // field (discussed and agreed with Dimi Jan 10, 2013) |
| encodedResponseP=base64EncodeAddEndZero(&zero, 1); |
| } |
| |
| if( addCommandResultData(rspResultElementP, uploadResponsesP[i].id, encodedResponseP, uploadResponsesP[i].ret, uploadResponsesP[i].intRet )==false) |
| { |
| ret=ROOTPA_ERROR_XML; |
| } |
| free(encodedResponseP); |
| |
| LOGD("handleUploadResponses, in loop idx %d ret %d ignore %d", i, uploadResponsesP[i].ret , uploadResponsesP[i].ignoreError); |
| if(uploadResponsesP[i].ret != ROOTPA_OK && false == uploadResponsesP[i].ignoreError) |
| { |
| LOGD("handleUploadResponses, stopping since the current message has error and ignoreError false %d", i); |
| break; |
| } |
| } |
| LOGD("<<handleUploadResponses"); |
| return ret; |
| } |
| |
| rootpaerror_t handleCommandAndFillResponse(xmlDocPtr xmlCommandP, xmlDocPtr xmlResponseP) |
| { |
| LOGD(">>handleCommandAndFillResponse"); |
| rootpaerror_t ret=ROOTPA_OK; |
| rootpaerror_t tmpRet=ROOTPA_OK; |
| |
| xmlNodePtr rspRootElementP = xmlDocGetRootElement(xmlResponseP); |
| if(NULL==rspRootElementP) return ROOTPA_ERROR_XML; |
| |
| CmpMessage* cmpCommandsP=NULL; |
| CommonMessage* uploadCommandsP=NULL; |
| |
| int numberOfCmpCommands=0; |
| int numberOfUploadCommands=0; |
| |
| commandtype_t commandType=UNKNOWN_TYPE; |
| uint32_t id=0; |
| char* commandValueP=NULL; |
| bool ignoreError=0; |
| xmlNodePtr commandNode=NULL; |
| |
| // parse command data out of xml, upload commands will also be executed |
| |
| while((commandNode=getNextCommand(xmlCommandP, commandNode))!=NULL) |
| { |
| getValues(commandNode, &commandType, &id, &commandValueP, &ignoreError); |
| switch(commandType) |
| { |
| case CMP: |
| { |
| numberOfCmpCommands=extractCmpCommand(&cmpCommandsP, numberOfCmpCommands, id, commandValueP, ignoreError); |
| if(0==numberOfCmpCommands) |
| { |
| ret=ROOTPA_ERROR_OUT_OF_MEMORY; |
| } |
| break; |
| } |
| case SO_UPLOAD: |
| // intentional fallthrough |
| case TLT_UPLOAD: |
| numberOfUploadCommands=handleUploadCommand(commandType, &uploadCommandsP, numberOfUploadCommands, id, commandValueP, ignoreError); |
| if(0==numberOfUploadCommands) |
| { |
| ret=ROOTPA_ERROR_OUT_OF_MEMORY; |
| } |
| break; |
| default: |
| LOGE("handleCommandAndFillResponse: received unknown command"); |
| // we will still work with the other commands in case there are any |
| break; |
| } |
| xmlFree(commandValueP); |
| |
| if(ROOTPA_ERROR_OUT_OF_MEMORY == ret) break; |
| |
| if(commandType != CMP && |
| false == ignoreError && |
| uploadCommandsP && |
| uploadCommandsP[numberOfUploadCommands-1].ret != ROOTPA_OK) break; // since upload commands are already executed in this loop |
| } |
| |
| // execute the actual content management protocol commands, if there are any |
| |
| CmpMessage* cmpResponsesP=NULL; |
| if(cmpCommandsP) |
| { |
| uint32_t internalError; |
| cmpResponsesP=malloc(sizeof(CmpMessage)*numberOfCmpCommands); |
| memset(cmpResponsesP, 0, sizeof(CmpMessage)*numberOfCmpCommands); |
| |
| if(NULL==cmpResponsesP) |
| { |
| ret=ROOTPA_ERROR_OUT_OF_MEMORY; |
| } |
| else |
| { |
| tmpRet=executeContentManagementCommands(numberOfCmpCommands, cmpCommandsP, cmpResponsesP, &internalError); |
| if(ROOTPA_OK!=tmpRet) |
| { |
| LOGE("call to executeContentManagementCommands failed with %d, continuing anyway", tmpRet); |
| // return code from executeContentManagementCommands is here more informative than anything else |
| // even in an error case we need to return response to SE, the errors are also included in the |
| // actual CMP messages. |
| ret=tmpRet; |
| } |
| } |
| } |
| |
| // fill response |
| if (ret!=ROOTPA_ERROR_OUT_OF_MEMORY) |
| { |
| xmlNodePtr resultListNodeP=xmlNewChild(rspRootElementP, nameSpace_, BAD_CAST "commandResultList", NULL); |
| tmpRet=handleCmpResponses(numberOfCmpCommands, cmpResponsesP, resultListNodeP); |
| if(ROOTPA_OK!=tmpRet) |
| { |
| LOGE("handleCommandAndFillResponse: not able to handle all Cmp responses, still continuing with UploadResponses %d", tmpRet); |
| ret=tmpRet; |
| } |
| tmpRet=handleUploadResponses(numberOfUploadCommands, uploadCommandsP, resultListNodeP); |
| if(ROOTPA_OK!=tmpRet) |
| { |
| LOGE("handleCommandAndFillResponse: not able to handle all Upload responses %d", tmpRet); |
| ret=tmpRet; |
| } |
| } |
| // cleanup what has not yet been cleaned |
| |
| int i; |
| for(i=0; i<numberOfCmpCommands; i++) |
| { |
| if(cmpCommandsP) free(cmpCommandsP[i].contentP); |
| if(cmpResponsesP) free(cmpResponsesP[i].contentP); |
| } |
| free(cmpCommandsP); |
| free(cmpResponsesP); |
| free(uploadCommandsP); |
| |
| LOGD("<<handleCommandAndFillResponse %d", ret); |
| return ret; |
| } |
| |
| void handleError(void* ctx, const char *format, ...) |
| { |
| char *errMsg; |
| va_list args; |
| va_start(args, format); |
| vasprintf(&errMsg, format, args); |
| va_end(args); |
| LOGW("From libxml2: %s", errMsg); |
| free(errMsg); |
| } |
| |
| /* |
| This is for saving the required xml schema files so that the libxml2 code can read it, |
| to be called only if the files do not exist of can not be parsed |
| */ |
| |
| void saveFile(char* filePath, char* fileContent) |
| { |
| LOGD(">>saveFile %s", filePath); |
| FILE* fh; |
| |
| if ((fh = fopen(filePath, "w")) != NULL) |
| { |
| fprintf(fh, "%s", fileContent); |
| fclose(fh); |
| } |
| else |
| { |
| LOGE("saveFiles no handle %s", filePath); |
| } |
| LOGD("<<saveFile"); |
| } |
| |
| |
| bool validXmlMessage(xmlDocPtr xmlDocP) |
| { |
| LOGD(">>validXmlMessage %s", enrollmentServiceFullPath_); |
| |
| int result=-2; |
| |
| #ifdef LIBXML_SCHEMAS_ENABLED |
| |
| xmlSchemaParserCtxtPtr parserCtxtP = NULL; |
| xmlSchemaPtr schemaP = NULL; |
| xmlSchemaValidCtxtPtr validCtxtP = NULL; |
| |
| // Here we store the schemas if they are not already on "disk". It seems |
| // xmlSchemaNewParserCtxt succeeds even if the file does not exists and it is |
| // xmlSchemaParse that requires the file to exists. That is why the files are |
| // created if schemaP==NULL. Since we are using static library, this can be |
| // easily controlled even if there are changes in the behavior |
| |
| parserCtxtP = xmlSchemaNewParserCtxt(enrollmentServiceFullPath_); |
| schemaP = xmlSchemaParse(parserCtxtP); |
| if (!schemaP) |
| { |
| LOGW("validXmlMessage, no schema ctxt, attempting to save xsd files"); |
| saveFile(platformTypesFullPath_, PLATFORM_TYPES_XSD); |
| saveFile(enrollmentServiceFullPath_, ENROLLMENT_SERVICE_XSD); |
| schemaP = xmlSchemaParse(parserCtxtP); |
| if (!schemaP){ |
| LOGE("validXmlMessage, was not able to save xsd files"); |
| goto cleanup; |
| } |
| } |
| |
| validCtxtP = xmlSchemaNewValidCtxt(schemaP); |
| if (!validCtxtP){ |
| LOGE("validXmlMessage, no validCtxtP"); |
| goto cleanup; |
| } |
| |
| result=xmlSchemaValidateDoc(validCtxtP, xmlDocP); |
| |
| cleanup: |
| |
| if (parserCtxtP) xmlSchemaFreeParserCtxt(parserCtxtP); |
| if (schemaP) xmlSchemaFree(schemaP); |
| if (validCtxtP) xmlSchemaFreeValidCtxt(validCtxtP); |
| |
| LOGD("<<validXmlMessage %d", result); |
| return ((0==result)?true:false); |
| |
| #else // !LIBXML_SCHEMAS_ENABLED |
| LOGD("<<validXmlMessage"); |
| return true; |
| #endif // LIBXML_SCHEMAS_ENABLED |
| |
| } |
| |
| uint8_t* validateDumpAndFree(xmlDocPtr xmlResponseP) |
| { |
| uint8_t* dumpedP=NULL; |
| if(!validXmlMessage(xmlResponseP)) |
| { |
| LOGE("validateDumpAndFree, invalid response"); |
| } |
| int size=0; |
| xmlChar* dumpP; |
| // xmlDocDumpMemory(xmlResponseP, &dumpP, &size); |
| xmlDocDumpMemoryEnc(xmlResponseP, &dumpP, &size, "UTF-8"); |
| if(dumpP!=NULL) |
| { |
| // doing this copy only because libxml2 documentation tells to |
| // release the memory with xmlFree, not free and we want to keep |
| // libxml use strictly in this file. It is likely that xmlFree is |
| // compatible with free but since I have not verified it, this is to |
| // be on the safe side |
| |
| dumpedP=malloc(size+1); |
| strncpy((char*) dumpedP, (char*) dumpP, size+1); |
| xmlFree(dumpP); |
| } |
| xmlFreeDoc(xmlResponseP); |
| |
| return dumpedP; |
| } |
| |
| // functions used from outside of this file |
| |
| /** |
| in case an error is returned *responseP is set to NULL |
| */ |
| rootpaerror_t handleXmlMessage(const char* messageP, const char** responseP) |
| { |
| LOGD(">>handleXmlMessage"); |
| rootpaerror_t ret=ROOTPA_OK; |
| rootpaerror_t tmpRet=ROOTPA_OK; |
| *responseP=NULL; |
| |
| if (NULL==messageP) |
| { |
| LOGE("handleXmlMessage, no messageP"); |
| return ROOTPA_ERROR_ILLEGAL_ARGUMENT; |
| } |
| |
| xmlSetStructuredErrorFunc(NULL, NULL); |
| xmlSetGenericErrorFunc(NULL, handleError); |
| xmlThrDefSetStructuredErrorFunc(NULL, NULL); |
| xmlThrDefSetGenericErrorFunc(NULL, handleError); |
| |
| xmlDocPtr xmlDocP= xmlParseMemory(messageP, strlen(messageP)); |
| if(NULL==xmlDocP) |
| { |
| LOGE("handleXmlMessage, can not parse xmlMessageP %s", messageP); |
| return ROOTPA_ERROR_XML; |
| } |
| |
| if(!validXmlMessage(xmlDocP)) |
| { |
| LOGE("handleXmlMessage, invalid message %s", messageP); |
| ret=ROOTPA_ERROR_XML; |
| // attempting to parse the message anyway. |
| } |
| |
| xmlDocPtr xmlResponseP=createXmlResponse(); |
| |
| // parse received command |
| |
| if(xmlResponseP) |
| { |
| tmpRet=handleCommandAndFillResponse(xmlDocP, xmlResponseP); |
| if(tmpRet!=ROOTPA_OK) ret=tmpRet; |
| } |
| else |
| { |
| ret=ROOTPA_ERROR_XML; |
| } |
| |
| if(xmlResponseP && xmlResponseP->children) // if there is something to return to SE, return it. |
| { |
| *responseP = (char*)validateDumpAndFree(xmlResponseP); |
| } |
| else |
| { |
| if(xmlResponseP) xmlFreeDoc(xmlResponseP); |
| } |
| |
| if(xmlDocP) xmlFreeDoc(xmlDocP); |
| xmlCleanupParser(); |
| |
| LOGD("<<handleXmlMessage %d %s", ret, ((NULL==*responseP)?"no *responseP":*responseP)); |
| return ret; |
| } |
| |
| rootpaerror_t fillSystemInfo(xmlNodePtr systemInfoNode, const osInfo_t* osSpecificInfoP) |
| { |
| LOGD(">>fillSystemInfo %ld", (long int) osSpecificInfoP); |
| if(osSpecificInfoP->imeiEsnP) |
| { |
| LOGD("imei %s", osSpecificInfoP->imeiEsnP); |
| if(xmlNewProp(systemInfoNode, BAD_CAST "imei", BAD_CAST osSpecificInfoP->imeiEsnP)==NULL) return ROOTPA_ERROR_XML; |
| } |
| |
| if(osSpecificInfoP->mnoP) |
| { |
| LOGD("mno %s", osSpecificInfoP->mnoP); |
| if(xmlNewProp(systemInfoNode, BAD_CAST "mno", BAD_CAST osSpecificInfoP->mnoP)==NULL) return ROOTPA_ERROR_XML; |
| } |
| |
| if(osSpecificInfoP->brandP) |
| { |
| LOGD("brand %s", osSpecificInfoP->brandP); |
| if(xmlNewProp(systemInfoNode, BAD_CAST "brand", BAD_CAST osSpecificInfoP->brandP)==NULL) return ROOTPA_ERROR_XML; |
| } |
| |
| if(osSpecificInfoP->manufacturerP) |
| { |
| LOGD("manufacturer %s", osSpecificInfoP->manufacturerP); |
| if(xmlNewProp(systemInfoNode, BAD_CAST "manufacturer", BAD_CAST osSpecificInfoP->manufacturerP)==NULL) return ROOTPA_ERROR_XML; |
| } |
| |
| if(osSpecificInfoP->hardwareP) |
| { |
| LOGD("hardware %s", osSpecificInfoP->hardwareP); |
| if(xmlNewProp(systemInfoNode, BAD_CAST "hardware", BAD_CAST osSpecificInfoP->hardwareP)==NULL) return ROOTPA_ERROR_XML; |
| } |
| |
| if(osSpecificInfoP->modelP) |
| { |
| LOGD("model %s", osSpecificInfoP->modelP); |
| if(xmlNewProp(systemInfoNode, BAD_CAST "model", BAD_CAST osSpecificInfoP->modelP)==NULL) return ROOTPA_ERROR_XML; |
| } |
| |
| if(osSpecificInfoP->versionP) |
| { |
| LOGD("version %s", osSpecificInfoP->versionP); |
| if(xmlNewProp(systemInfoNode, BAD_CAST "version", BAD_CAST osSpecificInfoP->versionP)==NULL) return ROOTPA_ERROR_XML; |
| } |
| |
| LOGD("<<fillSystemInfo"); |
| return ROOTPA_OK; |
| } |
| |
| rootpaerror_t fillMcVersion(xmlNodePtr mcVersionNode, int mcVersionTag, const mcVersionInfo_t* mcVersionP) |
| { |
| LOGD(">>fillMcVersion"); |
| char intBuffer[INT_BUFFER_LENGTH]; |
| |
| xmlSetStructuredErrorFunc(NULL, NULL); |
| xmlSetGenericErrorFunc(NULL, handleError); |
| xmlThrDefSetStructuredErrorFunc(NULL, NULL); |
| xmlThrDefSetGenericErrorFunc(NULL, handleError); |
| |
| if(xmlNewProp(mcVersionNode, BAD_CAST "productId", BAD_CAST mcVersionP->productId)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionMci); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionMci", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionSo); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionSo", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionMclf); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionMclf", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionContainer); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionContainer", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionMcConfig); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionMcConfig", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionTlApi); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionTlApi", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionDrApi); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionDrApi", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%u",mcVersionP->versionCmp); |
| if(xmlNewProp(mcVersionNode, BAD_CAST "versionCmp", BAD_CAST intBuffer)==NULL) return ROOTPA_ERROR_XML; |
| |
| LOGD("<<fillMcVersion"); |
| return ROOTPA_OK; |
| } |
| |
| rootpaerror_t buildXmlTrustletInstallationRequest(const char** responseP, trustletInstallationData_t data ) |
| { |
| char intBuffer[INT_BUFFER_LENGTH]; |
| LOGD(">>buildXmlTrustletInstallationRequest %ld (%ld %d %d)", (long int) responseP, (long int) data.dataP, data.dataLength, data.dataType); |
| rootpaerror_t ret=ROOTPA_OK; |
| if(NULL == responseP) return ROOTPA_ERROR_ILLEGAL_ARGUMENT; // data content checked earlier in commandhandler.c |
| |
| xmlDocPtr xmlResponseDocP=createXmlResponse(); |
| xmlNodePtr rspRootElementP = xmlDocGetRootElement(xmlResponseDocP); |
| if(NULL==rspRootElementP) return ROOTPA_ERROR_XML; |
| |
| xmlNodePtr systemInfoNode=xmlNewChild(rspRootElementP, nameSpace_, BAD_CAST "tltInstallationRequest", NULL); |
| if(NULL==systemInfoNode) return ROOTPA_ERROR_XML; |
| |
| xmlNodePtr mcDataNode=NULL; |
| char* encodedDataP=base64EncodeAddEndZero((char*) data.dataP, data.dataLength); |
| if(NULL==encodedDataP) |
| { |
| LOGE("buildXmlTrustletInstallationRequest: base64 encoding of data failed"); |
| return ROOTPA_ERROR_INTERNAL; |
| } |
| |
| if(data.dataType == REQUEST_DATA_TLT) |
| { |
| mcDataNode = xmlNewChild(systemInfoNode, nameSpace_, BAD_CAST "trustletAxf", BAD_CAST encodedDataP); |
| if(data.memoryType != DEFAULT_MEMORY_TYPE) |
| { |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%d",data.memoryType); |
| if(xmlNewProp(mcDataNode, BAD_CAST "memoryType", BAD_CAST intBuffer)==NULL) |
| { |
| free(encodedDataP); |
| return ROOTPA_ERROR_XML; |
| } |
| } |
| |
| if(data.numberOfInstances != DEFAULT_NUMBER_OF_INSTANCES) |
| { |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%d",data.numberOfInstances); |
| if(xmlNewProp(mcDataNode, BAD_CAST "numberOfInstances", BAD_CAST intBuffer)==NULL) |
| { |
| free(encodedDataP); |
| return ROOTPA_ERROR_XML; |
| } |
| } |
| |
| if(data.flags != DEFAULT_FLAGS) |
| { |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%d",data.flags); |
| if(xmlNewProp(mcDataNode, BAD_CAST "flags", BAD_CAST intBuffer)==NULL) |
| { |
| free(encodedDataP); |
| return ROOTPA_ERROR_XML; |
| } |
| } |
| } |
| else |
| { |
| mcDataNode = xmlNewChild(systemInfoNode, nameSpace_, BAD_CAST "trustletEncryptionKey", BAD_CAST encodedDataP); |
| } |
| |
| snprintf(intBuffer,INT_BUFFER_LENGTH,"%d",data.minTltVersion); |
| if(xmlNewProp(mcDataNode, BAD_CAST "minTltVersion", BAD_CAST intBuffer)==NULL) |
| { |
| free(encodedDataP); |
| return ROOTPA_ERROR_XML; |
| } |
| |
| char* pukHashStringP=base64EncodeAddEndZero((char*) data.tltPukHashP, data.tltPukHashLength); |
| if(NULL==pukHashStringP) |
| { |
| LOGE("buildXmlTrustletInstallationRequest: base64 encoding of PukHash failed"); |
| free(encodedDataP); |
| return ROOTPA_ERROR_INTERNAL; |
| } |
| if(xmlNewProp(mcDataNode, BAD_CAST "tltPukHash", BAD_CAST pukHashStringP)==NULL) |
| { |
| LOGE("buildXmlTrustletInstallationRequest: xmlNewProp failed"); |
| free(pukHashStringP); |
| free(encodedDataP); |
| return ROOTPA_ERROR_XML; |
| } |
| |
| free(pukHashStringP); |
| free(encodedDataP); |
| if(NULL==mcDataNode) return ROOTPA_ERROR_XML; |
| |
| if(ROOTPA_OK==ret) |
| { |
| *responseP=(char*)validateDumpAndFree(xmlResponseDocP); |
| } |
| |
| xmlCleanupParser(); |
| return ret; |
| LOGD("<<buildXmlTrustletInstallationRequest"); |
| } |
| |
| /** |
| in case an error is returned *responseP is set to NULL |
| */ |
| rootpaerror_t buildXmlSystemInfo(const char** responseP, int mcVersionTag, const mcVersionInfo_t* mcVersionP, const osInfo_t* osSpecificInfoP) |
| { |
| LOGD(">>buildXmlSystemInfo %ld %ld %ld", ( long int ) responseP, ( long int ) mcVersionP, ( long int ) osSpecificInfoP); |
| rootpaerror_t ret=ROOTPA_OK; |
| if(NULL == responseP || NULL == mcVersionP || NULL == osSpecificInfoP) return ROOTPA_ERROR_INTERNAL; |
| |
| xmlSetStructuredErrorFunc(NULL, NULL); |
| xmlSetGenericErrorFunc(NULL, handleError); |
| xmlThrDefSetStructuredErrorFunc(NULL, NULL); |
| xmlThrDefSetGenericErrorFunc(NULL, handleError); |
| |
| xmlDocPtr xmlResponseDocP=createXmlResponse(); |
| xmlNodePtr rspRootElementP = xmlDocGetRootElement(xmlResponseDocP); |
| if(NULL==rspRootElementP) return ROOTPA_ERROR_XML; |
| |
| xmlNodePtr systemInfoNode=xmlNewChild(rspRootElementP, nameSpace_, BAD_CAST "systemInformation", NULL); |
| if(NULL==systemInfoNode) return ROOTPA_ERROR_XML; |
| |
| xmlNodePtr mcVersionNode=xmlNewChild(systemInfoNode, typesNameSpace_, BAD_CAST "mcVersion", NULL); |
| if(NULL==mcVersionNode) return ROOTPA_ERROR_XML; |
| |
| ret=fillSystemInfo(systemInfoNode, osSpecificInfoP); |
| if(ROOTPA_OK!=ret) |
| { |
| LOGE("buildXmlSystemInfo: could not fill system info %d, continuing anyway", ret); |
| } |
| |
| ret=fillMcVersion(mcVersionNode, mcVersionTag, mcVersionP); |
| if(ROOTPA_OK!=ret) |
| { |
| LOGE("buildXmlSystemInfo: could not fill Mc version %d, continuing anyway", ret); |
| } |
| |
| |
| if(ROOTPA_OK==ret) |
| { |
| *responseP=(char*)validateDumpAndFree(xmlResponseDocP); |
| } |
| |
| xmlCleanupParser(); |
| return ret; |
| LOGD("<<buildXmlSystemInfo"); |
| } |
| |
| /** |
| set the path where to look for and store the xsd files |
| */ |
| void setXsdPaths(const char* xsdpathP) |
| { |
| memset(enrollmentServiceFullPath_, 0, XSD_PATH_MAX_LEN); |
| memset(platformTypesFullPath_, 0, XSD_PATH_MAX_LEN); |
| |
| if (xsdpathP!=NULL && strlen(xsdpathP)+1+sizeof(ENROLLMENT_SERVICE_XSD_NAME)<XSD_PATH_MAX_LEN) // ENROLLMENT_SERVICE_XSD_NAME is the longer of the two |
| { |
| strncpy(enrollmentServiceFullPath_, xsdpathP, XSD_PATH_MAX_LEN); |
| strncpy(platformTypesFullPath_, xsdpathP, XSD_PATH_MAX_LEN); |
| |
| strncat(enrollmentServiceFullPath_, "/", XSD_PATH_MAX_LEN-strlen(xsdpathP)); |
| strncat(platformTypesFullPath_, "/", XSD_PATH_MAX_LEN-strlen(xsdpathP)); |
| } |
| |
| strncat(enrollmentServiceFullPath_, ENROLLMENT_SERVICE_XSD_NAME, XSD_PATH_MAX_LEN-strlen(enrollmentServiceFullPath_)); |
| strncat(platformTypesFullPath_, PLATFORM_TYPES_XSD_NAME, XSD_PATH_MAX_LEN-strlen(platformTypesFullPath_)); |
| } |