diff options
Diffstat (limited to 'src/parser.c')
-rw-r--r-- | src/parser.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 00000000..fdb99c51 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,571 @@ +/** + * XML Security Library (http://www.aleksey.com/xmlsec). + * + * XML Parser transform and utility functions. + * + * This is free software; see Copyright file in the source + * distribution for preciese wording. + * + * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com> + */ +#include "globals.h" + +#include <stdlib.h> +#include <string.h> + +#include <libxml/tree.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> + +#include <xmlsec/xmlsec.h> +#include <xmlsec/xmltree.h> +#include <xmlsec/keys.h> +#include <xmlsec/transforms.h> +#include <xmlsec/parser.h> +#include <xmlsec/errors.h> + +/************************************************************************** + * + * Internal parser + * + *****************************************************************************/ +typedef struct _xmlSecParserCtx xmlSecParserCtx, + *xmlSecParserCtxPtr; +struct _xmlSecParserCtx { + xmlParserCtxtPtr parserCtx; +}; + +/************************************************************************** + * + * XML Parser transform + * + * xmlSecParserCtx is located after xmlSecTransform + * + ***************************************************************************/ +#define xmlSecParserSize \ + (sizeof(xmlSecTransform) + sizeof(xmlSecParserCtx)) +#define xmlSecParserGetCtx(transform) \ + ((xmlSecTransformCheckSize((transform), xmlSecParserSize)) ? \ + ((xmlSecParserCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform))) : \ + (xmlSecParserCtxPtr)NULL) + +static int xmlSecParserInitialize (xmlSecTransformPtr transform); +static void xmlSecParserFinalize (xmlSecTransformPtr transform); +static int xmlSecParserPushBin (xmlSecTransformPtr transform, + const xmlSecByte* data, + xmlSecSize dataSize, + int final, + xmlSecTransformCtxPtr transformCtx); +static int xmlSecParserPopXml (xmlSecTransformPtr transform, + xmlSecNodeSetPtr* nodes, + xmlSecTransformCtxPtr transformCtx); + +static xmlSecTransformKlass xmlSecParserKlass = { + /* klass/object sizes */ + sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */ + xmlSecParserSize, /* xmlSecSize objSize */ + + BAD_CAST "xml-parser", /* const xmlChar* name; */ + NULL, /* const xmlChar* href; */ + xmlSecTransformUsageDSigTransform, /* xmlSecTransformUsage usage; */ + + xmlSecParserInitialize, /* xmlSecTransformInitializeMethod initialize; */ + xmlSecParserFinalize, /* xmlSecTransformFinalizeMethod finalize; */ + NULL, /* xmlSecTransformNodeReadMethod readNode; */ + NULL, /* xmlSecTransformNodeWriteMethod writeNode; */ + NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */ + NULL, /* xmlSecTransformSetKeyMethod setKey; */ + NULL, /* xmlSecTransformValidateMethod validate; */ + xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */ + xmlSecParserPushBin, /* xmlSecTransformPushBinMethod pushBin; */ + NULL, /* xmlSecTransformPopBinMethod popBin; */ + NULL, /* xmlSecTransformPushXmlMethod pushXml; */ + xmlSecParserPopXml, /* xmlSecTransformPopXmlMethod popXml; */ + NULL, /* xmlSecTransformExecuteMethod execute; */ + + NULL, /* void* reserved0; */ + NULL, /* void* reserved1; */ +}; + +/** + * xmlSecTransformXmlParserGetKlass: + * + * The XML parser transform. + * + * Returns: XML parser transform klass. + */ +xmlSecTransformId +xmlSecTransformXmlParserGetKlass(void) { + return(&xmlSecParserKlass); +} + +static int +xmlSecParserInitialize(xmlSecTransformPtr transform) { + xmlSecParserCtxPtr ctx; + + xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1); + xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecParserSize), -1); + + ctx = xmlSecParserGetCtx(transform); + xmlSecAssert2(ctx != NULL, -1); + + /* initialize context */ + memset(ctx, 0, sizeof(xmlSecParserCtx)); + return(0); +} + +static void +xmlSecParserFinalize(xmlSecTransformPtr transform) { + xmlSecParserCtxPtr ctx; + + xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId)); + xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecParserSize)); + + ctx = xmlSecParserGetCtx(transform); + xmlSecAssert(ctx != NULL); + + if(ctx->parserCtx != NULL) { + xmlFreeParserCtxt(ctx->parserCtx); + } + memset(ctx, 0, sizeof(xmlSecParserCtx)); +} + +static int +xmlSecParserPushBin(xmlSecTransformPtr transform, const xmlSecByte* data, + xmlSecSize dataSize, int final, xmlSecTransformCtxPtr transformCtx) { + xmlSecParserCtxPtr ctx; + int ret; + + xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1); + xmlSecAssert2(transformCtx != NULL, -1); + + ctx = xmlSecParserGetCtx(transform); + xmlSecAssert2(ctx != NULL, -1); + + /* check/update current transform status */ + if(transform->status == xmlSecTransformStatusNone) { + xmlSecAssert2(ctx->parserCtx == NULL, -1); + + ctx->parserCtx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL); + if(ctx->parserCtx == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlCreatePushParserCtxt", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + + /* required for c14n! */ + ctx->parserCtx->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + ctx->parserCtx->replaceEntities = 1; + + transform->status = xmlSecTransformStatusWorking; + } else if(transform->status == xmlSecTransformStatusFinished) { + return(0); + } else if(transform->status != xmlSecTransformStatusWorking) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + NULL, + XMLSEC_ERRORS_R_INVALID_STATUS, + "status=%d", transform->status); + return(-1); + } + xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1); + xmlSecAssert2(ctx->parserCtx != NULL, -1); + + /* push data to the input buffer */ + if((data != NULL) && (dataSize > 0)) { + ret = xmlParseChunk(ctx->parserCtx, (const char*)data, dataSize, 0); + if(ret != 0) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlParseChunk", + XMLSEC_ERRORS_R_XML_FAILED, + "size=%d", dataSize); + return(-1); + } + } + + /* finish parsing and push to next in the chain */ + if(final != 0) { + ret = xmlParseChunk(ctx->parserCtx, NULL, 0, 1); + if((ret != 0) || (ctx->parserCtx->myDoc == NULL)) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlParseChunk", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + + /* todo: check that document is well formed? */ + transform->outNodes = xmlSecNodeSetCreate(ctx->parserCtx->myDoc, + NULL, xmlSecNodeSetTree); + if(transform->outNodes == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecNodeSetCreate", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlFreeDoc(ctx->parserCtx->myDoc); + ctx->parserCtx->myDoc = NULL; + return(-1); + } + xmlSecNodeSetDocDestroy(transform->outNodes); /* this node set "owns" the doc pointer */ + ctx->parserCtx->myDoc = NULL; + + /* push result to the next transform (if exist) */ + if(transform->next != NULL) { + ret = xmlSecTransformPushXml(transform->next, transform->outNodes, transformCtx); + if(ret < 0) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecTransformPushXml", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + } + + transform->status = xmlSecTransformStatusFinished; + } + + return(0); +} + +static int +xmlSecParserPopXml(xmlSecTransformPtr transform, xmlSecNodeSetPtr* nodes, + xmlSecTransformCtxPtr transformCtx) { + xmlSecParserCtxPtr ctx; + xmlParserInputBufferPtr buf; + xmlParserInputPtr input; + xmlParserCtxtPtr ctxt; + xmlDocPtr doc; + int ret; + + xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1); + xmlSecAssert2(nodes != NULL, -1); + xmlSecAssert2(transformCtx != NULL, -1); + + ctx = xmlSecParserGetCtx(transform); + xmlSecAssert2(ctx != NULL, -1); + + /* check/update current transform status */ + switch(transform->status) { + case xmlSecTransformStatusNone: + transform->status = xmlSecTransformStatusWorking; + break; + case xmlSecTransformStatusWorking: + /* just do nothing */ + break; + case xmlSecTransformStatusFinished: + (*nodes) = NULL; + return(0); + default: + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + NULL, + XMLSEC_ERRORS_R_INVALID_STATUS, + "status=%d", transform->status); + return(-1); + } + xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1); + + /* prepare parser context */ + if(transform->prev == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + NULL, + XMLSEC_ERRORS_R_INVALID_TRANSFORM, + "prev transform is null"); + return(-1); + } + + buf = xmlSecTransformCreateInputBuffer(transform->prev, transformCtx); + if(buf == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecTransformCreateInputBuffer", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlNewParserCtxt", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlFreeParserInputBuffer(buf); + return(-1); + } + + input = xmlNewIOInputStream(ctxt, buf, XML_CHAR_ENCODING_NONE); + if(input == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlNewParserCtxt", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlFreeParserCtxt(ctxt); + xmlFreeParserInputBuffer(buf); + return(-1); + } + + ret = inputPush(ctxt, input); + if(input == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "inputPush", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlFreeInputStream(input); + xmlFreeParserCtxt(ctxt); + return(-1); + } + + /* required for c14n! */ + ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + ctxt->replaceEntities = 1; + + /* finaly do the parsing */ + ret = xmlParseDocument(ctxt); + if(ret < 0) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlParseDocument", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + if(ctxt->myDoc != NULL) { + xmlFreeDoc(ctxt->myDoc); + ctxt->myDoc = NULL; + } + xmlFreeParserCtxt(ctxt); + return(-1); + } + + /* remember the result and free parsing context */ + doc = ctxt->myDoc; + ctxt->myDoc = NULL; + xmlFreeParserCtxt(ctxt); + + /* return result to the caller */ + (*nodes) = xmlSecNodeSetCreate(doc, NULL, xmlSecNodeSetTree); + if((*nodes) == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecNodeSetCreate", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlFreeDoc(doc); + return(-1); + } + xmlSecNodeSetDocDestroy((*nodes)); /* this node set "owns" the doc pointer */ + transform->status = xmlSecTransformStatusFinished; + return(0); +} + +/************************************************************************** + * + * XML Parser functions + * + *************************************************************************/ +typedef struct _xmlSecExtMemoryParserCtx { + const xmlSecByte *prefix; + xmlSecSize prefixSize; + const xmlSecByte *buffer; + xmlSecSize bufferSize; + const xmlSecByte *postfix; + xmlSecSize postfixSize; +} xmlSecExtMemoryParserCtx, *xmlSecExtMemoryParserCtxPtr; + +/** + * xmlSecParseFile: + * @filename: the filename. + * + * Loads XML Doc from file @filename. We need a special version because of + * c14n issue. The code is copied from xmlSAXParseFileWithData() function. + * + * Returns: pointer to the loaded XML document or NULL if an error occurs. + */ +xmlDocPtr +xmlSecParseFile(const char *filename) { + xmlDocPtr ret; + xmlParserCtxtPtr ctxt; + char *directory = NULL; + + xmlSecAssert2(filename != NULL, NULL); + + xmlInitParser(); + ctxt = xmlCreateFileParserCtxt(filename); + if (ctxt == NULL) { + return(NULL); + } + + /* todo: set directories from current doc? */ + if ((ctxt->directory == NULL) && (directory == NULL)) + directory = xmlParserGetDirectory(filename); + if ((ctxt->directory == NULL) && (directory != NULL)) + ctxt->directory = (char *) xmlStrdup((xmlChar *) directory); + + /* required for c14n! */ + ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + ctxt->replaceEntities = 1; + + xmlParseDocument(ctxt); + + if(ctxt->wellFormed) { + ret = ctxt->myDoc; + } else { + ret = NULL; + xmlFreeDoc(ctxt->myDoc); + ctxt->myDoc = NULL; + } + xmlFreeParserCtxt(ctxt); + return(ret); + +} + +/** + * xmlSecParseMemoryExt: + * @prefix: the first part of the input. + * @prefixSize: the size of the first part of the input. + * @buffer: the second part of the input. + * @bufferSize: the size of the second part of the input. + * @postfix: the third part of the input. + * @postfixSize: the size of the third part of the input. + * + * Loads XML Doc from 3 chunks of memory: @prefix, @buffer and @postfix. + * + * Returns: pointer to the loaded XML document or NULL if an error occurs. + */ +xmlDocPtr +xmlSecParseMemoryExt(const xmlSecByte *prefix, xmlSecSize prefixSize, + const xmlSecByte *buffer, xmlSecSize bufferSize, + const xmlSecByte *postfix, xmlSecSize postfixSize) { + xmlParserCtxtPtr ctxt = NULL; + xmlDocPtr doc = NULL; + int ret; + + /* create context */ + ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL); + if(ctxt == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + "xmlCreatePushParserCtxt", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + goto done; + } + + /* required for c14n! */ + ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + ctxt->replaceEntities = 1; + + /* prefix */ + if((prefix != NULL) && (prefixSize > 0)) { + ret = xmlParseChunk(ctxt, (const char*)prefix, prefixSize, 0); + if(ret != 0) { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + "xmlParseChunk", + XMLSEC_ERRORS_R_XML_FAILED, + "prefixSize=%d", prefixSize); + goto done; + } + } + + /* buffer */ + if((buffer != NULL) && (bufferSize > 0)) { + ret = xmlParseChunk(ctxt, (const char*)buffer, bufferSize, 0); + if(ret != 0) { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + "xmlParseChunk", + XMLSEC_ERRORS_R_XML_FAILED, + "bufferSize=%d", bufferSize); + goto done; + } + } + + /* postfix */ + if((postfix != NULL) && (postfixSize > 0)) { + ret = xmlParseChunk(ctxt, (const char*)postfix, postfixSize, 0); + if(ret != 0) { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + "xmlParseChunk", + XMLSEC_ERRORS_R_XML_FAILED, + "postfixSize=%d", postfixSize); + goto done; + } + } + + /* finishing */ + ret = xmlParseChunk(ctxt, NULL, 0, 1); + if((ret != 0) || (ctxt->myDoc == NULL)) { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + "xmlParseChunk", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + goto done; + } + doc = ctxt->myDoc; + +done: + if(ctxt != NULL) { + xmlFreeParserCtxt(ctxt); + } + return(doc); +} + + +/** + * xmlSecParseMemory: + * @buffer: the input buffer. + * @size: the input buffer size. + * @recovery: the flag. + * + * Loads XML Doc from memory. We need a special version because of + * c14n issue. The code is copied from xmlSAXParseMemory() function. + * + * Returns: pointer to the loaded XML document or NULL if an error occurs. + */ +xmlDocPtr +xmlSecParseMemory(const xmlSecByte *buffer, xmlSecSize size, int recovery) { + xmlDocPtr ret; + xmlParserCtxtPtr ctxt; + + xmlSecAssert2(buffer != NULL, NULL); + + ctxt = xmlCreateMemoryParserCtxt((char*)buffer, size); + if (ctxt == NULL) { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + "xmlCreateMemoryParserCtxt", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(NULL); + } + + /* required for c14n! */ + ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + ctxt->replaceEntities = 1; + + xmlParseDocument(ctxt); + + if((ctxt->wellFormed) || recovery) { + ret = ctxt->myDoc; + } else { + ret = NULL; + xmlFreeDoc(ctxt->myDoc); + ctxt->myDoc = NULL; + } + xmlFreeParserCtxt(ctxt); + return(ret); +} + |