/src/xmlsec/src/relationship.c
Line | Count | Source |
1 | | /** |
2 | | * XML Security Library (http://www.aleksey.com/xmlsec). |
3 | | * |
4 | | * This is free software; see the Copyright file in the source distribution for precise wording. |
5 | | * |
6 | | * Copyright (C) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. |
7 | | */ |
8 | | /** |
9 | | * @addtogroup xmlsec_core_transforms |
10 | | * @brief Relationship transform implementation. |
11 | | */ |
12 | | #include "globals.h" |
13 | | |
14 | | #include <stdlib.h> |
15 | | #include <string.h> |
16 | | |
17 | | #include <libxml/tree.h> |
18 | | #include <libxml/xpointer.h> |
19 | | #include <libxml/c14n.h> |
20 | | |
21 | | #include <xmlsec/xmlsec.h> |
22 | | #include <xmlsec/xmltree.h> |
23 | | #include <xmlsec/keys.h> |
24 | | #include <xmlsec/list.h> |
25 | | #include <xmlsec/transforms.h> |
26 | | #include <xmlsec/errors.h> |
27 | | |
28 | | #include "cast_helpers.h" |
29 | | |
30 | | |
31 | | /****************************************************************************** |
32 | | * |
33 | | * XML Relationshi transform |
34 | | * |
35 | | * * [Relationship transform](http://standards.iso.org/ittf/PubliclyAvailableStandards/c061796_ISO_IEC_29500-2_2012.zip) |
36 | | * |
37 | | * The relationships transform takes the XML document from the Relationships part and converts |
38 | | * it to another XML document. |
39 | | * |
40 | | * The package implementer might create relationships XML that contains content from several namespaces, |
41 | | * along with versioning instructions as defined in Part 3, “Markup Compatibility and Extensibility”. [O6.11] |
42 | | * |
43 | | * The relationships transform algorithm is as follows: |
44 | | * |
45 | | * Step 1: Process versioning instructions |
46 | | * 1. The package implementer shall process the versioning instructions, considering that the only |
47 | | * known namespace is the Relationships namespace. |
48 | | * 2. The package implementer shall remove all ignorable content, ignoring preservation attributes. |
49 | | * 3. The package implementer shall remove all versioning instructions. |
50 | | * |
51 | | * Step 2: Sort and filter relationships |
52 | | * 1. The package implementer shall remove all namespace declarations except the Relationships |
53 | | * namespace declaration. |
54 | | * 2. The package implementer shall remove the Relationships namespace prefix, if it is present. |
55 | | * 3. The package implementer shall sort relationship elements by Id value in lexicographical |
56 | | * order, considering Id values as case-sensitive Unicode strings. |
57 | | * 4. The package implementer shall remove all Relationship elements that do not have either an Id |
58 | | * value that matches any SourceId value or a Type value that matches any SourceType value, among |
59 | | * the SourceId and SourceType values specified in the transform definition. Producers and consumers |
60 | | * shall compare values as case-sensitive Unicode strings. [M6.27] The resulting XML document holds |
61 | | * all Relationship elements that either have an Id value that matches a SourceId value or a Type value |
62 | | * that matches a SourceType value specified in the transform definition. |
63 | | * |
64 | | * Step 3: Prepare for canonicalization |
65 | | * 1. The package implementer shall remove all characters between the Relationships start tag and |
66 | | * the first Relationship start tag. |
67 | | * 2. The package implementer shall remove any contents of the Relationship element. |
68 | | * 3. The package implementer shall remove all characters between the last Relationship end tag and |
69 | | * the Relationships end tag. |
70 | | * 4. If there are no Relationship elements, the package implementer shall remove all characters |
71 | | * between the Relationships start tag and the Relationships end tag. |
72 | | * 5. The package implementer shall remove comments from the Relationships XML content. |
73 | | * 6. The package implementer shall add a TargetMode attribute with its default value, if this |
74 | | * optional attribute is missing from the Relationship element. |
75 | | * 7. The package implementer can generate Relationship elements as start-tag/end-tag pairs with |
76 | | * empty content, or as empty elements. A canonicalization transform, applied immediately after the |
77 | | * Relationships Transform, converts all XML elements into start-tag/end-tag pairs. |
78 | | * |
79 | | * |
80 | | * IMPLEMENTATION NOTES (https://github.com/lsh123/xmlsec/pull/24): |
81 | | * |
82 | | * * We don't simply manipulate the XML tree, but do an XML tree -> output bytes transformation, |
83 | | * because we never write characters inside XML elements, we implicitly remove all character |
84 | | * contents, as required by step 3, point 1. It also simplifies the task of the situation that |
85 | | * realistically the input of the transformation is always a document that conforms to the OOXML |
86 | | * relationships XML schema, so in practice it'll never happen that the input document has e.g. |
87 | | * characters, as the schema requires that the document has only XML elements and attributes, |
88 | | * but no characters. |
89 | | * |
90 | | * * Step 2, point 4 talks about a SourceType value, but given that neither Microsoft Office, nor LibreOffice |
91 | | * writes that theoretical attribute, the implementation doesn't handle it. If there is a real-world situation |
92 | | * when there will be such an input, then it'll be easy to add support for that. But I didn't want to clutter |
93 | | * the current implementation with details that doesn't seem to be used in practice |
94 | | * |
95 | | * xmlSecTransform + xmlSecRelationshipCtx |
96 | | * |
97 | | *****************************************************************************/ |
98 | | typedef struct _xmlSecRelationshipCtx xmlSecRelationshipCtx, |
99 | | *xmlSecRelationshipCtxPtr; |
100 | | struct _xmlSecRelationshipCtx { |
101 | | xmlSecPtrListPtr sourceIdList; |
102 | | }; |
103 | | |
104 | 0 | XMLSEC_TRANSFORM_DECLARE(Relationship, xmlSecRelationshipCtx) |
105 | 0 | #define xmlSecRelationshipSize XMLSEC_TRANSFORM_SIZE(Relationship) |
106 | 0 |
|
107 | 0 | static int xmlSecRelationshipInitialize (xmlSecTransformPtr transform); |
108 | 0 | static void xmlSecRelationshipFinalize (xmlSecTransformPtr transform); |
109 | 0 | static int xmlSecTransformRelationshipPopBin (xmlSecTransformPtr transform, |
110 | 0 | xmlSecByte* data, |
111 | 0 | xmlSecSize maxDataSize, |
112 | 0 | xmlSecSize* dataSize, |
113 | 0 | xmlSecTransformCtxPtr transformCtx); |
114 | 0 | static int xmlSecTransformRelationshipPushXml(xmlSecTransformPtr transform, |
115 | 0 | xmlSecNodeSetPtr nodes, |
116 | 0 | xmlSecTransformCtxPtr transformCtx); |
117 | 0 | static int xmlSecRelationshipReadNode (xmlSecTransformPtr transform, |
118 | 0 | xmlNodePtr node, |
119 | 0 | xmlSecTransformCtxPtr transformCtx); |
120 | 0 |
|
121 | 0 | static int xmlSecTransformRelationshipProcessElementNode(xmlSecTransformPtr transform, |
122 | 0 | xmlOutputBufferPtr buf, |
123 | 0 | xmlNodePtr cur); |
124 | 0 |
|
125 | 0 |
|
126 | 0 | static xmlSecTransformKlass xmlSecRelationshipKlass = { |
127 | 0 | /* klass/object sizes */ |
128 | 0 | sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */ |
129 | 0 | xmlSecRelationshipSize, /* xmlSecSize objSize */ |
130 | 0 |
|
131 | 0 | xmlSecNameRelationship, /* const xmlChar* name; */ |
132 | 0 | xmlSecHrefRelationship, /* const xmlChar* href; */ |
133 | 0 | xmlSecTransformUsageDSigTransform, /* xmlSecTransformUsage usage; */ |
134 | 0 |
|
135 | 0 | xmlSecRelationshipInitialize, /* xmlSecTransformInitializeMethod initialize; */ |
136 | 0 | xmlSecRelationshipFinalize, /* xmlSecTransformFinalizeMethod finalize; */ |
137 | 0 | xmlSecRelationshipReadNode, /* xmlSecTransformNodeReadMethod readNode; */ |
138 | 0 | NULL, /* xmlSecTransformNodeWriteMethod writeNode; */ |
139 | 0 | NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */ |
140 | 0 | NULL, /* xmlSecTransformSetKeyMethod setKey; */ |
141 | 0 | NULL, /* xmlSecTransformValidateMethod validate; */ |
142 | 0 | xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */ |
143 | 0 | NULL, /* xmlSecTransformPushBinMethod pushBin; */ |
144 | 0 | xmlSecTransformRelationshipPopBin, /* xmlSecTransformPopBinMethod popBin; */ |
145 | 0 | xmlSecTransformRelationshipPushXml, /* xmlSecTransformPushXmlMethod pushXml; */ |
146 | 0 | NULL, /* xmlSecTransformPopXmlMethod popXml; */ |
147 | 0 | NULL, /* xmlSecTransformExecuteMethod execute; */ |
148 | 0 |
|
149 | 0 | NULL, /* void* reserved0; */ |
150 | 0 | NULL, /* void* reserved1; */ |
151 | 0 | }; |
152 | 0 |
|
153 | 0 | /** |
154 | 0 | * @brief Gets the Relationship transform klass. |
155 | 0 | * |
156 | 0 | * @return Relationship transform klass. |
157 | 0 | */ |
158 | 0 | xmlSecTransformId |
159 | 0 | xmlSecTransformRelationshipGetKlass(void) { |
160 | 0 | return(&xmlSecRelationshipKlass); |
161 | 0 | } |
162 | | |
163 | | static int |
164 | 0 | xmlSecRelationshipInitialize(xmlSecTransformPtr transform) { |
165 | 0 | xmlSecRelationshipCtxPtr ctx; |
166 | |
|
167 | 0 | xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId), -1); |
168 | 0 | xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize), -1); |
169 | |
|
170 | 0 | ctx = xmlSecRelationshipGetCtx(transform); |
171 | 0 | xmlSecAssert2(ctx != NULL, -1); |
172 | | |
173 | | /* initialize context */ |
174 | 0 | memset(ctx, 0, sizeof(xmlSecRelationshipCtx)); |
175 | |
|
176 | 0 | ctx->sourceIdList = xmlSecPtrListCreate(xmlSecStringListId); |
177 | 0 | if(ctx->sourceIdList == NULL) { |
178 | 0 | xmlSecInternalError("xmlSecPtrListCreate", |
179 | 0 | xmlSecTransformGetName(transform)); |
180 | 0 | return(-1); |
181 | 0 | } |
182 | 0 | return(0); |
183 | 0 | } |
184 | | |
185 | | static void |
186 | 0 | xmlSecRelationshipFinalize(xmlSecTransformPtr transform) { |
187 | 0 | xmlSecRelationshipCtxPtr ctx; |
188 | |
|
189 | 0 | xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId)); |
190 | 0 | xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize)); |
191 | |
|
192 | 0 | ctx = xmlSecRelationshipGetCtx(transform); |
193 | 0 | xmlSecAssert(ctx != NULL); |
194 | |
|
195 | 0 | if(ctx->sourceIdList != NULL) { |
196 | 0 | xmlSecPtrListDestroy(ctx->sourceIdList); |
197 | 0 | } |
198 | |
|
199 | 0 | memset(ctx, 0, sizeof(xmlSecRelationshipCtx)); |
200 | 0 | } |
201 | | |
202 | | static int |
203 | 0 | xmlSecRelationshipReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) { |
204 | 0 | xmlSecRelationshipCtxPtr ctx; |
205 | 0 | xmlNodePtr cur; |
206 | 0 | int ret; |
207 | |
|
208 | 0 | xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId), -1); |
209 | 0 | xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize), -1); |
210 | 0 | xmlSecAssert2(node != NULL, -1); |
211 | 0 | xmlSecAssert2(transformCtx != NULL, -1); |
212 | 0 | ctx = xmlSecRelationshipGetCtx(transform); |
213 | 0 | xmlSecAssert2(ctx != NULL, -1); |
214 | |
|
215 | 0 | cur = node->children; |
216 | 0 | while(cur != NULL) { |
217 | 0 | if(xmlSecCheckNodeName(cur, xmlSecNodeRelationshipReference, xmlSecRelationshipReferenceNs)) { |
218 | 0 | xmlChar* sourceId; |
219 | |
|
220 | 0 | sourceId = xmlGetProp(cur, xmlSecRelationshipAttrSourceId); |
221 | 0 | if(sourceId == NULL) { |
222 | 0 | xmlSecInvalidNodeAttributeError(cur, xmlSecRelationshipAttrSourceId, |
223 | 0 | NULL, "empty"); |
224 | 0 | return(-1); |
225 | 0 | } |
226 | | |
227 | 0 | ret = xmlSecPtrListAdd(ctx->sourceIdList, sourceId); |
228 | 0 | if(ret < 0) { |
229 | 0 | xmlSecInternalError("xmlSecPtrListAdd", |
230 | 0 | xmlSecTransformGetName(transform)); |
231 | 0 | xmlFree(sourceId); |
232 | 0 | return(-1); |
233 | 0 | } |
234 | 0 | } |
235 | | |
236 | 0 | cur = cur->next; |
237 | 0 | } |
238 | | |
239 | 0 | return(0); |
240 | 0 | } |
241 | | |
242 | | /* Sorts Relationship elements by Id value in lexicographical order. */ |
243 | | static int |
244 | 0 | xmlSecTransformRelationshipCompare(xmlNodePtr node1, xmlNodePtr node2) { |
245 | 0 | xmlChar* id1 = NULL; |
246 | 0 | xmlChar* id2 = NULL; |
247 | 0 | int ret; |
248 | |
|
249 | 0 | if(node1 == node2) { |
250 | 0 | return(0); |
251 | 0 | } |
252 | 0 | if(node1 == NULL) { |
253 | 0 | return(-1); |
254 | 0 | } |
255 | 0 | if(node2 == NULL) { |
256 | 0 | return(1); |
257 | 0 | } |
258 | | |
259 | 0 | id1 = xmlGetProp(node1, xmlSecRelationshipAttrId); |
260 | 0 | id2 = xmlGetProp(node2, xmlSecRelationshipAttrId); |
261 | 0 | if(id1 == NULL) { |
262 | 0 | ret = -1; |
263 | 0 | goto done; |
264 | 0 | } |
265 | 0 | if(id2 == NULL) { |
266 | 0 | ret = 1; |
267 | 0 | goto done; |
268 | 0 | } |
269 | | |
270 | 0 | ret = xmlStrcmp(id1, id2); |
271 | |
|
272 | 0 | done: |
273 | 0 | if (id1 != NULL) { |
274 | 0 | xmlFree(id1); |
275 | 0 | } |
276 | 0 | if (id2 != NULL) { |
277 | 0 | xmlFree(id2); |
278 | 0 | } |
279 | |
|
280 | 0 | return ret; |
281 | 0 | } |
282 | | |
283 | | /* |
284 | | * This is step 2, point 4: if the input sourceId list doesn't contain the Id attribute of the current node, |
285 | | * then exclude it from the output, instead of processing it. |
286 | | */ |
287 | | static int |
288 | 0 | xmlSecTransformRelationshipProcessNode(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur) { |
289 | 0 | int found = -1; |
290 | 0 | xmlSecRelationshipCtxPtr ctx; |
291 | 0 | xmlSecSize ii; |
292 | 0 | int ret; |
293 | |
|
294 | 0 | xmlSecAssert2(transform != NULL, -1); |
295 | 0 | xmlSecAssert2(buf != NULL, -1); |
296 | 0 | xmlSecAssert2(cur != NULL, -1); |
297 | |
|
298 | 0 | if(xmlSecCheckNodeName(cur, xmlSecNodeRelationship, xmlSecRelationshipsNs)) { |
299 | 0 | xmlChar* id = xmlGetProp(cur, xmlSecRelationshipAttrId); |
300 | 0 | if(id == NULL) { |
301 | 0 | xmlSecXmlError2("xmlGetProp(xmlSecRelationshipAttrId)", |
302 | 0 | xmlSecTransformGetName(transform), |
303 | 0 | "name=%s", xmlSecRelationshipAttrId); |
304 | 0 | return(-1); |
305 | 0 | } |
306 | | |
307 | 0 | ctx = xmlSecRelationshipGetCtx(transform); |
308 | 0 | for(ii = 0; ii < xmlSecPtrListGetSize(ctx->sourceIdList); ++ii) { |
309 | 0 | if(xmlStrcmp((xmlChar *)xmlSecPtrListGetItem(ctx->sourceIdList, ii), id) == 0) { |
310 | 0 | found = 1; |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | } |
314 | |
|
315 | 0 | xmlFree(id); |
316 | |
|
317 | 0 | if(found < 0) { |
318 | 0 | return(0); |
319 | 0 | } |
320 | 0 | } |
321 | | |
322 | 0 | ret = xmlSecTransformRelationshipProcessElementNode(transform, buf, cur); |
323 | 0 | if(ret < 0) { |
324 | 0 | xmlSecInternalError("xmlSecTransformRelationshipProcessElementNode", |
325 | 0 | xmlSecTransformGetName(transform)); |
326 | 0 | return(-1); |
327 | 0 | } |
328 | | |
329 | 0 | return(0); |
330 | 0 | } |
331 | | |
332 | | /* |
333 | | * This is step 2, point 3: sort elements by Id: we process other elements as-is, but for elements we collect them in a list, |
334 | | * then sort, and finally process them (process the head of the list, then pop the head, till the list becomes empty). |
335 | | */ |
336 | | static int |
337 | 0 | xmlSecTransformRelationshipProcessNodeList(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur) { |
338 | 0 | xmlListPtr list; |
339 | 0 | int ret; |
340 | |
|
341 | 0 | xmlSecAssert2(transform != NULL, -1); |
342 | 0 | xmlSecAssert2(buf != NULL, -1); |
343 | 0 | xmlSecAssert2(cur != NULL, -1); |
344 | |
|
345 | 0 | list = xmlListCreate(NULL, (xmlListDataCompare)xmlSecTransformRelationshipCompare); |
346 | 0 | if(list == NULL) { |
347 | 0 | xmlSecXmlError("xmlListCreate", xmlSecTransformGetName(transform)); |
348 | 0 | return(-1); |
349 | 0 | } |
350 | | |
351 | 0 | for(; cur; cur = cur->next) { |
352 | 0 | if(xmlStrcmp(cur->name, xmlSecNodeRelationship) == 0) { |
353 | 0 | if(xmlListInsert(list, cur) != 0) { |
354 | 0 | xmlSecXmlError("xmlListInsert", xmlSecTransformGetName(transform)); |
355 | 0 | return(-1); |
356 | 0 | } |
357 | 0 | } else { |
358 | 0 | ret = xmlSecTransformRelationshipProcessNode(transform, buf, cur); |
359 | 0 | if(ret < 0) { |
360 | 0 | xmlSecInternalError("xmlSecTransformRelationshipProcessNode", |
361 | 0 | xmlSecTransformGetName(transform)); |
362 | 0 | xmlListDelete(list); |
363 | 0 | return(-1); |
364 | 0 | } |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | 0 | xmlListSort(list); |
369 | |
|
370 | 0 | while(!xmlListEmpty(list)) { |
371 | 0 | xmlLinkPtr link = xmlListFront(list); |
372 | 0 | xmlNodePtr node = (xmlNodePtr)xmlLinkGetData(link); |
373 | |
|
374 | 0 | ret = xmlSecTransformRelationshipProcessNode(transform, buf, node); |
375 | 0 | if(ret < 0) { |
376 | 0 | xmlSecInternalError("xmlSecTransformRelationshipProcessNode", |
377 | 0 | xmlSecTransformGetName(transform)); |
378 | 0 | xmlListDelete(list); |
379 | 0 | return(-1); |
380 | 0 | } |
381 | | |
382 | 0 | xmlListPopFront(list); |
383 | 0 | } |
384 | | |
385 | | /* done */ |
386 | 0 | xmlListDelete(list); |
387 | 0 | return(0); |
388 | 0 | } |
389 | | |
390 | | static int |
391 | 0 | xmlSecTransformRelationshipWriteProp(xmlOutputBufferPtr buf, const xmlChar * name, const xmlChar * value) { |
392 | 0 | int ret; |
393 | |
|
394 | 0 | xmlSecAssert2(buf != NULL, -1); |
395 | 0 | xmlSecAssert2(name != NULL, -1); |
396 | |
|
397 | 0 | ret = xmlOutputBufferWriteString(buf, " "); |
398 | 0 | if(ret < 0) { |
399 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", NULL); |
400 | 0 | return(-1); |
401 | 0 | } |
402 | | |
403 | 0 | ret = xmlOutputBufferWriteString(buf, (const char*) name); |
404 | 0 | if(ret < 0) { |
405 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", NULL); |
406 | 0 | return(-1); |
407 | 0 | } |
408 | | |
409 | 0 | if(value != NULL) { |
410 | 0 | ret = xmlOutputBufferWriteString(buf, "=\""); |
411 | 0 | if(ret < 0) { |
412 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", NULL); |
413 | 0 | return(-1); |
414 | 0 | } |
415 | 0 | ret = xmlOutputBufferWriteString(buf, (const char*) value); |
416 | 0 | if(ret < 0) { |
417 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", NULL); |
418 | 0 | return(-1); |
419 | 0 | } |
420 | 0 | ret = xmlOutputBufferWriteString(buf, "\""); |
421 | 0 | if(ret < 0) { |
422 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", NULL); |
423 | 0 | return(-1); |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | 0 | return (0); |
428 | 0 | } |
429 | | |
430 | | static int |
431 | 0 | xmlSecTransformRelationshipWriteNs(xmlOutputBufferPtr buf, const xmlChar * href) { |
432 | 0 | xmlSecAssert2(buf != NULL, -1); |
433 | |
|
434 | 0 | return(xmlSecTransformRelationshipWriteProp(buf, BAD_CAST "xmlns", (href != NULL) ? href : BAD_CAST "")); |
435 | 0 | } |
436 | | |
437 | | |
438 | | static int |
439 | 0 | xmlSecTransformRelationshipProcessElementNode(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur) { |
440 | 0 | xmlAttrPtr attr; |
441 | 0 | int foundTargetMode = 0; |
442 | 0 | int ret; |
443 | |
|
444 | 0 | xmlSecAssert2(transform != NULL, -1); |
445 | 0 | xmlSecAssert2(buf != NULL, -1); |
446 | 0 | xmlSecAssert2(cur != NULL, -1); |
447 | 0 | xmlSecAssert2(cur->name != NULL, -1); |
448 | | |
449 | | /* write open node */ |
450 | 0 | ret = xmlOutputBufferWriteString(buf, "<"); |
451 | 0 | if(ret < 0) { |
452 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", |
453 | 0 | xmlSecTransformGetName(transform)); |
454 | 0 | return(-1); |
455 | 0 | } |
456 | 0 | ret = xmlOutputBufferWriteString(buf, (const char *)cur->name); |
457 | 0 | if(ret < 0) { |
458 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", |
459 | 0 | xmlSecTransformGetName(transform)); |
460 | 0 | return(-1); |
461 | 0 | } |
462 | | |
463 | | /* write namespaces */ |
464 | 0 | if(cur->nsDef != NULL) { |
465 | 0 | ret = xmlSecTransformRelationshipWriteNs(buf, cur->nsDef->href); |
466 | 0 | if(ret < 0) { |
467 | 0 | xmlSecInternalError("xmlSecTransformRelationshipWriteNs", |
468 | 0 | xmlSecTransformGetName(transform)); |
469 | 0 | return(-1); |
470 | 0 | } |
471 | 0 | } |
472 | | |
473 | | /* |
474 | | * write attributes: |
475 | | * |
476 | | * This is step 3, point 6: add default value of TargetMode if there is no such attribute. |
477 | | */ |
478 | 0 | for(attr = cur->properties; attr != NULL; attr = attr->next) { |
479 | 0 | xmlChar * value = xmlGetProp(cur, attr->name); |
480 | |
|
481 | 0 | if(xmlStrcmp(attr->name, xmlSecRelationshipAttrTargetMode) == 0) { |
482 | 0 | foundTargetMode = 1; |
483 | 0 | } |
484 | |
|
485 | 0 | ret = xmlSecTransformRelationshipWriteProp(buf, attr->name, value); |
486 | 0 | if(ret < 0) { |
487 | 0 | xmlSecInternalError("xmlSecTransformRelationshipWriteProp", |
488 | 0 | xmlSecTransformGetName(transform)); |
489 | 0 | xmlFree(value); |
490 | 0 | return(-1); |
491 | 0 | } |
492 | | |
493 | 0 | xmlFree(value); |
494 | 0 | } |
495 | | |
496 | | /* write TargetMode */ |
497 | 0 | if(xmlStrcmp(cur->name, xmlSecNodeRelationship) == 0 && !foundTargetMode) { |
498 | 0 | ret = xmlSecTransformRelationshipWriteProp(buf, xmlSecRelationshipAttrTargetMode, BAD_CAST "Internal"); |
499 | 0 | if(ret < 0) { |
500 | 0 | xmlSecInternalError("xmlSecTransformRelationshipWriteProp(TargetMode=Internal)", |
501 | 0 | xmlSecTransformGetName(transform)); |
502 | 0 | return(-1); |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | | /* finish writing open node */ |
507 | 0 | ret = xmlOutputBufferWriteString(buf, ">"); |
508 | 0 | if(ret < 0) { |
509 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", |
510 | 0 | xmlSecTransformGetName(transform)); |
511 | 0 | return(-1); |
512 | 0 | } |
513 | | |
514 | | /* write children */ |
515 | 0 | if(cur->children != NULL) { |
516 | 0 | ret = xmlSecTransformRelationshipProcessNodeList(transform, buf, cur->children); |
517 | 0 | if(ret < 0) { |
518 | 0 | xmlSecInternalError("xmlSecTransformRelationshipProcessNodeList", |
519 | 0 | xmlSecTransformGetName(transform)); |
520 | 0 | return(-1); |
521 | 0 | } |
522 | 0 | } |
523 | | |
524 | | /* write closing node */ |
525 | 0 | ret = xmlOutputBufferWriteString(buf, "</"); |
526 | 0 | if(ret < 0) { |
527 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", |
528 | 0 | xmlSecTransformGetName(transform)); |
529 | 0 | return(-1); |
530 | 0 | } |
531 | 0 | ret = xmlOutputBufferWriteString(buf, (const char *)cur->name); |
532 | 0 | if(ret < 0) { |
533 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", |
534 | 0 | xmlSecTransformGetName(transform)); |
535 | 0 | return(-1); |
536 | 0 | } |
537 | 0 | if(xmlOutputBufferWriteString(buf, ">") < 0) { |
538 | 0 | xmlSecXmlError("xmlOutputBufferWriteString", |
539 | 0 | xmlSecTransformGetName(transform)); |
540 | 0 | return(-1); |
541 | 0 | } |
542 | | |
543 | | /* done */ |
544 | 0 | return(0); |
545 | 0 | } |
546 | | |
547 | | static int |
548 | 0 | xmlSecTransformRelationshipExecute(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlDocPtr doc) { |
549 | 0 | int ret; |
550 | |
|
551 | 0 | xmlSecAssert2(transform != NULL, -1); |
552 | 0 | xmlSecAssert2(buf != NULL, -1); |
553 | 0 | xmlSecAssert2(doc != NULL, -1); |
554 | |
|
555 | 0 | if(doc->children != NULL) { |
556 | 0 | ret = xmlSecTransformRelationshipProcessNodeList(transform, buf, doc->children); |
557 | 0 | if(ret < 0) { |
558 | 0 | xmlSecInternalError("xmlSecTransformRelationshipProcessNodeList", |
559 | 0 | xmlSecTransformGetName(transform)); |
560 | 0 | return(-1); |
561 | 0 | } |
562 | 0 | } |
563 | | |
564 | 0 | return(0); |
565 | 0 | } |
566 | | |
567 | | static int |
568 | | xmlSecTransformRelationshipPushXml(xmlSecTransformPtr transform, xmlSecNodeSetPtr nodes, xmlSecTransformCtxPtr transformCtx) |
569 | 0 | { |
570 | 0 | xmlOutputBufferPtr buf; |
571 | 0 | xmlSecRelationshipCtxPtr ctx; |
572 | 0 | int ret; |
573 | |
|
574 | 0 | xmlSecAssert2(nodes != NULL, -1); |
575 | 0 | xmlSecAssert2(nodes->doc != NULL, -1); |
576 | 0 | xmlSecAssert2(transformCtx != NULL, -1); |
577 | |
|
578 | 0 | ctx = xmlSecRelationshipGetCtx(transform); |
579 | 0 | xmlSecAssert2(ctx != NULL, -1); |
580 | | |
581 | | /* check/update current transform status */ |
582 | 0 | switch(transform->status) { |
583 | 0 | case xmlSecTransformStatusNone: |
584 | 0 | transform->status = xmlSecTransformStatusWorking; |
585 | 0 | break; |
586 | 0 | case xmlSecTransformStatusWorking: |
587 | 0 | case xmlSecTransformStatusFinished: |
588 | 0 | return(0); |
589 | 0 | default: |
590 | 0 | xmlSecInvalidTransfromStatusError(transform); |
591 | 0 | return(-1); |
592 | 0 | } |
593 | 0 | xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1); |
594 | | |
595 | | /* prepare output buffer: next transform or ourselves */ |
596 | 0 | if(transform->next != NULL) { |
597 | 0 | buf = xmlSecTransformCreateOutputBuffer(transform->next, transformCtx); |
598 | 0 | if(buf == NULL) { |
599 | 0 | xmlSecInternalError("xmlSecTransformCreateOutputBuffer", |
600 | 0 | xmlSecTransformGetName(transform)); |
601 | 0 | return(-1); |
602 | 0 | } |
603 | 0 | } else { |
604 | 0 | buf = xmlSecBufferCreateOutputBuffer(&(transform->outBuf)); |
605 | 0 | if(buf == NULL) { |
606 | 0 | xmlSecInternalError("xmlSecBufferCreateOutputBuffer", |
607 | 0 | xmlSecTransformGetName(transform)); |
608 | 0 | return(-1); |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | 0 | ret = xmlSecTransformRelationshipExecute(transform, buf, nodes->doc); |
613 | 0 | if(ret < 0) { |
614 | 0 | xmlSecInternalError("xmlSecTransformRelationshipExecute", |
615 | 0 | xmlSecTransformGetName(transform)); |
616 | 0 | (void)xmlOutputBufferClose(buf); |
617 | 0 | return(-1); |
618 | 0 | } |
619 | | |
620 | 0 | ret = xmlOutputBufferClose(buf); |
621 | 0 | if(ret < 0) { |
622 | 0 | xmlSecXmlError("xmlOutputBufferClose", xmlSecTransformGetName(transform)); |
623 | 0 | return(-1); |
624 | 0 | } |
625 | 0 | transform->status = xmlSecTransformStatusFinished; |
626 | 0 | return(0); |
627 | 0 | } |
628 | | |
629 | | static int |
630 | 0 | xmlSecTransformRelationshipPopBin(xmlSecTransformPtr transform, xmlSecByte* data, xmlSecSize maxDataSize, xmlSecSize* dataSize, xmlSecTransformCtxPtr transformCtx) { |
631 | 0 | xmlSecBufferPtr out; |
632 | 0 | int ret; |
633 | |
|
634 | 0 | xmlSecAssert2(data != NULL, -1); |
635 | 0 | xmlSecAssert2(dataSize != NULL, -1); |
636 | 0 | xmlSecAssert2(transformCtx != NULL, -1); |
637 | |
|
638 | 0 | out = &(transform->outBuf); |
639 | 0 | if(transform->status == xmlSecTransformStatusNone) { |
640 | 0 | xmlOutputBufferPtr buf; |
641 | |
|
642 | 0 | xmlSecAssert2(transform->inNodes == NULL, -1); |
643 | |
|
644 | 0 | if(transform->prev == NULL) { |
645 | 0 | (*dataSize) = 0; |
646 | 0 | transform->status = xmlSecTransformStatusFinished; |
647 | 0 | return(0); |
648 | 0 | } |
649 | | |
650 | | /* get xml data from previous transform */ |
651 | 0 | ret = xmlSecTransformPopXml(transform->prev, &(transform->inNodes), transformCtx); |
652 | 0 | if(ret < 0) { |
653 | 0 | xmlSecInternalError("xmlSecTransformPopXml", |
654 | 0 | xmlSecTransformGetName(transform)); |
655 | 0 | return(-1); |
656 | 0 | } |
657 | | |
658 | | /* dump everything to internal buffer */ |
659 | 0 | buf = xmlSecBufferCreateOutputBuffer(out); |
660 | 0 | if(buf == NULL) { |
661 | 0 | xmlSecInternalError("xmlSecBufferCreateOutputBuffer", |
662 | 0 | xmlSecTransformGetName(transform)); |
663 | 0 | return(-1); |
664 | 0 | } |
665 | | |
666 | 0 | ret = xmlC14NExecute(transform->inNodes->doc, (xmlC14NIsVisibleCallback)xmlSecNodeSetContains, transform->inNodes, XML_C14N_1_0, NULL, 0, buf); |
667 | 0 | if(ret < 0) { |
668 | 0 | xmlSecInternalError("xmlC14NExecute", |
669 | 0 | xmlSecTransformGetName(transform)); |
670 | 0 | (void)xmlOutputBufferClose(buf); |
671 | 0 | return(-1); |
672 | 0 | } |
673 | | |
674 | 0 | ret = xmlOutputBufferClose(buf); |
675 | 0 | if(ret < 0) { |
676 | 0 | xmlSecXmlError("xmlOutputBufferClose", xmlSecTransformGetName(transform)); |
677 | 0 | return(-1); |
678 | 0 | } |
679 | 0 | transform->status = xmlSecTransformStatusWorking; |
680 | 0 | } |
681 | | |
682 | 0 | if(transform->status == xmlSecTransformStatusWorking) { |
683 | 0 | xmlSecSize outSize; |
684 | | |
685 | | /* return chunk after chunk */ |
686 | 0 | outSize = xmlSecBufferGetSize(out); |
687 | 0 | if(outSize > maxDataSize) { |
688 | 0 | outSize = maxDataSize; |
689 | 0 | } |
690 | 0 | if(outSize > transformCtx->binaryChunkSize) { |
691 | 0 | outSize = transformCtx->binaryChunkSize; |
692 | 0 | } |
693 | 0 | if(outSize > 0) { |
694 | 0 | xmlSecAssert2(xmlSecBufferGetData(out), -1); |
695 | |
|
696 | 0 | memcpy(data, xmlSecBufferGetData(out), outSize); |
697 | 0 | ret = xmlSecBufferRemoveHead(out, outSize); |
698 | 0 | if(ret < 0) { |
699 | 0 | xmlSecInternalError2("xmlSecBufferRemoveHead", |
700 | 0 | xmlSecTransformGetName(transform), |
701 | 0 | "size=" XMLSEC_SIZE_FMT, outSize); |
702 | 0 | return(-1); |
703 | 0 | } |
704 | 0 | } else if(xmlSecBufferGetSize(out) == 0) { |
705 | 0 | transform->status = xmlSecTransformStatusFinished; |
706 | 0 | } |
707 | 0 | (*dataSize) = outSize; |
708 | 0 | } else if(transform->status == xmlSecTransformStatusFinished) { |
709 | | /* the only way we can get here is if there is no output */ |
710 | 0 | xmlSecAssert2(xmlSecBufferGetSize(out) == 0, -1); |
711 | 0 | (*dataSize) = 0; |
712 | 0 | } else { |
713 | 0 | xmlSecInvalidTransfromStatusError(transform); |
714 | 0 | return(-1); |
715 | 0 | } |
716 | | |
717 | 0 | return(0); |
718 | 0 | } |