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 <sys/types.h>
63 #include <openssl/pkcs12.h>
64 #include <log4cpp/Category.hh>
65 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
66 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
67 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
70 using namespace shibboleth;
71 using namespace log4cpp;
74 // OpenSSL password callback...
75 int passwd_callback(char* buf, int len, int verify, void* passwd)
79 if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
81 strcpy(buf,reinterpret_cast<char*>(passwd));
88 // File-based resolver
90 class FileResolver : public ICredResolver
93 FileResolver(const DOMElement* e);
95 virtual void attach(void* ctx) const;
96 virtual XSECCryptoKey* getKey() const;
97 virtual saml::Iterator<XSECCryptoX509*> getCertificates() const { return m_xseccerts; }
98 virtual void dump(FILE* f) const;
101 enum format_t { DER=SSL_FILETYPE_ASN1, PEM=SSL_FILETYPE_PEM, _PKCS12 };
102 format_t m_keyformat;
103 string m_keypath,m_keypass;
104 vector<X509*> m_certs;
105 vector<XSECCryptoX509*> m_xseccerts;
108 IPlugIn* FileCredResolverFactory(const DOMElement* e)
110 return new FileResolver(e);
113 FileResolver::FileResolver(const DOMElement* e)
116 saml::NDC ndc("FileResolver");
118 static const XMLCh cPEM[] = { chLatin_P, chLatin_E, chLatin_M, chNull };
119 static const XMLCh cDER[] = { chLatin_D, chLatin_E, chLatin_R, chNull };
122 e=saml::XML::getFirstChildElement(e);
123 const XMLCh* format=e->getAttributeNS(NULL,SHIB_L(format));
124 if (!format || !*format || !XMLString::compareString(format,cPEM))
126 else if (!XMLString::compareString(format,cDER))
131 const XMLCh* password=e->getAttributeNS(NULL,SHIB_L(password));
133 auto_ptr_char kp(password);
137 const XMLCh* s=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path))->getFirstChild()->getNodeValue();
138 auto_ptr_char kpath(s);
141 struct _stat stat_buf;
142 if (_stat(kpath.get(), &stat_buf) != 0)
144 struct stat stat_buf;
145 if (stat(kpath.get(), &stat_buf) != 0)
148 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("key file '%s' can't be opened", kpath.get());
149 throw CredentialException("FileResolver() can't access key file");
151 m_keypath=kpath.get();
153 // Check for Certificate
154 e=saml::XML::getNextSiblingElement(e);
155 password=e->getAttributeNS(NULL,SHIB_L(password));
156 auto_ptr_char certpass(password);
157 s=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path))->getFirstChild()->getNodeValue();
158 auto_ptr_char certpath(s);
162 BIO* in=BIO_new(BIO_s_file_internal());
163 if (in && BIO_read_filename(in,certpath.get())>0) {
164 format=e->getAttributeNS(NULL,SHIB_L(format));
165 if (!format || !*format || !XMLString::compareString(format,cPEM)) {
166 while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get()))) {
167 m_certs.push_back(x);
170 else if (!XMLString::compareString(format,cDER)) {
171 x=d2i_X509_bio(in,NULL);
173 m_certs.push_back(x);
177 throw CredentialException("FileResolver() unable to load DER certificate from file");
181 PKCS12* p12=d2i_PKCS12_bio(in,NULL);
183 PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
187 m_certs.push_back(x);
193 throw CredentialException("FileResolver() unable to load PKCS12 certificate from file");
203 throw CredentialException("FileResolver() unable to load PEM certificate(s) from file");
210 // Load any extra CA files.
211 DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::CREDS_NS,SHIB_L(CAPath));
212 for (int i=0; nlist && i<nlist->getLength(); i++) {
213 s=static_cast<DOMElement*>(nlist->item(i))->getFirstChild()->getNodeValue();
214 auto_ptr_char capath(s);
216 in=BIO_new(BIO_s_file_internal());
217 if (in && BIO_read_filename(in,capath.get())>0) {
218 if (!format || !*format || !XMLString::compareString(format,cPEM)) {
219 while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get()))) {
220 m_certs.push_back(x);
223 else if (!XMLString::compareString(format,cDER)) {
224 x=d2i_X509_bio(in,NULL);
226 m_certs.push_back(x);
230 throw CredentialException("FileResolver() unable to load DER CA certificate from file");
234 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
236 PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
240 m_certs.push_back(x);
246 throw CredentialException("FileResolver() unable to load PKCS12 CA certificate from file");
255 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("CA file '%s' can't be opened", capath.get());
256 throw CredentialException("FileResolver() can't open CA file");
261 for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)
266 // Reflect certs over to XSEC form.
267 for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)
268 m_xseccerts.push_back(new OpenSSLCryptoX509(*j));
271 FileResolver::~FileResolver()
273 for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
275 for (vector<XSECCryptoX509*>::iterator j=m_xseccerts.begin(); j!=m_xseccerts.end(); j++)
279 void FileResolver::attach(void* ctx) const
282 saml::NDC ndc("FileResolver");
285 SSL_CTX* ssl_ctx=reinterpret_cast<SSL_CTX*>(ctx);
288 SSL_CTX_set_default_passwd_cb(ssl_ctx, passwd_callback);
289 SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, const_cast<char*>(m_keypass.c_str()));
295 ret=SSL_CTX_use_PrivateKey_file(ssl_ctx, m_keypath.c_str(), m_keyformat);
299 ret=SSL_CTX_use_RSAPrivateKey_file(ssl_ctx, m_keypath.c_str(), m_keyformat);
303 BIO* in=BIO_new(BIO_s_file_internal());
304 if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
306 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
308 PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
311 ret=SSL_CTX_use_PrivateKey(ssl_ctx, pkey);
323 throw CredentialException("FileResolver::attach() unable to set private key");
327 for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {
328 if (i==m_certs.begin()) {
329 if (SSL_CTX_use_certificate(ssl_ctx, *i) != 1) {
331 throw CredentialException("FileResolver::attach() unable to set EE certificate in context");
335 // When we add certs, they don't get ref counted, so we need to duplicate them.
336 X509* dup = X509_dup(*i);
337 if (SSL_CTX_add_extra_chain_cert(ssl_ctx, dup) != 1) {
340 throw CredentialException("FileResolver::attach() unable to add CA certificate to context");
346 XSECCryptoKey* FileResolver::getKey() const
350 BIO* in=BIO_new(BIO_s_file_internal());
351 if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
355 pkey=PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(m_keypass.c_str()));
359 pkey=d2i_PrivateKey_bio(in, NULL);
363 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
365 PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
374 // Now map it to an XSEC wrapper.
376 XSECCryptoKey* ret=NULL;
380 ret=new OpenSSLCryptoKeyRSA(pkey);
384 ret=new OpenSSLCryptoKeyDSA(pkey);
388 saml::NDC ndc("FileResolver");
389 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("unsupported private key type");
396 saml::NDC ndc("FileResolver");
398 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("FileResolver::getKey() unable to load private key from file");
402 void FileResolver::dump(FILE* f) const
406 BIO* in=BIO_new(BIO_s_file_internal());
407 if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
408 if (m_keyformat==DER)
409 rsa=d2i_RSAPrivateKey_bio(in,NULL);
410 else if (m_keyformat==PEM)
411 rsa=PEM_read_bio_RSAPrivateKey(in,NULL,passwd_callback,const_cast<char*>(m_keypass.c_str()));
414 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
416 PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
419 fprintf(f,"----- PRIVATE KEY -----\n");
420 if (pkey->type==EVP_PK_RSA)
421 RSA_print_fp(f,pkey->pkey.rsa,0);
422 else if (pkey->type==EVP_PK_DSA)
423 DSA_print_fp(f,pkey->pkey.dsa,0);
429 fprintf(f,"----- PRIVATE KEY -----\n");
430 RSA_print_fp(f,rsa,0);
439 // Dump certificates.
440 for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {
441 fprintf(f,"----- CERTIFICATE(S) -----\n");
442 #if (OPENSSL_VERSION_NUMBER > 0x009070000L)
443 X509_print_ex_fp(f,*i,XN_FLAG_SEP_MULTILINE,0);