blob: f62ed3893d018cfbe462d41fcff12aaf36736cf5 [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
*
* checkSig := (Very ugly) tool to check a signature embedded in an XML file
*
* $Id$
*
*/
#include "AnonymousResolver.hpp"
#include "InteropResolver.hpp"
// XSEC
#include <xsec/utils/XSECPlatformUtils.hpp>
#include <xsec/framework/XSECProvider.hpp>
#include <xsec/canon/XSECC14n20010315.hpp>
#include <xsec/dsig/DSIGSignature.hpp>
#include <xsec/framework/XSECException.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
#include "../../utils/XSECDOMUtils.hpp"
// General
#include <memory.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#else
# if defined(HAVE_DIRECT_H)
# include <direct.h>
# endif
#endif
#if defined (_DEBUG) && defined (_MSC_VER)
#include <crtdbg.h>
#endif
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/util/XMLException.hpp>
#include <xercesc/util/XMLUri.hpp>
#include <xercesc/util/Janitor.hpp>
XERCES_CPP_NAMESPACE_USE
using std::cerr;
using std::cout;
using std::endl;
#ifdef XSEC_HAVE_XALAN
// XALAN
#include <xalanc/XPath/XPathEvaluator.hpp>
#include <xalanc/XalanTransformer/XalanTransformer.hpp>
XALAN_USING_XALAN(XPathEvaluator)
XALAN_USING_XALAN(XalanTransformer)
#endif
#if defined (XSEC_HAVE_OPENSSL)
// OpenSSL
# include <xsec/enc/OpenSSL/OpenSSLCryptoKeyHMAC.hpp>
# include <openssl/err.h>
#endif
#if defined (XSEC_HAVE_WINCAPI)
# include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp>
# include <xsec/enc/WinCAPI/WinCAPICryptoKeyHMAC.hpp>
#endif
#if defined (XSEC_HAVE_NSS)
# include <xsec/enc/NSS/NSSCryptoProvider.hpp>
# include <xsec/enc/NSS/NSSCryptoKeyHMAC.hpp>
#endif
#include <time.h>
#ifndef XSEC_HAVE_XALAN
std::ostream& operator<< (std::ostream& target, const XMLCh * s)
{
char *p = XMLString::transcode(s);
target << p;
XSEC_RELEASE_XMLCH(p);
return target;
}
#endif
// ----------------------------------------------------------------------------
// Checksig
// ----------------------------------------------------------------------------
void printUsage(void) {
cerr << "\nUsage: checksig [options] <input file name>\n\n";
cerr << " Where options are :\n\n";
cerr << " --skiprefs/-s\n";
cerr << " Skip checking references - check signature only\n\n";
cerr << " --hmackey/-h <string>\n";
cerr << " Set an hmac key using the <string>\n\n";
cerr << " --xsecresolver/-x\n";
cerr << " Use the xml-security test XMLDSig URI resolver\n\n";
cerr << " --id <name>\n";
cerr << " Define an attribute Id by name\n\n";
cerr << " --idns/-d <ns uri> <name>\n";
cerr << " Define an attribute Id by namespace URI and name\n\n";
#if defined (XSEC_HAVE_OPENSSL)
cerr << " --interop/-i\n";
cerr << " Use the interop resolver for Baltimore interop examples\n\n";
#endif
#if defined(XSEC_HAVE_WINCAPI)
# if defined (XSEC_HAVE_OPENSSL)
cerr << " --wincapi/-w\n";
cerr << " Use the Windows CAPI crypto Provider\n\n";
# endif
cerr << " --winhmackey/-wh <string>\n";
cerr << " Use the Windows CAPI crypto provider and hash the <string>\n";
cerr << " into a Windows key using SHA-1\n\n";
#endif
cerr << " Exits with codes :\n";
cerr << " 0 = Signature OK\n";
cerr << " 1 = Signature Bad\n";
cerr << " 2 = Processing error\n";
}
int evaluate(int argc, char ** argv) {
char * filename = NULL;
char * hmacKeyStr = NULL;
char * useIdAttributeNS = NULL;
char * useIdAttributeName = NULL;
XSECCryptoKey * key = NULL;
bool useXSECURIResolver = false;
bool useAnonymousResolver = false;
bool useInteropResolver = false;
#if defined (XSEC_HAVE_WINCAPI)
HCRYPTPROV win32CSP = 0;
#endif
bool skipRefs = false;
if (argc < 2) {
printUsage();
return 2;
}
// Run through parameters
int paramCount = 1;
while (paramCount < argc - 1) {
if (_stricmp(argv[paramCount], "--hmackey") == 0 || _stricmp(argv[paramCount], "-h") == 0) {
paramCount++;
hmacKeyStr = argv[paramCount++];
}
else if (_stricmp(argv[paramCount], "--skiprefs") == 0 || _stricmp(argv[paramCount], "-s") == 0) {
skipRefs = true;
paramCount++;
}
else if (_stricmp(argv[paramCount], "--xsecresolver") == 0 || _stricmp(argv[paramCount], "-x") == 0) {
useXSECURIResolver = true;
paramCount++;
}
else if (_stricmp(argv[paramCount], "--id") == 0) {
if (paramCount +1 >= argc) {
printUsage();
return 2;
}
paramCount++;
useIdAttributeName = argv[paramCount++];
}
else if (_stricmp(argv[paramCount], "--idns") == 0 || _stricmp(argv[paramCount], "-d") == 0) {
if (paramCount +2 >= argc) {
printUsage();
return 2;
}
paramCount++;
useIdAttributeNS = argv[paramCount++];
useIdAttributeName = argv[paramCount++];
}
#if defined (XSEC_HAVE_OPENSSL)
else if (_stricmp(argv[paramCount], "--interop") == 0 || _stricmp(argv[paramCount], "-i") == 0) {
// Use the interop key resolver
useInteropResolver = true;
paramCount++;
}
#endif
else if (_stricmp(argv[paramCount], "--anonymousresolver") == 0 || _stricmp(argv[paramCount], "-a") ==0) {
useAnonymousResolver = true;
paramCount++;
}
#if defined (XSEC_HAVE_WINCAPI)
else if (_stricmp(argv[paramCount], "--wincapi") == 0 || _stricmp(argv[paramCount], "-w") == 0 ||
_stricmp(argv[paramCount], "--winhmackey") == 0 || _stricmp(argv[paramCount], "-wh") == 0) {
WinCAPICryptoProvider * cp = new WinCAPICryptoProvider();
XSECPlatformUtils::SetCryptoProvider(cp);
if (_stricmp(argv[paramCount], "--winhmackey") == 0 || _stricmp(argv[paramCount], "-wh") == 0) {
// Create a SHA-1 based key based on the <string> parameter
paramCount++;
if (!CryptAcquireContext(&win32CSP,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
cerr << "Error obtaining default RSA_PROV" << endl;
return 2;
}
HCRYPTKEY k;
HCRYPTHASH h;
BOOL fResult = CryptCreateHash(
win32CSP,
CALG_SHA,
0,
0,
&h);
if (fResult == 0) {
cerr << "Error creating hash to create windows hmac key from password" << endl;
return 2;
}
fResult = CryptHashData(
h,
(unsigned char *) argv[paramCount],
(DWORD) strlen(argv[paramCount]),
0);
if (fResult == 0) {
cerr << "Error hashing password to create windows hmac key" << endl;
return 2;
}
// Now create a key
fResult = CryptDeriveKey(
win32CSP,
CALG_RC2,
h,
CRYPT_EXPORTABLE,
&k);
if (fResult == 0) {
cerr << "Error deriving key from hash value" << endl;
return 2;
}
// Wrap in a WinCAPI object
WinCAPICryptoKeyHMAC * hk;
hk = new WinCAPICryptoKeyHMAC(win32CSP);
hk->setWinKey(k);
key = hk;
CryptDestroyHash(h);
}
paramCount++;
}
#endif
else {
cerr << "Unknown option: " << argv[paramCount] << endl << endl;
printUsage();
return 2;
}
}
#if defined (XSEC_HAVE_WINCAPI) && !defined(XSEC_HAVE_OPENSSL)
// Use default DSS provider
WinCAPICryptoProvider * cp = new WinCAPICryptoProvider();
XSECPlatformUtils::SetCryptoProvider(cp);
#endif
if (paramCount >= argc) {
printUsage();
return 2;
}
filename = argv[paramCount];
// Create and set up the parser
XercesDOMParser * parser = new XercesDOMParser;
Janitor<XercesDOMParser> j_parser(parser);
parser->setDoNamespaces(true);
parser->setCreateEntityReferenceNodes(true);
// Now parse out file
bool errorsOccured = false;
XMLSize_t errorCount = 0;
try
{
parser->parse(filename);
errorCount = parser->getErrorCount();
if (errorCount > 0)
errorsOccured = true;
}
catch (const XMLException& e)
{
cerr << "An error occurred during parsing\n Message: "
<< e.getMessage() << endl;
errorsOccured = true;
}
catch (const DOMException& e)
{
cerr << "A DOM error occurred during parsing\n DOMException code: "
<< e.code << endl;
errorsOccured = true;
}
if (errorsOccured) {
cout << "Errors during parse" << endl;
return (2);
}
/*
Now that we have the parsed file, get the DOM document and start looking at it
*/
DOMNode *doc; // The document that we parsed
doc = parser->getDocument();
DOMDocument *theDOM = parser->getDocument();
// Find the signature node
DOMNode *sigNode = findDSIGNode(doc, "Signature");
// Create the signature checker
if (sigNode == 0) {
cerr << "Could not find <Signature> node in " << argv[argc-1] << endl;
return 2;
}
XSECProvider prov;
XSECKeyInfoResolverDefault theKeyInfoResolver;
DSIGSignature * sig = prov.newSignatureFromDOM(theDOM, sigNode);
// The only way we can verify is using keys read directly from the KeyInfo list,
// so we add a KeyInfoResolverDefault to the Signature.
sig->setKeyInfoResolver(&theKeyInfoResolver);
// Register defined attribute name
if (useIdAttributeName != NULL) {
sig->setIdByAttributeName(true);
if (useIdAttributeNS != NULL) {
sig->registerIdAttributeNameNS(MAKE_UNICODE_STRING(useIdAttributeNS),
MAKE_UNICODE_STRING(useIdAttributeName));
} else {
sig->registerIdAttributeName(MAKE_UNICODE_STRING(useIdAttributeName));
}
}
// Check whether we should use the internal resolver
if (useXSECURIResolver == true ||
useAnonymousResolver == true ||
useInteropResolver == true) {
AnonymousResolver theAnonymousResolver;
// Map out base path of the file
#if XSEC_HAVE_GETCWD_DYN
char *path = getcwd(NULL, 0);
char *baseURI = (char*)malloc(strlen(path) + 8 + 1 + strlen(filename) + 1);
#else
char path[PATH_MAX];
char baseURI[(PATH_MAX * 2) + 10];
getcwd(path, PATH_MAX);
#endif
strcpy(baseURI, "file:///");
// Ugly and nasty but quick
if (filename[0] != '\\' && filename[0] != '/' && filename[1] != ':') {
strcat(baseURI, path);
strcat(baseURI, "/");
} else if (path[1] == ':') {
path[2] = '\0';
strcat(baseURI, path);
}
strcat(baseURI, filename);
// Find any ':' and "\" characters
int lastSlash = 0;
for (unsigned int i = 8; i < strlen(baseURI); ++i) {
if (baseURI[i] == '\\') {
lastSlash = i;
baseURI[i] = '/';
}
else if (baseURI[i] == '/')
lastSlash = i;
}
// The last "\\" must prefix the filename
baseURI[lastSlash + 1] = '\0';
XMLCh * baseURIXMLCh = XMLString::transcode(baseURI);
XMLUri uri(MAKE_UNICODE_STRING(baseURI));
#if XSEC_HAVE_GETCWD_DYN
free(path);
free(baseURI);
#endif
if (useAnonymousResolver == true) {
sig->setURIResolver(&theAnonymousResolver);
}
sig->getURIResolver()->setBaseURI(baseURIXMLCh);
#if defined (XSEC_HAVE_OPENSSL)
if (useInteropResolver == true) {
InteropResolver ires(&(baseURIXMLCh[8]));
sig->setKeyInfoResolver(&ires);
}
#endif
XSEC_RELEASE_XMLCH(baseURIXMLCh);
}
bool result;
try {
// Load a key if necessary
if (hmacKeyStr != NULL) {
XSECCryptoKeyHMAC * hmacKey = XSECPlatformUtils::g_cryptoProvider->keyHMAC();
hmacKey->setKey((unsigned char *) hmacKeyStr, (unsigned int) strlen(hmacKeyStr));
sig->setSigningKey(hmacKey);
}
else if (key != NULL) {
sig->setSigningKey(key);
}
sig->load();
if (skipRefs)
result = sig->verifySignatureOnly();
else
result = sig->verify();
}
catch (const XSECException &e) {
char * msg = XMLString::transcode(e.getMsg());
cerr << "An error occurred during signature verification\n Message: "
<< msg << endl;
XSEC_RELEASE_XMLCH(msg);
errorsOccured = true;
return 2;
}
catch (const XSECCryptoException &e) {
cerr << "An error occurred during signature verification\n Message: "
<< e.getMsg() << endl;
errorsOccured = true;
#if defined (XSEC_HAVE_OPENSSL)
ERR_load_crypto_strings();
BIO * bio_err;
if ((bio_err=BIO_new(BIO_s_file())) != NULL)
BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
ERR_print_errors(bio_err);
#endif
return 2;
}
#if 0
catch (...) {
cerr << "Unknown Exception type occurred. Cleaning up and exiting\n" << endl;
return 2;
}
#endif
int retResult;
if (result) {
cout << "Signature verified OK!" << endl;
retResult = 0;
}
else {
cout << "Signature failed verification" << endl;
char * e = XMLString::transcode(sig->getErrMsgs());
cout << e << endl;
XSEC_RELEASE_XMLCH(e);
retResult = 1;
}
#if defined (XSEC_HAVE_WINCAPI)
// Clean up the handle to the CSP
if (win32CSP != 0)
CryptReleaseContext(win32CSP, 0);
#endif
// Janitor will clean up the parser
return retResult;
}
int main(int argc, char **argv) {
int retResult;
#if defined (_DEBUG) && defined (_MSC_VER)
// Do some memory debugging under Visual C++
_CrtMemState s1, s2, s3;
// At this point we are about to start really using XSEC, so
// Take a "before" checkpoing
_CrtMemCheckpoint( &s1 );
#endif
// Initialise the XML system
try {
XMLPlatformUtils::Initialize();
#ifdef XSEC_HAVE_XALAN
XPathEvaluator::initialize();
XalanTransformer::initialize();
#endif
XSECPlatformUtils::Initialise();
}
catch (const XMLException &e) {
cerr << "Error during initialisation of Xerces" << endl;
cerr << "Error Message = : "
<< e.getMessage() << endl;
}
retResult = evaluate(argc, argv);
XSECPlatformUtils::Terminate();
#ifdef XSEC_HAVE_XALAN
XalanTransformer::terminate();
XPathEvaluator::terminate();
#endif
XMLPlatformUtils::Terminate();
#if defined (_DEBUG) && defined (_MSC_VER)
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2 ) && (
s3.lCounts[0] > 0 ||
s3.lCounts[1] > 1 ||
// s3.lCounts[2] > 2 || We don't worry about C Runtime
s3.lCounts[3] > 0 ||
s3.lCounts[4] > 0)) {
// Note that there is generally 1 Normal and 1 CRT block
// still taken. 1 is from Xalan and 1 from stdio
// Send all reports to STDOUT
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
// Dumpy memory stats
_CrtMemDumpAllObjectsSince( &s3 );
_CrtMemDumpStatistics( &s3 );
}
// Now turn off memory leak checking and end as there are some
// Globals that are allocated that get seen as leaks (Xalan?)
int dbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
dbgFlag &= ~(_CRTDBG_LEAK_CHECK_DF);
_CrtSetDbgFlag( dbgFlag );
#endif
return retResult;
}