/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/nodeset.c
618 строк
19 KB
Javid Khan
free node set when xmlSecNodeSetCreate fails in xmlSecNodeSetGetChildren (#1188)
29 май 2026, 17:03
Не верифицирован
29 май 2026, 17:03
2014dfd
Код
Авторство
О чём код?
/** * XML Security Library (http://www.aleksey.com/xmlsec). * * This is free software; see the Copyright file in the source distribution for precise wording. * * Copyright (C) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. */ /** * @addtogroup xmlsec_core_nodeset * @brief XML nodes set functions */ #include "globals.h" #include <stdlib.h> #include <string.h> #include <libxml/tree.h> #include <libxml/xpath.h> #include <libxml/xpathInternals.h> #include <xmlsec/xmlsec.h> #include <xmlsec/nodeset.h> #include <xmlsec/xmltree.h> #include <xmlsec/errors.h> #include <xmlsec/private.h> #include "cast_helpers.h" #define xmlSecGetParent(node) \ (((node)->type != XML_NAMESPACE_DECL) ? \ (node)->parent : \ (xmlNodePtr)((xmlNsPtr)(node))->next) static int xmlSecNodeSetContainsNode (xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent); static int xmlSecNodeSetWalkRecursive (xmlSecNodeSetPtr nset, xmlNodePtr startNode, xmlSecNodeSetWalkCallback walkFunc, void* data); /** * @brief Creates a new nodes set. * @details Creates new nodes set. Caller is responsible for freeing returned object * by calling #xmlSecNodeSetDestroy function. * @param doc the pointer to parent XML document. * @param nodes the list of nodes. * @param type the nodes set type. * @return pointer to newly allocated node set or NULL if an error occurs. */ xmlSecNodeSetPtr xmlSecNodeSetCreate(xmlDocPtr doc, xmlNodeSetPtr nodes, xmlSecNodeSetType type) { xmlSecNodeSetPtr nset; nset = (xmlSecNodeSetPtr)xmlMalloc(sizeof(xmlSecNodeSet)); if(nset == NULL) { xmlSecMallocError(sizeof(xmlSecNodeSet), NULL); return(NULL); } memset(nset, 0, sizeof(xmlSecNodeSet)); nset->doc = doc; nset->nodes = nodes; nset->type = type; nset->next = nset->prev = nset; return(nset); } /** * @brief Destroys a nodes set. * @details Destroys the nodes set created with #xmlSecNodeSetCreate function. * @param nset the pointer to node set. */ void xmlSecNodeSetDestroy(xmlSecNodeSetPtr nset) { xmlSecNodeSetPtr tmp; xmlDocPtr destroyDoc = NULL; xmlSecAssert(nset != NULL); while((tmp = nset) != NULL) { if((nset->next != NULL) && (nset->next != nset)) { nset->next->prev = nset->prev; nset->prev->next = nset->next; nset = nset->next; } else { nset = NULL; } if(tmp->nodes != NULL) { xmlXPathFreeNodeSet(tmp->nodes); } if((tmp->doc != NULL) && (tmp->destroyDoc != 0)) { /* all nodesets should belong to the same doc */ xmlSecAssert((destroyDoc == NULL) || (tmp->doc == destroyDoc)); destroyDoc = tmp->doc; /* can't destroy here because other node sets can refer to it */ } memset(tmp, 0, sizeof(xmlSecNodeSet)); xmlFree(tmp); } /* finally, destroy the doc if needed */ if(destroyDoc != NULL) { xmlFreeDoc(destroyDoc); } } /** * @brief Marks node set to destroy the parent document. * @details Instructs node set to destroy nodes parent doc when node set is destroyed. * @param nset the pointer to node set. */ void xmlSecNodeSetDocDestroy(xmlSecNodeSetPtr nset) { xmlSecAssert(nset != NULL); nset->destroyDoc = 1; } /* checks node against LibXML2 nodeset */ static int xmlSecNodeSetCheckNode(xmlNodeSetPtr nodes, xmlNodePtr node, xmlNodePtr parent) { xmlSecAssert2(node != NULL, 0); /* assume whole tree is included if nodes is NULL */ if(nodes == NULL) { return(1); } if(node->type != XML_NAMESPACE_DECL) { return(xmlXPathNodeSetContains(nodes, node)); } else { xmlNs ns; memcpy(&ns, node, sizeof(ns)); /* this is a libxml hack! check xpath.c for details */ if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) { ns.next = (xmlNsPtr)parent->parent; } else { ns.next = (xmlNsPtr)parent; } /** If the input is an XPath node-set, then the node-set must explicitly * contain every node to be rendered to the canonical form. */ return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns)); } } /* checks node or parents against LibXML2 nodeset */ static int xmlSecNodeSetCheckNodeOrParent(xmlNodeSetPtr nodes, xmlNodePtr node, xmlNodePtr parent) { xmlSecAssert2(node != NULL, 0); /* assume whole tree is included if nodes is NULL */ if(nodes == NULL) { return(1); } do { if(xmlSecNodeSetCheckNode(nodes, node, parent)) { return(1); } /* traverse up the tree, only element nodes can have children */ if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) { node = parent; parent = parent->parent; } else { node = NULL; parent = NULL; } } while(node != NULL); /* done */ return(0); } /* checks node against THIS nodeset only */ static int xmlSecNodeSetContainsNode(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) { xmlSecAssert2(nset != NULL, 0); xmlSecAssert2(node != NULL, 0); switch(nset->type) { case xmlSecNodeSetNormal: /* simple case */ return(xmlSecNodeSetCheckNode(nset->nodes, node, parent)); case xmlSecNodeSetInvert: /* simple case: return inverted result */ return(!xmlSecNodeSetCheckNode(nset->nodes, node, parent)); case xmlSecNodeSetTree: /* just traverse up the tree to see if any parents are in the nodeset */ return(xmlSecNodeSetCheckNodeOrParent(nset->nodes, node, parent)); case xmlSecNodeSetTreeWithoutComments: /* drop comments */ if(node->type == XML_COMMENT_NODE) { return(0); } /* just traverse up the tree to see if any parents are in the nodeset */ return(xmlSecNodeSetCheckNodeOrParent(nset->nodes, node, parent)); case xmlSecNodeSetTreeInvert: /* just traverse up the tree to see if any parents are in the nodeset and invert result */ return(!xmlSecNodeSetCheckNodeOrParent(nset->nodes, node, parent)); case xmlSecNodeSetTreeWithoutCommentsInvert: /* drop comments */ if(node->type == XML_COMMENT_NODE) { return(0); } /* just traverse up the tree to see if any parents are in the nodeset and invert result */ return(!xmlSecNodeSetCheckNodeOrParent(nset->nodes, node, parent)); default: xmlSecUnsupportedEnumValueError("node set type", nset->type, NULL); return(0); } } /** * @brief Checks if a node is in the nodes set. * @details Checks whether the @p node is in the nodes set or not. * @param nset the pointer to node set. * @param node the pointer to XML node to check. * @param parent the pointer to @p node parent node. * @return 1 if the @p node is in the nodes set @p nset, 0 if it is not * and a negative value if an error occurs. */ int xmlSecNodeSetContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) { int status = 1; xmlSecNodeSetPtr curNset; xmlSecAssert2(node != NULL, 0); /* special cases: */ if(nset == NULL) { return(1); } /* iterate through the nodesets list */ status = 1; curNset = nset; do { switch(curNset->op) { case xmlSecNodeSetIntersection: if(status && !xmlSecNodeSetContainsNode(curNset, node, parent)) { status = 0; } break; case xmlSecNodeSetSubtraction: if(status && xmlSecNodeSetContainsNode(curNset, node, parent)) { status = 0; } break; case xmlSecNodeSetUnion: if(!status && xmlSecNodeSetContainsNode(curNset, node, parent)) { status = 1; } break; default: xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_OPERATION, NULL, "node set operation=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(curNset->op)); return(-1); } curNset = curNset->next; } while(curNset != nset); /* done */ return(status); } /** * @brief Adds a nodes set to another with an operation. * @details Adds @p newNSet to the @p nset using operation @p op. * @param nset the pointer to current nodes set (or NULL). * @param newNSet the pointer to new nodes set. * @param op the operation type. * @return the pointer to combined nodes set or NULL if an error * occurs. */ xmlSecNodeSetPtr xmlSecNodeSetAdd(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet, xmlSecNodeSetOp op) { xmlSecAssert2(newNSet != NULL, NULL); xmlSecAssert2(newNSet->next == newNSet, NULL); newNSet->op = op; if(nset == NULL) { return(newNSet); } /* all nodesets should belong to the same doc */ xmlSecAssert2(nset->doc == newNSet->doc, NULL); newNSet->next = nset; newNSet->prev = nset->prev; nset->prev->next = newNSet; nset->prev = newNSet; return(nset); } /** * @brief DEPRECATED: Adds a nodes set as a child list. * @details Adds @p newNSet to the @p nset as child using operation @p op. * @param nset the pointer to current nodes set (or NULL). * @param newNSet the pointer to new nodes set. * @param op the operation type. * @return the pointer to combined nodes set or NULL if an error * occurs. */ xmlSecNodeSetPtr xmlSecNodeSetAddList(xmlSecNodeSetPtr nset XMLSEC_ATTRIBUTE_UNUSED, xmlSecNodeSetPtr newNSet XMLSEC_ATTRIBUTE_UNUSED, xmlSecNodeSetOp op XMLSEC_ATTRIBUTE_UNUSED ) { UNREFERENCED_PARAMETER(nset); UNREFERENCED_PARAMETER(newNSet); UNREFERENCED_PARAMETER(op); xmlSecNotImplementedError("xmlSecNodeSetAddList is deprecated"); return(NULL); } /** * @brief Walks all nodes in a set calling a callback function. * @details Calls the function @p walkFunc once per each node in the nodes set @p nset. * If the @p walkFunc returns a negative value, then the walk procedure * is interrupted. * @param nset the pointer to node set. * @param walkFunc the callback functions. * @param data the application specific data passed to the @p walkFunc. * @return 0 on success or a negative value if an error occurs. */ int xmlSecNodeSetWalk(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc, void* data) { xmlNodePtr cur; int ret = 0; xmlSecAssert2(nset != NULL, -1); xmlSecAssert2(nset->doc != NULL, -1); xmlSecAssert2(walkFunc != NULL, -1); /* special cases */ if(nset->nodes != NULL) { int ii; switch(nset->type) { case xmlSecNodeSetNormal: case xmlSecNodeSetTree: case xmlSecNodeSetTreeWithoutComments: for(ii = 0; (ret >= 0) && (ii < nset->nodes->nodeNr); ++ii) { ret = xmlSecNodeSetWalkRecursive(nset, nset->nodes->nodeTab[ii], walkFunc, data); if(ret < 0) { xmlSecInternalError("xmlSecNodeSetWalkRecursive", NULL); return(ret); } } return(ret); default: break; } } for(cur = nset->doc->children; (cur != NULL) && (ret >= 0); cur = cur->next) { ret = xmlSecNodeSetWalkRecursive(nset, cur, walkFunc, data); if(ret < 0) { xmlSecInternalError("xmlSecNodeSetWalkRecursive", NULL); return(ret); } } return(ret); } typedef struct { xmlSecNodeSetPtr nset; xmlSecNodeSetWalkCallback walkFunc; void* data; } xmlSecNodeSetWalkCtx; static int xmlSecNodeSetWalkRecursiveCallback(xmlNodePtr cur, void* data) { xmlSecNodeSetWalkCtx* ctx = (xmlSecNodeSetWalkCtx*)data; xmlNodePtr parent = xmlSecGetParent(cur); int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->nset != NULL, -1); xmlSecAssert2(ctx->walkFunc != NULL, -1); xmlSecAssert2(cur != NULL, -1); /* the node itself */ if(xmlSecNodeSetContains(ctx->nset, cur, parent)) { ret = ctx->walkFunc(ctx->nset, cur, parent, ctx->data); if(ret < 0) { return(-1); } } /* element and document nodes have children */ if((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_DOCUMENT_NODE)) { xmlAttrPtr attr; xmlNodePtr node; xmlNsPtr ns, tmp; attr = (xmlAttrPtr)cur->properties; while(attr != NULL) { if(xmlSecNodeSetContains(ctx->nset, (xmlNodePtr)attr, cur)) { ret = ctx->walkFunc(ctx->nset, (xmlNodePtr)attr, cur, ctx->data); if(ret < 0) { return(-1); } } attr = attr->next; } node = cur; while(node != NULL) { ns = node->nsDef; while(ns != NULL) { tmp = xmlSearchNs(ctx->nset->doc, cur, ns->prefix); if((tmp == ns) && xmlSecNodeSetContains(ctx->nset, (xmlNodePtr)ns, cur)) { ret = ctx->walkFunc(ctx->nset, (xmlNodePtr)ns, cur, ctx->data); if(ret < 0) { return(-1); } } ns = ns->next; } node = node->parent; } } /* done and continue the walk */ return(1); } static int xmlSecNodeSetWalkRecursive(xmlSecNodeSetPtr nset, xmlNodePtr startNode, xmlSecNodeSetWalkCallback walkFunc, void* data) { xmlSecNodeSetWalkCtx ctx; int ret; xmlSecAssert2(nset != NULL, -1); xmlSecAssert2(startNode != NULL, -1); xmlSecAssert2(walkFunc != NULL, -1); ctx.nset = nset; ctx.walkFunc = walkFunc; ctx.data = data; ret = xmlSecDepthFirstTreeWalk(startNode, xmlSecNodeSetWalkRecursiveCallback, &ctx); if(ret < 0) { xmlSecInternalError("xmlSecDepthFirstTreeWalk", NULL); return(-1); } return(0); } /** * @brief Creates a nodes set from parent subtree children. * @details Creates a new nodes set that contains: * - if @p withComments is not 0 and @p invert is 0: * all nodes in the @p parent subtree; * - if @p withComments is 0 and @p invert is 0: * all nodes in the @p parent subtree except comment nodes; * - if @p withComments is not 0 and @p invert not is 0: * all nodes in the @p doc except nodes in the @p parent subtree; * - if @p withComments is 0 and @p invert is 0: * all nodes in the @p doc except nodes in the @p parent subtree * and comment nodes. * @param doc the pointer to an XML document. * @param parent the pointer to parent XML node or NULL if we want to include all document nodes. * @param withComments the flag include comments or not. * @param invert the "invert" flag. * @return pointer to the newly created xmlSecNodeSet structure * or NULL if an error occurs. */ xmlSecNodeSetPtr xmlSecNodeSetGetChildren(xmlDocPtr doc, const xmlNodePtr parent, int withComments, int invert) { xmlSecNodeSetPtr nset; xmlNodeSetPtr nodes; xmlSecNodeSetType type; xmlSecAssert2(doc != NULL, NULL); nodes = xmlXPathNodeSetCreate(parent); if(nodes == NULL) { xmlSecXmlError("xmlXPathNodeSetCreate", NULL); return(NULL); } /* if parent is NULL then we add all the doc children */ if(parent == NULL) { xmlNodePtr cur; for(cur = doc->children; cur != NULL; cur = cur->next) { if(withComments || (cur->type != XML_COMMENT_NODE)) { xmlXPathNodeSetAdd(nodes, cur); } } } if(withComments && invert) { type = xmlSecNodeSetTreeInvert; } else if(withComments && !invert) { type = xmlSecNodeSetTree; } else if(!withComments && invert) { type = xmlSecNodeSetTreeWithoutCommentsInvert; } else { /* if(!withComments && !invert) */ type = xmlSecNodeSetTreeWithoutComments; } nset = xmlSecNodeSetCreate(doc, nodes, type); if(nset == NULL) { xmlSecInternalError("xmlSecNodeSetCreate", NULL); xmlXPathFreeNodeSet(nodes); return(NULL); } return(nset); } static int xmlSecNodeSetDumpTextNodesWalkCallback(xmlSecNodeSetPtr nset, xmlNodePtr cur, xmlNodePtr parent XMLSEC_ATTRIBUTE_UNUSED, void* data) { int ret; xmlSecAssert2(nset != NULL, -1); xmlSecAssert2(cur != NULL, -1); xmlSecAssert2(data != NULL, -1); UNREFERENCED_PARAMETER(parent); if(cur->type != XML_TEXT_NODE) { return(0); } ret = xmlOutputBufferWriteString((xmlOutputBufferPtr)data, (char*)(cur->content)); if(ret < 0) { xmlSecXmlError("xmlOutputBufferWriteString", NULL); return(-1); } return(0); } /** * @brief Dumps text node content from a nodes set. * @details Dumps content of all the text nodes from @p nset to @p out. * @param nset the pointer to node set. * @param out the output buffer. * @return 0 on success or a negative value otherwise. */ int xmlSecNodeSetDumpTextNodes(xmlSecNodeSetPtr nset, xmlOutputBufferPtr out) { xmlSecAssert2(nset != NULL, -1); xmlSecAssert2(out != NULL, -1); return(xmlSecNodeSetWalk(nset, xmlSecNodeSetDumpTextNodesWalkCallback, out)); } /** * @brief Prints information about @p nset to the @p output. * @param nset the pointer to node set. * @param output the pointer to output FILE. */ void xmlSecNodeSetDebugDump(xmlSecNodeSetPtr nset, FILE *output) { int ii, len; xmlNodePtr cur; xmlSecAssert(nset != NULL); xmlSecAssert(output != NULL); fprintf(output, "== Nodes set "); switch(nset->type) { case xmlSecNodeSetNormal: fprintf(output, "(xmlSecNodeSetNormal)\n"); break; case xmlSecNodeSetInvert: fprintf(output, "(xmlSecNodeSetInvert)\n"); break; case xmlSecNodeSetTree: fprintf(output, "(xmlSecNodeSetTree)\n"); break; case xmlSecNodeSetTreeWithoutComments: fprintf(output, "(xmlSecNodeSetTreeWithoutComments)\n"); break; case xmlSecNodeSetTreeInvert: fprintf(output, "(xmlSecNodeSetTreeInvert)\n"); break; case xmlSecNodeSetTreeWithoutCommentsInvert: fprintf(output, "(xmlSecNodeSetTreeWithoutCommentsInvert)\n"); break; case xmlSecNodeSetList: xmlSecNotImplementedError("xmlSecNodeSetList is deprecated"); fprintf(output, "(xmlSecNodeSetList)\n"); break; } len = xmlXPathNodeSetGetLength(nset->nodes); for(ii = 0; ii < len; ++ii) { cur = xmlXPathNodeSetItem(nset->nodes, ii); xmlSecAssert(cur != NULL); if(cur->type != XML_NAMESPACE_DECL) { fprintf(output, XMLSEC_ENUM_FMT ": %s\n", XMLSEC_ENUM_CAST(cur->type), (cur->name) ? cur->name : BAD_CAST "null"); } else { xmlNsPtr ns = (xmlNsPtr)cur; fprintf(output, XMLSEC_ENUM_FMT ": %s=%s (%s:%s)\n", XMLSEC_ENUM_CAST(cur->type), (ns->prefix) ? ns->prefix : BAD_CAST "null", (ns->href) ? ns->href : BAD_CAST "null", (((xmlNodePtr)ns->next)->ns && ((xmlNodePtr)ns->next)->ns->prefix) ? ((xmlNodePtr)ns->next)->ns->prefix : BAD_CAST "null", ((xmlNodePtr)ns->next)->name); } } }