blob: d551e09256ad09a247b971d9cd6f65c14f0c7fc4 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* XSEC
*
* XSECDOMUtils:= Utilities to manipulate DOM within XML-SECURITY
*
* $Id$
*
*/
// XSEC
#include <xsec/framework/XSECError.hpp>
#include <xsec/xkms/XKMSConstants.hpp>
#include "XSECDOMUtils.hpp"
// Xerces
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/TransService.hpp>
#include <string.h>
XERCES_CPP_NAMESPACE_USE
// --------------------------------------------------------------------------------
// Utilities to manipulate DSIG namespaces
// --------------------------------------------------------------------------------
const XMLCh * getDSIGLocalName(const DOMNode *node) {
if (!strEquals(node->getNamespaceURI(), DSIGConstants::s_unicodeStrURIDSIG))
return NULL; //DOMString("");
else
return node->getLocalName();
}
const XMLCh * getDSIG11LocalName(const DOMNode *node) {
if (!strEquals(node->getNamespaceURI(), DSIGConstants::s_unicodeStrURIDSIG11))
return NULL; //DOMString("");
else
return node->getLocalName();
}
const XMLCh * getECLocalName(const DOMNode * node) {
// Exclusive Canonicalisation namespace
// Probably should have a generic function
if (!strEquals(node->getNamespaceURI(), DSIGConstants::s_unicodeStrURIEC))
return NULL;
else
return node->getLocalName();
}
const XMLCh * getXPFLocalName(const DOMNode * node) {
// XPath Filter namespace
if (!strEquals(node->getNamespaceURI(), DSIGConstants::s_unicodeStrURIXPF))
return NULL;
else
return node->getLocalName();
}
const XMLCh * getXENCLocalName(const DOMNode *node) {
// XML Encryption namespace node
if (!strEquals(node->getNamespaceURI(), DSIGConstants::s_unicodeStrURIXENC))
return NULL;
else
return node->getLocalName();
}
const XMLCh * getXENC11LocalName(const DOMNode *node) {
// XML Encryption 1.1 namespace node
if (!strEquals(node->getNamespaceURI(), DSIGConstants::s_unicodeStrURIXENC11))
return NULL;
else
return node->getLocalName();
}
#ifdef XSEC_XKMS_ENABLED
const XMLCh * getXKMSLocalName(const DOMNode *node) {
// XKMS namespace node
if (!strEquals(node->getNamespaceURI(), XKMSConstants::s_unicodeStrURIXKMS))
return NULL;
else
return node->getLocalName();
}
#endif
// --------------------------------------------------------------------------------
// Find a nominated DSIG node in a document
// --------------------------------------------------------------------------------
DOMNode *findDSIGNode(DOMNode *n, const char * nodeName) {
const XMLCh * name = getDSIGLocalName(n);
if (strEquals(name, nodeName)) {
return n;
}
DOMNode *child = n->getFirstChild();
while (child != NULL) {
DOMNode *ret = findDSIGNode(child, nodeName);
if (ret != NULL)
return ret;
child = child->getNextSibling();
}
return child;
}
// --------------------------------------------------------------------------------
// Find a nominated XENC node in a document
// --------------------------------------------------------------------------------
DOMNode *findXENCNode(DOMNode *n, const char * nodeName) {
const XMLCh * name = getXENCLocalName(n);
if (strEquals(name, nodeName)) {
return n;
}
DOMNode *child = n->getFirstChild();
while (child != NULL) {
DOMNode *ret = findXENCNode(child, nodeName);
if (ret != NULL)
return ret;
child = child->getNextSibling();
}
return child;
}
// --------------------------------------------------------------------------------
// Find particular type of node child
// --------------------------------------------------------------------------------
DOMNode *findFirstChildOfType(DOMNode *n, DOMNode::NodeType t) {
DOMNode *c;
if (n == NULL)
return n;
c = n->getFirstChild();
while (c != NULL && c->getNodeType() != t)
c = c->getNextSibling();
return c;
}
DOMNode * findNextChildOfType(DOMNode *n, DOMNode::NodeType t) {
DOMNode * s = n;
if (s == NULL)
return s;
do {
s = s->getNextSibling();
} while (s != NULL && s->getNodeType() != t);
return s;
}
DOMElement *findFirstElementChild(DOMNode *n) {
DOMNode *c;
if (n == NULL)
return NULL;
c = n->getFirstChild();
while (c != NULL && c->getNodeType() != DOMNode::ELEMENT_NODE)
c = c->getNextSibling();
return (DOMElement *) c;
}
DOMElement * findNextElementChild(DOMNode *n) {
DOMNode * s = n;
if (s == NULL)
return NULL;
do {
s = s->getNextSibling();
} while (s != NULL && s->getNodeType() != DOMNode::ELEMENT_NODE);
return (DOMElement *) s;
}
// --------------------------------------------------------------------------------
// Make a QName
// --------------------------------------------------------------------------------
safeBuffer &makeQName(safeBuffer & qname, safeBuffer &prefix, const char * localName) {
if (prefix[0] == '\0') {
qname = localName;
}
else {
qname = prefix;
qname.sbStrcatIn(":");
qname.sbStrcatIn(localName);
}
return qname;
}
safeBuffer &makeQName(safeBuffer & qname, const XMLCh *prefix, const char * localName) {
if (prefix == NULL || prefix[0] == 0) {
qname.sbTranscodeIn(localName);
}
else {
qname.sbXMLChIn(prefix);
qname.sbXMLChAppendCh(XERCES_CPP_NAMESPACE_QUALIFIER chColon);
qname.sbXMLChCat(localName); // Will transcode
}
return qname;
}
safeBuffer &makeQName(safeBuffer & qname, const XMLCh *prefix, const XMLCh * localName) {
if (prefix == NULL || prefix[0] == 0) {
qname.sbXMLChIn(localName);
}
else {
qname.sbXMLChIn(prefix);
qname.sbXMLChAppendCh(XERCES_CPP_NAMESPACE_QUALIFIER chColon);
qname.sbXMLChCat(localName);
}
return qname;
}
// --------------------------------------------------------------------------------
// "Quick" Transcode (low performance)
// --------------------------------------------------------------------------------
XMLT::XMLT(const char * str) {
mp_unicodeStr = XMLString::transcode(str);
}
XMLT::~XMLT (void) {
XSEC_RELEASE_XMLCH(mp_unicodeStr);
}
XMLCh * XMLT::getUnicodeStr(void) {
return mp_unicodeStr;
}
// --------------------------------------------------------------------------------
// Gather text from children
// --------------------------------------------------------------------------------
void gatherChildrenText(DOMNode * parent, safeBuffer &output) {
DOMNode * c = parent->getFirstChild();
output.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty);
while (c != NULL) {
if (c->getNodeType() == DOMNode::TEXT_NODE)
output.sbXMLChCat(c->getNodeValue());
c = c->getNextSibling();
}
}
// --------------------------------------------------------------------------------
// Some UTF8 utilities
// --------------------------------------------------------------------------------
XMLCh * transcodeFromUTF8(const unsigned char * src) {
// Take a UTF-8 buffer and transcode to UTF-16
safeBuffer fullDest;
fullDest.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty);
XMLCh outputBuf[2050];
// Used to record byte sizes
unsigned char charSizes[2050];
// Grab a transcoder
XMLTransService::Codes failReason;
XMLTranscoder* t =
XMLPlatformUtils::fgTransService->makeNewTranscoderFor("UTF-8",
failReason,
2*1024,
XMLPlatformUtils::fgMemoryManager);
Janitor<XMLTranscoder> j_t(t);
// Need to loop through, 2K at a time
XMLSize_t bytesEaten, bytesEatenCounter;
XMLSize_t charactersEaten;
XMLSize_t totalBytesEaten = 0;
XMLSize_t bytesToEat = XMLString::stringLen((char *) src);
while (totalBytesEaten < bytesToEat) {
XMLSize_t toEat = bytesToEat - totalBytesEaten;
if (toEat > 2048)
toEat = 2048;
t->transcodeFrom(&src[totalBytesEaten],
toEat,
outputBuf,
2048,
bytesEaten,
charSizes);
// Determine how many characters were used
bytesEatenCounter = 0;
charactersEaten = 0;
while (bytesEatenCounter < bytesEaten) {
bytesEatenCounter += charSizes[charactersEaten++];
}
outputBuf[charactersEaten] = chNull;
fullDest.sbXMLChCat(outputBuf);
totalBytesEaten += bytesEaten;
}
// Dup and output
return XMLString::replicate(fullDest.rawXMLChBuffer());
}
char * transcodeToUTF8(const XMLCh * src) {
// Take a UTF-16 buffer and transcode to UTF-8
safeBuffer fullDest("");
unsigned char outputBuf[2050];
// Grab a transcoder
XMLTransService::Codes failReason;
XMLTranscoder* t =
XMLPlatformUtils::fgTransService->makeNewTranscoderFor("UTF-8",
failReason,
2*1024,
XMLPlatformUtils::fgMemoryManager);
Janitor<XMLTranscoder> j_t(t);
// Need to loop through, 2K at a time
XMLSize_t charactersEaten, charactersOutput;
XMLSize_t totalCharsEaten = 0;
XMLSize_t charsToEat = XMLString::stringLen(src);
while (totalCharsEaten < charsToEat) {
XMLSize_t toEat = charsToEat - totalCharsEaten;
if (toEat > 2048)
toEat = 2048;
charactersOutput = t->transcodeTo(&src[totalCharsEaten],
toEat,
(unsigned char * const) outputBuf,
2048,
charactersEaten,
XMLTranscoder::UnRep_RepChar);
outputBuf[charactersOutput] = '\0';
fullDest.sbStrcatIn((char *) outputBuf);
totalCharsEaten += charactersEaten;
}
// Dup and output
return XMLString::replicate(fullDest.rawCharBuffer());
}
// --------------------------------------------------------------------------------
// String decode/encode
// --------------------------------------------------------------------------------
/*
* Distinguished names have a particular encoding that needs to be performed prior
* to enclusion in the DOM
*/
XMLCh * encodeDName(const XMLCh * toEncode) {
XERCES_CPP_NAMESPACE_USE;
safeBuffer result;
static XMLCh s_strEncodedSpace[] = {
chBackSlash,
chDigit_2,
chDigit_0,
chNull
};
result.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty);
if (toEncode == NULL) {
return NULL;
}
// Find where the trailing whitespace starts
const XMLCh * ws = &toEncode[XMLString::stringLen(toEncode)];
ws--;
while (ws != toEncode &&
(*ws == '\t' || *ws == '\r' || *ws ==' ' || *ws == '\n'))
ws--;
// Set to first white space character, if we didn't get back to the start
if (toEncode != ws)
ws++;
// Now run through each character and encode if necessary
const XMLCh * i = toEncode;
if (*i == chPound) {
// "#" Characters escaped at the start of a string
result.sbXMLChAppendCh(chBackSlash);
}
while (*i != chNull && i != ws) {
if (*i <= 0x09) {
result.sbXMLChAppendCh(chBackSlash);
result.sbXMLChAppendCh(chDigit_0);
result.sbXMLChAppendCh(chDigit_0 + *i);
}
else if (*i <= 0x0f) {
result.sbXMLChAppendCh(chBackSlash);
result.sbXMLChAppendCh(chDigit_0);
result.sbXMLChAppendCh(chLatin_A + *i);
}
else if (*i <= 0x19) {
result.sbXMLChAppendCh(chBackSlash);
result.sbXMLChAppendCh(chDigit_1);
result.sbXMLChAppendCh(chDigit_0 + *i);
}
else if (*i <= 0x1f) {
result.sbXMLChAppendCh(chBackSlash);
result.sbXMLChAppendCh(chDigit_1);
result.sbXMLChAppendCh(chLatin_A + *i);
}
else if (*i == chComma) {
// Determine if this is an RDN separator
const XMLCh *j = i;
j++;
while (*j != chComma && *j != chEqual && *j != chNull)
j++;
if (*j != chEqual)
result.sbXMLChAppendCh(chBackSlash);
result.sbXMLChAppendCh(*i);
}
else {
if (*i == chPlus ||
*i == chDoubleQuote ||
*i == chBackSlash ||
*i == chOpenAngle ||
*i == chCloseAngle ||
*i == chSemiColon) {
result.sbXMLChAppendCh(chBackSlash);
}
result.sbXMLChAppendCh(*i);
}
i++;
}
// Now encode trailing white space
while (*i != chNull) {
if (*i == ' ')
result.sbXMLChCat(s_strEncodedSpace);
else
result.sbXMLChAppendCh(*i);
i++;
}
return XMLString::replicate(result.rawXMLChBuffer());
}
XMLCh * decodeDName(const XMLCh * toDecode) {
// Take an encoded name and decode to a normal XMLCh string
XERCES_CPP_NAMESPACE_USE;
safeBuffer result;
result.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty);
if (toDecode == NULL) {
return NULL;
}
const XMLCh * i = toDecode;
if (*i == chBackSlash && i[1] == chPound) {
result.sbXMLChAppendCh(chPound);
i++;
i++;
}
while (*i != chNull) {
if (*i == chBackSlash) {
i++;
if (*i == chDigit_0) {
i++;
if (*i >= chDigit_0 && *i <= chDigit_9) {
result.sbXMLChAppendCh(*i - chDigit_0);
}
else if (*i >= chLatin_A && *i <= chLatin_F) {
result.sbXMLChAppendCh(10 + *i - chLatin_A);
}
else if (*i >= chLatin_a && *i <= chLatin_f) {
result.sbXMLChAppendCh(10 + *i - chLatin_a);
}
else {
throw XSECException(XSECException::DNameDecodeError,
"Unexpected escaped character in Distinguished name");
}
}
else if (*i == chDigit_1) {
i++;
if (*i >= chDigit_0 && *i <= chDigit_9) {
result.sbXMLChAppendCh(16 + *i - chDigit_0);
}
else if (*i >= chLatin_A && *i <= chLatin_F) {
result.sbXMLChAppendCh(26 + *i - chLatin_A);
}
else if (*i >= chLatin_a && *i <= chLatin_f) {
result.sbXMLChAppendCh(26 + *i - chLatin_a);
}
else {
throw XSECException(XSECException::DNameDecodeError,
"Unexpected escaped character in Distinguished name");
}
}
else if (*i == chDigit_2) {
i++;
if (*i == '0') {
result.sbXMLChAppendCh(' ');
}
else {
throw XSECException(XSECException::DNameDecodeError,
"Unexpected escaped character in Distinguished name");
}
}
else if (*i == chComma ||
*i == chPlus ||
*i == chDoubleQuote ||
*i == chBackSlash ||
*i == chOpenAngle ||
*i == chCloseAngle ||
*i == chSemiColon) {
result.sbXMLChAppendCh(*i);
}
else {
throw XSECException(XSECException::DNameDecodeError,
"Unexpected escaped character in Distinguished name");
}
i++;
}
else {
result.sbXMLChAppendCh(*i++);
}
}
return XMLString::replicate(result.rawXMLChBuffer());
}
// --------------------------------------------------------------------------------
// Misc string functions
// --------------------------------------------------------------------------------
// These three functions are pretty much lifted from XMLURL.cpp in Xerces
static bool isHexDigit(const XMLCh toCheck)
{
if ((toCheck >= chDigit_0 && toCheck <= chDigit_9)
|| (toCheck >= chLatin_A && toCheck <= chLatin_F)
|| (toCheck >= chLatin_a && toCheck <= chLatin_f))
{
return true;
}
return false;
}
static unsigned int xlatHexDigit(const XMLCh toXlat)
{
if (!isHexDigit(toXlat)) {
throw XSECException(XSECException::ErrorOpeningURI,
"Unknown hex char");
}
if ((toXlat >= chDigit_0) && (toXlat <= chDigit_9))
return (unsigned int)(toXlat - chDigit_0);
if ((toXlat >= chLatin_A) && (toXlat <= chLatin_F))
return (unsigned int)(toXlat - chLatin_A) + 10;
return (unsigned int)(toXlat - chLatin_a) + 10;
}
XMLCh * cleanURIEscapes(const XMLCh * uriPath) {
XMLByte *ptr, *utf8Path;
XMLSize_t len = XMLString::stringLen(uriPath);
ptr = utf8Path = new XMLByte[len + 1];
for (XMLSize_t i = 0; i < len; i++) {
unsigned int value = uriPath[i];
if (value > 255) {
delete[] utf8Path;
throw XSECException(XSECException::ErrorOpeningURI, "Wide character in URI");
}
if (value == chPercent) {
if (!(i + 2 < len && isHexDigit(uriPath[i + 1]) && isHexDigit(uriPath[i + 2]))) {
delete[] utf8Path;
throw XSECException(XSECException::ErrorOpeningURI, "Bad escape sequence in URI");
}
value = (xlatHexDigit(uriPath[i + 1]) * 16) + (xlatHexDigit(uriPath[i + 2]));
i += 2;
}
*(ptr++) = (XMLByte) value;
}
*ptr = 0;
try {
XMLCh* unicodePath = transcodeFromUTF8(utf8Path);
delete[] utf8Path;
return unicodePath;
}
catch (const XMLException&) {
}
delete[] utf8Path;
throw XSECException(XSECException::ErrorOpeningURI, "Failed to transcode URI from UTF-8");
}
// --------------------------------------------------------------------------------
// Generate Ids
// --------------------------------------------------------------------------------
void makeHexByte(XMLCh * h, unsigned char b) {
unsigned char toConvert = (b & 0xf0);
toConvert = (toConvert >> 4);
if (toConvert < 10)
h[0] = chDigit_0 + toConvert;
else
h[0] = chLatin_a + toConvert - 10;
toConvert = (b & 0xf);
if (toConvert < 10)
h[1] = chDigit_0 + toConvert;
else
h[1] = chLatin_a + toConvert - 10;
}
XMLCh * generateId(unsigned int bytes) {
unsigned char b[128];
XMLCh id[258];
unsigned int toGen = (bytes > 128 ? 16 : bytes);
// Get the appropriate amount of random data
// Need to zeroise to ensure valgrind is happy
memset(b, 0, 128);
memset(id, 0, sizeof(id));
if (XSECPlatformUtils::g_cryptoProvider->getRandom(b, toGen) != toGen) {
throw XSECException(XSECException::CryptoProviderError,
"generateId - could not obtain enough random");
}
id[0] = chLatin_I;
unsigned int i;
for (i = 0; i < toGen; ++i) {
makeHexByte(&id[1+(i*2)], b[i]);
}
id[1+(i*2)] = chNull;
return XMLString::replicate(id);
}