upload source
[external/xmlsec1.git] / src / parser.c
1 /** 
2  * XML Security Library (http://www.aleksey.com/xmlsec).
3  *
4  * XML Parser transform and utility functions.
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 #include <stdlib.h>
14 #include <string.h>
15
16 #include <libxml/tree.h>
17 #include <libxml/parser.h>
18 #include <libxml/parserInternals.h>
19
20 #include <xmlsec/xmlsec.h>
21 #include <xmlsec/xmltree.h>
22 #include <xmlsec/keys.h>
23 #include <xmlsec/transforms.h>
24 #include <xmlsec/parser.h>
25 #include <xmlsec/errors.h>
26
27 /**************************************************************************
28  *
29  * Internal parser
30  *
31  *****************************************************************************/
32 typedef struct _xmlSecParserCtx                                 xmlSecParserCtx, 
33                                                                 *xmlSecParserCtxPtr;
34 struct _xmlSecParserCtx {
35     xmlParserCtxtPtr    parserCtx;
36 };          
37
38 /**************************************************************************
39  *
40  * XML Parser transform 
41  *
42  * xmlSecParserCtx is located after xmlSecTransform
43  * 
44  ***************************************************************************/
45 #define xmlSecParserSize        \
46     (sizeof(xmlSecTransform) + sizeof(xmlSecParserCtx)) 
47 #define xmlSecParserGetCtx(transform) \
48     ((xmlSecTransformCheckSize((transform), xmlSecParserSize)) ? \
49         ((xmlSecParserCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform))) : \
50         (xmlSecParserCtxPtr)NULL)           
51
52 static int              xmlSecParserInitialize                  (xmlSecTransformPtr transform);
53 static void             xmlSecParserFinalize                    (xmlSecTransformPtr transform);
54 static int              xmlSecParserPushBin                     (xmlSecTransformPtr transform, 
55                                                                  const xmlSecByte* data,
56                                                                  xmlSecSize dataSize,
57                                                                  int final,
58                                                                  xmlSecTransformCtxPtr transformCtx);
59 static int              xmlSecParserPopXml                      (xmlSecTransformPtr transform, 
60                                                                  xmlSecNodeSetPtr* nodes,
61                                                                  xmlSecTransformCtxPtr transformCtx);
62
63 static xmlSecTransformKlass xmlSecParserKlass = {
64     /* klass/object sizes */
65     sizeof(xmlSecTransformKlass),               /* xmlSecSize klassSize */
66     xmlSecParserSize,                           /* xmlSecSize objSize */
67
68     BAD_CAST "xml-parser",                      /* const xmlChar* name; */
69     NULL,                                       /* const xmlChar* href; */
70     xmlSecTransformUsageDSigTransform,          /* xmlSecTransformUsage usage; */
71     
72     xmlSecParserInitialize,                     /* xmlSecTransformInitializeMethod initialize; */
73     xmlSecParserFinalize,                       /* xmlSecTransformFinalizeMethod finalize; */
74     NULL,                                       /* xmlSecTransformNodeReadMethod readNode; */
75     NULL,                                       /* xmlSecTransformNodeWriteMethod writeNode; */
76     NULL,                                       /* xmlSecTransformSetKeyReqMethod setKeyReq; */
77     NULL,                                       /* xmlSecTransformSetKeyMethod setKey; */
78     NULL,                                       /* xmlSecTransformValidateMethod validate; */
79     xmlSecTransformDefaultGetDataType,          /* xmlSecTransformGetDataTypeMethod getDataType; */
80     xmlSecParserPushBin,                /* xmlSecTransformPushBinMethod pushBin; */
81     NULL,                                       /* xmlSecTransformPopBinMethod popBin; */
82     NULL,                                       /* xmlSecTransformPushXmlMethod pushXml; */
83     xmlSecParserPopXml,         /* xmlSecTransformPopXmlMethod popXml; */
84     NULL,                                       /* xmlSecTransformExecuteMethod execute; */
85
86     NULL,                                       /* void* reserved0; */
87     NULL,                                       /* void* reserved1; */
88 };
89
90 /**
91  * xmlSecTransformXmlParserGetKlass:
92  *
93  * The XML parser transform.
94  *
95  * Returns: XML parser transform klass.
96  */
97 xmlSecTransformId 
98 xmlSecTransformXmlParserGetKlass(void) {
99     return(&xmlSecParserKlass);
100 }
101
102 static int 
103 xmlSecParserInitialize(xmlSecTransformPtr transform) {    
104     xmlSecParserCtxPtr ctx;
105     
106     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1);
107     xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecParserSize), -1);
108
109     ctx = xmlSecParserGetCtx(transform);
110     xmlSecAssert2(ctx != NULL, -1);
111     
112     /* initialize context */
113     memset(ctx, 0, sizeof(xmlSecParserCtx));
114     return(0);
115 }
116
117 static void
118 xmlSecParserFinalize(xmlSecTransformPtr transform) {
119     xmlSecParserCtxPtr ctx;
120
121     xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId));
122     xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecParserSize));
123
124     ctx = xmlSecParserGetCtx(transform);
125     xmlSecAssert(ctx != NULL);
126     
127     if(ctx->parserCtx != NULL) {
128         xmlFreeParserCtxt(ctx->parserCtx);
129     }
130     memset(ctx, 0, sizeof(xmlSecParserCtx));
131 }
132
133 static int 
134 xmlSecParserPushBin(xmlSecTransformPtr transform, const xmlSecByte* data,
135                                 xmlSecSize dataSize, int final, xmlSecTransformCtxPtr transformCtx) {
136     xmlSecParserCtxPtr ctx;
137     int ret;
138     
139     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1);
140     xmlSecAssert2(transformCtx != NULL, -1);
141
142     ctx = xmlSecParserGetCtx(transform);
143     xmlSecAssert2(ctx != NULL, -1);
144
145     /* check/update current transform status */
146     if(transform->status == xmlSecTransformStatusNone) {
147         xmlSecAssert2(ctx->parserCtx == NULL, -1);
148         
149         ctx->parserCtx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
150         if(ctx->parserCtx == NULL) {
151             xmlSecError(XMLSEC_ERRORS_HERE,
152                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
153                         "xmlCreatePushParserCtxt",
154                         XMLSEC_ERRORS_R_XML_FAILED,
155                         XMLSEC_ERRORS_NO_MESSAGE);
156             return(-1);
157         }
158
159         /* required for c14n! */
160         ctx->parserCtx->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; 
161         ctx->parserCtx->replaceEntities = 1;
162
163         transform->status = xmlSecTransformStatusWorking;
164     } else if(transform->status == xmlSecTransformStatusFinished) {
165         return(0);
166     } else if(transform->status != xmlSecTransformStatusWorking) {
167         xmlSecError(XMLSEC_ERRORS_HERE, 
168                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
169                     NULL,
170                     XMLSEC_ERRORS_R_INVALID_STATUS,
171                     "status=%d", transform->status);
172         return(-1);
173     }
174     xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
175     xmlSecAssert2(ctx->parserCtx != NULL, -1);
176     
177     /* push data to the input buffer */
178     if((data != NULL) && (dataSize > 0)) {
179         ret = xmlParseChunk(ctx->parserCtx, (const char*)data, dataSize, 0);
180         if(ret != 0) {
181             xmlSecError(XMLSEC_ERRORS_HERE,
182                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
183                         "xmlParseChunk",
184                         XMLSEC_ERRORS_R_XML_FAILED,
185                         "size=%d", dataSize);
186             return(-1);
187         }       
188     }    
189     
190     /* finish parsing and push to next in the chain */
191     if(final != 0) {
192         ret = xmlParseChunk(ctx->parserCtx, NULL, 0, 1);
193         if((ret != 0) || (ctx->parserCtx->myDoc == NULL)) {
194             xmlSecError(XMLSEC_ERRORS_HERE,
195                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
196                         "xmlParseChunk",
197                         XMLSEC_ERRORS_R_XML_FAILED,
198                         XMLSEC_ERRORS_NO_MESSAGE);
199             return(-1);
200         }       
201
202         /* todo: check that document is well formed? */
203         transform->outNodes = xmlSecNodeSetCreate(ctx->parserCtx->myDoc, 
204                                                   NULL, xmlSecNodeSetTree);
205         if(transform->outNodes == NULL) {
206             xmlSecError(XMLSEC_ERRORS_HERE,
207                         xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
208                         "xmlSecNodeSetCreate",
209                         XMLSEC_ERRORS_R_XMLSEC_FAILED,
210                         XMLSEC_ERRORS_NO_MESSAGE);
211             xmlFreeDoc(ctx->parserCtx->myDoc);
212             ctx->parserCtx->myDoc = NULL;
213             return(-1);
214         }
215         xmlSecNodeSetDocDestroy(transform->outNodes); /* this node set "owns" the doc pointer */
216         ctx->parserCtx->myDoc = NULL;
217         
218         /* push result to the next transform (if exist) */
219         if(transform->next != NULL) {
220             ret = xmlSecTransformPushXml(transform->next, transform->outNodes, transformCtx);
221             if(ret < 0) {
222                 xmlSecError(XMLSEC_ERRORS_HERE,
223                             xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
224                             "xmlSecTransformPushXml",
225                             XMLSEC_ERRORS_R_XMLSEC_FAILED,
226                             XMLSEC_ERRORS_NO_MESSAGE);
227                 return(-1);
228             }
229         }        
230         
231         transform->status = xmlSecTransformStatusFinished;
232     }
233
234     return(0);
235 }
236
237 static int 
238 xmlSecParserPopXml(xmlSecTransformPtr transform, xmlSecNodeSetPtr* nodes,
239                                xmlSecTransformCtxPtr transformCtx) {
240     xmlSecParserCtxPtr ctx;
241     xmlParserInputBufferPtr buf;
242     xmlParserInputPtr input;
243     xmlParserCtxtPtr ctxt;
244     xmlDocPtr doc;
245     int ret;
246     
247     xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1);
248     xmlSecAssert2(nodes != NULL, -1);    
249     xmlSecAssert2(transformCtx != NULL, -1);
250
251     ctx = xmlSecParserGetCtx(transform);
252     xmlSecAssert2(ctx != NULL, -1);
253
254     /* check/update current transform status */
255     switch(transform->status) {
256     case xmlSecTransformStatusNone:
257         transform->status = xmlSecTransformStatusWorking;
258         break;
259     case xmlSecTransformStatusWorking:
260         /* just do nothing */
261         break;
262     case xmlSecTransformStatusFinished:
263         (*nodes) = NULL;
264         return(0);
265     default:
266         xmlSecError(XMLSEC_ERRORS_HERE, 
267                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
268                     NULL,
269                     XMLSEC_ERRORS_R_INVALID_STATUS,
270                     "status=%d", transform->status);
271         return(-1);
272     }
273     xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
274     
275     /* prepare parser context */
276     if(transform->prev == NULL) {
277         xmlSecError(XMLSEC_ERRORS_HERE,
278                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
279                     NULL,
280                     XMLSEC_ERRORS_R_INVALID_TRANSFORM,
281                     "prev transform is null");
282         return(-1);
283     }
284     
285     buf = xmlSecTransformCreateInputBuffer(transform->prev, transformCtx);
286     if(buf == NULL) {
287         xmlSecError(XMLSEC_ERRORS_HERE,
288                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
289                     "xmlSecTransformCreateInputBuffer",
290                     XMLSEC_ERRORS_R_XMLSEC_FAILED,
291                     XMLSEC_ERRORS_NO_MESSAGE);
292         return(-1);
293     }
294     
295     ctxt = xmlNewParserCtxt();
296     if (ctxt == NULL) {
297         xmlSecError(XMLSEC_ERRORS_HERE,
298                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
299                     "xmlNewParserCtxt",
300                     XMLSEC_ERRORS_R_XML_FAILED,
301                     XMLSEC_ERRORS_NO_MESSAGE);
302         xmlFreeParserInputBuffer(buf);
303         return(-1);
304     }
305     
306     input = xmlNewIOInputStream(ctxt, buf, XML_CHAR_ENCODING_NONE);
307     if(input == NULL) {
308         xmlSecError(XMLSEC_ERRORS_HERE,
309                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
310                     "xmlNewParserCtxt",
311                     XMLSEC_ERRORS_R_XML_FAILED,
312                     XMLSEC_ERRORS_NO_MESSAGE);
313         xmlFreeParserCtxt(ctxt);
314         xmlFreeParserInputBuffer(buf);
315         return(-1);
316     }
317     
318     ret = inputPush(ctxt, input);
319     if(input == NULL) {
320         xmlSecError(XMLSEC_ERRORS_HERE,
321                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
322                     "inputPush",
323                     XMLSEC_ERRORS_R_XML_FAILED,
324                     XMLSEC_ERRORS_NO_MESSAGE);
325         xmlFreeInputStream(input);
326         xmlFreeParserCtxt(ctxt);
327         return(-1);
328     }
329
330     /* required for c14n! */
331     ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; 
332     ctxt->replaceEntities = 1;
333
334     /* finaly do the parsing */
335     ret = xmlParseDocument(ctxt);
336     if(ret < 0) {
337         xmlSecError(XMLSEC_ERRORS_HERE,
338                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
339                     "xmlParseDocument",
340                     XMLSEC_ERRORS_R_XML_FAILED,
341                     XMLSEC_ERRORS_NO_MESSAGE);
342         if(ctxt->myDoc != NULL) {
343             xmlFreeDoc(ctxt->myDoc);
344             ctxt->myDoc = NULL;
345         }
346         xmlFreeParserCtxt(ctxt);
347         return(-1);
348     }
349     
350     /* remember the result and free parsing context */
351     doc = ctxt->myDoc;
352     ctxt->myDoc = NULL;
353     xmlFreeParserCtxt(ctxt);    
354
355     /* return result to the caller */
356     (*nodes) = xmlSecNodeSetCreate(doc, NULL, xmlSecNodeSetTree);
357     if((*nodes) == NULL) {
358         xmlSecError(XMLSEC_ERRORS_HERE,
359                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
360                     "xmlSecNodeSetCreate",
361                     XMLSEC_ERRORS_R_XMLSEC_FAILED,
362                     XMLSEC_ERRORS_NO_MESSAGE);
363         xmlFreeDoc(doc);
364         return(-1);
365     }   
366     xmlSecNodeSetDocDestroy((*nodes)); /* this node set "owns" the doc pointer */
367     transform->status = xmlSecTransformStatusFinished;
368     return(0);
369 }
370
371 /**************************************************************************
372  *
373  * XML Parser functions
374  *
375  *************************************************************************/
376 typedef struct _xmlSecExtMemoryParserCtx {
377     const xmlSecByte    *prefix; 
378     xmlSecSize                  prefixSize;
379     const xmlSecByte    *buffer;
380     xmlSecSize                  bufferSize;
381     const xmlSecByte    *postfix;
382     xmlSecSize                  postfixSize;
383 } xmlSecExtMemoryParserCtx, *xmlSecExtMemoryParserCtxPtr;
384
385 /** 
386  * xmlSecParseFile:
387  * @filename:           the filename.
388  *
389  * Loads XML Doc from file @filename. We need a special version because of 
390  * c14n issue. The code is copied from xmlSAXParseFileWithData() function.
391  *
392  * Returns: pointer to the loaded XML document or NULL if an error occurs.
393  */
394 xmlDocPtr
395 xmlSecParseFile(const char *filename) {
396     xmlDocPtr ret;
397     xmlParserCtxtPtr ctxt;
398     char *directory = NULL;
399     
400     xmlSecAssert2(filename != NULL, NULL);
401
402     xmlInitParser();
403     ctxt = xmlCreateFileParserCtxt(filename);
404     if (ctxt == NULL) {
405         return(NULL);
406     }
407
408     /* todo: set directories from current doc? */    
409     if ((ctxt->directory == NULL) && (directory == NULL))
410         directory = xmlParserGetDirectory(filename);
411     if ((ctxt->directory == NULL) && (directory != NULL))
412         ctxt->directory = (char *) xmlStrdup((xmlChar *) directory);
413
414     /* required for c14n! */
415     ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; 
416     ctxt->replaceEntities = 1;
417     
418     xmlParseDocument(ctxt);
419
420     if(ctxt->wellFormed) { 
421         ret = ctxt->myDoc;
422     } else {
423        ret = NULL;
424        xmlFreeDoc(ctxt->myDoc);
425        ctxt->myDoc = NULL;
426     }
427     xmlFreeParserCtxt(ctxt);    
428     return(ret);
429     
430 }
431
432 /**
433  * xmlSecParseMemoryExt:
434  * @prefix:             the first part of the input.
435  * @prefixSize:         the size of the first part of the input.
436  * @buffer:             the second part of the input.
437  * @bufferSize:         the size of the second part of the input.
438  * @postfix:            the third part of the input.
439  * @postfixSize:        the size of the third part of the input.
440  *
441  * Loads XML Doc from 3 chunks of memory: @prefix, @buffer and @postfix. 
442  *
443  * Returns: pointer to the loaded XML document or NULL if an error occurs.
444  */
445 xmlDocPtr
446 xmlSecParseMemoryExt(const xmlSecByte *prefix, xmlSecSize prefixSize,
447                      const xmlSecByte *buffer, xmlSecSize bufferSize, 
448                      const xmlSecByte *postfix, xmlSecSize postfixSize) {
449     xmlParserCtxtPtr ctxt = NULL;
450     xmlDocPtr doc = NULL;
451     int ret;
452     
453     /* create context */
454     ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
455     if(ctxt == NULL) {
456         xmlSecError(XMLSEC_ERRORS_HERE,
457                     NULL,
458                     "xmlCreatePushParserCtxt",
459                     XMLSEC_ERRORS_R_XML_FAILED,
460                     XMLSEC_ERRORS_NO_MESSAGE);
461         goto done;
462     }
463
464     /* required for c14n! */
465     ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; 
466     ctxt->replaceEntities = 1;
467
468     /* prefix */
469     if((prefix != NULL) && (prefixSize > 0)) {
470         ret = xmlParseChunk(ctxt, (const char*)prefix, prefixSize, 0);
471         if(ret != 0) {
472             xmlSecError(XMLSEC_ERRORS_HERE,
473                         NULL,
474                         "xmlParseChunk",
475                         XMLSEC_ERRORS_R_XML_FAILED,
476                         "prefixSize=%d", prefixSize);
477             goto done;
478         }
479     }   
480
481     /* buffer */
482     if((buffer != NULL) && (bufferSize > 0)) {
483         ret = xmlParseChunk(ctxt, (const char*)buffer, bufferSize, 0);
484         if(ret != 0) {
485             xmlSecError(XMLSEC_ERRORS_HERE,
486                         NULL,
487                         "xmlParseChunk",
488                         XMLSEC_ERRORS_R_XML_FAILED,
489                         "bufferSize=%d", bufferSize);
490             goto done;
491         }
492     }   
493
494     /* postfix */
495     if((postfix != NULL) && (postfixSize > 0)) {
496         ret = xmlParseChunk(ctxt, (const char*)postfix, postfixSize, 0);
497         if(ret != 0) {
498             xmlSecError(XMLSEC_ERRORS_HERE,
499                         NULL,
500                         "xmlParseChunk",
501                         XMLSEC_ERRORS_R_XML_FAILED,
502                         "postfixSize=%d", postfixSize);
503             goto done;
504         }
505     }   
506
507     /* finishing */
508     ret = xmlParseChunk(ctxt, NULL, 0, 1);
509     if((ret != 0) || (ctxt->myDoc == NULL)) {
510         xmlSecError(XMLSEC_ERRORS_HERE,
511                     NULL,
512                     "xmlParseChunk",
513                     XMLSEC_ERRORS_R_XML_FAILED,
514                     XMLSEC_ERRORS_NO_MESSAGE);
515         goto done;
516     }   
517     doc = ctxt->myDoc;
518
519 done:
520     if(ctxt != NULL) {
521         xmlFreeParserCtxt(ctxt);
522     }
523     return(doc);
524 }
525
526
527 /**
528  * xmlSecParseMemory:
529  * @buffer:             the input buffer.
530  * @size:               the input buffer size.
531  * @recovery:           the flag.
532  *
533  * Loads XML Doc from memory. We need a special version because of 
534  * c14n issue. The code is copied from xmlSAXParseMemory() function.
535  *
536  * Returns: pointer to the loaded XML document or NULL if an error occurs.
537  */
538 xmlDocPtr
539 xmlSecParseMemory(const xmlSecByte *buffer, xmlSecSize size, int recovery) {
540     xmlDocPtr ret;
541     xmlParserCtxtPtr ctxt;
542
543     xmlSecAssert2(buffer != NULL, NULL);
544     
545     ctxt = xmlCreateMemoryParserCtxt((char*)buffer, size);
546     if (ctxt == NULL) {
547         xmlSecError(XMLSEC_ERRORS_HERE,
548                     NULL,
549                     "xmlCreateMemoryParserCtxt",
550                     XMLSEC_ERRORS_R_XML_FAILED,
551                     XMLSEC_ERRORS_NO_MESSAGE);
552         return(NULL);
553     }
554
555     /* required for c14n! */
556     ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; 
557     ctxt->replaceEntities = 1;
558
559     xmlParseDocument(ctxt);
560
561     if((ctxt->wellFormed) || recovery) {
562         ret = ctxt->myDoc; 
563     } else {
564        ret = NULL;
565        xmlFreeDoc(ctxt->myDoc);
566        ctxt->myDoc = NULL;
567     }
568     xmlFreeParserCtxt(ctxt);    
569     return(ret);
570 }
571