From 7e46355a9a7a223077f4d83587fd842bbaf97e37 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 25 Jan 2016 09:50:03 +0100 Subject: [PATCH] OOXML Relationship Transform --- include/xmlsec/strings.h | 3 + include/xmlsec/transforms.h | 4 + src/strings.c | 3 + src/transforms.c | 11 + src/xpath.c | 480 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 501 insertions(+) diff --git a/include/xmlsec/strings.h b/include/xmlsec/strings.h index 07afb9d..9c72d1b 100644 --- a/include/xmlsec/strings.h +++ b/include/xmlsec/strings.h @@ -551,6 +551,9 @@ XMLSEC_EXPORT_VAR const xmlChar xmlSecXPath2FilterUnion[]; XMLSEC_EXPORT_VAR const xmlChar xmlSecNameXPointer[]; XMLSEC_EXPORT_VAR const xmlChar xmlSecNodeXPointer[]; +XMLSEC_EXPORT_VAR const xmlChar xmlSecNameRelationship[]; +XMLSEC_EXPORT_VAR const xmlChar xmlSecHrefRelationship[]; + /************************************************************************* * * Xslt strings diff --git a/include/xmlsec/transforms.h b/include/xmlsec/transforms.h index 4008cae..b0e31e4 100644 --- a/include/xmlsec/transforms.h +++ b/include/xmlsec/transforms.h @@ -961,6 +961,10 @@ XMLSEC_EXPORT int xmlSecTransformXPointerSetExpr (xmlSecTransformPtr transform const xmlChar* expr, xmlSecNodeSetType nodeSetType, xmlNodePtr hereNode); + +#define xmlSecTransformRelationshipId xmlSecTransformRelationshipGetKlass() +XMLSEC_EXPORT xmlSecTransformId xmlSecTransformRelationshipGetKlass (void); + #ifndef XMLSEC_NO_XSLT /** * xmlSecTransformXsltId: diff --git a/src/strings.c b/src/strings.c index 9897198..546e993 100644 --- a/src/strings.c +++ b/src/strings.c @@ -543,6 +543,9 @@ const xmlChar xmlSecXPath2FilterUnion[] = "union"; const xmlChar xmlSecNameXPointer[] = "xpointer"; const xmlChar xmlSecNodeXPointer[] = "XPointer"; +const xmlChar xmlSecNameRelationship[] = "relationship"; +const xmlChar xmlSecHrefRelationship[] = "http://schemas.openxmlformats.org/package/2006/RelationshipTransform"; + /************************************************************************* * * Xslt strings diff --git a/src/transforms.c b/src/transforms.c index 2ed3fe8..9e5ad27 100644 --- a/src/transforms.c +++ b/src/transforms.c @@ -271,6 +271,17 @@ xmlSecTransformIdsRegisterDefault(void) { return(-1); } + if (xmlSecTransformIdsRegister(xmlSecTransformRelationshipId) < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + "xmlSecTransformIdsRegister", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + "name=%s", + xmlSecErrorsSafeString(xmlSecTransformKlassGetName(xmlSecTransformRelationshipId))); + return -1; + } + #ifndef XMLSEC_NO_XSLT if(xmlSecTransformIdsRegister(xmlSecTransformXsltId) < 0) { xmlSecError(XMLSEC_ERRORS_HERE, diff --git a/src/xpath.c b/src/xpath.c index 8b0b4f8..e56920e 100644 --- a/src/xpath.c +++ b/src/xpath.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -1144,5 +1145,484 @@ xmlSecTransformVisa3DHackExecute(xmlSecTransformPtr transform, int last, return(0); } +/* OOXML Relationship Transform. */ +typedef struct _xmlSecRelationshipCtx xmlSecRelationshipCtx, *xmlSecRelationshipCtxPtr; +struct _xmlSecRelationshipCtx +{ + xmlSecPtrListPtr sourceIdList; +}; +#define xmlSecRelationshipSize (sizeof(xmlSecTransform) + sizeof(xmlSecRelationshipCtx)) +#define xmlSecRelationshipGetCtx(transform) ((xmlSecRelationshipCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform))) + +static int xmlSecRelationshipInitialize (xmlSecTransformPtr transform); +static void xmlSecRelationshipFinalize (xmlSecTransformPtr transform); +static int xmlSecTransformRelationshipPopBin(xmlSecTransformPtr transform, xmlSecByte* data, xmlSecSize maxDataSize, xmlSecSize* dataSize, xmlSecTransformCtxPtr transformCtx); +static int xmlSecTransformRelationshipPushXml(xmlSecTransformPtr transform, xmlSecNodeSetPtr nodes, xmlSecTransformCtxPtr transformCtx); +static int xmlSecRelationshipReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); + +static xmlSecTransformKlass xmlSecRelationshipKlass = +{ + /* klass/object sizes */ + sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */ + xmlSecRelationshipSize, /* xmlSecSize objSize */ + xmlSecNameRelationship, /* const xmlChar* name; */ + xmlSecHrefRelationship, /* const xmlChar* href; */ + xmlSecTransformUsageDSigTransform, /* xmlSecAlgorithmUsage usage; */ + xmlSecRelationshipInitialize, /* xmlSecTransformInitializeMethod initialize; */ + xmlSecRelationshipFinalize, /* xmlSecTransformFinalizeMethod finalize; */ + xmlSecRelationshipReadNode, /* xmlSecTransformNodeReadMethod readNode; */ + NULL, /* xmlSecTransformNodeWriteMethod writeNode; */ + NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */ + NULL, /* xmlSecTransformSetKeyMethod setKey; */ + NULL, /* xmlSecTransformValidateMethod validate; */ + xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */ + NULL, /* xmlSecTransformPushBinMethod pushBin; */ + xmlSecTransformRelationshipPopBin, /* xmlSecTransformPopBinMethod popBin; */ + xmlSecTransformRelationshipPushXml, /* xmlSecTransformPushXmlMethod pushXml; */ + NULL, /* xmlSecTransformPopXmlMethod popXml; */ + NULL, /* xmlSecTransformExecuteMethod execute; */ + NULL, /* void* reserved0; */ + NULL, /* void* reserved1; */ +}; + +xmlSecTransformId xmlSecTransformRelationshipGetKlass(void) +{ + return &xmlSecRelationshipKlass; +} + +static int xmlSecRelationshipInitialize(xmlSecTransformPtr transform) +{ + xmlSecRelationshipCtxPtr ctx; + int ret; + + xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId), -1); + xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize), -1); + + ctx = xmlSecRelationshipGetCtx(transform); + xmlSecAssert2(ctx != NULL, -1); + + /* initialize context */ + memset(ctx, 0, sizeof(xmlSecRelationshipCtx)); + + ctx->sourceIdList = xmlSecPtrListCreate(xmlSecStringListId); + if (!ctx->sourceIdList) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecPtrListCreate", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return -1; + } + + return 0; +} + +static void xmlSecRelationshipFinalize(xmlSecTransformPtr transform) +{ + xmlSecRelationshipCtxPtr ctx; + + xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId)); + xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize)); + + ctx = xmlSecRelationshipGetCtx(transform); + xmlSecAssert(ctx != NULL); + + if (ctx->sourceIdList) + xmlSecPtrListDestroy(ctx->sourceIdList); + + memset(ctx, 0, sizeof(xmlSecRelationshipCtx)); +} + +static int xmlSecRelationshipReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) +{ + xmlSecRelationshipCtxPtr ctx; + xmlNodePtr cur; + int ret; + + xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformRelationshipId), -1); + xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecRelationshipSize), -1); + xmlSecAssert2(node != NULL, -1); + xmlSecAssert2(transformCtx != NULL, -1); + ctx = xmlSecRelationshipGetCtx(transform); + xmlSecAssert2(ctx != NULL, -1); + + cur = node->children; + while (cur) + { + if (xmlSecCheckNodeName(cur, "RelationshipReference", "http://schemas.openxmlformats.org/package/2006/digital-signature")) + { + xmlChar* sourceId; + xmlChar* tmp; + + sourceId = xmlGetProp(cur, "SourceId"); + if (sourceId == NULL) + { + xmlSecError(XMLSEC_ERRORS_HERE, + NULL, + xmlSecErrorsSafeString("SourceId"), + XMLSEC_ERRORS_R_INVALID_NODE_ATTRIBUTE, + "node=%s", + xmlSecErrorsSafeString(xmlSecNodeGetName(node))); + return -1; + } + + tmp = xmlStrdup(sourceId); + if (!tmp) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + NULL, + XMLSEC_ERRORS_R_STRDUP_FAILED, + "len=%d", xmlStrlen(sourceId)); + return -1; + } + + ret = xmlSecPtrListAdd(ctx->sourceIdList, tmp); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecPtrListAdd", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlFree(tmp); + return -1; + } + + } + + cur = cur->next; + } + + return 0; +} + +int xmlSecTransformRelationshipProcessElementNode(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur) +{ + xmlAttrPtr attr; + int foundTargetMode = 0; + + xmlOutputBufferWriteString(buf, "<"); + xmlOutputBufferWriteString(buf, (const char *)cur->name); + + if (cur->nsDef) + { + xmlOutputBufferWriteString(buf, " xmlns=\""); + if (cur->nsDef->href) + xmlOutputBufferWriteString(buf, cur->nsDef->href); + xmlOutputBufferWriteString(buf, "\""); + } + + for (attr = cur->properties; attr; attr = attr->next) + { + xmlOutputBufferWriteString(buf, " "); + xmlOutputBufferWriteString(buf, (const char *)attr->name); + if (strcmp(attr->name, "TargetMode") == 0) + foundTargetMode = 1; + xmlOutputBufferWriteString(buf, "=\""); + xmlOutputBufferWriteString(buf, (const char *)xmlGetProp(cur, attr->name)); + xmlOutputBufferWriteString(buf, "\""); + } + if (strcmp(cur->name, "Relationship") == 0 && !foundTargetMode) + xmlOutputBufferWriteString(buf, " TargetMode=\"Internal\""); + xmlOutputBufferWriteString(buf, ">"); + + if (cur->children) + { + int ret = xmlSecTransformRelationshipProcessNodeList(transform, buf, cur->children); + if (ret < 0) + return -1; + } + + xmlOutputBufferWriteString(buf, "name); + xmlOutputBufferWriteString(buf, ">"); + + return 0; +} + +int xmlSecTransformRelationshipProcessNode(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur) +{ + int ret; + + switch (cur->type) + { + case XML_ELEMENT_NODE: + if (xmlSecCheckNodeName(cur, "Relationship", "http://schemas.openxmlformats.org/package/2006/relationships")) + { + xmlChar* id = xmlGetProp(cur, "Id"); + if (id) + { + int i; + int found = 0; + xmlSecRelationshipCtxPtr ctx; + + ctx = xmlSecRelationshipGetCtx(transform); + for (i = 0; i < xmlSecPtrListGetSize(ctx->sourceIdList); ++i) + { + if (strcmp(xmlSecPtrListGetItem(ctx->sourceIdList, i), id) == 0) + { + found = 1; + break; + } + } + + if (!found) + return 0; + } + } + + ret = xmlSecTransformRelationshipProcessElementNode(transform, buf, cur); + if (ret < 0) + return -1; + break; + } + + return 0; +} + +int xmlSecTransformRelationshipProcessNodeList(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlNodePtr cur) +{ + int ret; + + for (ret = 0; cur; cur = cur->next) + { + ret = xmlSecTransformRelationshipProcessNode(transform, buf, cur); + if (ret < 0) + return -1; + } + + return 0; +} + +int xmlSecTransformRelationshipExecute(xmlSecTransformPtr transform, xmlOutputBufferPtr buf, xmlDocPtr doc) +{ + if (doc->children != NULL) + { + int ret = xmlSecTransformRelationshipProcessNodeList(transform, buf, doc->children); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecTransformRelationshipProcessNodeList", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return -1; + } + } + + return 0; +} + +static int xmlSecTransformRelationshipPushXml(xmlSecTransformPtr transform, xmlSecNodeSetPtr nodes, xmlSecTransformCtxPtr transformCtx) +{ + xmlOutputBufferPtr buf; + xmlSecRelationshipCtxPtr ctx; + int ret; + + xmlSecAssert2(nodes != NULL, -1); + xmlSecAssert2(nodes->doc != NULL, -1); + xmlSecAssert2(transformCtx != NULL, -1); + + ctx = xmlSecRelationshipGetCtx(transform); + xmlSecAssert2(ctx != NULL, -1); + + /* check/update current transform status */ + switch(transform->status) + { + case xmlSecTransformStatusNone: + transform->status = xmlSecTransformStatusWorking; + break; + case xmlSecTransformStatusWorking: + case xmlSecTransformStatusFinished: + return(0); + default: + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + NULL, + XMLSEC_ERRORS_R_INVALID_STATUS, + "status=%d", transform->status); + return(-1); + } + xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1); + + /* prepare output buffer: next transform or ourselves */ + if(transform->next != NULL) + { + buf = xmlSecTransformCreateOutputBuffer(transform->next, transformCtx); + if(buf == NULL) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecTransformCreateOutputBuffer", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + } else + { + buf = xmlSecBufferCreateOutputBuffer(&(transform->outBuf)); + if (buf == NULL) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecBufferCreateOutputBuffer", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + } + + ret = xmlSecTransformRelationshipExecute(transform, buf, nodes->doc); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlC14NExecute", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlOutputBufferClose(buf); + return(-1); + } + + ret = xmlOutputBufferClose(buf); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlOutputBufferClose", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + transform->status = xmlSecTransformStatusFinished; + return(0); +} + +static int xmlSecTransformRelationshipPopBin(xmlSecTransformPtr transform, xmlSecByte* data, xmlSecSize maxDataSize, xmlSecSize* dataSize, xmlSecTransformCtxPtr transformCtx) +{ + xmlSecBufferPtr out; + int ret; + + xmlSecAssert2(data != NULL, -1); + xmlSecAssert2(dataSize != NULL, -1); + xmlSecAssert2(transformCtx != NULL, -1); + + out = &(transform->outBuf); + if (transform->status == xmlSecTransformStatusNone) + { + xmlOutputBufferPtr buf; + + xmlSecAssert2(transform->inNodes == NULL, -1); + + /* todo: isn't it an error? */ + if (transform->prev == NULL) + { + (*dataSize) = 0; + transform->status = xmlSecTransformStatusFinished; + return(0); + } + + /* get xml data from previous transform */ + ret = xmlSecTransformPopXml(transform->prev, &(transform->inNodes), transformCtx); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecTransformPopXml", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + + /* dump everything to internal buffer */ + buf = xmlSecBufferCreateOutputBuffer(out); + if (buf == NULL) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecBufferCreateOutputBuffer", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + + ret = xmlC14NExecute(transform->inNodes->doc, (xmlC14NIsVisibleCallback)xmlSecNodeSetContains, transform->inNodes, XML_C14N_1_0, NULL, 0, buf); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecTransformC14NExecute", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + xmlOutputBufferClose(buf); + return(-1); + } + ret = xmlOutputBufferClose(buf); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlOutputBufferClose", + XMLSEC_ERRORS_R_XML_FAILED, + XMLSEC_ERRORS_NO_MESSAGE); + return(-1); + } + transform->status = xmlSecTransformStatusWorking; + } + + if (transform->status == xmlSecTransformStatusWorking) + { + xmlSecSize outSize; + + /* return chunk after chunk */ + outSize = xmlSecBufferGetSize(out); + if (outSize > maxDataSize) + { + outSize = maxDataSize; + } + if (outSize > XMLSEC_TRANSFORM_BINARY_CHUNK) + { + outSize = XMLSEC_TRANSFORM_BINARY_CHUNK; + } + if (outSize > 0) + { + xmlSecAssert2(xmlSecBufferGetData(&(transform->outBuf)), -1); + + memcpy(data, xmlSecBufferGetData(&(transform->outBuf)), outSize); + ret = xmlSecBufferRemoveHead(&(transform->outBuf), outSize); + if (ret < 0) + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + "xmlSecBufferRemoveHead", + XMLSEC_ERRORS_R_XMLSEC_FAILED, + "size=%d", outSize); + return(-1); + } + } + else if (xmlSecBufferGetSize(out) == 0) + transform->status = xmlSecTransformStatusFinished; + (*dataSize) = outSize; + } + else if (transform->status == xmlSecTransformStatusFinished) + { + /* the only way we can get here is if there is no output */ + xmlSecAssert2(xmlSecBufferGetSize(out) == 0, -1); + (*dataSize) = 0; + } + else + { + xmlSecError(XMLSEC_ERRORS_HERE, + xmlSecErrorsSafeString(xmlSecTransformGetName(transform)), + NULL, + XMLSEC_ERRORS_R_INVALID_STATUS, + "status=%d", transform->status); + return(-1); + } + + return(0); +} -- 2.6.2