Switch exception class, seems to fail on some gcc versions.
[shibboleth/cpp-sp.git] / shibsp / handler / impl / StatusHandler.cpp
1 /*
2  *  Copyright 2001-2007 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 /**
18  * StatusHandler.cpp
19  * 
20  * Handler for exposing information about the internals of the SP.
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "ServiceProvider.h"
27 #include "handler/AbstractHandler.h"
28 #include "handler/RemotedHandler.h"
29 #include "util/CGIParser.h"
30
31 using namespace shibsp;
32 #ifndef SHIBSP_LITE
33 # include "SessionCache.h"
34 # include <saml/version.h>
35 using namespace opensaml::saml2md;
36 using namespace opensaml;
37 using namespace xmlsignature;
38 #endif
39 using namespace xmltooling;
40 using namespace std;
41
42 namespace shibsp {
43
44 #if defined (_MSC_VER)
45     #pragma warning( push )
46     #pragma warning( disable : 4250 )
47 #endif
48
49     class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter
50     {
51     public:
52         short acceptNode(const DOMNode* node) const {
53             return FILTER_REJECT;
54         }
55     };
56
57     static SHIBSP_DLLLOCAL Blocker g_Blocker;
58
59     class SHIBSP_API StatusHandler : public AbstractHandler, public RemotedHandler
60     {
61     public:
62         StatusHandler(const DOMElement* e, const char* appId);
63         virtual ~StatusHandler() {}
64
65         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
66         void receive(DDF& in, ostream& out);
67
68     private:
69         pair<bool,long> processMessage(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
70
71         set<string> m_acl;
72     };
73
74 #if defined (_MSC_VER)
75     #pragma warning( pop )
76 #endif
77
78     Handler* SHIBSP_DLLLOCAL StatusHandlerFactory(const pair<const DOMElement*,const char*>& p)
79     {
80         return new StatusHandler(p.first, p.second);
81     }
82
83 #ifndef XMLTOOLING_NO_XMLSEC
84     vector<XSECCryptoX509*> g_NoCerts;
85 #else
86     vector<string> g_NoCerts;
87 #endif
88
89     class DummyRequest : public HTTPRequest
90     {
91     public:
92         DummyRequest(const char* url) : m_parser(NULL), m_url(url), m_scheme(NULL), m_query(NULL), m_port(0) {
93 #ifdef HAVE_STRCASECMP
94             if (url && !strncasecmp(url,"http://",7)) {
95                 m_scheme="http";
96                 url+=7;
97             }
98             else if (url && !strncasecmp(url,"https://",8)) {
99                 m_scheme="https";
100                 url+=8;
101             }
102             else
103 #else
104             if (url && !strnicmp(url,"http://",7)) {
105                 m_scheme="http";
106                 m_port = 80;
107                 url+=7;
108             }
109             else if (url && !strnicmp(url,"https://",8)) {
110                 m_scheme="https";
111                 m_port = 443;
112                 url+=8;
113             }
114             else
115 #endif
116                 throw invalid_argument("Target parameter was not an absolute URL.");
117
118             m_query = strchr(url,'?');
119             if (m_query)
120                 m_query++;
121
122             const char* slash = strchr(url, '/');
123             const char* colon = strchr(url, ':');
124             if (colon && colon < slash) {
125                 m_hostname.assign(url, colon-url);
126                 string port(colon + 1, slash - colon);
127                 m_port = atoi(port.c_str());
128             }
129             else {
130                 m_hostname.assign(url, slash - url);
131             }
132             m_uri = slash;
133         }
134
135         ~DummyRequest() {
136             delete m_parser;
137         }
138
139         const char* getRequestURL() const {
140             return m_url;
141         }
142         const char* getScheme() const {
143             return m_scheme;
144         }
145         const char* getHostname() const {
146             return m_hostname.c_str();
147         }
148         int getPort() const {
149             return m_port;
150         }
151         const char* getRequestURI() const {
152             return m_uri.c_str();
153         }
154         const char* getMethod() const {
155             return "GET";
156         }
157         string getContentType() const {
158             return "";
159         }
160         long getContentLength() const {
161             return 0;
162         }
163         string getRemoteAddr() const {
164             return "";
165         }
166         string getRemoteUser() const {
167             return "";
168         }
169         const char* getRequestBody() const {
170             return NULL;
171         }
172         const char* getQueryString() const {
173             return m_query;
174         }
175         const char* getParameter(const char* name) const
176         {
177             if (!m_parser)
178                 m_parser=new CGIParser(*this);
179             
180             pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
181             return (bounds.first==bounds.second) ? NULL : bounds.first->second;
182         }
183         vector<const char*>::size_type getParameters(const char* name, vector<const char*>& values) const
184         {
185             if (!m_parser)
186                 m_parser=new CGIParser(*this);
187
188             pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
189             while (bounds.first!=bounds.second) {
190                 values.push_back(bounds.first->second);
191                 ++bounds.first;
192             }
193             return values.size();
194         }
195         string getHeader(const char* name) const {
196             return "";
197         }
198         virtual const
199 #ifndef XMLTOOLING_NO_XMLSEC
200             std::vector<XSECCryptoX509*>&
201 #else
202             std::vector<std::string>& 
203 #endif
204             getClientCertificates() const {
205                 return g_NoCerts;
206         }
207
208     private:
209         mutable CGIParser* m_parser;
210         const char* m_url;
211         const char* m_scheme;
212         const char* m_query;
213         int m_port;
214         string m_hostname,m_uri;
215     };
216 };
217
218 StatusHandler::StatusHandler(const DOMElement* e, const char* appId)
219     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".StatusHandler"), &g_Blocker)
220 {
221     string address(appId);
222     address += getString("Location").second;
223     setAddress(address.c_str());
224     if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
225         pair<bool,const char*> acl = getString("acl");
226         if (acl.first) {
227             string aclbuf=acl.second;
228             int j = 0;
229             for (unsigned int i=0;  i < aclbuf.length();  i++) {
230                 if (aclbuf.at(i)==' ') {
231                     m_acl.insert(aclbuf.substr(j, i-j));
232                     j = i+1;
233                 }
234             }
235             m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
236         }
237     }
238 }
239
240 pair<bool,long> StatusHandler::run(SPRequest& request, bool isHandler) const
241 {
242     SPConfig& conf = SPConfig::getConfig();
243     if (conf.isEnabled(SPConfig::InProcess)) {
244         if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) {
245             m_log.error("status handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str());
246             istringstream msg("Status Handler Blocked");
247             return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
248         }
249     }
250
251     const char* target = request.getParameter("target");
252     if (target) {
253         // RequestMap query, so handle it inproc.
254         DummyRequest dummy(target);
255         RequestMapper::Settings settings = request.getApplication().getServiceProvider().getRequestMapper()->getSettings(dummy);
256         map<string,const char*> props;
257         settings.first->getAll(props);
258
259         request.setContentType("text/xml");
260         stringstream msg;
261         msg << "<StatusHandler>";
262             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
263 #ifndef SHIBSP_LITE
264                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
265                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
266 #endif
267                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
268             msg << "<RequestSettings";
269             for (map<string,const char*>::const_iterator p = props.begin(); p != props.end(); ++p)
270                 msg << ' ' << p->first << "='" << p->second << "'";
271             msg << '>' << target << "</RequestSettings>";
272             msg << "<Status><OK/></Status>";
273         msg << "</StatusHandler>";
274         return make_pair(true,request.sendResponse(msg));
275     }
276     
277     try {
278         if (conf.isEnabled(SPConfig::OutOfProcess)) {
279             // When out of process, we run natively and directly process the message.
280             return processMessage(request.getApplication(), request, request);
281         }
282         else {
283             // When not out of process, we remote all the message processing.
284             DDF out,in = wrap(request);
285             DDFJanitor jin(in), jout(out);            
286             out=request.getServiceProvider().getListenerService()->send(in);
287             return unwrap(request, out);
288         }
289     }
290     catch (XMLToolingException& ex) {
291         m_log.error("error while processing request: %s", ex.what());
292         request.setContentType("text/xml");
293         stringstream msg;
294         msg << "<StatusHandler>";
295             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
296 #ifndef SHIBSP_LITE
297                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
298                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
299 #endif
300                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
301             msg << "<Status><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></Status>";
302         msg << "</StatusHandler>";
303         return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
304     }
305     catch (exception& ex) {
306         m_log.error("error while processing request: %s", ex.what());
307         request.setContentType("text/xml");
308         stringstream msg;
309         msg << "<StatusHandler>";
310             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
311 #ifndef SHIBSP_LITE
312                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
313                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
314 #endif
315                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
316             msg << "<Status><Exception type='std::exception'>" << ex.what() << "</Exception></Status>";
317         msg << "</StatusHandler>";
318         return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
319     }
320 }
321
322 void StatusHandler::receive(DDF& in, ostream& out)
323 {
324     // Find application.
325     const char* aid=in["application_id"].string();
326     const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
327     if (!app) {
328         // Something's horribly wrong.
329         m_log.error("couldn't find application (%s) for status request", aid ? aid : "(missing)");
330         throw ConfigurationException("Unable to locate application for status request, deleted?");
331     }
332     
333     // Wrap a response shim.
334     DDF ret(NULL);
335     DDFJanitor jout(ret);
336     auto_ptr<HTTPRequest> req(getRequest(in));
337     auto_ptr<HTTPResponse> resp(getResponse(ret));
338         
339     // Since we're remoted, the result should either be a throw, a false/0 return,
340     // which we just return as an empty structure, or a response/redirect,
341     // which we capture in the facade and send back.
342     processMessage(*app, *req.get(), *resp.get());
343     out << ret;
344 }
345
346 pair<bool,long> StatusHandler::processMessage(
347     const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse
348     ) const
349 {
350 #ifndef SHIBSP_LITE
351     m_log.debug("processing status request");
352
353     stringstream s;
354     s << "<StatusHandler>";
355     const char* status = "<OK/>";
356
357     s << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
358         << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
359         << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
360         << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
361
362     const char* param = NULL;
363     if (param) {
364     }
365     else {
366         // General configuration and status report.
367         try {
368             SessionCache* sc = application.getServiceProvider().getSessionCache(false);
369             if (sc) {
370                 sc->test();
371                 s << "<SessionCache><OK/></SessionCache>";
372             }
373             else {
374                 s << "<SessionCache><None/></SessionCache>";
375             }
376         }
377         catch (XMLToolingException& ex) {
378             s << "<SessionCache><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></SessionCache>";
379             status = "<Partial/>";
380         }
381         catch (exception& ex) {
382             s << "<SessionCache><Exception type='std::exception'>" << ex.what() << "</Exception></SessionCache>";
383             status = "<Partial/>";
384         }
385
386         s << "<Application id='" << application.getId() << "' entityID='" << application.getString("entityID").second << "'/>";
387
388         s << "<Handlers>";
389         vector<const Handler*> handlers;
390         application.getHandlers(handlers);
391         for (vector<const Handler*>::const_iterator h = handlers.begin(); h != handlers.end(); ++h) {
392             s << "<Handler type='" << (*h)->getType() << "' Location='" << (*h)->getString("Location").second << "'";
393             if ((*h)->getString("Binding").first)
394                 s << " Binding='" << (*h)->getString("Binding").second << "'";
395             s << "/>";
396         }
397         s << "</Handlers>";
398
399         const PropertySet* relyingParty=NULL;
400         param=httpRequest.getParameter("entityID");
401         if (param) {
402             Locker mlock(application.getMetadataProvider());
403             relyingParty = application.getRelyingParty(application.getMetadataProvider()->getEntityDescriptor(param));
404         }
405         if (!relyingParty)
406             relyingParty = application.getRelyingParty(NULL);
407         CredentialResolver* credResolver=application.getCredentialResolver();
408         if (credResolver) {
409             Locker credLocker(credResolver);
410             CredentialCriteria cc;
411             cc.setUsage(Credential::SIGNING_CREDENTIAL);
412             pair<bool,const char*> keyName = relyingParty->getString("keyName");
413             if (keyName.first)
414                 cc.getKeyNames().insert(keyName.second);
415             vector<const Credential*> creds;
416             credResolver->resolve(creds,&cc);
417             for (vector<const Credential*>::const_iterator c = creds.begin(); c != creds.end(); ++c) {
418                 KeyInfo* kinfo = (*c)->getKeyInfo();
419                 if (kinfo) {
420                     auto_ptr<KeyDescriptor> kd(KeyDescriptorBuilder::buildKeyDescriptor());
421                     kd->setUse(KeyDescriptor::KEYTYPE_SIGNING);
422                     kd->setKeyInfo(kinfo);
423                     s << *(kd.get());
424                 }
425             }
426
427             cc.setUsage(Credential::ENCRYPTION_CREDENTIAL);
428             creds.clear();
429             cc.getKeyNames().clear();
430             credResolver->resolve(creds,&cc);
431             for (vector<const Credential*>::const_iterator c = creds.begin(); c != creds.end(); ++c) {
432                 KeyInfo* kinfo = (*c)->getKeyInfo();
433                 if (kinfo) {
434                     auto_ptr<KeyDescriptor> kd(KeyDescriptorBuilder::buildKeyDescriptor());
435                     kd->setUse(KeyDescriptor::KEYTYPE_ENCRYPTION);
436                     kd->setKeyInfo(kinfo);
437                     s << *(kd.get());
438                 }
439             }
440         }
441     }
442
443     s << "<Status>" << status << "</Status></StatusHandler>";
444
445     httpResponse.setContentType("text/xml");
446     return make_pair(true, httpResponse.sendResponse(s));
447 #else
448     return make_pair(false,0);
449 #endif
450 }