https://issues.shibboleth.net/jira/browse/SSPCPP-119
[shibboleth/cpp-sp.git] / shib / BasicTrust.cpp
1 /*
2  *  Copyright 2001-2005 Internet2
3  * 
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /* BasicTrust.cpp - a trust implementation that relies solely on standard SAML metadata
18
19    Scott Cantor
20    4/9/05
21
22    $History:$
23 */
24
25 #include "internal.h"
26
27 #include <openssl/x509.h>
28 #include <xsec/enc/XSECCryptoException.hpp>
29 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
30 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
31 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
32
33 using namespace shibboleth::logging;
34 using namespace shibboleth;
35 using namespace saml;
36 using namespace std;
37
38 IPlugIn* BasicTrustFactory(const DOMElement* e)
39 {
40     return new BasicTrust(e);
41 }
42
43 static const XMLCh resolver[] =
44 { chLatin_K, chLatin_e, chLatin_y, chLatin_I, chLatin_n, chLatin_f, chLatin_o,
45   chLatin_R, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chNull
46 };
47 static const XMLCh type[] =
48 { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };
49
50 static const XMLCh debug[] =
51 { chLatin_d, chLatin_e, chLatin_b, chLatin_u, chLatin_g, chNull };
52
53 BasicTrust::BasicTrust(const DOMElement* e) : m_debug(false)
54 {
55     // Debug mode?
56     const XMLCh* flag=e->getAttributeNS(NULL,debug);
57     if (flag && (*flag==chLatin_t || *flag==chDigit_1))
58         m_debug=true;
59     
60     // Find any KeyResolver plugins.
61     e=saml::XML::getFirstChildElement(e);
62     while (e) {
63         if (!XMLString::compareString(resolver,e->getLocalName()) && e->hasAttributeNS(NULL,type)) {
64             try {
65                 auto_ptr_char temp(e->getAttributeNS(NULL,type));
66                 m_resolvers.push_back(KeyInfoResolver::getInstance(temp.get(),e));
67             }
68             catch (SAMLException& ex) {
69                 Category::getInstance(SHIB_LOGCAT".Trust.Basic").error(
70                     "caught SAML exception building KeyInfoResolver plugin: %s",ex.what()
71                     );
72             }
73 #ifndef _DEBUG
74             catch (...) {
75                 Category::getInstance(SHIB_LOGCAT".Trust.Basic").error("caught unknown exception building KeyInfoResolver plugin");
76             }
77 #endif
78         }
79         e=saml::XML::getNextSiblingElement(e);
80     }
81     m_resolvers.push_back(KeyInfoResolver::getInstance(e));
82 }
83
84 BasicTrust::~BasicTrust()
85 {
86     for (vector<KeyInfoResolver*>::iterator i=m_resolvers.begin(); i!=m_resolvers.end(); i++)
87         delete *i;
88 }
89
90 bool BasicTrust::validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName)
91 {
92 #ifdef _DEBUG
93     saml::NDC ndc("validate");
94 #endif
95     Category& log=Category::getInstance(SHIB_LOGCAT".Trust.Basic");
96
97     if (!certEE) {
98         log.error("no certificate provided for comparison");
99         return false;
100     }
101
102     if (m_debug) {
103         log.warn("running in debug mode, we accept anything!");
104         return true;
105     }
106
107     // The new "basic" trust implementation relies solely on certificates living within the
108     // role interface to verify the EE certificate.
109
110     log.debug("comparing key inside certificate to KeyDescriptors");
111     Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
112     while (kd_i.hasNext()) {
113         const IKeyDescriptor* kd=kd_i.next();
114         if (kd->getUse()==IKeyDescriptor::encryption)
115             continue;
116         DSIGKeyInfoList* KIL=kd->getKeyInfo();
117         if (!KIL)
118             continue;
119         Iterator<KeyInfoResolver*> resolvers(m_resolvers);
120         while (resolvers.hasNext()) {
121             XSECCryptoKey* key=NULL;
122             try {
123                 key=((XSECKeyInfoResolver*)*resolvers.next())->resolveKey(KIL);
124             }
125             catch (XSECCryptoException& ex) {
126                 log.error("caught an XMLSec crypto exception while resolving key: %s", ex.getMsg());
127             }
128             if (key) {
129                 log.debug("KeyDescriptor resolved into a key, comparing it...");
130                 if (key->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
131                     log.error("only the OpenSSL XSEC provider is supported");
132                     continue;
133                 }
134
135                 switch (key->getKeyType()) {
136                     case XSECCryptoKey::KEY_RSA_PUBLIC:
137                     case XSECCryptoKey::KEY_RSA_PAIR:
138                     {
139                         RSA* rsa = static_cast<OpenSSLCryptoKeyRSA*>(key)->getOpenSSLRSA();
140                         EVP_PKEY* evp = X509_PUBKEY_get(X509_get_X509_PUBKEY(reinterpret_cast<X509*>(certEE)));
141                         if (rsa && evp && evp->type == EVP_PKEY_RSA &&
142                                 BN_cmp(rsa->n,evp->pkey.rsa->n) == 0 && BN_cmp(rsa->e,evp->pkey.rsa->e) == 0) {
143                             if (evp)
144                                 EVP_PKEY_free(evp);
145                             log.debug("matching key found in KeyDescriptor");
146                             return true;
147                         }
148                         if (evp)
149                             EVP_PKEY_free(evp);
150                         break;
151                     }
152                 
153                     case XSECCryptoKey::KEY_DSA_PUBLIC:
154                     case XSECCryptoKey::KEY_DSA_PAIR:
155                     {
156                         DSA* dsa = static_cast<OpenSSLCryptoKeyDSA*>(key)->getOpenSSLDSA();
157                         EVP_PKEY* evp = X509_PUBKEY_get(X509_get_X509_PUBKEY(reinterpret_cast<X509*>(certEE)));
158                         if (dsa && evp && evp->type == EVP_PKEY_DSA && BN_cmp(dsa->pub_key,evp->pkey.dsa->pub_key) == 0) {
159                             if (evp)
160                                 EVP_PKEY_free(evp);
161                             log.debug("matching key found in KeyDescriptor");
162                             return true;
163                         }
164                         if (evp)
165                             EVP_PKEY_free(evp);
166                         break;
167                     }
168
169                     default:
170                         log.warn("unknown key type in KeyDescriptor, skipping...");
171                 }
172             }
173         }
174     }
175     
176     log.debug("failed to find a matching key for certificate in KeyDescriptors");
177     return false;
178 }
179
180 bool BasicTrust::validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator)
181 {
182 #ifdef _DEBUG
183     saml::NDC ndc("validate");
184 #endif
185     Category& log=Category::getInstance(SHIB_LOGCAT".Trust.Basic");
186
187     if (m_debug) {
188         log.warn("running in debug mode, we accept anything!");
189         return true;
190     }
191
192     // The new "basic" trust implementation relies solely on keys living within the
193     // role interface to verify the token. No indirection of any sort is allowed,
194     // unless an alternate key resolver is involved.
195  
196     log.debug("validating signature with KeyDescriptors");
197     Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
198     while (kd_i.hasNext()) {
199         const IKeyDescriptor* kd=kd_i.next();
200         if (kd->getUse()==IKeyDescriptor::encryption)
201             continue;
202         DSIGKeyInfoList* KIL=kd->getKeyInfo();
203         if (!KIL)
204             continue;
205         Iterator<KeyInfoResolver*> resolvers(m_resolvers);
206         while (resolvers.hasNext()) {
207             XSECCryptoKey* key=NULL;
208             try {
209                 key=((XSECKeyInfoResolver*)*resolvers.next())->resolveKey(KIL);
210             }
211             catch (XSECCryptoException& ex) {
212                 log.error("caught an XMLSec crypto exception while resolving key: %s", ex.getMsg());
213             }
214             if (key) {
215                 log.debug("KeyDescriptor resolved into a key, trying it...");
216                 try {
217                     token.verify(key);
218                     log.debug("signature verified with KeyDescriptor");
219                     return true;
220                 }
221                 catch (SAMLException& e) {
222                     log.debug("verification with KeyDescriptor failed: %s", e.what());
223                 }
224             }
225         }
226     }
227     
228     log.debug("failed to validate signature with KeyDescriptors");
229     return false;
230 }