Add XML-based client SSL config.
[shibboleth/cpp-sp.git] / shib-target / shib-config.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
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.
22  *
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
28  *
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.
33  *
34  *
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.
48  */
49
50 /*
51  * shib-config.cpp -- ShibTarget initialization and finalization routines
52  *
53  * Created By:  Derek Atkins <derek@ihtfp.com>
54  *
55  * $Id$
56  */
57
58 #include "internal.h"
59
60 #include <shib/shib-threads.h>
61
62 #include <log4cpp/PropertyConfigurator.hh>
63 #include <log4cpp/Category.hh>
64
65 #ifndef SHIBTARGET_INIFILE
66 #define SHIBTARGET_INIFILE "/opt/shibboleth/etc/shibboleth/shibboleth.ini"
67 #endif
68
69 class STConfig : public ShibTargetConfig
70 {
71 public:
72   STConfig(const char* app_name, const char* inifile);
73   ~STConfig();
74   void shutdown();
75   void init();
76   ShibINI& getINI() { return *ini; }
77
78   Iterator<const XMLCh*> getPolicies() { return Iterator<const XMLCh*>(policies); }
79
80   void ref();
81 private:
82   SAMLConfig& samlConf;
83   ShibConfig& shibConf;
84   ShibINI* ini;
85   string m_app_name;
86   int refcount;
87   vector<const XMLCh*> policies;
88   string m_SocketName;
89 #ifdef WANT_TCP_SHAR
90   vector<string> m_SocketACL;
91 #endif
92   friend ShibSockName shib_target_sockname();
93   friend ShibSockName shib_target_sockacl(unsigned int);
94 };
95
96 namespace {
97   STConfig * g_Config = NULL;
98   Mutex * g_lock = NULL;
99 }
100
101 CCache* shibtarget::g_shibTargetCCache = NULL;
102
103 /****************************************************************************/
104 // External Interface
105
106
107 void ShibTargetConfig::preinit()
108 {
109   if (g_lock) return;
110   g_lock = Mutex::create();
111 }
112
113 ShibTargetConfig& ShibTargetConfig::init(const char* app_name, const char* inifile)
114 {
115   if (!g_lock)
116     throw runtime_error ("ShibTargetConfig not pre-initialized");
117
118   if (!app_name)
119     throw runtime_error ("No Application name");
120   Lock lock(g_lock);
121
122   if (g_Config) {
123     g_Config->ref();
124     return *g_Config;
125   }
126
127   g_Config = new STConfig(app_name, inifile);
128   g_Config->init();
129   return *g_Config;
130 }
131
132 ShibTargetConfig& ShibTargetConfig::getConfig()
133 {
134     if (!g_Config)
135         throw SAMLException("ShibTargetConfig::getConfig() called with NULL configuration");
136     return *g_Config;
137 }
138
139 /****************************************************************************/
140 // STConfig
141
142 STConfig::STConfig(const char* app_name, const char* inifile)
143   :  samlConf(SAMLConfig::getConfig()), shibConf(ShibConfig::getConfig()),
144      m_app_name(app_name)
145 {
146   try {
147     ini = new ShibINI((inifile ? inifile : SHIBTARGET_INIFILE));
148   } catch (...) {
149     cerr << "Unable to load the INI file: " << 
150       (inifile ? inifile : SHIBTARGET_INIFILE) << endl;
151     throw;
152   }
153 }
154
155 void STConfig::init()
156 {
157   string app = m_app_name;
158   string tag;
159
160   // Initialize Log4cpp
161   if (ini->get_tag (app, SHIBTARGET_TAG_LOGGER, true, &tag)) {
162     cerr << "Loading new logging configuration from " << tag << "\n";
163     try {
164       PropertyConfigurator::configure(tag);
165       cerr << "New logging configuration loaded, check log destination for process status..." << "\n";
166     } catch (ConfigureFailure& e) {
167       cerr << "Error reading configuration: " << e.what() << "\n";
168     }
169   } else {
170     Category& category = Category::getRoot();
171     category.setPriority(log4cpp::Priority::DEBUG);
172     cerr << "No logger configuration found\n";
173   }
174
175   Category& log = Category::getInstance("shibtarget.STConfig");
176
177   saml::NDC ndc("STConfig::init");
178
179   // Init SAML Configuration
180   if (ini->get_tag (app, SHIBTARGET_TAG_SAMLCOMPAT, true, &tag))
181     samlConf.compatibility_mode = ShibINI::boolean(tag);
182   if (ini->get_tag (app, SHIBTARGET_TAG_SCHEMAS, true, &tag))
183     samlConf.schema_dir = tag;
184
185   // Init SAML Binding Configuration
186   if (ini->get_tag (app, SHIBTARGET_TAG_AATIMEOUT, true, &tag))
187     samlConf.binding_defaults.timeout = atoi(tag.c_str());
188   if (ini->get_tag (app, SHIBTARGET_TAG_AACONNECTTO, true, &tag))
189     samlConf.binding_defaults.conn_timeout = atoi(tag.c_str());
190   if (ini->get_tag (app, SHIBTARGET_TAG_CERTFILE, true, &tag))
191       log.error("using OBSOLETE certfile setting, please migrate to the XML-based credential format (see the latest target deploy guide)");
192   if (ini->get_tag (app, SHIBTARGET_TAG_KEYFILE, true, &tag))
193       log.error("using OBSOLETE keyfile setting, please migrate to the XML-based credential format (see the latest target deploy guide)");
194   if (ini->get_tag (app, SHIBTARGET_TAG_KEYPASS, true, &tag))
195       log.error("using OBSOLETE keypass setting, please migrate to the XML-based credential format (see the latest target deploy guide)");
196   if (ini->get_tag (app, SHIBTARGET_TAG_CALIST, true, &tag))
197       log.error("using OBSOLETE calist setting, please use the XML-based trust format (see the latest target deploy guide)");
198
199   try {
200     if (!samlConf.init()) {
201       log.fatal ("Failed to initialize SAML Library");
202       throw runtime_error ("Failed to initialize SAML Library");
203     } else
204       log.debug ("SAML Initialized");
205   } catch (...) {
206     log.crit ("Died initializing SAML Library");
207     throw;    
208   }
209
210   // Init Shib
211   try { 
212     if (!shibConf.init()) {
213       log.fatal ("Failed to initialize Shib library");
214       throw runtime_error ("Failed to initialize Shib Library");
215     } else
216       log.debug ("Shib Initialized");
217   } catch (...) {
218     log.crit ("Failed initializing Shib library.");
219     throw;
220   }
221
222   // Load any SAML extensions
223   string ext = "extensions:saml";
224   if (ini->exists(ext)) {
225     saml::NDC ndc("load_extensions");
226     ShibINI::Iterator* iter = ini->tag_iterator(ext);
227
228     for (const string* str = iter->begin(); str; str = iter->next()) {
229       string file = ini->get(ext, *str);
230       try
231       {
232         samlConf.saml_register_extension(file.c_str(),ini);
233         log.debug("%s: loading %s", str->c_str(), file.c_str());
234       }
235       catch (SAMLException& e)
236       {
237         log.crit("%s: %s", str->c_str(), e.what());
238       }
239     }
240     delete iter;
241   }
242
243   // Load the specified metadata.
244   if (ini->get_tag(app, SHIBTARGET_TAG_METADATA, true, &tag) && ini->exists(tag))
245   {
246     ShibINI::Iterator* iter=ini->tag_iterator(tag);
247     for (const string* prov=iter->begin(); prov; prov=iter->next())
248     {
249         string sources=ini->get(tag,*prov);
250         int j = 0;
251         for (int i = 0; i < sources.length(); i++)
252         {
253             if (sources.at(i) == ';')
254             {
255                 string val = sources.substr(j, i-j);
256                 j = i+1;
257                 log.info("registering metadata provider: type=%s, source=%s",prov->c_str(),val.c_str());
258                 if (!shibConf.addMetadata(prov->c_str(),val.c_str()))
259                 {
260                     log.crit("error adding metadata provider: type=%s, source=%s",prov->c_str(),val.c_str());
261                     if (app == SHIBTARGET_SHAR)
262                         throw runtime_error("error adding metadata provider");
263                 }
264             }
265         }
266         string val = sources.substr(j, sources.length()-j);
267         log.info("registering metadata provider: type=%s, source=%s",prov->c_str(),val.c_str());
268         if (!shibConf.addMetadata(prov->c_str(),val.c_str()))
269         {
270             log.crit("error adding metadata provider: type=%s, source=%s",prov->c_str(),val.c_str());
271             if (app == SHIBTARGET_SHAR)
272                 throw runtime_error("error adding metadata provider");
273         }
274     }
275     delete iter;
276   }
277   
278   // Backward-compatibility-hack to pull in aap-uri from [shire] and load
279   // as attribute metadata. We load this for anything, not just the SHIRE.
280   if (ini->get_tag(SHIBTARGET_SHIRE, "aap-uri", false, &tag))
281   {
282     log.warn("using DEPRECATED aap-uri setting for backward compatibility, please read the latest target deploy guide");
283     log.info("registering metadata provider: type=edu.internet2.middleware.shibboleth.target.AAP.XML, source=%s",tag.c_str());
284     if (!shibConf.addMetadata("edu.internet2.middleware.shibboleth.target.AAP.XML",tag.c_str()))
285     {
286         log.crit("error adding metadata provider: type=edu.internet2.middleware.shibboleth.target.AAP.XML, source=%s",tag.c_str());
287         if (!strcmp(app.c_str(), SHIBTARGET_SHAR))
288             throw runtime_error("error adding metadata provider");
289     }
290   }
291   
292   // Load SAML policies.
293   if (ini->exists(SHIBTARGET_POLICIES)) {
294     log.info("loading SAML policies");
295     ShibINI::Iterator* iter = ini->tag_iterator(SHIBTARGET_POLICIES);
296
297     for (const string* str = iter->begin(); str; str = iter->next()) {
298         policies.push_back(XMLString::transcode(ini->get(SHIBTARGET_POLICIES, *str).c_str()));
299     }
300     delete iter;
301   }
302   
303   // Initialize the SHAR Cache
304   if (!strcmp (app.c_str(), SHIBTARGET_SHAR)) {
305     const char * cache_type = NULL;
306     if (ini->get_tag (app, SHIBTARGET_TAG_CACHETYPE, true, &tag))
307       cache_type = tag.c_str();
308
309     g_shibTargetCCache = CCache::getInstance(cache_type);
310   }
311
312   // Process socket settings.
313   m_SocketName=ini->get(SHIBTARGET_GENERAL, "sharsocket");
314   if (m_SocketName.empty())
315     m_SocketName=SHIB_SHAR_SOCKET;
316
317 #ifdef WANT_TCP_SHAR
318   string sockacl=ini->get(SHIBTARGET_SHAR, "sharacl");
319   if (sockacl.length()>0)
320   {
321     int j = 0;
322     for (int i = 0;  i < sockacl.length();  i++)
323     {
324         if (sockacl.at(i)==' ')
325         {
326             string addr=sockacl.substr(j, i-j);
327             j = i+1;
328             m_SocketACL.push_back(addr);
329         }
330     }
331     string addr=sockacl.substr(j, sockacl.length()-j);
332     m_SocketACL.push_back(addr);
333   }
334   else
335     m_SocketACL.push_back("127.0.0.1");
336 #endif
337
338   ref();
339   log.debug("finished");
340 }
341
342 STConfig::~STConfig()
343 {
344   for (vector<const XMLCh*>::iterator i=policies.begin(); i!=policies.end(); i++)
345     delete const_cast<XMLCh*>(*i);
346   
347   delete g_shibTargetCCache;
348   delete ini;
349
350   shibConf.term();
351   samlConf.term();
352 }
353
354 void STConfig::ref()
355 {
356   refcount++;
357 }
358
359 void STConfig::shutdown()
360 {
361   refcount--;
362   if (!refcount) {
363     delete g_Config;
364     g_Config = NULL;
365   }
366 }
367
368 extern "C" ShibSockName shib_target_sockname(void)
369 {
370     return (g_Config ? g_Config->m_SocketName.c_str() : (ShibSockName)0);
371 }
372
373 extern "C" ShibSockName shib_target_sockacl(unsigned int index)
374 {
375 #ifdef WANT_TCP_SHAR
376     if (g_Config && index<g_Config->m_SocketACL.size())
377         return g_Config->m_SocketACL[index].c_str();
378 #endif
379     return (ShibSockName)0;
380 }