2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
24 * Handler for exposing information about the internals of the SP.
28 #include "Application.h"
29 #include "exceptions.h"
30 #include "ServiceProvider.h"
31 #include "SPRequest.h"
32 #include "handler/RemotedHandler.h"
33 #include "handler/SecuredHandler.h"
34 #include "util/CGIParser.h"
36 #include <boost/iterator/indirect_iterator.hpp>
37 #include <boost/scoped_ptr.hpp>
38 #include <xmltooling/version.h>
39 #include <xmltooling/util/DateTime.h>
41 #ifdef HAVE_SYS_UTSNAME_H
42 # include <sys/utsname.h>
45 using namespace shibsp;
47 # include "SessionCache.h"
48 # include "metadata/MetadataProviderCriteria.h"
49 # include <saml/version.h>
50 # include <saml/saml2/metadata/Metadata.h>
51 # include <xmltooling/security/Credential.h>
52 # include <xmltooling/security/CredentialCriteria.h>
53 using namespace opensaml::saml2md;
54 using namespace opensaml;
55 using namespace xmlsignature;
57 using namespace xmltooling;
58 using namespace boost;
63 #if defined (_MSC_VER)
64 #pragma warning( push )
65 #pragma warning( disable : 4250 )
68 class SHIBSP_API StatusHandler : public SecuredHandler, public RemotedHandler
71 StatusHandler(const DOMElement* e, const char* appId);
72 virtual ~StatusHandler() {}
74 pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
75 void receive(DDF& in, ostream& out);
78 pair<bool,long> processMessage(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
79 ostream& systemInfo(ostream& os) const;
82 #if defined (_MSC_VER)
83 #pragma warning( pop )
86 Handler* SHIBSP_DLLLOCAL StatusHandlerFactory(const pair<const DOMElement*,const char*>& p)
88 return new StatusHandler(p.first, p.second);
91 #ifndef XMLTOOLING_NO_XMLSEC
92 vector<XSECCryptoX509*> g_NoCerts;
94 vector<string> g_NoCerts;
97 static char _x2c(const char *what)
101 digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
103 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
107 class DummyRequest : public HTTPRequest
110 DummyRequest(const char* url) : m_parser(nullptr), m_url(url), m_scheme(nullptr), m_query(nullptr), m_port(0) {
111 #ifdef HAVE_STRCASECMP
112 if (url && !strncasecmp(url,"http://", 7)) {
117 else if (url && !strncasecmp(url,"https://", 8)) {
124 if (url && !strnicmp(url,"http://", 7)) {
129 else if (url && !strnicmp(url,"https://", 8)) {
136 throw invalid_argument("Target parameter was not an absolute URL.");
138 m_query = strchr(url,'?');
142 const char* slash = strchr(url, '/');
143 const char* colon = strchr(url, ':');
144 if (colon && colon < slash) {
145 m_hostname.assign(url, colon-url);
146 string port(colon + 1, slash - colon);
147 m_port = atoi(port.c_str());
150 m_hostname.assign(url, slash - url);
158 else if (*slash != '%') {
163 if (!isxdigit(*slash) || !isxdigit(*(slash+1)))
164 throw invalid_argument("Bad request, contained unsupported encoded characters.");
165 m_uri += _x2c(slash);
172 virtual ~DummyRequest() {}
174 const char* getRequestURL() const {
177 const char* getScheme() const {
180 const char* getHostname() const {
181 return m_hostname.c_str();
183 int getPort() const {
186 const char* getRequestURI() const {
187 return m_uri.c_str();
189 const char* getMethod() const {
192 string getContentType() const {
195 long getContentLength() const {
198 string getRemoteAddr() const {
201 string getRemoteUser() const {
204 const char* getRequestBody() const {
207 const char* getQueryString() const {
210 const char* getParameter(const char* name) const
213 m_parser.reset(new CGIParser(*this));
215 pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
216 return (bounds.first == bounds.second) ? nullptr : bounds.first->second;
218 vector<const char*>::size_type getParameters(const char* name, vector<const char*>& values) const
221 m_parser.reset(new CGIParser(*this));
223 pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
224 while (bounds.first != bounds.second) {
225 values.push_back(bounds.first->second);
228 return values.size();
230 string getHeader(const char* name) const {
234 #ifndef XMLTOOLING_NO_XMLSEC
235 std::vector<XSECCryptoX509*>&
237 std::vector<std::string>&
239 getClientCertificates() const {
244 mutable scoped_ptr<CGIParser> m_parser;
246 const char* m_scheme;
249 string m_hostname,m_uri;
253 StatusHandler::StatusHandler(const DOMElement* e, const char* appId)
254 : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT ".StatusHandler"))
256 string address(appId);
257 address += getString("Location").second;
258 setAddress(address.c_str());
261 pair<bool,long> StatusHandler::run(SPRequest& request, bool isHandler) const
263 // Check ACL in base class.
264 pair<bool,long> ret = SecuredHandler::run(request, isHandler);
268 const char* target = request.getParameter("target");
270 // RequestMap query, so handle it inproc.
271 DummyRequest dummy(target);
272 RequestMapper::Settings settings = request.getApplication().getServiceProvider().getRequestMapper()->getSettings(dummy);
273 map<string,const char*> props;
274 settings.first->getAll(props);
276 DateTime now(time(nullptr));
278 auto_ptr_char timestamp(now.getFormattedString());
279 request.setContentType("text/xml");
281 msg << "<StatusHandler time='" << timestamp.get() << "'>";
282 msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
283 << "' XML-Tooling-C='" << gXMLToolingDotVersionStr
285 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
286 << "' OpenSAML-C='" << gOpenSAMLDotVersionStr
288 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
289 systemInfo(msg) << "<RequestSettings";
290 for (map<string,const char*>::const_iterator p = props.begin(); p != props.end(); ++p)
291 msg << ' ' << p->first << "='" << p->second << "'";
292 msg << '>' << target << "</RequestSettings>";
293 msg << "<Status><OK/></Status>";
294 msg << "</StatusHandler>";
295 return make_pair(true, request.sendResponse(msg));
299 if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
300 // When out of process, we run natively and directly process the message.
301 return processMessage(request.getApplication(), request, request);
304 // When not out of process, we remote all the message processing.
305 DDF out,in = wrap(request);
306 DDFJanitor jin(in), jout(out);
307 out=request.getServiceProvider().getListenerService()->send(in);
308 return unwrap(request, out);
311 catch (XMLToolingException& ex) {
312 m_log.error("error while processing request: %s", ex.what());
313 DateTime now(time(nullptr));
315 auto_ptr_char timestamp(now.getFormattedString());
316 request.setContentType("text/xml");
318 msg << "<StatusHandler time='" << timestamp.get() << "'>";
319 msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
320 << "' XML-Tooling-C='" << gXMLToolingDotVersionStr
322 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
323 << "' OpenSAML-C='" << gOpenSAMLDotVersionStr
325 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
326 systemInfo(msg) << "<Status><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></Status>";
327 msg << "</StatusHandler>";
328 return make_pair(true, request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
330 catch (std::exception& ex) {
331 m_log.error("error while processing request: %s", ex.what());
332 DateTime now(time(nullptr));
334 auto_ptr_char timestamp(now.getFormattedString());
335 request.setContentType("text/xml");
337 msg << "<StatusHandler time='" << timestamp.get() << "'>";
338 msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
339 << "' XML-Tooling-C='" << gXMLToolingDotVersionStr
341 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
342 << "' OpenSAML-C='" << gOpenSAMLDotVersionStr
344 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
345 systemInfo(msg) << "<Status><Exception type='std::exception'>" << ex.what() << "</Exception></Status>";
346 msg << "</StatusHandler>";
347 return make_pair(true, request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
351 void StatusHandler::receive(DDF& in, ostream& out)
354 const char* aid = in["application_id"].string();
355 const Application* app = aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
357 // Something's horribly wrong.
358 m_log.error("couldn't find application (%s) for status request", aid ? aid : "(missing)");
359 throw ConfigurationException("Unable to locate application for status request, deleted?");
362 // Wrap a response shim.
364 DDFJanitor jout(ret);
365 scoped_ptr<HTTPRequest> req(getRequest(in));
366 scoped_ptr<HTTPResponse> resp(getResponse(ret));
368 // Since we're remoted, the result should either be a throw, a false/0 return,
369 // which we just return as an empty structure, or a response/redirect,
370 // which we capture in the facade and send back.
371 processMessage(*app, *req, *resp);
375 pair<bool,long> StatusHandler::processMessage(
376 const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse
380 m_log.debug("processing status request");
382 DateTime now(time(nullptr));
384 auto_ptr_char timestamp(now.getFormattedString());
387 s << "<StatusHandler time='" << timestamp.get() << "'>";
388 const char* status = "<OK/>";
390 s << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
391 << "' XML-Tooling-C='" << gXMLToolingDotVersionStr
392 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
393 << "' OpenSAML-C='" << gOpenSAMLDotVersionStr
394 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
398 const char* param = nullptr;
402 // General configuration and status report.
404 SessionCache* sc = application.getServiceProvider().getSessionCache(false);
407 s << "<SessionCache><OK/></SessionCache>";
410 s << "<SessionCache><None/></SessionCache>";
413 catch (XMLToolingException& ex) {
414 s << "<SessionCache><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></SessionCache>";
415 status = "<Partial/>";
417 catch (std::exception& ex) {
418 s << "<SessionCache><Exception type='std::exception'>" << ex.what() << "</Exception></SessionCache>";
419 status = "<Partial/>";
422 MetadataProvider* m = application.getMetadataProvider(false);
425 const PropertySet* relyingParty = nullptr;
426 param=httpRequest.getParameter("entityID");
428 relyingParty = application.getRelyingParty(m->getEntityDescriptor(MetadataProviderCriteria(application, param)).first);
430 relyingParty = &application;
432 s << "<Application id='" << application.getId() << "' entityID='" << relyingParty->getString("entityID").second << "'/>";
438 vector<const Handler*> handlers;
439 application.getHandlers(handlers);
440 for (indirect_iterator<vector<const Handler*>::const_iterator> h = make_indirect_iterator(handlers.begin());
441 h != make_indirect_iterator(handlers.end()); ++h) {
442 s << "<Handler type='" << h->getType() << "' Location='" << h->getString("Location").second << "'";
443 if (h->getString("Binding").first)
444 s << " Binding='" << h->getString("Binding").second << "'";
449 CredentialResolver* credResolver = application.getCredentialResolver();
451 Locker credLocker(credResolver);
452 CredentialCriteria cc;
453 cc.setUsage(Credential::SIGNING_CREDENTIAL);
454 pair<bool,const char*> keyName = relyingParty->getString("keyName");
456 cc.getKeyNames().insert(keyName.second);
457 vector<const Credential*> creds;
458 credResolver->resolve(creds, &cc);
459 for (vector<const Credential*>::const_iterator c = creds.begin(); c != creds.end(); ++c) {
460 KeyInfo* kinfo = (*c)->getKeyInfo();
462 scoped_ptr<KeyDescriptor> kd(KeyDescriptorBuilder::buildKeyDescriptor());
463 kd->setUse(KeyDescriptor::KEYTYPE_SIGNING);
464 kd->setKeyInfo(kinfo);
469 cc.setUsage(Credential::ENCRYPTION_CREDENTIAL);
471 cc.getKeyNames().clear();
472 credResolver->resolve(creds, &cc);
473 for (vector<const Credential*>::const_iterator c = creds.begin(); c != creds.end(); ++c) {
474 KeyInfo* kinfo = (*c)->getKeyInfo();
476 scoped_ptr<KeyDescriptor> kd(KeyDescriptorBuilder::buildKeyDescriptor());
477 kd->setUse(KeyDescriptor::KEYTYPE_ENCRYPTION);
478 kd->setKeyInfo(kinfo);
485 s << "<Status>" << status << "</Status></StatusHandler>";
487 httpResponse.setContentType("text/xml");
488 return make_pair(true, httpResponse.sendResponse(s));
490 return make_pair(false, 0L);
495 typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
498 ostream& StatusHandler::systemInfo(ostream& os) const
500 #if defined(HAVE_SYS_UTSNAME_H)
501 struct utsname sysinfo;
502 if (uname(&sysinfo) == 0) {
504 if (*sysinfo.sysname)
505 os << " sysname='" << sysinfo.sysname << "'";
506 if (*sysinfo.nodename)
507 os << " nodename='" << sysinfo.nodename << "'";
508 if (*sysinfo.release)
509 os << " release='" << sysinfo.release << "'";
510 if (*sysinfo.version)
511 os << " version='" << sysinfo.version << "'";
512 if (*sysinfo.machine)
513 os << " machine='" << sysinfo.machine << "'";
517 OSVERSIONINFOEX osvi;
518 memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
519 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
520 if(GetVersionEx((OSVERSIONINFO*)&osvi)) {
522 << " version='" << osvi.dwMajorVersion << "." << osvi.dwMinorVersion << "'"
523 << " build='" << osvi.dwBuildNumber << "'";
524 if (osvi.wServicePackMajor > 0)
525 os << " servicepack='" << osvi.wServicePackMajor << "." << osvi.wServicePackMinor << "'";
526 switch (osvi.wProductType) {
527 case VER_NT_WORKSTATION:
528 os << " producttype='Workstation'";
531 case VER_NT_DOMAIN_CONTROLLER:
532 os << " producttype='Server'";
537 memset(&si, 0, sizeof(SYSTEM_INFO));
538 PGNSI pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo");
543 switch (si.dwProcessorType) {
544 case PROCESSOR_ARCHITECTURE_INTEL:
545 os << " arch='i386'";
547 case PROCESSOR_ARCHITECTURE_AMD64:
548 os << " arch='x86_64'";
550 case PROCESSOR_ARCHITECTURE_IA64:
551 os << " arch='IA64'";
554 os << " cpucount='" << si.dwNumberOfProcessors << "'";
557 memset(&ms, 0, sizeof(MEMORYSTATUSEX));
558 ms.dwLength = sizeof(MEMORYSTATUSEX);
559 if (GlobalMemoryStatusEx(&ms)) {
560 os << " memory='" << (ms.ullTotalPhys / (1024 * 1024)) << "M'";