/**
* XML Security Library
*
* The Transforms Element (http://www.w3.org/TR/xmldsig-core/#sec-Transforms)
*
* The optional Transforms element contains an ordered list of Transform
* elements; these describe how the signer obtained the data object that
* was digested.
*
* Schema Definition:
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* DTD:
*
*
*
*
*
*
* See Copyright for the status of this software.
*
* Author: Aleksey Sanin
*/
#include "globals.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define XMLSEC_TRANSFORM_BUFFER_SIZE 1024
static xmlSecTransformId xmlSecAllTransforms[100];
/* internal functions */
static int xmlSecTransformStateParseUri(xmlSecTransformStatePtr state, const char *uri);
static void xmlSecTransformStateDestroyCurrentDoc(xmlSecTransformStatePtr state);
static int xmlSecTransformCreateXml(xmlSecTransformStatePtr state);
static int xmlSecTransformCreateBin(xmlSecTransformStatePtr state);
static int xmlSecTransformCreateBinFromXml(xmlSecTransformStatePtr state);
static int xmlSecTransformCreateBinFromUri(xmlSecTransformStatePtr state);
static int xmlSecTransformPreBase64Decode(const xmlNodePtr node, xmlSecNodeSetPtr nodeSet,
xmlOutputBufferPtr output);
/**********************************************************************
*
* Hi-level functions
*
*********************************************************************/
/**
* xmlSecTransformsInit:
*
* Trnasforms engine initialization (called from xmlSecInit() function).
* The applications should not call this function directly.
*/
void xmlSecTransformsInit(void) {
int i = 0;
/* encoding */
xmlSecAllTransforms[i++] = xmlSecEncBase64Encode;
xmlSecAllTransforms[i++] = xmlSecEncBase64Decode;
/* digest methods */
#ifndef XMLSEC_NO_SHA1
xmlSecAllTransforms[i++] = xmlSecDigestSha1;
#endif /* XMLSEC_NO_SHA1 */
#ifndef XMLSEC_NO_RIPEMD160
xmlSecAllTransforms[i++] = xmlSecDigestRipemd160;
#endif /* XMLSEC_NO_RIPEMD160 */
/* MAC */
#ifndef XMLSEC_NO_HMAC
xmlSecAllTransforms[i++] = xmlSecMacHmacSha1;
xmlSecAllTransforms[i++] = xmlSecMacHmacRipeMd160;
xmlSecAllTransforms[i++] = xmlSecMacHmacMd5;
#endif /* XMLSEC_NO_HMAC */
/* signature */
#ifndef XMLSEC_NO_DSA
xmlSecAllTransforms[i++] = xmlSecSignDsaSha1;
#endif /* XMLSEC_NO_DSA */
#ifndef XMLSEC_NO_RSA
xmlSecAllTransforms[i++] = xmlSecSignRsaSha1;
#endif /* XMLSEC_NO_RSA */
/* c14n methods */
xmlSecAllTransforms[i++] = xmlSecC14NInclusive;
xmlSecAllTransforms[i++] = xmlSecC14NInclusiveWithComments;
xmlSecAllTransforms[i++] = xmlSecC14NExclusive;
xmlSecAllTransforms[i++] = xmlSecC14NExclusiveWithComments;
/* XML transforms */
xmlSecAllTransforms[i++] = xmlSecTransformEnveloped;
xmlSecAllTransforms[i++] = xmlSecTransformXPath;
xmlSecAllTransforms[i++] = xmlSecTransformXPath2;
xmlSecAllTransforms[i++] = xmlSecTransformXPointer;
#ifndef XMLSEC_NO_XSLT
xmlSecAllTransforms[i++] = xmlSecTransformXslt;
#endif /* XMLSEC_NO_XSLT */
/* encryption */
#ifndef XMLSEC_NO_DES
xmlSecAllTransforms[i++] = xmlSecEncDes3Cbc;
#endif /* XMLSEC_NO_DES */
#ifndef XMLSEC_NO_AES
xmlSecAllTransforms[i++] = xmlSecEncAes128Cbc;
xmlSecAllTransforms[i++] = xmlSecEncAes192Cbc;
xmlSecAllTransforms[i++] = xmlSecEncAes256Cbc;
#endif /* XMLSEC_NO_AES */
/* Key Transports */
#ifndef XMLSEC_NO_RSA
xmlSecAllTransforms[i++] = xmlSecEncRsaPkcs1;
xmlSecAllTransforms[i++] = xmlSecEncRsaOaep;
#endif /* XMLSEC_NO_RSA */
/* key wrappers */
#ifndef XMLSEC_NO_AES
xmlSecAllTransforms[i++] = xmlSecKWDes3Cbc;
xmlSecAllTransforms[i++] = xmlSecKWAes128;
xmlSecAllTransforms[i++] = xmlSecKWAes192;
xmlSecAllTransforms[i++] = xmlSecKWAes256;
#endif /* XMLSEC_NO_DES */
/* Input/memory buffer */
xmlSecAllTransforms[i++] = xmlSecInputUri;
xmlSecAllTransforms[i++] = xmlSecMemBuf;
/* MUST be the last in the list */
xmlSecAllTransforms[i++] = xmlSecTransformUnknown;
}
/**
* xmlSecTransformsNodeRead:
* @state: the pointer to current transform state.
* @transformsNode: the pointer to the node.
*
* Reads the transform node and updates @state,
*
* Returns 0 on success or a negative value otherwise.
*/
int
xmlSecTransformsNodeRead(xmlSecTransformStatePtr state,
xmlNodePtr transformsNode) {
xmlNodePtr cur;
xmlSecTransformPtr transform;
int ret;
xmlSecAssert2(state != NULL, -1);
xmlSecAssert2(transformsNode != NULL, -1);
cur = xmlSecGetNextElementNode(transformsNode->children);
while((cur != NULL) && xmlSecCheckNodeName(cur, BAD_CAST "Transform", xmlSecDSigNs)) {
transform = xmlSecTransformNodeRead(cur, xmlSecUsageDSigTransform, 0);
if(transform == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformNodeRead");
return(-1);
}
ret = xmlSecTransformStateUpdate(state, transform);
if(ret < 0){
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformStateUpdate");
xmlSecTransformDestroy(transform, 1);
return(-1);
}
cur = xmlSecGetNextElementNode(cur->next);
}
if(cur != NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_NODE,
(cur->name != NULL) ? (char*)cur->name : "NULL");
return(-1);
}
return(0);
}
/**
* xmlSecTransformNodeRead:
* @transformNode: the pointer to node.
* @usage: the usage of the transfomr (signature, encryption, etc.).
* @dontDestroy: the flag whether we need to destroy the transform.
*
* Reads transform from the @transformNode as follows:
* 1) reads "Algorithm" attribute;
* 2) checks the list of known algorithms;
* 3) calls transform create method;
* 4) calls transform read transform node method.
*
* Returns the pointer to newly allocated #xmlSecTransform structure
* or NULL if an error occurs.
*/
xmlSecTransformPtr
xmlSecTransformNodeRead(xmlNodePtr transformNode, xmlSecTransformUsage usage,
int dontDestroy) {
xmlChar *href;
xmlSecTransformId id;
xmlSecTransformPtr transform;
int ret;
xmlSecAssert2(transformNode != NULL, NULL);
href = xmlGetProp(transformNode, BAD_CAST "Algorithm");
if(href == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_NODE_ATTRIBUTE,
"Algorithm");
return(NULL);
}
id = xmlSecTransformFind(href);
if(id == xmlSecTransformUnknown) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformFind(href=\"%s\")", href);
xmlFree(href);
return(NULL);
}
transform = xmlSecTransformCreate(id, usage, dontDestroy);
if(!xmlSecTransformIsValid(transform)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreate(href=\"%s\")", href);
xmlFree(href);
return(NULL);
}
ret = xmlSecTransformRead(transform, transformNode);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformRead - %d", ret);
xmlSecTransformDestroy(transform, 1);
xmlFree(href);
return(NULL);
}
xmlFree(href);
return(transform);
}
/**
* xmlSecTransformNodeWrite:
* @transformNode: the pointer to node.
* @id: the transform id.
*
* Writes Agorithm attribute in the transform node.
*
* Returns 0 on success or a negative value otherwise.
*/
int
xmlSecTransformNodeWrite(xmlNodePtr transformNode, xmlSecTransformId id) {
xmlSecAssert2(transformNode != NULL, -1);
xmlSecAssert2(id != NULL, -1);
if(xmlSetProp(transformNode, BAD_CAST "Algorithm", id->href) == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XML_FAILED,
"xmlSetProp(Algorithm)");
return(-1);
}
return(0);
}
/**************************************************************************
*
* Transform Info
*
**************************************************************************/
/**
* xmlSecTransformFind:
* @href: the transform href.
*
* Searches the list of known transforms for transform with given href
*
* Returns the id of the found transform or NULL if an error occurs
* or transform is not found.
*/
xmlSecTransformId
xmlSecTransformFind(const xmlChar* href) {
xmlSecTransformId *ptr;
xmlSecAssert2(href != NULL, NULL);
ptr = xmlSecAllTransforms;
while((*ptr) != xmlSecTransformUnknown) {
if(xmlStrEqual((*ptr)->href, href)) {
return(*ptr);
}
++ptr;
}
/* not found */
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"href=%s", href);
return(xmlSecTransformUnknown);
}
/**********************************************************************
*
* Transform
*
*********************************************************************/
/**
* xmlSecTransformCreate:
* @id: the transform id to create.
* @usage: the proposed transform usage.
* @dontDestroy: the flag that controls wherther the transforms
* can destroy the transforms automatically
*
* Creates new transform from the transform id object.
*
* Returns the pointer to newly created #xmlSecTransform structure
* or NULL if an error occurs.
*/
xmlSecTransformPtr
xmlSecTransformCreate(xmlSecTransformId id, xmlSecTransformUsage usage,
int dontDestroy) {
xmlSecTransformPtr transform;
xmlSecAssert2(id != NULL, NULL);
xmlSecAssert2(id->create != NULL, NULL);
if((id->usage & usage) != usage) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_USAGE,
"transform usage=0x%08x, requested usage %0x08x",
(unsigned)id->usage, (unsigned)usage);
return(NULL);
}
transform = id->create(id);
if(transform == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"id->create");
return(NULL);
}
transform->dontDestroy = dontDestroy;
return(transform);
}
/**
* xmlSecTransformDestroy:
* @transform: the pointer to #xmlSecTransform structure.
* @forceDestroy: the flag whether the transform destruction will be
* forced.
*
* Destroys transform by calling appropriate transform specific function.
*/
void
xmlSecTransformDestroy(xmlSecTransformPtr transform, int forceDestroy) {
xmlSecAssert(transform != NULL);
xmlSecAssert(transform->id != NULL);
xmlSecAssert(transform->id->destroy != NULL);
if(!xmlSecTransformIsValid(transform)){
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
" ");
return;
}
/*
* Special case for binary transforms: need to remove from chain
*/
if(transform->id->type == xmlSecTransformTypeBinary) {
xmlSecBinTransformRemove(transform);
}
if((transform->dontDestroy) && (!forceDestroy)){
/* requested do not destroy transform */
return;
}
transform->id->destroy(transform);
}
/**
* xmlSecTransformRead:
* @transform: the pointer to #xmlSecTransform structure.
* @transformNode: the pointer to the node.
*
* Reads transform information from the @transformNode using
* transform specific function.
*
* Returns 0 on success or a negative value otherwise.
*/
int
xmlSecTransformRead(xmlSecTransformPtr transform, xmlNodePtr transformNode) {
xmlSecAssert2(transform != NULL, -1);
xmlSecAssert2(transformNode != NULL, -1);
if(!xmlSecTransformIsValid(transform)){
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
" ");
return(-1);
}
if(transform->id->read != NULL) {
return(transform->id->read(transform, transformNode));
}
return(0);
}
/**********************************************************************
*
* Binary transform
*
*********************************************************************/
/**
* xmlSecBinTransformAddKey:
* @transform: the pointer to #xmlSecTransform structure.
* @key: the pointer to #xmlSecKey structure.
*
* Sets the key for binary transform (wrapper for transform specific
* addBinKey() method).
*
* Returns 0 on success or a negative value otherwise.
*/
int
xmlSecBinTransformAddKey(xmlSecTransformPtr transform, xmlSecKeyPtr key) {
xmlSecBinTransformId id;
xmlSecAssert2(transform != NULL, -1);
xmlSecAssert2(key != NULL, -1);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return(-1);
}
id = ((xmlSecBinTransformId)(transform->id));
if(id->addBinKey != NULL) {
return((id->addBinKey)((xmlSecBinTransformPtr)transform, key));
}
return(0);
}
/**
* xmlSecBinTransformRead:
* @transform: the pointer to #xmlSecTransform structure.
* @buf: the output buffer.
* @size: the output buffer size.
*
* Reads chunk of data from the transform (wrapper transform specific
* readBin() function).
*
* Returns the number of bytes in the buffer or negative value
* if an error occurs.
*/
int
xmlSecBinTransformRead(xmlSecTransformPtr transform,
unsigned char *buf, size_t size) {
xmlSecBinTransformId id;
xmlSecAssert2(transform != NULL, -1);
xmlSecAssert2(buf != NULL, -1);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return(-1);
}
id = ((xmlSecBinTransformId)(transform->id));
if(id->readBin != NULL) {
return((id->readBin)((xmlSecBinTransformPtr)transform, buf, size));
}
return(0);
}
/**
* xmlSecBinTransformWrite:
* @transform: the pointer to #xmlSecTransform structure.
* @buf: the input data buffer.
* @size: the input data size.
*
* Writes data to the transform (wrapper to the transform specific
* writeBin() function).
*
* Returns 0 if success or a negative value otherwise.
*/
int
xmlSecBinTransformWrite(xmlSecTransformPtr transform,
const unsigned char *buf, size_t size) {
xmlSecBinTransformId id;
xmlSecAssert2(transform != NULL, -1);
xmlSecAssert2(buf != NULL, -1);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return(-1);
}
id = ((xmlSecBinTransformId)(transform->id));
if(id->writeBin != NULL) {
return((id->writeBin)((xmlSecBinTransformPtr)transform, buf, size));
}
return(0);
}
/**
* xmlSecBinTransformFlush:
* @transform: the pointer to #xmlSecTransform structure.
*
* Finalizes writing (wrapper for transform specific flushBin() method).
*
* Returns 0 if success or negative value otherwise.
*/
int
xmlSecBinTransformFlush(xmlSecTransformPtr transform) {
xmlSecBinTransformId id;
xmlSecAssert2(transform != NULL, -1);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return(-1);
}
id = ((xmlSecBinTransformId)(transform->id));
if(id->flushBin != NULL) {
return((id->flushBin)((xmlSecBinTransformPtr)transform));
}
return(0);
}
/**
* xmlSecBinTransformAddAfter:
* @curTransform: the pointer to current transform (may be NULL).
* @newTransform: the pointer to new transform.
*
* Adds @newTransform after the @curTransform.
*
* Returns pointer to the new transforms chain or NULL if an error occurs.
*/
xmlSecTransformPtr
xmlSecBinTransformAddAfter(xmlSecTransformPtr curTransform,
xmlSecTransformPtr newTransform) {
xmlSecBinTransformPtr c;
xmlSecBinTransformPtr n;
xmlSecAssert2(newTransform != NULL, NULL);
if(((curTransform != NULL) && !xmlSecTransformCheckType(curTransform, xmlSecTransformTypeBinary)) ||
!xmlSecTransformCheckType(newTransform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return(NULL);
}
c = (xmlSecBinTransformPtr)curTransform;
n = (xmlSecBinTransformPtr)newTransform;
if(c != NULL) {
n->prev = c;
n->next = c->next;
c->next = n;
if(n->next != NULL) {
n->next->prev = n;
}
} else {
n->next = n->prev = NULL;
}
return(newTransform);
}
/**
* xmlSecBinTransformAddBefore
* @curTransform: the pointer to current transform (may be NULL).
* @newTransform: the pointer to new transform.
*
* Adds @newTransform before the @curTransform.
*
* Returns pointer to the new transforms chain or NULL if an error occurs.
*/
xmlSecTransformPtr
xmlSecBinTransformAddBefore(xmlSecTransformPtr curTransform,
xmlSecTransformPtr newTransform) {
xmlSecBinTransformPtr c;
xmlSecBinTransformPtr n;
xmlSecAssert2(newTransform != NULL, NULL);
if(((curTransform != NULL) && !xmlSecTransformCheckType(curTransform, xmlSecTransformTypeBinary)) ||
!xmlSecTransformCheckType(newTransform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return(NULL);
}
c = (xmlSecBinTransformPtr)curTransform;
n = (xmlSecBinTransformPtr)newTransform;
if(c != NULL) {
n->next = c;
n->prev = c->prev;
c->prev = n;
if(n->prev != NULL) {
n->prev->next = n;
}
} else {
n->next = n->prev = NULL;
}
return(newTransform);
}
/**
* xmlSecBinTransformRemove:
* @transform: the pointer to #xmlSecTransform structure.
*
* Removes @transform from the chain. This method MUST be called by any
* bin transform id destructor.
*/
void
xmlSecBinTransformRemove(xmlSecTransformPtr transform) {
xmlSecBinTransformPtr t;
xmlSecAssert(transform != NULL);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return;
}
t = (xmlSecBinTransformPtr)transform;
if(t->next != NULL) {
t->next->prev = t->prev;
}
if(t->prev != NULL) {
t->prev->next = t->next;
}
t->next = t->prev = NULL;
}
/**
* xmlSecBinTransformDestroyAll:
* @transform: the pointer to #xmlSecTransform structure.
*
* Destroys all transforms in the chain.
*/
void
xmlSecBinTransformDestroyAll(xmlSecTransformPtr transform) {
xmlSecBinTransformPtr t;
xmlSecAssert(transform != NULL);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return;
}
t = (xmlSecBinTransformPtr)transform;
while(t->next != NULL) {
xmlSecTransformDestroy((xmlSecTransformPtr)(t->next), 0);
}
while(t->prev != NULL) {
xmlSecTransformDestroy((xmlSecTransformPtr)(t->prev), 0);
}
xmlSecTransformDestroy((xmlSecTransformPtr)t, 0);
}
/**
* xmlSecBinTransformSetEncrypt:
* @transform: the pointer to #xmlSecTransform structure.
* @encrypt: the encrypt/decrypt (or encode/decode) flag.
*
* Sets the @transform direction - encrypt/decrypt (or encode/decode).
*/
void
xmlSecBinTransformSetEncrypt(xmlSecTransformPtr transform, int encrypt) {
xmlSecBinTransformPtr t;
xmlSecAssert(transform != NULL);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeBinary)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeBinary");
return;
}
t = (xmlSecBinTransformPtr)transform;
t->encode = encrypt;
}
/**************************************************************************
*
* XML Transform
*
*************************************************************************/
/**
* xmlSecXmlTransformExecute:
* @transform: the pointer to XML transform.
* @ctxDoc: the pointer to the document containing the transform's
* node.
* @doc: the pointer to the pointer to current document.
* @nodes: the pointer to the pointer to current and result nodes set.
*
* Executes the XML @transform and returns result nodes set in @nodes
* (wrapper for transform specific executeXml() method).
*
* Returns 0 on success or a negative value otherwise.
*/
int
xmlSecXmlTransformExecute(xmlSecTransformPtr transform, xmlDocPtr ctxDoc,
xmlDocPtr *doc, xmlSecNodeSetPtr *nodes) {
xmlSecXmlTransformId id;
xmlSecAssert2(transform != NULL, -1);
xmlSecAssert2(doc != NULL, -1);
xmlSecAssert2((*doc) != NULL, -1);
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeXml)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeXml");
return(-1);
}
id = ((xmlSecXmlTransformId)(transform->id));
if(id->executeXml != NULL) {
return((id->executeXml)((xmlSecXmlTransformPtr)transform, ctxDoc,
doc, nodes));
}
return(0);
}
/*************************************************************************
*
* C14N Transform
*
************************************************************************/
/**
* xmlSecC14NTransformExecute:
* @transform: the pointer to C14N transform.
* @doc: the pointer to current document.
* @nodes: the pointer to current nodes set.
* @buffer: the result buffer.
*
* Executes the C14N @transform and returns result in the @buffer
* (wrapper for transform specific executeC14n() method). If the
* @trnaform is NULL then the default #xmlSecC14NInclusive
* transform is executed.
*
* Returns 0 on success or a negative value otherwise.
*/
int
xmlSecC14NTransformExecute(xmlSecTransformPtr transform,
xmlDocPtr doc, xmlSecNodeSetPtr nodes,
xmlOutputBufferPtr buffer) {
xmlSecC14NTransformId id;
xmlSecAssert2(doc != NULL, -1);
xmlSecAssert2(buffer != NULL, -1);
if(transform != NULL) {
if(!xmlSecTransformCheckType(transform, xmlSecTransformTypeC14N)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
"xmlSecTransformTypeC14N");
return(-1);
}
id = ((xmlSecC14NTransformId)(transform->id));
} else {
id = (xmlSecC14NTransformId)xmlSecC14NInclusive; /* the default c14n transform */
}
if(id->executeC14N != NULL) {
return((id->executeC14N)((xmlSecC14NTransformPtr)transform,
doc, nodes, buffer));
}
return(0);
}
/***************************************************************************
*
* Transforms State
*
**************************************************************************/
/**
* xmlSecTransformStateCreate:
* @doc: the pointer to XML document that contains node.
* @nodeSet: the original nodes set.
* @uri: the original uri.
*
* Creates new transform state.
*
* Returns pointer to newly allocated #xmlSecTransformState structure
* or NULL if an error occurs.
*/
xmlSecTransformStatePtr
xmlSecTransformStateCreate(xmlDocPtr doc, xmlSecNodeSetPtr nodeSet,
const char *uri) {
xmlSecTransformStatePtr state;
int ret;
/*
* Allocate a new xmlSecTransformState and fill the fields.
*/
state = (xmlSecTransformStatePtr) xmlMalloc(sizeof(xmlSecTransformState));
if(state == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_MALLOC_FAILED,
"sizeof(xmlSecTransformState)=%d",
sizeof(xmlSecTransformState));
return(NULL);
}
memset(state, 0, sizeof(xmlSecTransformState));
state->curBuf = xmlBufferCreate();
if(state->curBuf == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XML_FAILED,
"xmlBufferCreate");
xmlSecTransformStateDestroy(state);
return(NULL);
}
state->initDoc = doc;
state->initNodeSet = nodeSet;
ret = xmlSecTransformStateParseUri(state, uri);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformStateParseUri(%s)", (uri != NULL) ? uri : "NULL");
xmlSecTransformStateDestroy(state);
return(NULL);
}
return(state);
}
/**
* xmlSecTransformStateDestroy:
* @state: the pointer to #xmlSecTransformState structure.
*
* Destroys the transform state.
*/
void
xmlSecTransformStateDestroy(xmlSecTransformStatePtr state) {
xmlSecAssert(state != NULL);
xmlSecTransformStateDestroyCurrentDoc(state);
if(state->curBuf != NULL) {
xmlBufferEmpty(state->curBuf);
xmlBufferFree(state->curBuf);
}
if(state->curFirstBinTransform != NULL) {
xmlSecBinTransformDestroyAll(state->curFirstBinTransform);
} else if(state->curLastBinTransform != NULL) {
xmlSecBinTransformDestroyAll(state->curLastBinTransform);
}
if(state->initUri != NULL) {
xmlFree(state->initUri);
}
memset(state, 0, sizeof(xmlSecTransformState));
xmlFree(state);
}
/**
* xmlSecTransformStateUpdate:
* @state: the pointer to #xmlSecTransformState structure.
* @transform: the pointer to #xmlSecTransform structure.
*
* Updates the current @state with @transform. Note all transforms are
* applied immidiatelly.
*
* Returns 0 on success or negative value otherwise.
*/
int
xmlSecTransformStateUpdate(xmlSecTransformStatePtr state,
xmlSecTransformPtr transform) {
int ret;
xmlSecAssert2(state != NULL, -1);
xmlSecAssert2(transform != NULL, -1);
if(!xmlSecTransformIsValid(transform)){
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TRANSFORM,
" ");
return(-1);
}
switch(transform->id->type) {
case xmlSecTransformTypeBinary:
/* simply add transform to the chain */
transform = xmlSecBinTransformAddAfter(state->curLastBinTransform,
transform);
if(transform == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecBinTransformAddAfter");
return(-1);
}
if(state->curFirstBinTransform == NULL) {
state->curFirstBinTransform = transform;
}
state->curLastBinTransform = transform;
break;
case xmlSecTransformTypeXml: {
xmlDocPtr doc;
xmlSecNodeSetPtr nodes;
ret = xmlSecTransformCreateXml(state);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreateXml - %d", ret);
return(-1);
}
doc = state->curDoc;
nodes = state->curNodeSet;
ret = xmlSecXmlTransformExecute(transform, state->initDoc, &doc, &nodes);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecXmlTransformExecute - %d", ret);
return(-1);
}
xmlSecTransformDestroy(transform, 0);
if(doc != state->curDoc) {
xmlSecTransformStateDestroyCurrentDoc(state);
} else if(nodes != state->curNodeSet) {
if((state->curNodeSet != NULL) && (state->curNodeSet != state->initNodeSet)) {
xmlSecNodeSetDestroy(state->curNodeSet);
}
}
state->curDoc = doc;
state->curNodeSet = nodes;
break;
}
case xmlSecTransformTypeC14N:
ret = xmlSecTransformCreateXml(state);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreateXml - %d", ret);
return(-1);
}
state->curC14NTransform = transform;
break;
default:
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TYPE,
"transform type %d", transform->id->type);
return(-1);
}
return(0);
}
/**
* xmlSecTransformStateFinal:
* @state: the pointer to #xmlSecTransformState structure.
* @type: the desired final type.
*
* Finalazies transforms @state (applies all pending transforms) and
* creates a result of the desired @type.
*
* Returns 0 on success or negative value otherwise.
*/
int
xmlSecTransformStateFinal(xmlSecTransformStatePtr state,
xmlSecTransformResult type) {
int ret;
xmlSecAssert2(state != NULL, -1);
switch(type) {
case xmlSecTransformResultBinary:
ret = xmlSecTransformCreateBin(state);
break;
case xmlSecTransformResultXml:
ret = xmlSecTransformCreateXml(state);
break;
default:
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_TYPE,
"result type %d", type);
return(-1);
}
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreateBin or xmlSecTransformCreateXml - %d", ret);
return(-1);
}
return(0);
}
/**
* xmlSecTransformStateParseUri:
*
* Parses uri and loads the document if required:
*
* http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel:
*
* The following examples demonstrate what the URI attribute identifies and
* how it is dereferenced:
*
* - URI="http://example.com/bar.xml"
* Identifies the octets that represent the external resource
* 'http://example.com/bar.xml', that is probably an XML document given
* its file extension.
* - URI="http://example.com/bar.xml#chapter1"
* Identifies the element with ID attribute value 'chapter1' of the
* external XML resource 'http://example.com/bar.xml', provided as an
* octet stream. Again, for the sake of interoperability, the element
* identified as 'chapter1' should be obtained using an XPath transform
* rather than a URI fragment (barename XPointer resolution in external
* resources is not REQUIRED in this specification).
* - URI=""
* Identifies the node-set (minus any comment nodes) of the XML resource
* containing the signature
* - URI="#chapter1"
* Identifies a node-set containing the element with ID attribute value
* 'chapter1' of the XML resource containing the signature. XML Signature
* (and its applications) modify this node-set to include the element plus
* all descendents including namespaces and attributes -- but not comments.
*
*/
static int
xmlSecTransformStateParseUri(xmlSecTransformStatePtr state, const char *uri) {
const char* xptr;
xmlSecAssert2(state != NULL, -1);
if(uri == NULL) {
state->curDoc = state->initDoc;
state->curNodeSet = state->initNodeSet;
} else if(strcmp(uri, "") == 0) {
/* all nodes set but comments */
state->curDoc = state->initDoc;
state->curNodeSet = xmlSecNodeSetGetChildren(state->initDoc,
xmlDocGetRootElement(state->initDoc),
0, 0);
if(state->curNodeSet == NULL){
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecNodeSetGetChildren");
return(-1);
}
} else if((xptr = strchr(uri, '#')) == NULL) {
state->initUri = (char*)xmlStrdup(BAD_CAST uri);
if(state->initUri == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_MALLOC_FAILED,
"xmlStrdup");
return(-1);
}
/* simple URI -- do not load document for now */
} else {
state->initUri = (char*)xmlStrndup(BAD_CAST uri, xptr - uri);
if(state->initUri == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_MALLOC_FAILED,
"xmlStrndup");
return(-1);
}
/* if the uri is not empty, need to load document */
if(strlen(state->initUri) > 0) {
state->curDoc = xmlSecParseFile(state->initUri);
if(state->curDoc == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecParseFile(%s)", state->initUri);
return(-1);
}
} else {
state->curDoc = state->initDoc;
}
/*
* now evaluate xptr if it is present and does not
* equal to everything
*/
if((xptr != NULL) && (strcmp(xptr, "#xpointer(/)") != 0)) {
xmlXPathContextPtr ctxt;
xmlXPathObjectPtr res;
xmlSecNodeSetType type;
ctxt = xmlXPtrNewContext(state->curDoc,
xmlDocGetRootElement(state->curDoc),
NULL);
if(ctxt == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XML_FAILED,
"xmlXPtrNewContext");
return(-1);
}
/* evaluate expression but skip '#' */
if((strncmp(xptr, "#xpointer(", 10) == 0) || (strncmp(xptr, "#xmlns(", 7) == 0)) {
type = xmlSecNodeSetTree;
res = xmlXPtrEval(BAD_CAST (xptr + 1), ctxt);
} else {
static char tmpl[] = "xpointer(id(\'%s\'))";
char* tmp;
int size;
/* we need to construct new expression */
size = xmlStrlen(BAD_CAST tmpl) +
xmlStrlen(BAD_CAST xptr) + 2;
tmp = (char*)xmlMalloc(size * sizeof(char));
if(tmp == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_MALLOC_FAILED,
"%d", size);
xmlXPathFreeContext(ctxt);
return(-1);
}
sprintf(tmp, tmpl, xptr + 1);
type = xmlSecNodeSetTreeWithoutComments;
res = xmlXPtrEval(BAD_CAST tmp, ctxt);
xmlFree(tmp);
}
if(res == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XML_FAILED,
"xmlXPtrEval(%s)", xptr + 1);
xmlXPathFreeContext(ctxt);
return(-1);
}
if((res->nodesetval == NULL) || (res->nodesetval->nodeNr == 0)) {
/* TODO: it is warning, not an error! */
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_NODESET,
"empty");
}
state->curNodeSet = xmlSecNodeSetCreate(state->curDoc,
res->nodesetval,
type);
if(state->curNodeSet == NULL){
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecNodeSetCreate");
xmlXPathFreeObject(res);
xmlXPathFreeContext(ctxt);
return(-1);
}
res->nodesetval = NULL;
xmlXPathFreeObject(res);
xmlXPathFreeContext(ctxt);
}
}
return(0);
}
/**
* xmlSecTransformStateDestroyCurrentDoc:
*/
static void
xmlSecTransformStateDestroyCurrentDoc(xmlSecTransformStatePtr state) {
xmlSecAssert(state != NULL);
if((state->curDoc != NULL) && (state->curDoc != state->initDoc)) {
xmlFreeDoc(state->curDoc);
}
if((state->curNodeSet != NULL) && (state->curNodeSet != state->initNodeSet)) {
xmlSecNodeSetDestroy(state->curNodeSet);
}
state->curDoc = NULL;
state->curNodeSet = NULL;
}
/**
* xmlSecTransformCreateXml:
*
* Creates XML document from current state:
* 1) if there is a pending c14n or binary transforms -- apply
* 2) if curDoc == NULL and initUri != NULL then load uri
* 3) if curDoc != NULL do nothing (no pending transforms)
* otherwise initUir and curDoc are both null and it is an error
*/
static int
xmlSecTransformCreateXml(xmlSecTransformStatePtr state) {
int ret;
xmlSecAssert2(state != NULL, -1);
if((state->curDoc == NULL) && (state->initUri == NULL)) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_DATA,
"both doc and uri are null");
return(-1);
}
if((state->curDoc == NULL) && (state->curFirstBinTransform == NULL)) {
/* load XML document directly from file */
state->curDoc = xmlSecParseFile(state->initUri);
if(state->curDoc == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecParseFile(%s)", state->initUri);
return(-1);
}
state->curNodeSet = NULL;
} else if((state->curFirstBinTransform != NULL) || (state->curC14NTransform != NULL)) {
/*
* bin transforms chain is defined or c14n is pending
* the source is curDoc
*/
ret = xmlSecTransformCreateBin(state);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreateBin - %d", ret);
return(-1);
}
/* parse XML doc from memory */
state->curDoc = xmlSecParseMemory(xmlBufferContent(state->curBuf),
xmlBufferLength(state->curBuf), 1);
if(state->curDoc == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecParseMemory");
return(-1);
}
/* do not forget to empty buffer! */
xmlBufferEmpty(state->curBuf);
} else {
/*
* do nothing because curDoc != NULL and there is no pending
* binary or c14n transforms
*/
}
return(0);
}
/**
* xmlSecTransformCreateBin:
*/
static int
xmlSecTransformCreateBin(xmlSecTransformStatePtr state) {
int ret;
xmlSecAssert2(state != NULL, -1);
if(state->curDoc != NULL) {
ret = xmlSecTransformCreateBinFromXml(state);
} else if(state->initUri != NULL) {
ret = xmlSecTransformCreateBinFromUri(state);
} else {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_INVALID_DATA,
"both doc and uri are null");
return(-1);
}
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreateBinFromXml or xmlSecTransformCreateBinFromUri - %d", ret);
return(-1);
}
return(0);
}
/**
* xmlSecTransformCreateBinFromXml:
*/
static int
xmlSecTransformCreateBinFromXml(xmlSecTransformStatePtr state) {
xmlSecTransformPtr buffer;
xmlOutputBufferPtr output;
int ret;
xmlSecAssert2(state != NULL, -1);
xmlSecAssert2(state->curDoc != NULL, -1);
/* first of all, add the memory buffer at the end */
buffer = xmlSecTransformCreate(xmlSecMemBuf, 0, 0);
if(buffer == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreate(xmlSecMemBuf)");
return(-1);
}
if(xmlSecBinTransformAddAfter(state->curLastBinTransform, buffer) == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecBinTransformAddAfter");
xmlSecTransformDestroy(buffer, 1);
return(-1);
}
if(state->curFirstBinTransform == NULL) state->curFirstBinTransform = buffer;
state->curLastBinTransform = buffer;
/* now create output buffer for c14n */
output = xmlOutputBufferCreateIO((xmlOutputWriteCallback)xmlSecBinTransformWrite,
(xmlOutputCloseCallback)xmlSecBinTransformFlush,
(void*)state->curFirstBinTransform, NULL);
if(output == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XML_FAILED,
"xmlOutputBufferCreateIO");
return(-1);
}
/*
* by default (state->c14n == NULL) we use inclusive c14n:
*
* If the data object is a node-set and the next transform requires octets,
* the signature application MUST attempt to convert the node-set to an octet
* stream using Canonical XML [XML-C14N].
*
* the story is different if the first
* transform is base64 decode:
*
* http://www.w3.org/TR/xmldsig-core/#sec-Base-64
*
* This transform requires an octet stream for input. If an XPath node-set
* (or sufficiently functional alternative) is given as input, then it is
* converted to an octet stream by performing operations logically equivalent
* to 1) applying an XPath transform with expression self::text(), then 2)
* taking the string-value of the node-set. Thus, if an XML element is
* identified by a barename XPointer in the Reference URI, and its content
* consists solely of base64 encoded character data, then this transform
* automatically strips away the start and end tags of the identified element
* and any of its descendant elements as well as any descendant comments and
* processing instructions. The output of this transform is an octet stream.
*/
if((state->curC14NTransform == NULL) &&
xmlSecTransformCheckId(state->curFirstBinTransform, xmlSecEncBase64Decode)) {
ret = xmlSecTransformPreBase64Decode(state->curDoc->children,
state->curNodeSet, output);
} else {
ret = xmlSecC14NTransformExecute(state->curC14NTransform,
state->curDoc, state->curNodeSet, output);
if(state->curC14NTransform != NULL) {
xmlSecTransformDestroy(state->curC14NTransform, 0);
state->curC14NTransform = NULL;
}
}
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformPreBase64Decode or xmlSecC14NTransformExecute - %d", ret);
xmlOutputBufferClose(output);
return(-1);
}
/* flush data in the buffer by closing it */
ret = xmlOutputBufferClose(output);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XML_FAILED,
"xmlOutputBufferClose");
return(-1);
}
/* "reassign" the buffer */
if(state->curBuf != NULL) {
xmlBufferEmpty(state->curBuf);
xmlBufferFree(state->curBuf);
}
state->curBuf = xmlSecMemBufTransformGetBuffer(buffer, 1);
/* cleanup */
xmlSecBinTransformDestroyAll(state->curFirstBinTransform);
state->curFirstBinTransform = state->curLastBinTransform = NULL;
xmlSecTransformStateDestroyCurrentDoc(state);
return(0);
}
/**
* xmlSecTransformCreateBinFromXml:
*/
static int
xmlSecTransformCreateBinFromUri(xmlSecTransformStatePtr state) {
xmlSecTransformPtr ptr;
unsigned char buffer[XMLSEC_TRANSFORM_BUFFER_SIZE];
int ret;
xmlSecAssert2(state != NULL, -1);
xmlSecAssert2(state->initUri != NULL, -1);
/* add the uri load at the beginning */
ptr = xmlSecTransformCreate(xmlSecInputUri, 0, 0);
if(ptr == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformCreate(xmlSecInputUri)");
return(-1);
}
ret = xmlSecInputUriTransformOpen(ptr, state->initUri);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecInputUriTransformOpen(%s) - %d", state->initUri, ret);
xmlSecTransformDestroy(ptr, 1);
return(-1);
}
if(xmlSecBinTransformAddBefore(state->curFirstBinTransform, ptr) == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecBinTransformAddBefore");
xmlSecTransformDestroy(ptr, 1);
return(-1);
}
if(state->curLastBinTransform == NULL) state->curLastBinTransform = ptr;
state->curFirstBinTransform = ptr;
/* empty the current buffer */
xmlBufferEmpty(state->curBuf);
do {
ret = xmlSecBinTransformRead(state->curLastBinTransform, buffer, XMLSEC_TRANSFORM_BUFFER_SIZE);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecBinTransformRead - %d", ret);
return(-1);
} else if(ret > 0) {
xmlBufferAdd(state->curBuf, buffer, ret);
}
} while(ret > 0);
/* cleanup */
xmlSecBinTransformDestroyAll(state->curFirstBinTransform);
state->curFirstBinTransform = state->curLastBinTransform = NULL;
return(0);
}
/**
* xmlSecTransformPreBase64Decode:
*
* http://www.w3.org/TR/xmldsig-core/#sec-Base-64:
*
* Base64 transform
* This transform requires an octet stream for input. If an XPath node-set
* (or sufficiently functional alternative) is given as input, then it is
* converted to an octet stream by performing operations logically equivalent
* to 1) applying an XPath transform with expression self::text(), then 2)
* taking the string-value of the node-set. Thus, if an XML element is
* identified by a barename XPointer in the Reference URI, and its content
* consists solely of base64 encoded character data, then this transform
* automatically strips away the start and end tags of the identified element
* and any of its descendant elements as well as any descendant comments and
* processing instructions. The output of this transform is an octet stream.
*
*/
static int
xmlSecTransformPreBase64DecodeWalk(xmlSecNodeSetPtr nodeSet, xmlNodePtr cur,
xmlNodePtr parent ATTRIBUTE_UNUSED,
void* data) {
xmlSecAssert2(nodeSet != NULL, -1);
xmlSecAssert2(cur != NULL, -1);
xmlSecAssert2(data != NULL, -1);
if(cur->type == XML_TEXT_NODE) {
xmlOutputBufferWriteString((xmlOutputBufferPtr)data,
(char*)(cur->content));
}
return(0);
}
static int
xmlSecTransformPreBase64Decode(const xmlNodePtr node, xmlSecNodeSetPtr nodeSet,
xmlOutputBufferPtr output) {
xmlNodePtr cur;
int ret;
xmlSecAssert2(node != NULL, -1);
xmlSecAssert2(output != NULL, -1);
if(nodeSet != NULL) {
if(xmlSecNodeSetWalk(nodeSet, xmlSecTransformPreBase64DecodeWalk, output) < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecNodeSetWalk");
return(-1);
}
} else if(node->type == XML_ELEMENT_NODE) {
cur = node->children;
while(cur != NULL) {
ret = xmlSecTransformPreBase64Decode(cur, NULL, output);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"xmlSecTransformPreBase64Decode - %d", ret);
return(-1);
}
}
} else if(node->type == XML_TEXT_NODE) {
xmlOutputBufferWriteString(output, (char*)node->content);
}
return(0);
}