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 * Base class for handlers that need SP request/response layer to be remoted.
28 #include "exceptions.h"
29 #include "Application.h"
30 #include "GSSRequest.h"
31 #include "ServiceProvider.h"
32 #include "SPRequest.h"
33 #include "handler/RemotedHandler.h"
36 #include <boost/scoped_ptr.hpp>
37 #include <xmltooling/unicode.h>
38 #include <xercesc/util/Base64.hpp>
41 # include "util/CGIParser.h"
42 # include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
43 # include <xsec/enc/XSECCryptoException.hpp>
44 # include <xsec/framework/XSECException.hpp>
45 # include <xsec/framework/XSECProvider.hpp>
48 #ifdef HAVE_GSSAPI_NAMINGEXTS
49 # ifdef SHIBSP_HAVE_GSSMIT
50 # include <gssapi/gssapi_ext.h>
54 using namespace shibsp;
55 using namespace opensaml;
56 using namespace xmltooling;
57 using namespace xercesc;
58 using namespace boost;
63 class SHIBSP_DLLLOCAL RemotedRequest :
64 #ifdef SHIBSP_HAVE_GSSAPI
70 mutable scoped_ptr<CGIParser> m_parser;
71 mutable vector<XSECCryptoX509*> m_certs;
72 #ifdef SHIBSP_HAVE_GSSAPI
73 mutable gss_ctx_id_t m_gssctx;
74 mutable gss_name_t m_gssname;
77 RemotedRequest(DDF& input) : m_input(input), m_parser(nullptr)
78 #ifdef SHIBSP_HAVE_GSSAPI
79 , m_gssctx(GSS_C_NO_CONTEXT), m_gssname(GSS_C_NO_NAME)
84 virtual ~RemotedRequest() {
85 for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
86 #ifdef SHIBSP_HAVE_GSSAPI
88 if (m_gssctx != GSS_C_NO_CONTEXT)
89 gss_delete_sec_context(&minor, &m_gssctx, GSS_C_NO_BUFFER);
90 if (m_gssname != GSS_C_NO_NAME)
91 gss_release_name(&minor, &m_gssname);
96 const char* getScheme() const {
97 return m_input["scheme"].string();
99 bool isSecure() const {
100 return HTTPRequest::isSecure();
102 const char* getHostname() const {
103 return m_input["hostname"].string();
105 int getPort() const {
106 return m_input["port"].integer();
108 std::string getContentType() const {
109 DDF s = m_input["content_type"];
110 return s.string() ? s.string() : "";
112 long getContentLength() const {
113 return m_input["content_length"].integer();
115 const char* getRequestBody() const {
116 return m_input["body"].string();
119 const char* getParameter(const char* name) const;
120 std::vector<const char*>::size_type getParameters(const char* name, std::vector<const char*>& values) const;
122 std::string getRemoteUser() const {
123 DDF s = m_input["remote_user"];
124 return s.string() ? s.string() : "";
126 std::string getRemoteAddr() const {
127 DDF s = m_input["client_addr"];
128 return s.string() ? s.string() : "";
131 const std::vector<XSECCryptoX509*>& getClientCertificates() const;
133 #ifdef SHIBSP_HAVE_GSSAPI
135 gss_ctx_id_t getGSSContext() const;
136 gss_name_t getGSSName() const;
140 const char* getMethod() const {
141 return m_input["method"].string();
143 const char* getRequestURI() const {
144 return m_input["uri"].string();
146 const char* getRequestURL() const {
147 return m_input["url"].string();
149 const char* getQueryString() const {
150 return m_input["query"].string();
152 std::string getHeader(const char* name) const {
153 DDF s = m_input["headers"][name];
154 return s.string() ? s.string() : "";
158 class SHIBSP_DLLLOCAL RemotedResponse : public virtual HTTPResponse
162 RemotedResponse(DDF& output) : m_output(output) {}
163 virtual ~RemotedResponse() {}
166 long sendResponse(std::istream& inputStream, long status);
169 void setResponseHeader(const char* name, const char* value);
170 long sendRedirect(const char* url);
174 const char* RemotedRequest::getParameter(const char* name) const
177 m_parser.reset(new CGIParser(*this));
179 pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
180 return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
183 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
186 m_parser.reset(new CGIParser(*this));
188 pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
189 while (bounds.first != bounds.second) {
190 values.push_back(bounds.first->second);
193 return values.size();
196 const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
198 if (m_certs.empty()) {
199 DDF certs = m_input["certificates"];
200 DDF cert = certs.first();
201 while (cert.string()) {
203 auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
204 if (strstr(cert.string(), "BEGIN"))
205 x509->loadX509PEM(cert.string(), cert.strlen());
207 x509->loadX509Base64Bin(cert.string(), cert.strlen());
208 m_certs.push_back(x509.get());
211 catch(XSECException& e) {
212 auto_ptr_char temp(e.getMsg());
213 Category::getInstance(SHIBSP_LOGCAT ".SPRequest").error("XML-Security exception loading client certificate: %s", temp.get());
215 catch(XSECCryptoException& e) {
216 Category::getInstance(SHIBSP_LOGCAT ".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
224 #ifdef SHIBSP_HAVE_GSSAPI
225 gss_ctx_id_t RemotedRequest::getGSSContext() const
227 if (m_gssctx == GSS_C_NO_CONTEXT) {
228 const char* encoded = m_input["gss_context"].string();
231 XMLByte* decoded = Base64::decode(reinterpret_cast<const XMLByte*>(encoded), &x);
233 gss_buffer_desc importbuf;
234 importbuf.length = x;
235 importbuf.value = decoded;
237 OM_uint32 major = gss_import_sec_context(&minor, &importbuf, &m_gssctx);
238 if (major != GSS_S_COMPLETE)
239 m_gssctx = GSS_C_NO_CONTEXT;
240 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
241 XMLString::release(&decoded);
243 XMLString::release((char**)&decoded);
251 gss_name_t RemotedRequest::getGSSName() const
253 if (m_gssname == GSS_C_NO_NAME) {
254 const char* encoded = m_input["gss_name"].string();
257 XMLByte* decoded = Base64::decode(reinterpret_cast<const XMLByte*>(encoded), &x);
258 gss_buffer_desc importbuf;
259 importbuf.length = x;
260 importbuf.value = decoded;
261 OM_uint32 major,minor;
262 #ifdef HAVE_GSSAPI_COMPOSITE_NAME
263 major = gss_import_name(&minor, &importbuf, GSS_C_NT_EXPORT_NAME_COMPOSITE, &m_gssname);
265 major = gss_import_name(&minor, &importbuf, GSS_C_NT_EXPORT_NAME, &m_gssname);
267 if (major != GSS_S_COMPLETE)
268 m_gssname = GSS_C_NO_NAME;
269 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
270 XMLString::release(&decoded);
272 XMLString::release((char**)&decoded);
276 if (m_gssname == GSS_C_NO_NAME) {
277 gss_ctx_id_t ctx = getGSSContext();
278 if (ctx != GSS_C_NO_CONTEXT) {
280 OM_uint32 major = gss_inquire_context(&minor, ctx, &m_gssname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
281 if (major != GSS_S_COMPLETE)
282 m_gssname = GSS_C_NO_NAME;
290 long RemotedResponse::sendResponse(std::istream& in, long status)
296 msg.append(buf, in.gcount());
298 if (!m_output.isstruct())
299 m_output.structure();
300 m_output.addmember("response.data").string(msg.c_str());
301 m_output.addmember("response.status").integer(status);
305 void RemotedResponse::setResponseHeader(const char* name, const char* value)
307 if (!m_output.isstruct())
308 m_output.structure();
309 DDF hdrs = m_output["headers"];
311 hdrs = m_output.addmember("headers").list();
312 DDF h = DDF(name).string(value);
316 long RemotedResponse::sendRedirect(const char* url)
318 if (!m_output.isstruct())
319 m_output.structure();
320 m_output.addmember("redirect").unsafe_string(url);
321 return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED;
326 void RemotedHandler::setAddress(const char* address)
328 if (!m_address.empty())
329 throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
331 SPConfig& conf = SPConfig::getConfig();
332 if (!conf.isEnabled(SPConfig::InProcess)) {
333 ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
335 listener->regListener(m_address.c_str(), this);
337 Category::getInstance(SHIBSP_LOGCAT ".Handler").info("no ListenerService available, handler remoting disabled");
341 set<string> RemotedHandler::m_remotedHeaders;
343 RemotedHandler::RemotedHandler()
347 RemotedHandler::~RemotedHandler()
349 SPConfig& conf = SPConfig::getConfig();
350 ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
351 if (listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
352 listener->unregListener(m_address.c_str(),this);
355 void RemotedHandler::addRemotedHeader(const char* header)
357 m_remotedHeaders.insert(header);
360 DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
362 DDF in = DDF(m_address.c_str()).structure();
363 in.addmember("application_id").string(request.getApplication().getId());
364 in.addmember("scheme").string(request.getScheme());
365 in.addmember("hostname").unsafe_string(request.getHostname());
366 in.addmember("port").integer(request.getPort());
367 in.addmember("content_type").string(request.getContentType().c_str());
368 in.addmember("body").string(request.getRequestBody());
369 in.addmember("content_length").integer(request.getContentLength());
370 in.addmember("remote_user").string(request.getRemoteUser().c_str());
371 in.addmember("client_addr").string(request.getRemoteAddr().c_str());
372 in.addmember("method").string(request.getMethod());
373 in.addmember("uri").unsafe_string(request.getRequestURI());
374 in.addmember("url").unsafe_string(request.getRequestURL());
375 in.addmember("query").string(request.getQueryString());
377 if (headers || !m_remotedHeaders.empty()) {
379 DDF hin = in.addmember("headers").structure();
381 for (vector<string>::const_iterator h = headers->begin(); h != headers->end(); ++h) {
382 hdr = request.getHeader(h->c_str());
384 hin.addmember(h->c_str()).unsafe_string(hdr.c_str());
387 for (set<string>::const_iterator hh = m_remotedHeaders.begin(); hh != m_remotedHeaders.end(); ++hh) {
388 hdr = request.getHeader(hh->c_str());
390 hin.addmember(hh->c_str()).unsafe_string(hdr.c_str());
396 const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
398 DDF clist = in.addmember("certificates").list();
399 for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
400 DDF x509 = DDF(nullptr).string((*x)->getDEREncodingSB().rawCharBuffer());
405 const vector<string>& xvec = request.getClientCertificates();
407 DDF clist = in.addmember("certificates").list();
408 for (vector<string>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
409 DDF x509 = DDF(nullptr).string(x->c_str());
416 #ifdef SHIBSP_HAVE_GSSAPI
417 const GSSRequest* gss = dynamic_cast<const GSSRequest*>(&request);
419 gss_ctx_id_t ctx = gss->getGSSContext();
420 if (ctx != GSS_C_NO_CONTEXT) {
422 gss_buffer_desc contextbuf = GSS_C_EMPTY_BUFFER;
423 OM_uint32 major = gss_export_sec_context(&minor, &ctx, &contextbuf);
424 if (major == GSS_S_COMPLETE) {
426 XMLByte* out = Base64::encode(reinterpret_cast<const XMLByte*>(contextbuf.value), contextbuf.length, &len);
427 gss_release_buffer(&minor, &contextbuf);
430 ctx.append(reinterpret_cast<char*>(out), len);
431 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
432 XMLString::release(&out);
434 XMLString::release((char**)&out);
436 in.addmember("gss_context").string(ctx.c_str());
439 request.log(SPRequest::SPError, "error while base64-encoding GSS context");
443 request.log(SPRequest::SPError, "error while exporting GSS context");
446 #ifdef HAVE_GSSAPI_NAMINGEXTS
448 gss_name_t name = gss->getGSSName();
449 if (name != GSS_C_NO_NAME) {
451 gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
452 OM_uint32 major = gss_export_name_composite(&minor, name, &namebuf);
453 if (major == GSS_S_COMPLETE) {
455 XMLByte* out = Base64::encode(reinterpret_cast<const XMLByte*>(namebuf.value), namebuf.length, &len);
456 gss_release_buffer(&minor, &namebuf);
459 nm.append(reinterpret_cast<char*>(out), len);
460 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
461 XMLString::release(&out);
463 XMLString::release((char**)&out);
465 in.addmember("gss_name").string(nm.c_str());
468 request.log(SPRequest::SPError, "error while base64-encoding GSS name");
472 request.log(SPRequest::SPError, "error while exporting GSS name");
483 pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
485 DDF h = out["headers"];
487 while (hdr.isstring()) {
488 #ifdef HAVE_STRCASECMP
489 if (!strcasecmp(hdr.name(), "Content-Type"))
491 if (!stricmp(hdr.name(), "Content-Type"))
493 request.setContentType(hdr.string());
495 request.setResponseHeader(hdr.name(), hdr.string());
500 return make_pair(true, request.sendRedirect(h.string()));
503 istringstream s(h["data"].string());
504 return make_pair(true, request.sendResponse(s, h["status"].integer()));
506 return make_pair(false, 0L);
509 HTTPRequest* RemotedHandler::getRequest(DDF& in) const
512 return new RemotedRequest(in);
514 throw ConfigurationException("Cannot process message using lite version of shibsp library.");
518 HTTPResponse* RemotedHandler::getResponse(DDF& out) const
521 return new RemotedResponse(out);
523 throw ConfigurationException("Cannot process message using lite version of shibsp library.");