Coverage Report

Created: 2026-10-17 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
19
#include <xmlsec/xmlsec.h>
20
#include <xmlsec/xmltree.h>
21
#include <xmlsec/list.h>
22
#include <xmlsec/transforms.h>
23
#include <xmlsec/errors.h>
24
25
#include "cast_helpers.h"
26
27
0
#define xmlSecRelationshipXmlNs         BAD_CAST "http://www.w3.org/XML/1998/namespace"
28
29
30
/******************************************************************************
31
 *
32
 * XML Relationship transform
33
 *
34
 *  * [Relationship transform](http://standards.iso.org/ittf/PubliclyAvailableStandards/c061796_ISO_IEC_29500-2_2012.zip)
35
 *
36
 * The relationships transform takes the XML document from the Relationships part and converts
37
 * it to another XML document.
38
 *
39
 * The package implementer might create relationships XML that contains content from several namespaces,
40
 * along with versioning instructions as defined in Part 3, “Markup Compatibility and Extensibility”. [O6.11]
41
 *
42
 * The relationships transform algorithm is as follows:
43
 *
44
 * Step 1: Process versioning instructions
45
 *   1. The package implementer shall process the versioning instructions, considering that the only
46
 *   known namespace is the Relationships namespace.
47
 *   2. The package implementer shall remove all ignorable content, ignoring preservation attributes.
48
 *   3. The package implementer shall remove all versioning instructions.
49
 *
50
 * Step 2: Sort and filter relationships
51
 *   1. The package implementer shall remove all namespace declarations except the Relationships
52
 *   namespace declaration.
53
 *   2. The package implementer shall remove the Relationships namespace prefix, if it is present.
54
 *   3. The package implementer shall sort relationship elements by Id value in lexicographical
55
 *   order, considering Id values as case-sensitive Unicode strings.
56
 *   4. The package implementer shall remove all Relationship elements that do not have either an Id
57
 *   value that matches any SourceId value or a Type value that matches any SourceType value, among
58
 *   the SourceId and SourceType values specified in the transform definition. Producers and consumers
59
 *   shall compare values as case-sensitive Unicode strings. [M6.27] The resulting XML document holds
60
 *   all Relationship elements that either have an Id value that matches a SourceId value or a Type value
61
 *   that matches a SourceType value specified in the transform definition.
62
 *
63
 * Step 3: Prepare for canonicalization
64
 *   1. The package implementer shall remove all characters between the Relationships start tag and
65
 *   the first Relationship start tag.
66
 *   2. The package implementer shall remove any contents of the Relationship element.
67
 *   3. The package implementer shall remove all characters between the last Relationship end tag and
68
 *   the Relationships end tag.
69
 *   4. If there are no Relationship elements, the package implementer shall remove all characters
70
 *   between the Relationships start tag and the Relationships end tag.
71
 *   5. The package implementer shall remove comments from the Relationships XML content.
72
 *   6. The package implementer shall add a TargetMode attribute with its default value, if this
73
 *   optional attribute is missing from the Relationship element.
74
 *   7. The package implementer can generate Relationship elements as start-tag/end-tag pairs with
75
 *   empty content, or as empty elements. A canonicalization transform, applied immediately after the
76
 *   Relationships Transform, converts all XML elements into start-tag/end-tag pairs.
77
 *
78
 *
79
 *   IMPLEMENTATION NOTES (https://github.com/lsh123/xmlsec/pull/24):
80
 *
81
 *   * We don't simply manipulate the XML tree, but do an XML tree -> output bytes transformation,
82
 *     because we never write characters inside XML elements, we implicitly remove all character
83
 *     contents, as required by step 3, point 1. It also simplifies the task of the situation that
84
 *     realistically the input of the transformation is always a document that conforms to the OOXML
85
 *     relationships XML schema, so in practice it'll never happen that the input document has e.g.
86
 *     characters, as the schema requires that the document has only XML elements and attributes,
87
 *     but no characters.
88
 *
89
 *   * Step 1 says that versioning instructions are processed with only the Relationships namespace
90
 *     treated as known. In non-legacy mode we therefore serialize only elements in the Relationships
91
 *     namespace plus unprefixed or built-in xml:* attributes; MCE attributes/elements and other
92
 *     foreign-namespaced content are omitted from the canonical output.
93
 *
94
 *   * Step 2, point 4 talks about a SourceType value, but given that neither Microsoft Office, nor LibreOffice
95
 *     writes that theoretical attribute, the implementation doesn't handle it. If there is a real-world situation
96
 *     when there will be such an input, then it'll be easy to add support for that. But I didn't want to clutter
97
 *     the current implementation with details that don't seem to be used in practice
98
 *
99
 * xmlSecTransform + xmlSecRelationshipCtx
100
 *
101
  *****************************************************************************/
102
typedef struct _xmlSecRelationshipCtx           xmlSecRelationshipCtx,
103
                                                *xmlSecRelationshipCtxPtr;
104
struct _xmlSecRelationshipCtx {
105
    xmlSecPtrListPtr sourceIdList;
106
};
107
108
0
XMLSEC_TRANSFORM_DECLARE(Relationship, xmlSecRelationshipCtx)
109
0
#define xmlSecRelationshipSize XMLSEC_TRANSFORM_SIZE(Relationship)
110
0
111
0
static int              xmlSecRelationshipInitialize      (xmlSecTransformPtr transform);
112
0
static void             xmlSecRelationshipFinalize        (xmlSecTransformPtr transform);
113
0
static int              xmlSecTransformRelationshipPopBin (xmlSecTransformPtr transform,
114
0
                                                           xmlSecByte* data,
115
0
                                                           xmlSecSize maxDataSize,
116
0
                                                           xmlSecSize* dataSize,
117
0
                                                           xmlSecTransformCtxPtr transformCtx);
118
0
static int              xmlSecTransformRelationshipPushXml(xmlSecTransformPtr transform,
119
0
                                                           xmlSecNodeSetPtr nodes,
120
0
                                                           xmlSecTransformCtxPtr transformCtx);
121
0
static int              xmlSecRelationshipReadNode        (xmlSecTransformPtr transform,
122
0
                                                           xmlNodePtr node,
123
0
                                                           xmlSecTransformCtxPtr transformCtx);
124
0
125
0
static int              xmlSecTransformRelationshipProcessElementNode(xmlSecTransformPtr transform,
126
0
                                                            xmlOutputBufferPtr buf,
127
0
                                                            xmlNodePtr cur,
128
0
                                                            unsigned int depth,
129
0
                                                            xmlSecTransformCtxPtr transformCtx);
130
0
131
0
132
0
static xmlSecTransformKlass xmlSecRelationshipKlass = {
133
0
    /* klass/object sizes */
134
0
    sizeof(xmlSecTransformKlass),               /* xmlSecSize klassSize */
135
0
    xmlSecRelationshipSize,                     /* xmlSecSize objSize */
136
0
137
0
    xmlSecNameRelationship,                     /* const xmlChar* name; */
138
0
    xmlSecHrefRelationship,                     /* const xmlChar* href; */
139
0
    xmlSecTransformUsageDSigTransform,          /* xmlSecTransformUsage usage; */
140
0
141
0
    xmlSecRelationshipInitialize,               /* xmlSecTransformInitializeMethod initialize; */
142
0
    xmlSecRelationshipFinalize,                 /* xmlSecTransformFinalizeMethod finalize; */
143
0
    xmlSecRelationshipReadNode,                 /* xmlSecTransformNodeReadMethod readNode; */
144
0
    NULL,                                       /* xmlSecTransformNodeWriteMethod writeNode; */
145
0
    NULL,                                       /* xmlSecTransformSetKeyReqMethod setKeyReq; */
146
0
    NULL,                                       /* xmlSecTransformSetKeyMethod setKey; */
147
0
    NULL,                                       /* xmlSecTransformVerifyMethod verify; */
148
0
    xmlSecTransformDefaultGetDataType,          /* xmlSecTransformGetDataTypeMethod getDataType; */
149
0
    NULL,                                       /* xmlSecTransformPushBinMethod pushBin; */
150
0
    xmlSecTransformRelationshipPopBin,          /* xmlSecTransformPopBinMethod popBin; */
151
0
    xmlSecTransformRelationshipPushXml,         /* xmlSecTransformPushXmlMethod pushXml; */
152
0
    NULL,                                       /* xmlSecTransformPopXmlMethod popXml; */
153
0
    NULL,                                       /* xmlSecTransformExecuteMethod execute; */
154
0
155
0
    NULL,                                       /* void* reserved0; */
156
0
    NULL,                                       /* void* reserved1; */
157
0
};
158
0
159
0
/**
160
0
 * @brief Gets the Relationship transform klass.
161
0
 *
162
0
 * @return Relationship transform klass.
163
0
 */
164
0
xmlSecTransformId
165
1
xmlSecTransformRelationshipGetKlass(void) {
166
1
    return(&xmlSecRelationshipKlass);
167
1
}
168
169
static int
170
0
xmlSecRelationshipInitialize(xmlSecTransformPtr transform) {
171
0
    xmlSecRelationshipCtxPtr ctx;
172
173
0
    xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId), -1);
174
0
    xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize), -1);
175
176
0
    ctx = xmlSecRelationshipGetCtx(transform);
177
0
    xmlSecAssert2(ctx != NULL, -1);
178
179
    /* initialize context */
180
0
    memset(ctx, 0, sizeof(xmlSecRelationshipCtx));
181
182
0
    ctx->sourceIdList = xmlSecPtrListCreate(xmlSecStringListId);
183
0
    if(ctx->sourceIdList == NULL) {
184
0
        xmlSecInternalError("xmlSecPtrListCreate",
185
0
                            xmlSecTransformGetName(transform));
186
0
        return(-1);
187
0
    }
188
0
    return(0);
189
0
}
190
191
static void
192
0
xmlSecRelationshipFinalize(xmlSecTransformPtr transform) {
193
0
    xmlSecRelationshipCtxPtr ctx;
194
195
0
    xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId));
196
0
    xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize));
197
198
0
    ctx = xmlSecRelationshipGetCtx(transform);
199
0
    xmlSecAssert(ctx != NULL);
200
201
0
    if(ctx->sourceIdList != NULL) {
202
0
       xmlSecPtrListDestroy(ctx->sourceIdList);
203
0
    }
204
205
0
    memset(ctx, 0, sizeof(xmlSecRelationshipCtx));
206
0
}
207
208
static int
209
0
xmlSecRelationshipReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) {
210
0
    xmlSecRelationshipCtxPtr ctx;
211
0
    xmlNodePtr cur;
212
0
    xmlChar* sourceId;
213
0
    int ret;
214
215
0
    xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId), -1);
216
0
    xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize), -1);
217
0
    xmlSecAssert2(node != NULL, -1);
218
0
    xmlSecAssert2(transformCtx != NULL, -1);
219
220
    /* Collect list of source ids from all RelationshipReference nodes */
221
0
    ctx = xmlSecRelationshipGetCtx(transform);
222
0
    xmlSecAssert2(ctx != NULL, -1);
223
224
0
    for(cur = node->children; cur != NULL; cur = cur->next) {
225
        /* we only care about RelationshipReference nodes */
226
0
        if(!xmlSecCheckNodeName(cur, xmlSecNodeRelationshipReference, xmlSecRelationshipReferenceNs)) {
227
0
            continue;
228
0
        }
229
230
        /* which MUST have sourceId attribute. xmlGetProp() returns NULL both when the
231
         * attribute is absent and when it cannot allocate the return value, so check
232
         * for presence first to report the correct error. */
233
0
        if(!xmlHasProp(cur, xmlSecRelationshipAttrSourceId)) {
234
0
            xmlSecInvalidNodeAttributeError(cur, xmlSecRelationshipAttrSourceId, NULL, "missing");
235
0
            return(-1);
236
0
        }
237
0
        sourceId = xmlGetProp(cur, xmlSecRelationshipAttrSourceId);
238
0
        if(sourceId == NULL) {
239
0
            xmlSecXmlError("xmlGetProp", xmlSecTransformGetName(transform));
240
0
            return(-1);
241
0
        }
242
243
        /* add it to the list */
244
0
        ret = xmlSecPtrListAdd(ctx->sourceIdList, sourceId);
245
0
        if(ret < 0) {
246
0
            xmlSecInternalError("xmlSecPtrListAdd", xmlSecTransformGetName(transform));
247
0
            xmlFree(sourceId);
248
0
            return(-1);
249
0
        }
250
0
    }
251
252
    /* done */
253
0
    return(0);
254
0
}
255
256
/*
257
 * An item of the list used to collect Relationship elements before sorting:
258
 * the element itself plus the (cached) value of its Id attribute. Caching the
259
 * Id value avoids re-reading (and re-allocating) the attribute on every
260
 * comparison while the list is being sorted.
261
 */
262
typedef struct _xmlSecRelationshipListItem      xmlSecRelationshipListItem,
263
                                                *xmlSecRelationshipListItemPtr;
264
struct _xmlSecRelationshipListItem {
265
    xmlNodePtr                  node;  /**< the Relationship element. */
266
    xmlChar*                    id;  /**< the cached value of the Id attribute (NULL if the element has no Id or it could not be read). */
267
};
268
269
static void             xmlSecRelationshipListItemDestroy     (xmlSecPtr item);
270
271
static xmlSecPtrListKlass xmlSecRelationshipListItemKlass = {
272
    BAD_CAST "relationship-list-items",
273
    NULL,                                       /* xmlSecPtrDuplicateItemMethod duplicateItem; */
274
    xmlSecRelationshipListItemDestroy,          /* xmlSecPtrDestroyItemMethod destroyItem; */
275
    NULL,                                       /* xmlSecPtrDebugDumpItemMethod debugDumpItem; */
276
    NULL,                                       /* xmlSecPtrDebugDumpItemMethod debugXmlDumpItem; */
277
};
278
279
/**
280
 * @brief Creates a new list item for @p node.
281
 * @details The Id attribute of @p node is read once and cached in the new
282
 * item. Caller is responsible for freeing the returned item by calling
283
 * #xmlSecRelationshipListItemDestroy function.
284
 * @param node the pointer to the Relationship element.
285
 * @return pointer to the newly allocated item or NULL if an error occurs.
286
 */
287
static xmlSecRelationshipListItemPtr
288
0
xmlSecRelationshipListItemCreate(xmlNodePtr node) {
289
0
    xmlSecRelationshipListItemPtr item;
290
291
0
    xmlSecAssert2(node != NULL, NULL);
292
293
0
    item = (xmlSecRelationshipListItemPtr)xmlMalloc(sizeof(xmlSecRelationshipListItem));
294
0
    if(item == NULL) {
295
0
        xmlSecMallocError(sizeof(xmlSecRelationshipListItem), NULL);
296
0
        return(NULL);
297
0
    }
298
299
0
    item->node = node;
300
    /*
301
     * xmlGetProp() returns NULL both when the Id attribute is absent and
302
     * when it cannot allocate the return value; in either case the item is
303
     * left with no cached Id (the element is dropped later by
304
     * xmlSecTransformRelationshipProcessNode, which reports the same
305
     * condition).
306
     */
307
0
    item->id = xmlGetProp(node, xmlSecRelationshipAttrId);
308
0
    return(item);
309
0
}
310
311
static void
312
0
xmlSecRelationshipListItemDestroy(xmlSecPtr item) {
313
0
    xmlSecRelationshipListItemPtr listItem = (xmlSecRelationshipListItemPtr)item;
314
315
0
    xmlSecAssert(item != NULL);
316
317
0
    if(listItem->id != NULL) {
318
0
        xmlFree(listItem->id);
319
0
    }
320
0
    xmlFree(listItem);
321
0
}
322
323
/*
324
 * Compares two list items by their cached Id value in lexicographical order
325
 * (case-sensitive). Items without an Id value sort before items that have
326
 * one; two items that both lack an Id compare equal, so the comparator is
327
 * a strict weak ordering.
328
 */
329
static int
330
0
xmlSecTransformRelationshipCompareItems(xmlSecPtr item1, xmlSecPtr item2) {
331
0
    const xmlSecRelationshipListItem* listItem1 = (const xmlSecRelationshipListItem*)item1;
332
0
    const xmlSecRelationshipListItem* listItem2 = (const xmlSecRelationshipListItem*)item2;
333
0
    int ret;
334
335
0
    if((listItem1->id == NULL) && (listItem2->id == NULL)) {
336
0
        ret = 0;
337
0
    } else if(listItem1->id == NULL) {
338
0
        ret = -1;
339
0
    } else if(listItem2->id == NULL) {
340
0
        ret = 1;
341
0
    } else {
342
0
        ret = xmlStrcmp(listItem1->id, listItem2->id);
343
0
    }
344
0
    return(ret);
345
0
}
346
347
348
static int
349
0
xmlSecRelationshipCtxFindSourceId(xmlSecRelationshipCtxPtr ctx, const xmlChar* id) {
350
0
    xmlSecSize ii, size;
351
352
0
    xmlSecAssert2(ctx != NULL, -1);
353
0
    xmlSecAssert2(id != NULL, -1);
354
355
0
    size = xmlSecPtrListGetSize(ctx->sourceIdList);
356
0
    for(ii = 0; ii < size; ++ii) {
357
0
        if(xmlStrcmp((xmlChar *)xmlSecPtrListGetItem(ctx->sourceIdList, ii), id) == 0) {
358
0
            return(1);
359
0
        }
360
0
    }
361
362
0
    return(0);
363
0
}
364
365
static int
366
0
xmlSecTransformRelationshipIsOutputElement(xmlNodePtr cur) {
367
0
    xmlSecAssert2(cur != NULL, 0);
368
369
0
    return((cur->ns != NULL) &&
370
0
           (cur->ns->href != NULL) &&
371
0
           (xmlStrcmp(cur->ns->href, xmlSecRelationshipsNs) == 0));
372
0
}
373
374
static int
375
0
xmlSecTransformRelationshipIsOutputAttr(xmlAttrPtr attr) {
376
0
    xmlSecAssert2(attr != NULL, 0);
377
378
0
    return((attr->ns == NULL) ||
379
0
           ((attr->ns->href != NULL) &&
380
0
            (xmlStrcmp(attr->ns->href, xmlSecRelationshipXmlNs) == 0)));
381
0
}
382
383
384
/*
385
 * Step 1 processes versioning instructions with only the Relationships namespace
386
 * treated as known, so in non-legacy mode we drop any element outside that
387
 * namespace before serialization. Step 2, point 4 then filters surviving
388
 * Relationship elements by SourceId.
389
 */
390
static int
391
0
xmlSecTransformRelationshipProcessNode(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur, unsigned int depth, xmlSecTransformCtxPtr transformCtx) {
392
0
    xmlSecRelationshipCtxPtr ctx;
393
0
    int ret;
394
395
0
    xmlSecAssert2(transform != NULL, -1);
396
0
    xmlSecAssert2(buf != NULL, -1);
397
0
    xmlSecAssert2(cur != NULL, -1);
398
0
    xmlSecAssert2(transformCtx != NULL, -1);
399
400
0
    ctx = xmlSecRelationshipGetCtx(transform);
401
0
    xmlSecAssert2(ctx != NULL, -1);
402
403
0
    if(((transformCtx->flags & XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY) == 0) &&
404
0
       !xmlSecTransformRelationshipIsOutputElement(cur)) {
405
0
        return(0);
406
0
    }
407
408
0
    if(xmlSecCheckNodeName(cur, xmlSecNodeRelationship, xmlSecRelationshipsNs)) {
409
0
        xmlChar* id = xmlGetProp(cur, xmlSecRelationshipAttrId);
410
0
        if(id == NULL) {
411
            /* xmlGetProp() returns NULL both when the Id attribute is absent
412
             * and when it cannot allocate the return value. Current behavior
413
             * treats either case as "no usable Id", so per step 2, point 4
414
             * the node is dropped instead of failing the transform. */
415
0
            return(0);
416
0
        }
417
418
0
        ret = xmlSecRelationshipCtxFindSourceId(ctx, id);
419
0
        if(ret < 0) {
420
0
            xmlSecInternalError("xmlSecRelationshipCtxFindSourceId", xmlSecTransformGetName(transform));
421
0
            xmlFree(id);
422
0
            return(-1);
423
0
        }
424
0
        xmlFree(id);
425
426
0
        if(ret == 0) {
427
            /* Id is not in the sourceId list, so we skip this node */
428
0
            return(0);
429
0
        }
430
0
    }
431
432
0
    ret = xmlSecTransformRelationshipProcessElementNode(transform, buf, cur, depth, transformCtx);
433
0
    if(ret < 0) {
434
0
        xmlSecInternalError("xmlSecTransformRelationshipProcessElementNode", xmlSecTransformGetName(transform));
435
0
        return(-1);
436
0
    }
437
438
    /* done */
439
0
    return(0);
440
0
}
441
442
/*
443
 * This is step 2, point 3: sort elements by Id: we process other elements as-is, but for
444
 * Relationship elements we collect them in a list, sort the list in place, and finally
445
 * process the elements in the sorted order.
446
 */
447
static int
448
0
xmlSecTransformRelationshipProcessNodeList(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur, unsigned int depth, xmlSecTransformCtxPtr transformCtx) {
449
0
    xmlSecPtrListPtr list;
450
0
    xmlSecRelationshipListItemPtr item = NULL;
451
0
    xmlSecSize ii;
452
0
    int ret;
453
454
0
    xmlSecAssert2(transform != NULL, -1);
455
0
    xmlSecAssert2(buf != NULL, -1);
456
0
    xmlSecAssert2(cur != NULL, -1);
457
0
    xmlSecAssert2(transformCtx != NULL, -1);
458
459
    /* check depth */
460
0
    if((transformCtx->maxDepth > 0) && (depth > transformCtx->maxDepth)) {
461
0
        xmlSecInternalError2("xmlSecTransformRelationshipProcessNodeList", NULL,
462
0
            "recursion level exceeded: level=%u", depth);
463
0
        return(-1);
464
0
    }
465
466
0
    list = xmlSecPtrListCreate(&xmlSecRelationshipListItemKlass);
467
0
    if(list == NULL) {
468
0
        xmlSecInternalError("xmlSecPtrListCreate", xmlSecTransformGetName(transform));
469
0
        return(-1);
470
0
    }
471
472
0
    for(; cur; cur = cur->next) {
473
        /*
474
         * Step 3: remove all characters (text nodes) and comments from the Relationships
475
         * XML content. Only element nodes are serialized; text/comment/PI nodes are skipped
476
         * so they are not written out as spurious <text>/<comment> elements.
477
         *
478
         * Legacy mode (XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY): restore the old
479
         * behaviour and serialize non-element nodes as well.
480
         */
481
0
        if(((transformCtx->flags & XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY) == 0) &&
482
0
           (cur->type != XML_ELEMENT_NODE)) {
483
0
            continue;
484
0
        }
485
486
        /*
487
         * Step 2: only Relationship elements in the Relationships namespace are sorted and
488
         * filtered. Match on name AND namespace so a foreign element that merely shares the
489
         * local name "Relationship" is not mistaken for a real relationship entry.
490
         */
491
0
        if(xmlSecCheckNodeName(cur, xmlSecNodeRelationship, xmlSecRelationshipsNs)) {
492
0
            item = xmlSecRelationshipListItemCreate(cur);
493
0
            if(item == NULL) {
494
0
                xmlSecInternalError("xmlSecRelationshipListItemCreate", xmlSecTransformGetName(transform));
495
0
                xmlSecPtrListDestroy(list);
496
0
                return(-1);
497
0
            }
498
0
            ret = xmlSecPtrListAdd(list, (xmlSecPtr)item);
499
0
            if(ret < 0) {
500
0
                xmlSecInternalError("xmlSecPtrListAdd", xmlSecTransformGetName(transform));
501
0
                xmlSecRelationshipListItemDestroy((xmlSecPtr)item);
502
0
                xmlSecPtrListDestroy(list);
503
0
                return(-1);
504
0
            }
505
0
            item = NULL;
506
0
        } else {
507
0
            ret = xmlSecTransformRelationshipProcessNode(transform, buf, cur, depth, transformCtx);
508
0
            if(ret < 0) {
509
0
                xmlSecInternalError("xmlSecTransformRelationshipProcessNode", xmlSecTransformGetName(transform));
510
0
                xmlSecPtrListDestroy(list);
511
0
                return(-1);
512
0
            }
513
0
        }
514
0
    }
515
516
    /* sort the collected elements by their cached Id value */
517
0
    ret = xmlSecPtrListSort(list, xmlSecTransformRelationshipCompareItems);
518
0
    if(ret < 0) {
519
0
        xmlSecInternalError("xmlSecPtrListSort", xmlSecTransformGetName(transform));
520
0
        xmlSecPtrListDestroy(list);
521
0
        return(-1);
522
0
    }
523
524
0
    for(ii = 0; ii < xmlSecPtrListGetSize(list); ++ii) {
525
0
        const xmlSecRelationshipListItem* listItem =
526
0
            (const xmlSecRelationshipListItem*)xmlSecPtrListGetItem(list, ii);
527
528
0
        ret = xmlSecTransformRelationshipProcessNode(transform, buf, listItem->node, depth, transformCtx);
529
0
        if(ret < 0) {
530
0
            xmlSecInternalError("xmlSecTransformRelationshipProcessNode", xmlSecTransformGetName(transform));
531
0
            xmlSecPtrListDestroy(list);
532
0
            return(-1);
533
0
        }
534
0
    }
535
536
    /* done */
537
0
    xmlSecPtrListDestroy(list);
538
0
    return(0);
539
0
}
540
541
/*
542
 * Writes an attribute value, escaping the XML special characters (&, <, >, ") so that the
543
 * generated document stays well-formed. Without this, a Target value containing e.g. '&' or '"'
544
 * would produce invalid XML that fails to re-parse ("EntityRef: expecting ';'").
545
 */
546
static int
547
0
xmlSecTransformRelationshipWriteEscapedValue(xmlOutputBufferPtr buf, const xmlChar* value) {
548
0
    xmlChar* escaped;
549
0
    xmlChar* dst;
550
0
    const xmlChar* src;
551
0
    size_t len;
552
0
    int ret;
553
554
0
    xmlSecAssert2(buf != NULL, -1);
555
0
    xmlSecAssert2(value != NULL, -1);
556
557
    /* Worst case every character expands to "&quot;" (6 bytes). */
558
0
    len = strlen((const char*)value);
559
0
    if(len > ((SIZE_MAX - 1U) / 6U)) {
560
0
        xmlSecInvalidSizeError("value", len, ((SIZE_MAX - 1U) / 6U), NULL);
561
0
        return(-1);
562
0
    }
563
0
    escaped = xmlMalloc(len * 6 + 1);
564
0
    if(escaped == NULL) {
565
0
        xmlSecXmlError("xmlMalloc", NULL);
566
0
        return(-1);
567
0
    }
568
569
0
    dst = escaped;
570
0
    for(src = value; *src != '\0'; ++src) {
571
0
        switch(*src) {
572
0
        case '&':
573
0
            memcpy(dst, "&amp;", 5);
574
0
            dst += 5;
575
0
            break;
576
0
        case '<':
577
0
            memcpy(dst, "&lt;", 4);
578
0
            dst += 4;
579
0
            break;
580
0
        case '>':
581
0
            memcpy(dst, "&gt;", 4);
582
0
            dst += 4;
583
0
            break;
584
0
        case '"':
585
0
            memcpy(dst, "&quot;", 6);
586
0
            dst += 6;
587
0
            break;
588
0
        default:
589
0
            *dst++ = *src;
590
0
            break;
591
0
        }
592
0
    }
593
0
    *dst = '\0';
594
595
0
    ret = xmlOutputBufferWriteString(buf, (const char*)escaped);
596
0
    xmlFree(escaped);
597
0
    return(ret);
598
0
}
599
600
static int
601
0
xmlSecTransformRelationshipWriteProp(xmlOutputBufferPtr buf, const xmlChar * name, const xmlChar * value) {
602
0
    int ret;
603
604
0
    xmlSecAssert2(buf != NULL, -1);
605
0
    xmlSecAssert2(name != NULL, -1);
606
607
0
    ret = xmlOutputBufferWriteString(buf, " ");
608
0
    if(ret < 0) {
609
0
        xmlSecXmlError("xmlOutputBufferWriteString", NULL);
610
0
        return(-1);
611
0
    }
612
613
0
    ret = xmlOutputBufferWriteString(buf, (const char*) name);
614
0
    if(ret < 0) {
615
0
        xmlSecXmlError("xmlOutputBufferWriteString", NULL);
616
0
        return(-1);
617
0
    }
618
619
0
    if(value != NULL) {
620
0
        ret = xmlOutputBufferWriteString(buf, "=\"");
621
0
        if(ret < 0) {
622
0
            xmlSecXmlError("xmlOutputBufferWriteString", NULL);
623
0
            return(-1);
624
0
        }
625
0
        ret = xmlSecTransformRelationshipWriteEscapedValue(buf, value);
626
0
        if(ret < 0) {
627
0
            xmlSecXmlError("xmlSecTransformRelationshipWriteEscapedValue", NULL);
628
0
            return(-1);
629
0
        }
630
0
        ret = xmlOutputBufferWriteString(buf, "\"");
631
0
        if(ret < 0) {
632
0
            xmlSecXmlError("xmlOutputBufferWriteString", NULL);
633
0
            return(-1);
634
0
        }
635
0
    }
636
637
0
    return (0);
638
0
}
639
640
/*
641
 * Writes a single attribute to the output buffer using the attribute itself as
642
 * the value source. In non-legacy mode the caller only passes attributes that
643
 * survive Step 1 / Step 2 filtering, which means unprefixed attributes plus
644
 * built-in xml:* attributes.
645
 */
646
static int
647
0
xmlSecTransformRelationshipWriteAttribute(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlAttrPtr attr) {
648
0
    xmlChar * value = NULL;
649
0
    xmlChar * nameCopy = NULL;
650
0
    const xmlChar * attrName;
651
0
    size_t prefixLen;
652
0
    size_t nameLen;
653
0
    int ret;
654
655
0
    xmlSecAssert2(transform != NULL, -1);
656
0
    xmlSecAssert2(buf != NULL, -1);
657
0
    xmlSecAssert2(attr != NULL, -1);
658
659
    /* get correct attribute name */
660
0
    if((attr->ns != NULL) && (attr->ns->prefix != NULL)) {
661
0
        prefixLen = strlen((const char*)attr->ns->prefix);
662
0
        nameLen = strlen((const char*)attr->name);
663
0
        nameCopy = xmlMalloc(prefixLen + nameLen + 2);
664
0
        if(nameCopy == NULL) {
665
0
            xmlSecXmlError("xmlMalloc", NULL);
666
0
            return(-1);
667
0
        }
668
0
        memcpy(nameCopy, attr->ns->prefix, prefixLen);
669
0
        nameCopy[prefixLen] = ':';
670
0
        memcpy(nameCopy + prefixLen + 1, attr->name, nameLen);
671
0
        nameCopy[prefixLen + nameLen + 1] = '\0';
672
0
        attrName = nameCopy;
673
0
    } else {
674
0
        attrName = attr->name;
675
0
    }
676
677
    /*
678
     * Get the attribute value as raw (unescaped) text. The third argument to
679
     * xmlNodeListGetString() is the "inLine" flag: when non-zero it returns the
680
     * unescaped content, which xmlSecTransformRelationshipWriteProp() then
681
     * escapes exactly once. An attribute without child nodes has an empty value
682
     * (e.g. Target=""), so skip the call and serialize an empty string instead
683
     * of failing.
684
     */
685
0
    if(attr->children != NULL) {
686
0
        value = xmlNodeListGetString(attr->doc, attr->children, 1);
687
0
        if(value == NULL) {
688
0
            xmlSecXmlError("xmlNodeListGetString", xmlSecTransformGetName(transform));
689
0
            xmlFree(nameCopy);
690
0
            return(-1);
691
0
        }
692
0
    }
693
694
    /* finally write the attribute to the output; an attribute without child
695
     * nodes has an empty value, e.g. Target="" */
696
0
    ret = xmlSecTransformRelationshipWriteProp(buf, attrName, (value != NULL) ? value : BAD_CAST "");
697
0
    if(ret < 0) {
698
0
        xmlSecInternalError("xmlSecTransformRelationshipWriteProp", xmlSecTransformGetName(transform));
699
0
        xmlFree(nameCopy);
700
0
        if(value != NULL) {
701
0
            xmlFree(value);
702
0
        }
703
0
        return(-1);
704
0
    }
705
706
0
    xmlFree(nameCopy);
707
0
    if(value != NULL) {
708
0
        xmlFree(value);
709
0
    }
710
0
    return(0);
711
0
}
712
713
/*
714
 * Write children.
715
 *
716
 * Step 3, point 2: remove any contents of the Relationship element. In
717
 * non-legacy mode a Relationship element is written with no content even
718
 * if the (non-conformant) input has content inside it; legacy mode keeps
719
 * the old behaviour of serializing the children.
720
 */
721
static int
722
xmlSecTransformRelationshipProcessElementNodeChildren(
723
    xmlSecTransformPtr transform,
724
    xmlOutputBufferPtr buf,
725
    xmlNodePtr cur,
726
    unsigned int depth,
727
    xmlSecTransformCtxPtr transformCtx
728
0
) {
729
0
    int ret;
730
731
0
    xmlSecAssert2(transform != NULL, -1);
732
0
    xmlSecAssert2(buf != NULL, -1);
733
0
    xmlSecAssert2(cur != NULL, -1);
734
0
    xmlSecAssert2(cur->name != NULL, -1);
735
0
    xmlSecAssert2(transformCtx != NULL, -1);
736
737
    /* do nothing if there are no children */
738
0
    if(cur->children == NULL) {
739
0
        return(0);
740
0
    }
741
742
    /* in non-legacy mode, skip writing children for the Relationship element */
743
0
    if(
744
0
        ((transformCtx->flags & XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY) == 0) &&
745
0
        (xmlSecCheckNodeName(cur, xmlSecNodeRelationship, xmlSecRelationshipsNs) == 1)
746
0
     ) {
747
0
        return(0);
748
0
    }
749
750
    /* process the list of child nodes */
751
0
    ret = xmlSecTransformRelationshipProcessNodeList(transform, buf, cur->children, depth + 1, transformCtx);
752
0
    if(ret < 0) {
753
0
        xmlSecInternalError("xmlSecTransformRelationshipProcessNodeList", xmlSecTransformGetName(transform));
754
0
        return(-1);
755
0
    }
756
757
0
    return(0);
758
0
}
759
760
static int
761
0
xmlSecTransformRelationshipProcessElementNode(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur, unsigned int depth, xmlSecTransformCtxPtr transformCtx) {
762
0
    xmlAttrPtr attr;
763
0
    xmlNsPtr ns;
764
0
    int foundTargetMode = 0;
765
0
    int relNsWritten = 0;
766
0
    int ret;
767
768
0
    xmlSecAssert2(transform != NULL, -1);
769
0
    xmlSecAssert2(buf != NULL, -1);
770
0
    xmlSecAssert2(cur != NULL, -1);
771
0
    xmlSecAssert2(cur->name != NULL, -1);
772
0
    xmlSecAssert2(transformCtx != NULL, -1);
773
774
    /* write open node */
775
0
    ret = xmlOutputBufferWriteString(buf, "<");
776
0
    if(ret < 0) {
777
0
        xmlSecXmlError("xmlOutputBufferWriteString",
778
0
                            xmlSecTransformGetName(transform));
779
0
        return(-1);
780
0
    }
781
0
    ret = xmlOutputBufferWriteString(buf, (const char *)cur->name);
782
0
    if(ret < 0) {
783
0
        xmlSecXmlError("xmlOutputBufferWriteString",
784
0
                            xmlSecTransformGetName(transform));
785
0
        return(-1);
786
0
    }
787
788
    /*
789
     * Write namespaces.
790
     *
791
     * Step 2, point 1: remove all namespace declarations except the Relationships namespace
792
    * declaration. Step 1 has already filtered out versioning instructions and
793
    * foreign-namespaced content from the serialized output, so we emit only the
794
    * namespace binding for Relationships itself here. Foreign namespace
795
    * declarations (e.g. xmlns:foo="...") are dropped so they do not leak into
796
    * the canonical output.
797
     *
798
     * Step 2, point 2: remove the Relationships namespace prefix, if it is present. Element
799
     * names are written bare, so the Relationships namespace is bound as the default
800
     * namespace (xmlns="...") for them to resolve into it.
801
     *
802
     * Legacy mode (XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY): restore the old behaviour
803
     * of writing a single unprefixed xmlns="..." declaration for the first declared namespace,
804
     * regardless of whether it is the Relationships namespace.
805
     */
806
0
    if((transformCtx->flags & XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY) != 0) {
807
0
        if(cur->nsDef != NULL) {
808
0
            ret = xmlSecTransformRelationshipWriteProp(buf, BAD_CAST "xmlns",
809
0
                        (cur->nsDef->href != NULL) ? cur->nsDef->href : BAD_CAST "");
810
0
            if(ret < 0) {
811
0
                xmlSecInternalError("xmlSecTransformRelationshipWriteProp(xmlns)",
812
0
                                    xmlSecTransformGetName(transform));
813
0
                return(-1);
814
0
            }
815
0
        }
816
0
    } else {
817
0
        for(ns = cur->nsDef; ns != NULL; ns = ns->next) {
818
0
            if((ns->href == NULL) || (xmlStrcmp(ns->href, xmlSecRelationshipsNs) != 0)) {
819
0
                continue;
820
0
            }
821
0
            if(relNsWritten) {
822
                /* The Relationships namespace is already emitted as the default
823
                 * namespace; skip any further declarations bound to it. */
824
0
                continue;
825
0
            }
826
0
            ret = xmlSecTransformRelationshipWriteProp(buf, BAD_CAST "xmlns", ns->href);
827
0
            if(ret < 0) {
828
0
                xmlSecInternalError("xmlSecTransformRelationshipWriteProp(xmlns)",
829
0
                                    xmlSecTransformGetName(transform));
830
0
                return(-1);
831
0
            }
832
0
            relNsWritten = 1;
833
0
        }
834
0
    }
835
836
    /*
837
     * Write attributes.
838
     *
839
     * This is step 3, point 6: add default value of TargetMode if there is no such attribute.
840
     * Only the unprefixed TargetMode attribute counts as present; a namespaced one must not
841
     * suppress the default.
842
     */
843
0
    for(attr = cur->properties; attr != NULL; attr = attr->next) {
844
0
        if((attr->ns == NULL) && (xmlStrcmp(attr->name, xmlSecRelationshipAttrTargetMode) == 0)) {
845
0
            foundTargetMode = 1;
846
0
        }
847
848
0
        if(((transformCtx->flags & XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY) == 0) &&
849
0
           !xmlSecTransformRelationshipIsOutputAttr(attr)) {
850
0
            continue;
851
0
        }
852
853
0
        ret = xmlSecTransformRelationshipWriteAttribute(transform, buf, attr);
854
0
        if(ret < 0) {
855
0
            xmlSecInternalError("xmlSecTransformRelationshipWriteAttribute",
856
0
                                xmlSecTransformGetName(transform));
857
0
            return(-1);
858
0
        }
859
0
    }
860
861
    /*
862
     * Step 3, point 6: add a TargetMode attribute with its default value ("Internal") if this
863
     * optional attribute is missing. Only apply to real Relationship elements (Relationships
864
     * namespace), not to foreign elements that merely share the local name.
865
     */
866
0
    if(xmlSecCheckNodeName(cur, xmlSecNodeRelationship, xmlSecRelationshipsNs) && !foundTargetMode) {
867
0
        ret = xmlSecTransformRelationshipWriteProp(buf, xmlSecRelationshipAttrTargetMode, BAD_CAST "Internal");
868
0
        if(ret < 0) {
869
0
            xmlSecInternalError("xmlSecTransformRelationshipWriteProp(TargetMode=Internal)", xmlSecTransformGetName(transform));
870
0
            return(-1);
871
0
        }
872
0
    }
873
874
    /* finish writing open node */
875
0
    ret = xmlOutputBufferWriteString(buf, ">");
876
0
    if(ret < 0) {
877
0
        xmlSecXmlError("xmlOutputBufferWriteString", xmlSecTransformGetName(transform));
878
0
        return(-1);
879
0
    }
880
881
    /*
882
     * Write children.
883
     */
884
0
    ret = xmlSecTransformRelationshipProcessElementNodeChildren(transform, buf, cur, depth, transformCtx);
885
0
    if(ret < 0) {
886
0
        xmlSecInternalError("xmlSecTransformRelationshipProcessElementNodeChildren", xmlSecTransformGetName(transform));
887
0
        return(-1);
888
0
    }
889
890
    /* write closing node */
891
0
    ret = xmlOutputBufferWriteString(buf, "</");
892
0
    if(ret < 0) {
893
0
        xmlSecXmlError("xmlOutputBufferWriteString", xmlSecTransformGetName(transform));
894
0
        return(-1);
895
0
    }
896
0
    ret = xmlOutputBufferWriteString(buf, (const char *)cur->name);
897
0
    if(ret < 0) {
898
0
        xmlSecXmlError("xmlOutputBufferWriteString", xmlSecTransformGetName(transform));
899
0
        return(-1);
900
0
    }
901
0
    if(xmlOutputBufferWriteString(buf, ">") < 0) {
902
0
        xmlSecXmlError("xmlOutputBufferWriteString", xmlSecTransformGetName(transform));
903
0
        return(-1);
904
0
    }
905
906
    /* done */
907
0
    return(0);
908
0
}
909
910
static int
911
0
xmlSecTransformRelationshipExecute(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlDocPtr doc, xmlSecTransformCtxPtr transformCtx) {
912
0
    int ret;
913
914
0
    xmlSecAssert2(transform != NULL, -1);
915
0
    xmlSecAssert2(buf != NULL, -1);
916
0
    xmlSecAssert2(doc != NULL, -1);
917
0
    xmlSecAssert2(transformCtx != NULL, -1);
918
919
    /*
920
     * In non-legacy mode the top-level content must be in the Relationships namespace;
921
     * otherwise the whole document would be silently dropped and the transform would
922
     * produce empty output. Fail explicitly instead.
923
     */
924
0
    if((transformCtx->flags & XMLSEC_TRANSFORMCTX_FLAGS_RELATIONSHIP_LEGACY) == 0) {
925
0
        xmlNodePtr top;
926
0
        for(top = doc->children; top != NULL; top = top->next) {
927
0
            if((top->type == XML_ELEMENT_NODE) &&
928
0
               !xmlSecTransformRelationshipIsOutputElement(top)) {
929
0
                xmlSecInvalidNodeContentError(top, xmlSecTransformGetName(transform),
930
0
                    "top-level node is not in the Relationships namespace");
931
0
                return(-1);
932
0
            }
933
0
        }
934
0
    }
935
936
0
    if(doc->children != NULL) {
937
0
        ret = xmlSecTransformRelationshipProcessNodeList(transform, buf, doc->children, 0, transformCtx);
938
0
        if(ret < 0) {
939
0
            xmlSecInternalError("xmlSecTransformRelationshipProcessNodeList", xmlSecTransformGetName(transform));
940
0
            return(-1);
941
0
        }
942
0
    }
943
944
0
    return(0);
945
0
}
946
947
static int
948
xmlSecTransformRelationshipPushXml(xmlSecTransformPtr transform, xmlSecNodeSetPtr nodes, xmlSecTransformCtxPtr transformCtx)
949
0
{
950
0
    xmlOutputBufferPtr buf;
951
0
    xmlSecRelationshipCtxPtr ctx;
952
0
    int ret;
953
954
0
    xmlSecAssert2(transform != NULL, -1);
955
0
    xmlSecAssert2(nodes != NULL, -1);
956
0
    xmlSecAssert2(nodes->doc != NULL, -1);
957
0
    xmlSecAssert2(transformCtx != NULL, -1);
958
959
0
    ctx = xmlSecRelationshipGetCtx(transform);
960
0
    xmlSecAssert2(ctx != NULL, -1);
961
962
    /* check/update current transform status */
963
0
    switch(transform->status) {
964
0
    case xmlSecTransformStatusNone:
965
0
       transform->status = xmlSecTransformStatusWorking;
966
0
       break;
967
0
    case xmlSecTransformStatusWorking:
968
0
    case xmlSecTransformStatusFinished:
969
0
       return(0);
970
0
    default:
971
0
       xmlSecInvalidTransformStatusError(transform);
972
0
       return(-1);
973
0
    }
974
0
    xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
975
976
    /* prepare output buffer: next transform or ourselves */
977
0
    if(transform->next != NULL) {
978
0
       buf = xmlSecTransformCreateOutputBuffer(transform->next, transformCtx);
979
0
       if(buf == NULL) {
980
0
           xmlSecInternalError("xmlSecTransformCreateOutputBuffer", xmlSecTransformGetName(transform));
981
0
           transform->status = xmlSecTransformStatusFail;
982
0
           return(-1);
983
0
       }
984
0
    } else {
985
0
       buf = xmlSecBufferCreateOutputBuffer(&(transform->outBuf));
986
0
       if(buf == NULL) {
987
0
           xmlSecInternalError("xmlSecBufferCreateOutputBuffer", xmlSecTransformGetName(transform));
988
0
           transform->status = xmlSecTransformStatusFail;
989
0
           return(-1);
990
0
       }
991
0
    }
992
993
0
    ret = xmlSecTransformRelationshipExecute(transform, buf, nodes->doc, transformCtx);
994
0
    if(ret < 0) {
995
0
       xmlSecInternalError("xmlSecTransformRelationshipExecute", xmlSecTransformGetName(transform));
996
       /* the buffer must be closed to release it (the IO buffer is only
997
        * freed by its close callback). The close pushes the partial data
998
        * into the next transform, which is harmless here: the whole
999
        * operation is aborted and the produced data is discarded */
1000
0
       (void)xmlOutputBufferClose(buf);
1001
       /* discard any partial output produced before the failure so it
1002
        * cannot be picked up by a later operation and mark the transform as failed */
1003
0
       xmlSecBufferEmpty(&(transform->outBuf));
1004
0
       transform->status = xmlSecTransformStatusFail;
1005
0
       return(-1);
1006
0
    }
1007
1008
0
    ret = xmlOutputBufferClose(buf);
1009
0
    if(ret < 0) {
1010
0
       xmlSecXmlError("xmlOutputBufferClose", xmlSecTransformGetName(transform));
1011
0
       transform->status = xmlSecTransformStatusFail;
1012
0
       return(-1);
1013
0
    }
1014
0
    transform->status = xmlSecTransformStatusFinished;
1015
0
    return(0);
1016
0
}
1017
1018
static int
1019
0
xmlSecTransformRelationshipPopBin(xmlSecTransformPtr transform, xmlSecByte* data, xmlSecSize maxDataSize, xmlSecSize* dataSize, xmlSecTransformCtxPtr transformCtx) {
1020
    /*
1021
     * Intentionally unimplemented. The xmlsec1 sign/verify and encrypt/decrypt flows drive
1022
     * the transform chain in push mode only: a parser is inserted before this transform (to
1023
     * turn the input bytes into XML) and its PushXml serializes the result and writes it
1024
     * straight into the following transform, so the pull-style PopBin entry point is never
1025
     * reached. The popBin method pointer is kept set solely so that data-type negotiation
1026
     * (xmlSecTransformConnect) still reports a "Bin" output and inserts the required parser
1027
     * after this transform; if it is ever invoked we fail loudly rather than produce output.
1028
     */
1029
0
    (void)transform;
1030
0
    (void)data;
1031
0
    (void)maxDataSize;
1032
0
    (void)dataSize;
1033
0
    (void)transformCtx;
1034
    xmlSecNotImplementedError("xmlSecTransformRelationshipPopBin");
1035
0
    return(-1);
1036
0
}