2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 /* CredResolvers.cpp - implementations of the ICredResolver interface
60 #include <openssl/pkcs12.h>
61 #include <log4cpp/Category.hh>
62 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
63 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
64 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
67 using namespace shibboleth;
68 using namespace log4cpp;
71 // OpenSSL password callback...
72 int passwd_callback(char* buf, int len, int verify, void* passwd)
76 if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
78 strcpy(buf,reinterpret_cast<char*>(passwd));
85 // File-based resolver
87 class FileResolver : public ICredResolver
90 FileResolver(const DOMElement* e);
92 virtual void attach(void* ctx) const;
93 virtual XSECCryptoKey* getKey() const;
94 virtual saml::Iterator<XSECCryptoX509*> getCertificates() const { return m_xseccerts; }
95 virtual void dump(FILE* f) const;
98 enum format_t { DER=SSL_FILETYPE_ASN1, PEM=SSL_FILETYPE_PEM, _PKCS12 };
100 string m_keypath,m_keypass;
101 vector<X509*> m_certs;
102 vector<XSECCryptoX509*> m_xseccerts;
105 extern "C" ICredResolver* FileCredResolverFactory(const DOMElement* e)
107 return new FileResolver(e);
110 FileResolver::FileResolver(const DOMElement* e)
112 saml::NDC ndc("FileResolver");
113 static const XMLCh cPEM[] = { chLatin_P, chLatin_E, chLatin_M, chNull };
114 static const XMLCh cDER[] = { chLatin_D, chLatin_E, chLatin_R, chNull };
117 e=saml::XML::getFirstChildElement(e);
118 const XMLCh* format=e->getAttributeNS(NULL,SHIB_L(format));
119 if (!format || !*format || !XMLString::compareString(format,cPEM))
121 else if (!XMLString::compareString(format,cDER))
126 const XMLCh* password=e->getAttributeNS(NULL,SHIB_L(password));
128 auto_ptr_char kp(password);
132 const XMLCh* s=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path))->getFirstChild()->getNodeValue();
133 auto_ptr_char kpath(s);
136 struct _stat stat_buf;
137 if (_stat(kpath.get(), &stat_buf) != 0)
139 struct stat stat_buf;
140 if (stat(kpath.get(), &stat_buf) != 0)
143 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("key file '%s' can't be opened", kpath.get());
144 throw CredentialException("FileResolver() can't access key file");
146 m_keypath=kpath.get();
148 // Check for Certificate
149 e=saml::XML::getNextSiblingElement(e);
150 password=e->getAttributeNS(NULL,SHIB_L(password));
151 auto_ptr_char certpass(password);
152 s=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path))->getFirstChild()->getNodeValue();
153 auto_ptr_char certpath(s);
157 BIO* in=BIO_new(BIO_s_file_internal());
158 if (in && BIO_read_filename(in,certpath.get())>0) {
159 format=e->getAttributeNS(NULL,SHIB_L(format));
160 if (!format || !*format || !XMLString::compareString(format,cPEM)) {
161 while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get()))) {
162 m_certs.push_back(x);
165 else if (!XMLString::compareString(format,cDER)) {
166 x=d2i_X509_bio(in,NULL);
168 m_certs.push_back(x);
172 throw CredentialException("FileResolver() unable to load DER certificate from file");
176 PKCS12* p12=d2i_PKCS12_bio(in,NULL);
178 PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
182 m_certs.push_back(x);
188 throw CredentialException("FileResolver() unable to load PKCS12 certificate from file");
197 // Load any extra CA files.
198 DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::CREDS_NS,SHIB_L(CAPath));
199 for (int i=0; nlist && i<nlist->getLength(); i++) {
200 s=static_cast<DOMElement*>(nlist->item(i))->getFirstChild()->getNodeValue();
201 auto_ptr_char capath(s);
203 in=BIO_new(BIO_s_file_internal());
204 if (in && BIO_read_filename(in,capath.get())>0) {
205 if (!format || !*format || !XMLString::compareString(format,cPEM)) {
206 while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get()))) {
207 m_certs.push_back(x);
210 else if (!XMLString::compareString(format,cDER)) {
211 x=d2i_X509_bio(in,NULL);
213 m_certs.push_back(x);
217 throw CredentialException("FileResolver() unable to load DER CA certificate from file");
221 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
223 PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
227 m_certs.push_back(x);
233 throw CredentialException("FileResolver() unable to load PKCS12 CA certificate from file");
242 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("CA file '%s' can't be opened", capath.get());
243 throw CredentialException("FileResolver() can't open CA file");
248 for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)
253 // Reflect certs over to XSEC form.
254 for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)
255 m_xseccerts.push_back(new OpenSSLCryptoX509(*j));
258 FileResolver::~FileResolver()
260 for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
262 for (vector<XSECCryptoX509*>::iterator j=m_xseccerts.begin(); j!=m_xseccerts.end(); j++)
266 void FileResolver::attach(void* ctx) const
268 saml::NDC ndc("FileResolver");
270 SSL_CTX* ssl_ctx=reinterpret_cast<SSL_CTX*>(ctx);
273 SSL_CTX_set_default_passwd_cb(ssl_ctx, passwd_callback);
274 SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, const_cast<char*>(m_keypass.c_str()));
280 ret=SSL_CTX_use_PrivateKey_file(ssl_ctx, m_keypath.c_str(), m_keyformat);
284 ret=SSL_CTX_use_RSAPrivateKey_file(ssl_ctx, m_keypath.c_str(), m_keyformat);
288 BIO* in=BIO_new(BIO_s_file_internal());
289 if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
291 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
293 PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
296 ret=SSL_CTX_use_PrivateKey(ssl_ctx, pkey);
308 throw CredentialException("FileResolver::attach() unable to set private key");
312 for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {
313 if (i==m_certs.begin()) {
314 if (SSL_CTX_use_certificate(ssl_ctx, *i)!=1) {
316 throw CredentialException("FileResolver::attach() unable to set EE certificate in context");
320 // When we add certs, they don't get ref counted, so we need to duplicate them.
321 X509* dup = X509_dup(*i);
322 if (SSL_CTX_add_extra_chain_cert(ssl_ctx, dup) != 0) {
325 throw CredentialException("FileResolver::attach() unable to add CA certificate to context");
331 XSECCryptoKey* FileResolver::getKey() const
335 BIO* in=BIO_new(BIO_s_file_internal());
336 if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
340 pkey=PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(m_keypass.c_str()));
344 pkey=d2i_PrivateKey_bio(in, NULL);
348 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
350 PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
359 // Now map it to an XSEC wrapper.
361 XSECCryptoKey* ret=NULL;
365 ret=new OpenSSLCryptoKeyRSA(pkey);
369 ret=new OpenSSLCryptoKeyDSA(pkey);
373 saml::NDC ndc("FileResolver");
374 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("unsupported private key type");
381 saml::NDC ndc("FileResolver");
383 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("FileResolver::getKey() unable to load private key from file");
387 void FileResolver::dump(FILE* f) const
391 BIO* in=BIO_new(BIO_s_file_internal());
392 if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
393 if (m_keyformat==DER)
394 rsa=d2i_RSAPrivateKey_bio(in,NULL);
395 else if (m_keyformat==PEM)
396 rsa=PEM_read_bio_RSAPrivateKey(in,NULL,passwd_callback,const_cast<char*>(m_keypass.c_str()));
399 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
401 PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
404 fprintf(f,"----- PRIVATE KEY -----\n");
405 if (pkey->type==EVP_PK_RSA)
406 RSA_print_fp(f,pkey->pkey.rsa,0);
407 else if (pkey->type==EVP_PK_DSA)
408 DSA_print_fp(f,pkey->pkey.dsa,0);
414 fprintf(f,"----- PRIVATE KEY -----\n");
415 RSA_print_fp(f,rsa,0);
424 // Dump certificates.
425 for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {
426 fprintf(f,"----- CERTIFICATE(S) -----\n");
427 #if (OPENSSL_VERSION_NUMBER > 0x009070000L)
428 X509_print_ex_fp(f,*i,XN_FLAG_SEP_MULTILINE,0);