/*
- * The Shibboleth License, Version 1.
- * Copyright (c) 2002
- * University Corporation for Advanced Internet Development, Inc.
- * All rights reserved
+ * Copyright 2001-2005 Internet2
+ *
+ * Licensed 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
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution, if any, must include
- * the following acknowledgment: "This product includes software developed by
- * the University Corporation for Advanced Internet Development
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
- * may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear.
- *
- * Neither the name of Shibboleth nor the names of its contributors, nor
- * Internet2, nor the University Corporation for Advanced Internet Development,
- * Inc., nor UCAID may be used to endorse or promote products derived from this
- * software without specific prior written permission. For written permission,
- * please contact shibboleth@shibboleth.org
- *
- * Products derived from this software may not be called Shibboleth, Internet2,
- * UCAID, or the University Corporation for Advanced Internet Development, nor
- * may Shibboleth appear in their name, without prior written permission of the
- * University Corporation for Advanced Internet Development.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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.
*/
/* siterefresh.cpp - command-line tool to refresh and verify metadata
$Id$
*/
-#include "../shib-target/shib-target.h"
+#include <shib-target/shib-target.h>
+
+#if defined (_MSC_VER) || defined(__BORLANDC__)
+# include "config_win32.h"
+#else
+# include "config.h"
+#endif
+
+#if defined(HAVE_LOG4SHIB)
+# include <log4shib/Category.hh>
+# include <log4shib/OstreamAppender.hh>
+namespace siterefresh {
+ namespace logging = log4shib;
+};
+#elif defined(HAVE_LOG4CPP)
+# include <log4cpp/Category.hh>
+# include <log4cpp/OstreamAppender.hh>
+namespace siterefresh {
+ namespace logging = log4cpp;
+};
+#else
+# error "Supported logging library not available."
+#endif
#include <fstream>
-#include <log4cpp/Category.hh>
-#include <log4cpp/OstreamAppender.hh>
#include <xercesc/framework/URLInputSource.hpp>
+#include <xercesc/framework/StdInInputSource.hpp>
#include <xsec/enc/XSECCryptoProvider.hpp>
#include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
using namespace std;
using namespace saml;
using namespace shibboleth;
-using namespace log4cpp;
+using namespace siterefresh::logging;
-void verifySignature(DOMDocument* doc, DOMElement* sigNode, const char* cert=NULL)
+static const XMLCh TRUST_NS[] = // urn:mace:shibboleth:trust:1.0
+{ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,
+ chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,
+ chLatin_t, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chColon, chDigit_1, chPeriod, chDigit_0, chNull
+};
+
+static const XMLCh TRUST_SCHEMA_ID[] = // shibboleth-trust-1.0.xsd
+{ chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chDash,
+ chLatin_t, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chDash, chDigit_1, chPeriod, chDigit_0, chPeriod,
+ chLatin_x, chLatin_s, chLatin_d, chNull
+};
+
+static const XMLCh SHIB_SCHEMA_ID[] = // shibboleth.xsd
+{ chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chPeriod,
+ chLatin_x, chLatin_s, chLatin_d, chNull
+};
+
+void verifySignature(DOMDocument* doc, DOMNode* sigNode, const char* cert=NULL)
{
Category& log=Category::getInstance("siterefresh");
+ static const XMLCh ID[]={chLatin_I, chLatin_D, chNull};
// Load the signature.
XSECProvider prov;
// Verify the signature coverage.
DSIGReferenceList* refs=sig->getReferenceList();
- if (sig->getSignatureMethod()==SIGNATURE_RSA && refs && refs->getSize()==1) {
+ if (refs && refs->getSize()==1) {
DSIGReference* ref=refs->item(0);
if (ref) {
const XMLCh* URI=ref->getURI();
- if (URI==NULL || *URI==0) {
+ if (!URI || !*URI || (*URI==chPound &&
+ !XMLString::compareString(&URI[1],static_cast<DOMElement*>(sigNode->getParentNode())->getAttributeNS(NULL,ID)))) {
DSIGTransformList* tlist=ref->getTransforms();
- for (int i=0; tlist && i<tlist->getSize(); i++) {
+ for (unsigned int i=0; tlist && i<tlist->getSize(); i++) {
if (tlist->item(i)->getTransformType()==TRANSFORM_ENVELOPED_SIGNATURE)
valid=true;
else if (tlist->item(i)->getTransformType()!=TRANSFORM_EXC_C14N &&
}
if (cert) {
- // Load the certificate, stripping the first and last lines.
+ // Load the certificate, stripping the header and trailer.
string certbuf,line;
auto_ptr<OpenSSLCryptoX509> x509(new OpenSSLCryptoX509());
+ bool sawheader=false;
ifstream infile(cert);
- while (!getline(infile,line).fail())
- if (line.find("CERTIFICATE")==string::npos)
- certbuf+=line + '\n';
+ while (!getline(infile,line).fail()) {
+ if (line.find("CERTIFICATE-----")==string::npos) {
+ if (sawheader)
+ certbuf+=line + '\n';
+ }
+ else
+ sawheader=true;
+ }
x509->loadX509Base64Bin(certbuf.data(),certbuf.length());
sig->setSigningKey(x509->clonePublicKey());
}
{
int ret=0;
SAMLConfig& conf=SAMLConfig::getConfig();
- bool verify=true;
+ bool verify=true,quiet=false;
char* url_param=NULL;
char* cert_param=NULL;
char* out_param=NULL;
url_param=argv[++i];
else if (!strcmp(argv[i],"--noverify"))
verify=false;
+ else if (!strcmp(argv[i],"--quiet") || !strcmp(argv[i],"-q"))
+ quiet=true;
else if (!strcmp(argv[i],"--cert") && i+1<argc)
cert_param=argv[++i];
else if (!strcmp(argv[i],"--out") && i+1<argc)
name_param=argv[++i];
}
- if (!url_param || !out_param || (verify && !cert_param)) {
+ if (verify && !cert_param) {
cout << "usage: " << argv[0] << endl <<
"\t--url <URL of metadata>" << endl <<
- "\t--out <pathname to copy data to>" << endl <<
"\t--noverify OR --cert <PEM Certificate>" << endl <<
+ "\t[--out <pathname to copy data to>]" << endl <<
"\t[--schema <schema path>]" << endl <<
"\t[--rootns <root element XML namespace>]" << endl <<
- "\t[--rootname <root element name>]" << endl;
+ "\t[--rootname <root element name>]" << endl <<
+ "\t[--quiet]" << endl;
return -100;
}
+ static const XMLCh Trust[] = { chLatin_T, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chNull };
+ static const XMLCh SiteGroup[] =
+ { chLatin_S, chLatin_i, chLatin_t, chLatin_e, chLatin_G, chLatin_r, chLatin_o, chLatin_u, chLatin_p, chNull };
+ static const XMLCh EntitiesDescriptor[] =
+ { chLatin_E, chLatin_n, chLatin_t, chLatin_i, chLatin_t, chLatin_i, chLatin_e, chLatin_s,
+ chLatin_D, chLatin_e, chLatin_s, chLatin_c, chLatin_r, chLatin_i, chLatin_p, chLatin_t, chLatin_o, chLatin_r, chNull };
+ static const XMLCh EntityDescriptor[] =
+ { chLatin_E, chLatin_n, chLatin_t, chLatin_i, chLatin_t, chLatin_y,
+ chLatin_D, chLatin_e, chLatin_s, chLatin_c, chLatin_r, chLatin_i, chLatin_p, chLatin_t, chLatin_o, chLatin_r, chNull };
+
Category::setRootPriority(Priority::WARN);
Category::getRoot().addAppender(new OstreamAppender("default",&cerr));
Category& log=Category::getInstance("siterefresh");
if (!conf.init())
return -10;
- static const XMLCh Trust[] = { chLatin_T, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chNull };
- static const XMLCh SiteGroup[] =
- { chLatin_S, chLatin_i, chLatin_t, chLatin_e, chLatin_G, chLatin_r, chLatin_o, chLatin_u, chLatin_p, chNull };
-
- static const XMLCh TRUST_NS[] = // urn:mace:shibboleth:trust:1.0
- { chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,
- chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,
- chLatin_t, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chColon, chDigit_1, chPeriod, chDigit_0, chNull
- };
-
- static const XMLCh TRUST_SCHEMA_ID[] = // shibboleth-trust-1.0.xsd
- { chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chDash,
- chLatin_t, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chDash, chDigit_1, chPeriod, chDigit_0, chPeriod,
- chLatin_x, chLatin_s, chLatin_d, chNull
- };
-
- static const XMLCh SHIB_SCHEMA_ID[] = // shibboleth.xsd
- { chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chPeriod,
- chLatin_x, chLatin_s, chLatin_d, chNull
- };
-
- saml::XML::registerSchema(shibboleth::Constants::SHIB_NS,SHIB_SCHEMA_ID);
+ saml::XML::registerSchema(Constants::SHIB_NS,SHIB_SCHEMA_ID);
saml::XML::registerSchema(TRUST_NS,TRUST_SCHEMA_ID);
+ saml::XML::registerSchema(shibtarget::XML::SAML2META_NS,shibtarget::XML::SAML2META_SCHEMA_ID);
+ saml::XML::registerSchema(shibtarget::XML::SAML2ASSERT_NS,shibtarget::XML::SAML2ASSERT_SCHEMA_ID);
+ saml::XML::registerSchema(shibtarget::XML::XMLENC_NS,shibtarget::XML::XMLENC_SCHEMA_ID);
try {
// Parse the specified document.
saml::XML::Parser p;
static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull};
- URLInputSource src(base,url_param);
- Wrapper4InputSource dsrc(&src,false);
- DOMDocument* doc=p.parse(dsrc);
-
+ DOMDocument* doc=NULL;
+ if (url_param && *url_param) {
+ URLInputSource src(base,url_param);
+ Wrapper4InputSource dsrc(&src,false);
+ doc=p.parse(dsrc);
+ }
+ else {
+ StdInInputSource src;
+ Wrapper4InputSource dsrc(&src,false);
+ doc=p.parse(dsrc);
+ }
+
// Check root element.
if (ns_param && name_param) {
auto_ptr_XMLCh ns(ns_param);
if (!saml::XML::isElementNamed(doc->getDocumentElement(),ns.get(),name.get()))
throw MalformedException(string("Root element does not match specified QName of {") + ns_param + "}:" + name_param);
}
- else if (!saml::XML::isElementNamed(doc->getDocumentElement(),shibboleth::Constants::SHIB_NS,SiteGroup) &&
- !saml::XML::isElementNamed(doc->getDocumentElement(),TRUST_NS,Trust))
- throw MalformedException("Root element does not match SiteGroup or Trust");
+ else if (!saml::XML::isElementNamed(doc->getDocumentElement(),Constants::SHIB_NS,SiteGroup) &&
+ !saml::XML::isElementNamed(doc->getDocumentElement(),shibtarget::XML::SAML2META_NS,EntitiesDescriptor) &&
+ !saml::XML::isElementNamed(doc->getDocumentElement(),shibtarget::XML::SAML2META_NS,EntityDescriptor) &&
+ !saml::XML::isElementNamed(doc->getDocumentElement(),TRUST_NS,Trust))
+ throw MalformedException("Root element does not signify a known metadata or trust format");
- // If we're verifying, grab the embedded signature.
- DOMElement* n=saml::XML::getLastChildElement(doc->getDocumentElement(),saml::XML::XMLSIG_NS,L(Signature));
+ // Verify the "root" signature.
+ DOMElement* rootSig=saml::XML::getFirstChildElement(doc->getDocumentElement(),saml::XML::XMLSIG_NS,L(Signature));
if (verify) {
- if (n)
- verifySignature(doc,n,cert_param);
+ if (rootSig) {
+ verifySignature(doc,rootSig,cert_param);
+ }
else {
doc->release();
- log.error("unable to locate a signature to verify in document");
- throw InvalidCryptoException("Verification implies that the document must be signed");
+ log.error("unable to locate root signature to verify in document");
+ throw InvalidCryptoException("Verification implies that the document must be signed");
}
}
- else if (n) {
- log.warn("verification of signer disabled, make sure you trust the source of this file!");
- verifySignature(doc,n);
+ else if (rootSig) {
+ if (!quiet)
+ log.warn("verification of signer disabled, make sure you trust the source of this file!");
+ verifySignature(doc,rootSig,cert_param);
}
else {
- log.warn("verification disabled, and file is unsigned!");
+ if (!quiet)
+ log.warn("verification disabled, and file is unsigned!");
}
- // Output the data to the specified file.
- ofstream outfile(out_param);
- outfile << *(doc->getDocumentElement());
+ // Verify all signatures.
+ DOMNodeList* siglist=doc->getElementsByTagNameNS(saml::XML::XMLSIG_NS,L(Signature));
+ for (XMLSize_t i=0; siglist && i<siglist->getLength(); i++)
+ if (siglist->item(i) != rootSig)
+ verifySignature(doc,siglist->item(i),cert_param);
+ if (out_param) {
+ // Output the data to the specified file.
+ ofstream outfile(out_param);
+ outfile << *(doc->getDocumentElement());
+ }
+ else
+ cout << *(doc->getDocumentElement());
doc->release();
}
catch (InvalidCryptoException&) {
ret=-1;
}
catch(SAMLException& e) {
- log.errorStream() << "caught a SAML exception: " << e.what() << CategoryStream::ENDLINE;
+ log.errorStream() << "caught a SAML exception: " << e.what() << siterefresh::logging::eol;
ret=-2;
}
catch(XMLException& e) {
auto_ptr_char temp(e.getMessage());
- log.errorStream() << "caught an XML exception: " << temp.get() << CategoryStream::ENDLINE;
+ log.errorStream() << "caught an XML exception: " << temp.get() << siterefresh::logging::eol;
ret=-3;
}
catch(XSECException& e) {
auto_ptr_char temp(e.getMsg());
- log.errorStream() << "caught an XMLSec exception: " << temp.get() << CategoryStream::ENDLINE;
+ log.errorStream() << "caught an XMLSec exception: " << temp.get() << siterefresh::logging::eol;
ret=-4;
}
catch(XSECCryptoException& e) {
- log.errorStream() << "caught an XMLSecCrypto exception: " << e.getMsg() << CategoryStream::ENDLINE;
+ log.errorStream() << "caught an XMLSecCrypto exception: " << e.getMsg() << siterefresh::logging::eol;
ret=-5;
}
catch(...) {
- log.errorStream() << "caught an unknown exception" << CategoryStream::ENDLINE;
+ log.errorStream() << "caught an unknown exception" << siterefresh::logging::eol;
ret=-6;
}