Git init
[external/xmlsec1.git] / src / xslt.c
1 /** 
2  * XML Security Library (http://www.aleksey.com/xmlsec).
3  *
4  * XSLT Transform (http://www.w3.org/TR/xmldsig-core/#sec-XSLT)
5  *
6  * This is free software; see Copyright file in the source
7  * distribution for preciese wording.
8  * 
9  * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
10  */
11 #include "globals.h"
12
13 #ifndef XMLSEC_NO_XSLT
14
15 #include <stdlib.h>
16 #include <string.h>
17  
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>
23
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>
31
32 /**************************************************************************
33  *
34  * Internal xslt ctx
35  *
36  *****************************************************************************/
37 typedef struct _xmlSecXsltCtx                   xmlSecXsltCtx, *xmlSecXsltCtxPtr;
38 struct _xmlSecXsltCtx {
39     xsltStylesheetPtr   xslt;
40     xmlParserCtxtPtr    parserCtx;
41 };          
42
43 /****************************************************************************
44  *
45  * XSLT transform
46  *
47  * xmlSecXsltCtx is located after xmlSecTransform
48  * 
49  ***************************************************************************/
50 #define xmlSecXsltSize  \
51     (sizeof(xmlSecTransform) + sizeof(xmlSecXsltCtx))   
52 #define xmlSecXsltGetCtx(transform) \
53     ((xmlSecXsltCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform)))
54
55 static int              xmlSecXsltInitialize                    (xmlSecTransformPtr transform);
56 static void             xmlSecXsltFinalize                      (xmlSecTransformPtr transform);
57 static int              xmlSecXsltReadNode                      (xmlSecTransformPtr transform,
58                                                                  xmlNodePtr node,
59                                                                  xmlSecTransformCtxPtr transformCtx);
60 static int              xmlSecXsltPushBin                       (xmlSecTransformPtr transform, 
61                                                                  const xmlSecByte* data,
62                                                                  xmlSecSize dataSize,
63                                                                  int final,
64                                                                  xmlSecTransformCtxPtr transformCtx);
65 static int              xmlSecXsltExecute                       (xmlSecTransformPtr transform, 
66                                                                  int last,
67                                                                  xmlSecTransformCtxPtr transformCtx);
68 static int              xmlSecXslProcess                        (xmlSecBufferPtr in,
69                                                                  xmlSecBufferPtr out,
70                                                                  xsltStylesheetPtr stylesheet);
71 static xmlSecTransformKlass xmlSecXsltKlass = {
72     /* klass/object sizes */
73     sizeof(xmlSecTransformKlass),               /* xmlSecSize klassSize */
74     xmlSecXsltSize,                             /* xmlSecSize objSize */
75
76     xmlSecNameXslt,                             /* const xmlChar* name; */
77     xmlSecHrefXslt,                             /* const xmlChar* href; */
78     xmlSecTransformUsageDSigTransform,          /* xmlSecAlgorithmUsage usage; */
79
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; */
93     
94     NULL,                                       /* void* reserved0; */
95     NULL,                                       /* void* reserved1; */
96 };
97
98 /**
99  * xmlSecTransformXsltGetKlass:
100  *
101  * XSLT transform klass (http://www.w3.org/TR/xmldsig-core/#sec-XSLT):
102  *
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.
112  *
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).]
117  *
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].
127  *
128  * Returns: pointer to XSLT transform klass.
129  */
130 xmlSecTransformId 
131 xmlSecTransformXsltGetKlass(void) {
132     return(&xmlSecXsltKlass);
133 }
134     
135 static int 
136 xmlSecXsltInitialize(xmlSecTransformPtr transform) {    
137     xmlSecXsltCtxPtr ctx;
138     
139     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
140     xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
141
142     ctx = xmlSecXsltGetCtx(transform);
143     xmlSecAssert2(ctx != NULL, -1);
144     
145     /* initialize context */
146     memset(ctx, 0, sizeof(xmlSecXsltCtx));
147     return(0);
148 }
149
150 static void
151 xmlSecXsltFinalize(xmlSecTransformPtr transform) {
152     xmlSecXsltCtxPtr ctx;
153
154     xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformXsltId));
155     xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecXsltSize));
156
157     ctx = xmlSecXsltGetCtx(transform);
158     xmlSecAssert(ctx != NULL);
159     
160     if(ctx->xslt != NULL) {
161         xsltFreeStylesheet(ctx->xslt);
162     }
163     if(ctx->parserCtx != NULL) {
164         xmlFreeParserCtxt(ctx->parserCtx);
165     }
166     memset(ctx, 0, sizeof(xmlSecXsltCtx));
167 }
168
169 static int
170 xmlSecXsltReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) {
171     xmlSecXsltCtxPtr ctx;
172     xmlBufferPtr buffer;
173     xmlDocPtr doc;
174     xmlNodePtr cur;
175     
176     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
177     xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
178     xmlSecAssert2(node != NULL, -1);    
179     xmlSecAssert2(transformCtx != NULL, -1);    
180
181     ctx = xmlSecXsltGetCtx(transform);
182     xmlSecAssert2(ctx != NULL, -1);
183     xmlSecAssert2(ctx->xslt == NULL, -1);
184
185     /* read content in the buffer */    
186     buffer = xmlBufferCreate();
187     if(buffer == NULL) {
188         xmlSecError(XMLSEC_ERRORS_HERE,
189                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
190                     "xmlBufferCreate",
191                     XMLSEC_ERRORS_R_XML_FAILED,
192                     XMLSEC_ERRORS_NO_MESSAGE);
193         return(-1);
194     }    
195     cur = node->children;
196     while(cur != NULL) {
197         xmlNodeDump(buffer, cur->doc, cur, 0, 0);
198         cur = cur->next;
199     }
200     
201     /* parse the buffer */
202     doc = xmlSecParseMemory(xmlBufferContent(buffer), 
203                              xmlBufferLength(buffer), 1);
204     if(doc == NULL) {
205         xmlSecError(XMLSEC_ERRORS_HERE,
206                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
207                     "xmlSecParseMemory",
208                     XMLSEC_ERRORS_R_XMLSEC_FAILED,
209                     XMLSEC_ERRORS_NO_MESSAGE);
210         xmlBufferFree(buffer);
211         return(-1);
212     }
213
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() */    
224         xmlFreeDoc(doc);
225         xmlBufferFree(buffer);
226         return(-1);
227     }
228     
229     xmlBufferFree(buffer);
230     return(0);
231 }
232
233 static int 
234 xmlSecXsltPushBin(xmlSecTransformPtr transform, const xmlSecByte* data,
235                                 xmlSecSize dataSize, int final, xmlSecTransformCtxPtr transformCtx) {
236     xmlSecXsltCtxPtr ctx;
237     int ret;
238     
239     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
240     xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
241     xmlSecAssert2(transformCtx != NULL, -1);
242
243     ctx = xmlSecXsltGetCtx(transform);
244     xmlSecAssert2(ctx != NULL, -1);
245     xmlSecAssert2(ctx->xslt != NULL, -1);
246
247     /* check/update current transform status */
248     if(transform->status == xmlSecTransformStatusNone) {
249         xmlSecAssert2(ctx->parserCtx == NULL, -1);
250         
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);
258             return(-1);
259         }
260
261         /* required for c14n! */
262         ctx->parserCtx->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; 
263         ctx->parserCtx->replaceEntities = 1;
264
265         transform->status = xmlSecTransformStatusWorking;
266     } else if(transform->status == xmlSecTransformStatusFinished) {
267         return(0);
268     } else if(transform->status != xmlSecTransformStatusWorking) {
269         xmlSecError(XMLSEC_ERRORS_HERE, 
270                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
271                     NULL,
272                     XMLSEC_ERRORS_R_INVALID_STATUS,
273                     "status=%d", transform->status);
274         return(-1);
275     }
276     xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
277     xmlSecAssert2(ctx->parserCtx != NULL, -1);
278     
279     /* push data to the input buffer */
280     if((data != NULL) && (dataSize > 0)) {
281         ret = xmlParseChunk(ctx->parserCtx, (const char*)data, dataSize, 0);
282         if(ret != 0) {
283             xmlSecError(XMLSEC_ERRORS_HERE,
284                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
285                         "xmlParseChunk",
286                         XMLSEC_ERRORS_R_XML_FAILED,
287                         "size=%d", dataSize);
288             return(-1);
289         }       
290     }    
291     
292     /* finish parsing, apply xslt transforms and push to next in the chain */
293     if(final != 0) {
294         xmlDocPtr docIn;
295         xmlDocPtr docOut;
296         xmlOutputBufferPtr output;
297
298         /* finalize */
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)),
303                         "xmlParseChunk",
304                         XMLSEC_ERRORS_R_XML_FAILED,
305                         XMLSEC_ERRORS_NO_MESSAGE);
306             return(-1);
307         }       
308
309         /* todo: check that document is well formed? */
310         docIn = ctx->parserCtx->myDoc;
311         ctx->parserCtx->myDoc = NULL;
312
313         docOut = xsltApplyStylesheet(ctx->xslt, docIn, NULL);
314         if(docOut == NULL) {
315             xmlSecError(XMLSEC_ERRORS_HERE,
316                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
317                         "xsltApplyStylesheet",
318                         XMLSEC_ERRORS_R_XSLT_FAILED,
319                         XMLSEC_ERRORS_NO_MESSAGE);
320             xmlFreeDoc(docIn);
321             return(-1);
322         }
323         xmlFreeDoc(docIn);
324     
325         if(transform->next != NULL) {
326             output = xmlSecTransformCreateOutputBuffer(transform->next, transformCtx);
327             if(output == NULL) {
328                 xmlSecError(XMLSEC_ERRORS_HERE,
329                             xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
330                             "xmlSecTransformCreateOutputBuffer",
331                             XMLSEC_ERRORS_R_XMLSEC_FAILED,
332                             XMLSEC_ERRORS_NO_MESSAGE);
333                 xmlFreeDoc(docOut);
334                 return(-1);
335             }
336         } else {
337             output = xmlSecBufferCreateOutputBuffer(&(transform->outBuf));
338             if(output == NULL) {
339                 xmlSecError(XMLSEC_ERRORS_HERE,
340                             xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
341                             "xmlSecBufferCreateOutputBuffer",
342                             XMLSEC_ERRORS_R_XMLSEC_FAILED,
343                             XMLSEC_ERRORS_NO_MESSAGE);
344                 xmlFreeDoc(docOut);
345                 return(-1);
346             }
347         }       
348
349         ret = xsltSaveResultTo(output, docOut, ctx->xslt);
350         if(ret < 0) {
351             xmlSecError(XMLSEC_ERRORS_HERE,
352                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
353                         "xsltSaveResultTo",
354                         XMLSEC_ERRORS_R_XSLT_FAILED,
355                         XMLSEC_ERRORS_NO_MESSAGE);
356             xmlOutputBufferClose(output);
357             xmlFreeDoc(docOut);
358             return(-1);
359         }
360         ret = xmlOutputBufferClose(output);
361         if(ret < 0) {
362             xmlSecError(XMLSEC_ERRORS_HERE,
363                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
364                         "xmlOutputBufferClose",
365                         XMLSEC_ERRORS_R_XML_FAILED,
366                         XMLSEC_ERRORS_NO_MESSAGE);
367             xmlFreeDoc(docOut);
368             return(-1);
369         }
370         xmlFreeDoc(docOut);
371
372         transform->status = xmlSecTransformStatusFinished;
373     }
374
375     return(0);
376 }
377
378 static int 
379 xmlSecXsltExecute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) {
380     xmlSecXsltCtxPtr ctx;
381     xmlSecBufferPtr in, out;
382     xmlSecSize inSize, outSize;
383     int ret;
384
385     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
386     xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
387     xmlSecAssert2(transformCtx != NULL, -1);
388
389     ctx = xmlSecXsltGetCtx(transform);
390     xmlSecAssert2(ctx != NULL, -1);
391     xmlSecAssert2(ctx->xslt != NULL, -1);
392
393     in = &(transform->inBuf);
394     out = &(transform->outBuf);
395     inSize = xmlSecBufferGetSize(in);
396     outSize = xmlSecBufferGetSize(out);    
397     
398     if(transform->status == xmlSecTransformStatusNone) {
399         transform->status = xmlSecTransformStatusWorking;
400     } 
401     
402     if((transform->status == xmlSecTransformStatusWorking) && (last == 0)) {
403         /* just do nothing */
404         xmlSecAssert2(outSize == 0, -1);
405
406     } else  if((transform->status == xmlSecTransformStatusWorking) && (last != 0)) {
407         xmlSecAssert2(outSize == 0, -1);
408
409         ret = xmlSecXslProcess(in, out, ctx->xslt);
410         if(ret < 0) {
411             xmlSecError(XMLSEC_ERRORS_HERE, 
412                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
413                         "xmlSecXslProcess",
414                         XMLSEC_ERRORS_R_XMLSEC_FAILED,
415                         XMLSEC_ERRORS_NO_MESSAGE);
416             return(-1);
417         }
418         
419         ret = xmlSecBufferRemoveHead(in, inSize);
420         if(ret < 0) {
421             xmlSecError(XMLSEC_ERRORS_HERE, 
422                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
423                         "xmlSecBufferRemoveHead",
424                         XMLSEC_ERRORS_R_XMLSEC_FAILED,
425                         "size=%d", inSize);
426             return(-1);
427         }
428         
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);
433     } else {
434         xmlSecError(XMLSEC_ERRORS_HERE, 
435                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
436                     NULL,
437                     XMLSEC_ERRORS_R_INVALID_STATUS,
438                     "status=%d", transform->status);
439         return(-1);
440     }
441     return(0);
442 }
443
444 /* TODO: create PopBin method instead */
445 static int 
446 xmlSecXslProcess(xmlSecBufferPtr in, xmlSecBufferPtr out,  xsltStylesheetPtr stylesheet) {
447     xmlDocPtr docIn = NULL;
448     xmlDocPtr docOut = NULL;
449     xmlOutputBufferPtr output = NULL;
450     int res = -1;
451     int ret;
452
453     xmlSecAssert2(in != NULL, -1);
454     xmlSecAssert2(out != NULL, -1);
455     xmlSecAssert2(stylesheet != NULL, -1);
456
457     docIn = xmlSecParseMemory(xmlSecBufferGetData(in), xmlSecBufferGetSize(in), 1);
458     if(docIn == NULL) {
459         xmlSecError(XMLSEC_ERRORS_HERE,
460                     NULL,
461                     "xmlSecParseMemory",
462                     XMLSEC_ERRORS_R_XMLSEC_FAILED,
463                     XMLSEC_ERRORS_NO_MESSAGE);
464         goto done;      
465     }
466
467     docOut = xsltApplyStylesheet(stylesheet, docIn, NULL);
468     if(docOut == NULL) {
469         xmlSecError(XMLSEC_ERRORS_HERE,
470                     NULL,
471                     "xsltApplyStylesheet",
472                     XMLSEC_ERRORS_R_XSLT_FAILED,
473                     XMLSEC_ERRORS_NO_MESSAGE);
474         goto done;      
475     }
476
477     output = xmlSecBufferCreateOutputBuffer(out);
478     if(output == NULL) {
479         xmlSecError(XMLSEC_ERRORS_HERE,
480                     NULL,
481                     "xmlSecBufferCreateOutputBuffer",
482                     XMLSEC_ERRORS_R_XMLSEC_FAILED,
483                     XMLSEC_ERRORS_NO_MESSAGE);
484         goto done;      
485     }
486
487     ret = xsltSaveResultTo(output, docOut, stylesheet);
488     if(ret < 0) {
489         xmlSecError(XMLSEC_ERRORS_HERE,
490                     NULL,
491                     "xsltSaveResultTo",
492                     XMLSEC_ERRORS_R_XSLT_FAILED,
493                     XMLSEC_ERRORS_NO_MESSAGE);
494         goto done;      
495     }
496
497     ret = xmlOutputBufferClose(output);
498     output = NULL;
499     if(ret < 0) {
500         xmlSecError(XMLSEC_ERRORS_HERE,
501                     NULL,
502                     "xmlOutputBufferClose",
503                     XMLSEC_ERRORS_R_XML_FAILED,
504                     XMLSEC_ERRORS_NO_MESSAGE);
505         return(-1);
506     }
507
508     res = 0;
509
510 done:   
511     if(output != NULL) xmlOutputBufferClose(output);
512     if(docIn != NULL) xmlFreeDoc(docIn);
513     if(docOut != NULL) xmlFreeDoc(docOut);
514     return(res);    
515 }
516
517 #endif /* XMLSEC_NO_XSLT */
518