/**
* 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>
* Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
*/
#include "xmlsec_globals.h"
#include <stdlib.h>
#include <string.h>
#include <stdapis/libxml2/libxml2_tree.h>
#include <stdapis/libxml2/libxml2_parser.h>
#include <stdapis/libxml2/libxml2_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.
*/
EXPORT_C
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);
}
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.
*/
EXPORT_C
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);
}
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.
*/
EXPORT_C
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.
*/
EXPORT_C
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);
}