/**
* XML Security Library (http://www.aleksey.com/xmlsec).
*
* XSLT Transform (http://www.w3.org/TR/xmldsig-core/#sec-XSLT)
*
* 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_config.h"
#ifndef XMLSEC_NO_XSLT
#include "xmlsec_globals.h"
#include <stdlib.h>
#include <string.h>
#include <libxml2_tree.h>
#include <libxml2_xmlmemory.h> //added for symbian port
#include <libxslt/libxslt_xslt.h>
#include <libxslt/libxslt_xsltinternals.h>
#include <libxslt/libxslt_transform.h>
#include <libxslt/libxslt_xsltutils.h>
#include "xmlsec_xmlsec.h"
#include "xmlsec_xmltree.h"
#include "xmlsec_keys.h"
#include "xmlsec_transforms.h"
#include "xmlsec_keys.h"
#include "xmlsec_parser.h"
#include "xmlsec_errors.h"
/**************************************************************************
*
* Internal xslt ctx
*
*****************************************************************************/
typedef struct _xmlSecXsltCtx xmlSecXsltCtx, *xmlSecXsltCtxPtr;
struct _xmlSecXsltCtx {
xsltStylesheetPtr xslt;
xmlParserCtxtPtr parserCtx;
};
/****************************************************************************
*
* XSLT transform
*
* xmlSecXsltCtx is located after xmlSecTransform
*
***************************************************************************/
#define xmlSecXsltSize \
(sizeof(xmlSecTransform) + sizeof(xmlSecXsltCtx))
#define xmlSecXsltGetCtx(transform) \
((xmlSecXsltCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform)))
static int xmlSecXsltInitialize (xmlSecTransformPtr transform);
static void xmlSecXsltFinalize (xmlSecTransformPtr transform);
static int xmlSecXsltReadNode (xmlSecTransformPtr transform,
xmlNodePtr node,
xmlSecTransformCtxPtr transformCtx);
static int xmlSecXsltPushBin (xmlSecTransformPtr transform,
const xmlSecByte* data,
xmlSecSize dataSize,
int final,
xmlSecTransformCtxPtr transformCtx);
static int xmlSecXsltExecute (xmlSecTransformPtr transform,
int last,
xmlSecTransformCtxPtr transformCtx);
static int xmlSecXslProcess (xmlSecBufferPtr in,
xmlSecBufferPtr out,
xsltStylesheetPtr stylesheet);
static xmlSecTransformKlass xmlSecXsltKlass = {
/* klass/object sizes */
sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
xmlSecXsltSize, /* xmlSecSize objSize */
xmlSecNameXslt, /* const xmlChar* name; */
xmlSecHrefXslt, /* const xmlChar* href; */
xmlSecTransformUsageDSigTransform, /* xmlSecAlgorithmUsage usage; */
xmlSecXsltInitialize, /* xmlSecTransformInitializeMethod initialize; */
xmlSecXsltFinalize, /* xmlSecTransformFinalizeMethod finalize; */
xmlSecXsltReadNode, /* xmlSecTransformNodeReadMethod readNode; */
NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */
NULL, /* xmlSecTransformSetKeyMethod setKey; */
NULL, /* xmlSecTransformValidateMethod validate; */
xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
xmlSecXsltPushBin, /* xmlSecTransformPushBinMethod pushBin; */
xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
NULL, /* xmlSecTransformPushXmlMethod pushXml; */
NULL, /* xmlSecTransformPopXmlMethod popXml; */
xmlSecXsltExecute, /* xmlSecTransformExecuteMethod execute; */
NULL, /* void* reserved0; */
NULL, /* void* reserved1; */
};
/**
* xmlSecTransformXsltGetKlass:
*
* XSLT transform klass (http://www.w3.org/TR/xmldsig-core/#sec-XSLT):
*
* The normative specification for XSL Transformations is [XSLT].
* Specification of a namespace-qualified stylesheet element, which MUST be
* the sole child of the Transform element, indicates that the specified style
* sheet should be used. Whether this instantiates in-line processing of local
* XSLT declarations within the resource is determined by the XSLT processing
* model; the ordered application of multiple stylesheet may require multiple
* Transforms. No special provision is made for the identification of a remote
* stylesheet at a given URI because it can be communicated via an xsl:include
* or xsl:import within the stylesheet child of the Transform.
*
* This transform requires an octet stream as input. If the actual input is an
* XPath node-set, then the signature application should attempt to convert it
* to octets (apply Canonical XML]) as described in the Reference Processing
* Model (section 4.3.3.2).]
*
* The output of this transform is an octet stream. The processing rules for
* the XSL style sheet or transform element are stated in the XSLT specification
* [XSLT]. We RECOMMEND that XSLT transform authors use an output method of xml
* for XML and HTML. As XSLT implementations do not produce consistent
* serializations of their output, we further RECOMMEND inserting a transform
* after the XSLT transform to canonicalize the output. These steps will help
* to ensure interoperability of the resulting signatures among applications
* that support the XSLT transform. Note that if the output is actually HTML,
* then the result of these steps is logically equivalent [XHTML].
*
* Returns pointer to XSLT transform klass.
*/
xmlSecTransformId
xmlSecTransformXsltGetKlass(void) {
return(&xmlSecXsltKlass);
}
static int
xmlSecXsltInitialize(xmlSecTransformPtr transform) {
xmlSecXsltCtxPtr ctx;
xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
ctx = xmlSecXsltGetCtx(transform);
xmlSecAssert2(ctx != NULL, -1);
/* initialize context */
memset(ctx, 0, sizeof(xmlSecXsltCtx));
return(0);
}
static void
xmlSecXsltFinalize(xmlSecTransformPtr transform) {
xmlSecXsltCtxPtr ctx;
xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformXsltId));
xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecXsltSize));
ctx = xmlSecXsltGetCtx(transform);
xmlSecAssert(ctx != NULL);
if(ctx->xslt != NULL) {
xsltFreeStylesheet(ctx->xslt);
}
if(ctx->parserCtx != NULL) {
xmlFreeParserCtxt(ctx->parserCtx);
}
memset(ctx, 0, sizeof(xmlSecXsltCtx));
}
/**
* xmlSecXsltReadNode:
*/
static int
xmlSecXsltReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) {
xmlSecXsltCtxPtr ctx;
xmlBufferPtr buffer;
xmlDocPtr doc;
xmlNodePtr cur;
xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
xmlSecAssert2(node != NULL, -1);
xmlSecAssert2(transformCtx != NULL, -1);
ctx = xmlSecXsltGetCtx(transform);
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(ctx->xslt == NULL, -1);
/* read content in the buffer */
buffer = xmlBufferCreate();
if(buffer == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlBufferCreate",
XMLSEC_ERRORS_R_XML_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
cur = node->children;
while(cur != NULL) {
xmlNodeDump(buffer, cur->doc, cur, 0, 0);
cur = cur->next;
}
/* parse the buffer */
doc = xmlSecParseMemory(xmlBufferContent(buffer),
xmlBufferLength(buffer), 1);
if(doc == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecParseMemory",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlBufferFree(buffer);
return(-1);
}
/* pre-process stylesheet */
ctx->xslt = xsltParseStylesheetDoc(doc);
if(ctx->xslt == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xsltParseStylesheetDoc",
XMLSEC_ERRORS_R_XSLT_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
/* after parsing stylesheet doc is assigned
* to it and will be freed by xsltFreeStylesheet() */
xmlFreeDoc(doc);
xmlBufferFree(buffer);
return(-1);
}
xmlBufferFree(buffer);
return(0);
}
static int
xmlSecXsltPushBin(xmlSecTransformPtr transform, const xmlSecByte* data,
xmlSecSize dataSize, int final, xmlSecTransformCtxPtr transformCtx) {
xmlSecXsltCtxPtr ctx;
int ret;
xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
xmlSecAssert2(transformCtx != NULL, -1);
ctx = xmlSecXsltGetCtx(transform);
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(ctx->xslt != 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, apply xslt transforms and push to next in the chain */
if(final != 0) {
xmlDocPtr docIn;
xmlDocPtr docOut;
xmlOutputBufferPtr output;
/* finalize */
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);
}
docIn = ctx->parserCtx->myDoc;
ctx->parserCtx->myDoc = NULL;
docOut = xsltApplyStylesheet(ctx->xslt, docIn, NULL);
if(docOut == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xsltApplyStylesheet",
XMLSEC_ERRORS_R_XSLT_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlFreeDoc(docIn);
return(-1);
}
xmlFreeDoc(docIn);
if(transform->next != NULL) {
output = xmlSecTransformCreateOutputBuffer(transform->next, transformCtx);
if(output == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecTransformCreateOutputBuffer",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlFreeDoc(docOut);
return(-1);
}
} else {
output = xmlSecBufferCreateOutputBuffer(&(transform->outBuf));
if(output == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBufferCreateOutputBuffer",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlFreeDoc(docOut);
return(-1);
}
}
ret = xsltSaveResultTo(output, docOut, ctx->xslt);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xsltSaveResultTo",
XMLSEC_ERRORS_R_XSLT_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlOutputBufferClose(output);
xmlFreeDoc(docOut);
return(-1);
}
ret = xmlOutputBufferClose(output);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlOutputBufferClose",
XMLSEC_ERRORS_R_XML_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlFreeDoc(docOut);
return(-1);
}
xmlFreeDoc(docOut);
transform->status = xmlSecTransformStatusFinished;
}
return(0);
}
static int
xmlSecXsltExecute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) {
xmlSecXsltCtxPtr ctx;
xmlSecBufferPtr in, out;
xmlSecSize inSize, outSize;
int ret;
xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
xmlSecAssert2(transformCtx != NULL, -1);
ctx = xmlSecXsltGetCtx(transform);
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(ctx->xslt != NULL, -1);
in = &(transform->inBuf);
out = &(transform->outBuf);
inSize = xmlSecBufferGetSize(in);
outSize = xmlSecBufferGetSize(out);
if(transform->status == xmlSecTransformStatusNone) {
transform->status = xmlSecTransformStatusWorking;
}
if((transform->status == xmlSecTransformStatusWorking) && (last == 0)) {
/* just do nothing */
xmlSecAssert2(outSize == 0, -1);
} else if((transform->status == xmlSecTransformStatusWorking) && (last != 0)) {
xmlSecAssert2(outSize == 0, -1);
ret = xmlSecXslProcess(in, out, ctx->xslt);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecXslProcess",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
ret = xmlSecBufferRemoveHead(in, inSize);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBufferRemoveHead",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"size=%d", inSize);
return(-1);
}
transform->status = xmlSecTransformStatusFinished;
} else if(transform->status == xmlSecTransformStatusFinished) {
/* the only way we can get here is if there is no input */
xmlSecAssert2(inSize == 0, -1);
} else {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
NULL,
XMLSEC_ERRORS_R_INVALID_STATUS,
"status=%d", transform->status);
return(-1);
}
return(0);
}
static int
xmlSecXslProcess(xmlSecBufferPtr in, xmlSecBufferPtr out, xsltStylesheetPtr stylesheet) {
xmlDocPtr docIn = NULL;
xmlDocPtr docOut = NULL;
xmlOutputBufferPtr output = NULL;
int res = -1;
int ret;
xmlSecAssert2(in != NULL, -1);
xmlSecAssert2(out != NULL, -1);
xmlSecAssert2(stylesheet != NULL, -1);
docIn = xmlSecParseMemory(xmlSecBufferGetData(in), xmlSecBufferGetSize(in), 1);
if(docIn == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecParseMemory",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
goto done;
}
docOut = xsltApplyStylesheet(stylesheet, docIn, NULL);
if(docOut == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xsltApplyStylesheet",
XMLSEC_ERRORS_R_XSLT_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
goto done;
}
output = xmlSecBufferCreateOutputBuffer(out);
if(output == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBufferCreateOutputBuffer",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
goto done;
}
ret = xsltSaveResultTo(output, docOut, stylesheet);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xsltSaveResultTo",
XMLSEC_ERRORS_R_XSLT_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
goto done;
}
ret = xmlOutputBufferClose(output);
output = NULL;
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlOutputBufferClose",
XMLSEC_ERRORS_R_XML_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
res = 0;
done:
if(output != NULL) xmlOutputBufferClose(output);
if(docIn != NULL) xmlFreeDoc(docIn);
if(docOut != NULL) xmlFreeDoc(docOut);
return(res);
}
#endif /* XMLSEC_NO_XSLT */