| /** |
| * 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); |
| |
| } |
| |