2 * Copyright 2001-2007 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* siterefresh.cpp - command-line tool to refresh and verify metadata
25 #include <saml/SAMLConfig.h>
26 #include <saml/saml2/metadata/Metadata.h>
27 #include <saml/util/SAMLConstants.h>
28 #include <xmltooling/XMLToolingConfig.h>
29 #include <xmltooling/signature/Signature.h>
30 #include <xmltooling/util/XMLHelper.h>
33 #include <log4cpp/Category.hh>
34 #include <log4cpp/OstreamAppender.hh>
35 #include <xercesc/framework/URLInputSource.hpp>
36 #include <xercesc/framework/StdInInputSource.hpp>
37 #include <xercesc/framework/Wrapper4InputSource.hpp>
38 #include <xsec/enc/XSECCryptoProvider.hpp>
39 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
40 #include <xsec/enc/XSECCryptoException.hpp>
41 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
42 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
43 #include <xsec/framework/XSECProvider.hpp>
44 #include <xsec/framework/XSECException.hpp>
45 #include <xsec/dsig/DSIGTransformC14n.hpp>
46 #include <xsec/dsig/DSIGReference.hpp>
47 #include <xsec/dsig/DSIGTransformList.hpp>
49 using namespace xmlsignature;
50 using namespace xmlconstants;
51 using namespace xmltooling;
52 using namespace samlconstants;
53 using namespace opensaml::saml2md;
54 using namespace opensaml;
55 using namespace xercesc;
56 using namespace log4cpp;
59 void verifySignature(DOMDocument* doc, DOMNode* sigNode, const char* cert=NULL)
61 Category& log=Category::getInstance("siterefresh");
62 static const XMLCh ID[]={chLatin_I, chLatin_D, chNull};
64 // Load the signature.
66 DSIGSignature* sig=NULL;
68 sig=prov.newSignatureFromDOM(doc,sigNode);
73 // Verify the signature coverage.
74 DSIGReferenceList* refs=sig->getReferenceList();
75 if (sig->getSignatureMethod()==SIGNATURE_RSA && refs && refs->getSize()==1) {
76 DSIGReference* ref=refs->item(0);
78 const XMLCh* URI=ref->getURI();
79 if (!URI || !*URI || (*URI==chPound &&
80 !XMLString::compareString(&URI[1],static_cast<DOMElement*>(sigNode->getParentNode())->getAttributeNS(NULL,ID)))) {
81 DSIGTransformList* tlist=ref->getTransforms();
82 for (unsigned int i=0; tlist && i<tlist->getSize(); i++) {
83 if (tlist->item(i)->getTransformType()==TRANSFORM_ENVELOPED_SIGNATURE)
85 else if (tlist->item(i)->getTransformType()!=TRANSFORM_EXC_C14N) {
95 log.error("detected an invalid signature profile");
96 throw SignatureException("detected an invalid signature profile");
100 // Load the certificate, stripping the header and trailer.
102 auto_ptr<OpenSSLCryptoX509> x509(new OpenSSLCryptoX509());
103 bool sawheader=false;
104 ifstream infile(cert);
105 while (!getline(infile,line).fail()) {
106 if (line.find("CERTIFICATE-----")==string::npos) {
108 certbuf+=line + '\n';
113 x509->loadX509Base64Bin(certbuf.data(),certbuf.length());
114 sig->setSigningKey(x509->clonePublicKey());
117 log.warn("verifying with key inside signature, this is a sanity check but provides no security");
118 XSECKeyInfoResolverDefault resolver;
119 sig->setKeyInfoResolver(resolver.clone());
122 if (!sig->verify()) {
123 log.error("detected an invalid signature value");
124 throw SignatureException("detected an invalid signature value");
127 prov.releaseSignature(sig);
131 prov.releaseSignature(sig);
136 int main(int argc,char* argv[])
139 SAMLConfig& conf=SAMLConfig::getConfig();
141 char* url_param=NULL;
142 char* cert_param=NULL;
143 char* out_param=NULL;
144 char* path=getenv("SHIBSCHEMAS");
146 char* name_param=NULL;
148 for (int i=1; i<argc; i++) {
149 if (!strcmp(argv[i],"--schema") && i+1<argc)
151 else if (!strcmp(argv[i],"--url") && i+1<argc)
153 else if (!strcmp(argv[i],"--noverify"))
155 else if (!strcmp(argv[i],"--cert") && i+1<argc)
156 cert_param=argv[++i];
157 else if (!strcmp(argv[i],"--out") && i+1<argc)
159 else if (!strcmp(argv[i],"--rootns") && i+1<argc)
161 else if (!strcmp(argv[i],"--rootname") && i+1<argc)
162 name_param=argv[++i];
165 if (verify && !cert_param) {
166 cout << "usage: " << argv[0] << endl <<
167 "\t--url <URL of metadata>" << endl <<
168 "\t--noverify OR --cert <PEM Certificate>" << endl <<
169 "\t[--out <pathname to copy data to>]" << endl <<
170 "\t[--schema <schema path>]" << endl <<
171 "\t[--rootns <root element XML namespace>]" << endl <<
172 "\t[--rootname <root element name>]" << endl;
176 Category::setRootPriority(Priority::WARN);
177 Category::getRoot().addAppender(new OstreamAppender("default",&cerr));
178 Category& log=Category::getInstance("siterefresh");
183 saml::XML::registerSchema(shibtarget::XML::SAML2META_NS,shibtarget::XML::SAML2META_SCHEMA_ID);
184 saml::XML::registerSchema(shibtarget::XML::SAML2ASSERT_NS,shibtarget::XML::SAML2ASSERT_SCHEMA_ID);
185 saml::XML::registerSchema(shibtarget::XML::XMLENC_NS,shibtarget::XML::XMLENC_SCHEMA_ID);
189 // Parse the specified document.
190 static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull};
191 DOMDocument* doc=NULL;
192 if (url_param && *url_param) {
193 URLInputSource src(base,url_param);
194 Wrapper4InputSource dsrc(&src,false);
195 doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
198 StdInInputSource src;
199 Wrapper4InputSource dsrc(&src,false);
200 doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
203 // Check root element.
204 if (ns_param && name_param) {
205 auto_ptr_XMLCh ns(ns_param);
206 auto_ptr_XMLCh name(name_param);
207 if (!XMLHelper::isNodeNamed(doc->getDocumentElement(),ns.get(),name.get()))
208 throw XMLObjectException(string("Root element does not match specified QName of {") + ns_param + "}:" + name_param);
210 else if (!XMLHelper::isNodeNamed(doc->getDocumentElement(),SAML20MD_NS,EntitiesDescriptor::LOCAL_NAME) &&
211 !XMLHelper::isNodeNamed(doc->getDocumentElement(),SAML20MD_NS,EntityDescriptor::LOCAL_NAME))
212 throw XMLObjectException("Root element does not signify a known metadata format");
214 // Verify the "root" signature.
215 DOMElement* rootSig=XMLHelper::getFirstChildElement(doc->getDocumentElement(),XMLSIG_NS,Signature::LOCAL_NAME);
218 verifySignature(doc,rootSig,cert_param);
222 log.error("unable to locate root signature to verify in document");
223 throw SignatureException("Verification implies that the document must be signed");
227 log.warn("verification of signer disabled, make sure you trust the source of this file!");
228 verifySignature(doc,rootSig,cert_param);
231 log.warn("verification disabled, and file is unsigned!");
234 // Verify all signatures.
235 DOMNodeList* siglist=doc->getElementsByTagNameNS(XMLSIG_NS,Signature::LOCAL_NAME);
236 for (unsigned int i=0; siglist && i<siglist->getLength(); i++)
237 verifySignature(doc,siglist->item(i),cert_param);
240 // Output the data to the specified file.
241 ofstream outfile(out_param);
242 outfile << *(doc->getDocumentElement());
245 cout << *(doc->getDocumentElement());
248 catch (SignatureException&) {
251 catch(XMLToolingException& e) {
252 log.errorStream() << "caught an XMLTooling exception: " << e.what() << CategoryStream::ENDLINE;
255 catch(XMLException& e) {
256 auto_ptr_char temp(e.getMessage());
257 log.errorStream() << "caught an XML exception: " << temp.get() << CategoryStream::ENDLINE;
260 catch(XSECException& e) {
261 auto_ptr_char temp(e.getMsg());
262 log.errorStream() << "caught an XMLSec exception: " << temp.get() << CategoryStream::ENDLINE;
265 catch(XSECCryptoException& e) {
266 log.errorStream() << "caught an XMLSecCrypto exception: " << e.getMsg() << CategoryStream::ENDLINE;
270 log.errorStream() << "caught an unknown exception" << CategoryStream::ENDLINE;