2 * XML Security Library (http://www.aleksey.com/xmlsec).
4 * XSLT Transform (http://www.w3.org/TR/xmldsig-core/#sec-XSLT)
6 * This is free software; see Copyright file in the source
7 * distribution for preciese wording.
9 * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
13 #ifndef XMLSEC_NO_XSLT
18 #include <libxml/tree.h>
19 #include <libxslt/xslt.h>
20 #include <libxslt/xsltInternals.h>
21 #include <libxslt/transform.h>
22 #include <libxslt/xsltutils.h>
24 #include <xmlsec/xmlsec.h>
25 #include <xmlsec/xmltree.h>
26 #include <xmlsec/keys.h>
27 #include <xmlsec/transforms.h>
28 #include <xmlsec/keys.h>
29 #include <xmlsec/parser.h>
30 #include <xmlsec/errors.h>
32 /**************************************************************************
36 *****************************************************************************/
37 typedef struct _xmlSecXsltCtx xmlSecXsltCtx, *xmlSecXsltCtxPtr;
38 struct _xmlSecXsltCtx {
39 xsltStylesheetPtr xslt;
40 xmlParserCtxtPtr parserCtx;
43 /****************************************************************************
47 * xmlSecXsltCtx is located after xmlSecTransform
49 ***************************************************************************/
50 #define xmlSecXsltSize \
51 (sizeof(xmlSecTransform) + sizeof(xmlSecXsltCtx))
52 #define xmlSecXsltGetCtx(transform) \
53 ((xmlSecXsltCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform)))
55 static int xmlSecXsltInitialize (xmlSecTransformPtr transform);
56 static void xmlSecXsltFinalize (xmlSecTransformPtr transform);
57 static int xmlSecXsltReadNode (xmlSecTransformPtr transform,
59 xmlSecTransformCtxPtr transformCtx);
60 static int xmlSecXsltPushBin (xmlSecTransformPtr transform,
61 const xmlSecByte* data,
64 xmlSecTransformCtxPtr transformCtx);
65 static int xmlSecXsltExecute (xmlSecTransformPtr transform,
67 xmlSecTransformCtxPtr transformCtx);
68 static int xmlSecXslProcess (xmlSecBufferPtr in,
70 xsltStylesheetPtr stylesheet);
71 static xmlSecTransformKlass xmlSecXsltKlass = {
72 /* klass/object sizes */
73 sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
74 xmlSecXsltSize, /* xmlSecSize objSize */
76 xmlSecNameXslt, /* const xmlChar* name; */
77 xmlSecHrefXslt, /* const xmlChar* href; */
78 xmlSecTransformUsageDSigTransform, /* xmlSecAlgorithmUsage usage; */
80 xmlSecXsltInitialize, /* xmlSecTransformInitializeMethod initialize; */
81 xmlSecXsltFinalize, /* xmlSecTransformFinalizeMethod finalize; */
82 xmlSecXsltReadNode, /* xmlSecTransformNodeReadMethod readNode; */
83 NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
84 NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */
85 NULL, /* xmlSecTransformSetKeyMethod setKey; */
86 NULL, /* xmlSecTransformValidateMethod validate; */
87 xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
88 xmlSecXsltPushBin, /* xmlSecTransformPushBinMethod pushBin; */
89 xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
90 NULL, /* xmlSecTransformPushXmlMethod pushXml; */
91 NULL, /* xmlSecTransformPopXmlMethod popXml; */
92 xmlSecXsltExecute, /* xmlSecTransformExecuteMethod execute; */
94 NULL, /* void* reserved0; */
95 NULL, /* void* reserved1; */
99 * xmlSecTransformXsltGetKlass:
101 * XSLT transform klass (http://www.w3.org/TR/xmldsig-core/#sec-XSLT):
103 * The normative specification for XSL Transformations is [XSLT].
104 * Specification of a namespace-qualified stylesheet element, which MUST be
105 * the sole child of the Transform element, indicates that the specified style
106 * sheet should be used. Whether this instantiates in-line processing of local
107 * XSLT declarations within the resource is determined by the XSLT processing
108 * model; the ordered application of multiple stylesheet may require multiple
109 * Transforms. No special provision is made for the identification of a remote
110 * stylesheet at a given URI because it can be communicated via an xsl:include
111 * or xsl:import within the stylesheet child of the Transform.
113 * This transform requires an octet stream as input. If the actual input is an
114 * XPath node-set, then the signature application should attempt to convert it
115 * to octets (apply Canonical XML]) as described in the Reference Processing
116 * Model (section 4.3.3.2).]
118 * The output of this transform is an octet stream. The processing rules for
119 * the XSL style sheet or transform element are stated in the XSLT specification
120 * [XSLT]. We RECOMMEND that XSLT transform authors use an output method of xml
121 * for XML and HTML. As XSLT implementations do not produce consistent
122 * serializations of their output, we further RECOMMEND inserting a transform
123 * after the XSLT transform to canonicalize the output. These steps will help
124 * to ensure interoperability of the resulting signatures among applications
125 * that support the XSLT transform. Note that if the output is actually HTML,
126 * then the result of these steps is logically equivalent [XHTML].
128 * Returns: pointer to XSLT transform klass.
131 xmlSecTransformXsltGetKlass(void) {
132 return(&xmlSecXsltKlass);
136 xmlSecXsltInitialize(xmlSecTransformPtr transform) {
137 xmlSecXsltCtxPtr ctx;
139 xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
140 xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
142 ctx = xmlSecXsltGetCtx(transform);
143 xmlSecAssert2(ctx != NULL, -1);
145 /* initialize context */
146 memset(ctx, 0, sizeof(xmlSecXsltCtx));
151 xmlSecXsltFinalize(xmlSecTransformPtr transform) {
152 xmlSecXsltCtxPtr ctx;
154 xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformXsltId));
155 xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecXsltSize));
157 ctx = xmlSecXsltGetCtx(transform);
158 xmlSecAssert(ctx != NULL);
160 if(ctx->xslt != NULL) {
161 xsltFreeStylesheet(ctx->xslt);
163 if(ctx->parserCtx != NULL) {
164 xmlFreeParserCtxt(ctx->parserCtx);
166 memset(ctx, 0, sizeof(xmlSecXsltCtx));
170 xmlSecXsltReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) {
171 xmlSecXsltCtxPtr ctx;
176 xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
177 xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
178 xmlSecAssert2(node != NULL, -1);
179 xmlSecAssert2(transformCtx != NULL, -1);
181 ctx = xmlSecXsltGetCtx(transform);
182 xmlSecAssert2(ctx != NULL, -1);
183 xmlSecAssert2(ctx->xslt == NULL, -1);
185 /* read content in the buffer */
186 buffer = xmlBufferCreate();
188 xmlSecError(XMLSEC_ERRORS_HERE,
189 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
191 XMLSEC_ERRORS_R_XML_FAILED,
192 XMLSEC_ERRORS_NO_MESSAGE);
195 cur = node->children;
197 xmlNodeDump(buffer, cur->doc, cur, 0, 0);
201 /* parse the buffer */
202 doc = xmlSecParseMemory(xmlBufferContent(buffer),
203 xmlBufferLength(buffer), 1);
205 xmlSecError(XMLSEC_ERRORS_HERE,
206 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
208 XMLSEC_ERRORS_R_XMLSEC_FAILED,
209 XMLSEC_ERRORS_NO_MESSAGE);
210 xmlBufferFree(buffer);
214 /* pre-process stylesheet */
215 ctx->xslt = xsltParseStylesheetDoc(doc);
216 if(ctx->xslt == NULL) {
217 xmlSecError(XMLSEC_ERRORS_HERE,
218 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
219 "xsltParseStylesheetDoc",
220 XMLSEC_ERRORS_R_XSLT_FAILED,
221 XMLSEC_ERRORS_NO_MESSAGE);
222 /* after parsing stylesheet doc is assigned
223 * to it and will be freed by xsltFreeStylesheet() */
225 xmlBufferFree(buffer);
229 xmlBufferFree(buffer);
234 xmlSecXsltPushBin(xmlSecTransformPtr transform, const xmlSecByte* data,
235 xmlSecSize dataSize, int final, xmlSecTransformCtxPtr transformCtx) {
236 xmlSecXsltCtxPtr ctx;
239 xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
240 xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
241 xmlSecAssert2(transformCtx != NULL, -1);
243 ctx = xmlSecXsltGetCtx(transform);
244 xmlSecAssert2(ctx != NULL, -1);
245 xmlSecAssert2(ctx->xslt != NULL, -1);
247 /* check/update current transform status */
248 if(transform->status == xmlSecTransformStatusNone) {
249 xmlSecAssert2(ctx->parserCtx == NULL, -1);
251 ctx->parserCtx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
252 if(ctx->parserCtx == NULL) {
253 xmlSecError(XMLSEC_ERRORS_HERE,
254 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
255 "xmlCreatePushParserCtxt",
256 XMLSEC_ERRORS_R_XML_FAILED,
257 XMLSEC_ERRORS_NO_MESSAGE);
261 /* required for c14n! */
262 ctx->parserCtx->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
263 ctx->parserCtx->replaceEntities = 1;
265 transform->status = xmlSecTransformStatusWorking;
266 } else if(transform->status == xmlSecTransformStatusFinished) {
268 } else if(transform->status != xmlSecTransformStatusWorking) {
269 xmlSecError(XMLSEC_ERRORS_HERE,
270 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
272 XMLSEC_ERRORS_R_INVALID_STATUS,
273 "status=%d", transform->status);
276 xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
277 xmlSecAssert2(ctx->parserCtx != NULL, -1);
279 /* push data to the input buffer */
280 if((data != NULL) && (dataSize > 0)) {
281 ret = xmlParseChunk(ctx->parserCtx, (const char*)data, dataSize, 0);
283 xmlSecError(XMLSEC_ERRORS_HERE,
284 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
286 XMLSEC_ERRORS_R_XML_FAILED,
287 "size=%d", dataSize);
292 /* finish parsing, apply xslt transforms and push to next in the chain */
296 xmlOutputBufferPtr output;
299 ret = xmlParseChunk(ctx->parserCtx, NULL, 0, 1);
300 if((ret != 0) || (ctx->parserCtx->myDoc == NULL)) {
301 xmlSecError(XMLSEC_ERRORS_HERE,
302 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
304 XMLSEC_ERRORS_R_XML_FAILED,
305 XMLSEC_ERRORS_NO_MESSAGE);
309 /* todo: check that document is well formed? */
310 docIn = ctx->parserCtx->myDoc;
311 ctx->parserCtx->myDoc = NULL;
313 docOut = xsltApplyStylesheet(ctx->xslt, docIn, NULL);
315 xmlSecError(XMLSEC_ERRORS_HERE,
316 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
317 "xsltApplyStylesheet",
318 XMLSEC_ERRORS_R_XSLT_FAILED,
319 XMLSEC_ERRORS_NO_MESSAGE);
325 if(transform->next != NULL) {
326 output = xmlSecTransformCreateOutputBuffer(transform->next, transformCtx);
328 xmlSecError(XMLSEC_ERRORS_HERE,
329 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
330 "xmlSecTransformCreateOutputBuffer",
331 XMLSEC_ERRORS_R_XMLSEC_FAILED,
332 XMLSEC_ERRORS_NO_MESSAGE);
337 output = xmlSecBufferCreateOutputBuffer(&(transform->outBuf));
339 xmlSecError(XMLSEC_ERRORS_HERE,
340 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
341 "xmlSecBufferCreateOutputBuffer",
342 XMLSEC_ERRORS_R_XMLSEC_FAILED,
343 XMLSEC_ERRORS_NO_MESSAGE);
349 ret = xsltSaveResultTo(output, docOut, ctx->xslt);
351 xmlSecError(XMLSEC_ERRORS_HERE,
352 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
354 XMLSEC_ERRORS_R_XSLT_FAILED,
355 XMLSEC_ERRORS_NO_MESSAGE);
356 xmlOutputBufferClose(output);
360 ret = xmlOutputBufferClose(output);
362 xmlSecError(XMLSEC_ERRORS_HERE,
363 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
364 "xmlOutputBufferClose",
365 XMLSEC_ERRORS_R_XML_FAILED,
366 XMLSEC_ERRORS_NO_MESSAGE);
372 transform->status = xmlSecTransformStatusFinished;
379 xmlSecXsltExecute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) {
380 xmlSecXsltCtxPtr ctx;
381 xmlSecBufferPtr in, out;
382 xmlSecSize inSize, outSize;
385 xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
386 xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
387 xmlSecAssert2(transformCtx != NULL, -1);
389 ctx = xmlSecXsltGetCtx(transform);
390 xmlSecAssert2(ctx != NULL, -1);
391 xmlSecAssert2(ctx->xslt != NULL, -1);
393 in = &(transform->inBuf);
394 out = &(transform->outBuf);
395 inSize = xmlSecBufferGetSize(in);
396 outSize = xmlSecBufferGetSize(out);
398 if(transform->status == xmlSecTransformStatusNone) {
399 transform->status = xmlSecTransformStatusWorking;
402 if((transform->status == xmlSecTransformStatusWorking) && (last == 0)) {
403 /* just do nothing */
404 xmlSecAssert2(outSize == 0, -1);
406 } else if((transform->status == xmlSecTransformStatusWorking) && (last != 0)) {
407 xmlSecAssert2(outSize == 0, -1);
409 ret = xmlSecXslProcess(in, out, ctx->xslt);
411 xmlSecError(XMLSEC_ERRORS_HERE,
412 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
414 XMLSEC_ERRORS_R_XMLSEC_FAILED,
415 XMLSEC_ERRORS_NO_MESSAGE);
419 ret = xmlSecBufferRemoveHead(in, inSize);
421 xmlSecError(XMLSEC_ERRORS_HERE,
422 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
423 "xmlSecBufferRemoveHead",
424 XMLSEC_ERRORS_R_XMLSEC_FAILED,
429 transform->status = xmlSecTransformStatusFinished;
430 } else if(transform->status == xmlSecTransformStatusFinished) {
431 /* the only way we can get here is if there is no input */
432 xmlSecAssert2(inSize == 0, -1);
434 xmlSecError(XMLSEC_ERRORS_HERE,
435 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
437 XMLSEC_ERRORS_R_INVALID_STATUS,
438 "status=%d", transform->status);
444 /* TODO: create PopBin method instead */
446 xmlSecXslProcess(xmlSecBufferPtr in, xmlSecBufferPtr out, xsltStylesheetPtr stylesheet) {
447 xmlDocPtr docIn = NULL;
448 xmlDocPtr docOut = NULL;
449 xmlOutputBufferPtr output = NULL;
453 xmlSecAssert2(in != NULL, -1);
454 xmlSecAssert2(out != NULL, -1);
455 xmlSecAssert2(stylesheet != NULL, -1);
457 docIn = xmlSecParseMemory(xmlSecBufferGetData(in), xmlSecBufferGetSize(in), 1);
459 xmlSecError(XMLSEC_ERRORS_HERE,
462 XMLSEC_ERRORS_R_XMLSEC_FAILED,
463 XMLSEC_ERRORS_NO_MESSAGE);
467 docOut = xsltApplyStylesheet(stylesheet, docIn, NULL);
469 xmlSecError(XMLSEC_ERRORS_HERE,
471 "xsltApplyStylesheet",
472 XMLSEC_ERRORS_R_XSLT_FAILED,
473 XMLSEC_ERRORS_NO_MESSAGE);
477 output = xmlSecBufferCreateOutputBuffer(out);
479 xmlSecError(XMLSEC_ERRORS_HERE,
481 "xmlSecBufferCreateOutputBuffer",
482 XMLSEC_ERRORS_R_XMLSEC_FAILED,
483 XMLSEC_ERRORS_NO_MESSAGE);
487 ret = xsltSaveResultTo(output, docOut, stylesheet);
489 xmlSecError(XMLSEC_ERRORS_HERE,
492 XMLSEC_ERRORS_R_XSLT_FAILED,
493 XMLSEC_ERRORS_NO_MESSAGE);
497 ret = xmlOutputBufferClose(output);
500 xmlSecError(XMLSEC_ERRORS_HERE,
502 "xmlOutputBufferClose",
503 XMLSEC_ERRORS_R_XML_FAILED,
504 XMLSEC_ERRORS_NO_MESSAGE);
511 if(output != NULL) xmlOutputBufferClose(output);
512 if(docIn != NULL) xmlFreeDoc(docIn);
513 if(docOut != NULL) xmlFreeDoc(docOut);
517 #endif /* XMLSEC_NO_XSLT */