2 * Copyright 2001-2010 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * Base class for handlers that need SP request/response layer to be remoted.
24 #include "exceptions.h"
25 #include "Application.h"
26 #include "GSSRequest.h"
27 #include "ServiceProvider.h"
28 #include "SPRequest.h"
29 #include "handler/RemotedHandler.h"
32 #include <xmltooling/unicode.h>
33 #include <xercesc/util/Base64.hpp>
36 # include "util/CGIParser.h"
37 # include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
38 # include <xsec/enc/XSECCryptoException.hpp>
39 # include <xsec/framework/XSECException.hpp>
40 # include <xsec/framework/XSECProvider.hpp>
43 using namespace shibsp;
44 using namespace opensaml;
45 using namespace xmltooling;
46 using namespace xercesc;
51 class SHIBSP_DLLLOCAL RemotedRequest :
58 mutable CGIParser* m_parser;
59 mutable vector<XSECCryptoX509*> m_certs;
61 mutable gss_ctx_id_t m_gss;
64 RemotedRequest(DDF& input) : m_input(input), m_parser(nullptr)
66 , m_gss(GSS_C_NO_CONTEXT)
71 virtual ~RemotedRequest() {
72 for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
75 if (m_gss != GSS_C_NO_CONTEXT) {
77 gss_delete_sec_context(&minor, &m_gss, GSS_C_NO_BUFFER);
83 const char* getScheme() const {
84 return m_input["scheme"].string();
86 bool isSecure() const {
87 return HTTPRequest::isSecure();
89 const char* getHostname() const {
90 return m_input["hostname"].string();
93 return m_input["port"].integer();
95 std::string getContentType() const {
96 DDF s = m_input["content_type"];
97 return s.string() ? s.string() : "";
99 long getContentLength() const {
100 return m_input["content_length"].integer();
102 const char* getRequestBody() const {
103 return m_input["body"].string();
106 const char* getParameter(const char* name) const;
107 std::vector<const char*>::size_type getParameters(const char* name, std::vector<const char*>& values) const;
109 std::string getRemoteUser() const {
110 DDF s = m_input["remote_user"];
111 return s.string() ? s.string() : "";
113 std::string getRemoteAddr() const {
114 DDF s = m_input["client_addr"];
115 return s.string() ? s.string() : "";
118 const std::vector<XSECCryptoX509*>& getClientCertificates() const;
122 gss_ctx_id_t getGSSContext() const;
126 const char* getMethod() const {
127 return m_input["method"].string();
129 const char* getRequestURI() const {
130 return m_input["uri"].string();
132 const char* getRequestURL() const {
133 return m_input["url"].string();
135 const char* getQueryString() const {
136 return m_input["query"].string();
138 std::string getHeader(const char* name) const {
139 DDF s = m_input["headers"][name];
140 return s.string() ? s.string() : "";
144 class SHIBSP_DLLLOCAL RemotedResponse : public virtual HTTPResponse
148 RemotedResponse(DDF& output) : m_output(output) {}
149 virtual ~RemotedResponse() {}
152 long sendResponse(std::istream& inputStream, long status);
155 void setResponseHeader(const char* name, const char* value);
156 long sendRedirect(const char* url);
160 const char* RemotedRequest::getParameter(const char* name) const
163 m_parser=new CGIParser(*this);
165 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
166 return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
169 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
172 m_parser=new CGIParser(*this);
174 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
175 while (bounds.first!=bounds.second) {
176 values.push_back(bounds.first->second);
179 return values.size();
182 const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
184 if (m_certs.empty()) {
185 DDF certs = m_input["certificates"];
186 DDF cert = certs.first();
187 while (cert.string()) {
189 auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
190 if (strstr(cert.string(), "BEGIN"))
191 x509->loadX509PEM(cert.string(), cert.strlen());
193 x509->loadX509Base64Bin(cert.string(), cert.strlen());
194 m_certs.push_back(x509.release());
196 catch(XSECException& e) {
197 auto_ptr_char temp(e.getMsg());
198 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", temp.get());
200 catch(XSECCryptoException& e) {
201 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
210 gss_ctx_id_t RemotedRequest::getGSSContext() const
212 if (m_gss == GSS_C_NO_CONTEXT) {
213 const char* encoded = m_input["gss_context"].string();
216 XMLByte* decoded=Base64::decode(reinterpret_cast<const XMLByte*>(encoded), &x);
218 gss_buffer_desc importbuf;
219 importbuf.length = x;
220 importbuf.value = decoded;
222 OM_uint32 major = gss_import_sec_context(&minor, &importbuf, &m_gss);
223 if (major != GSS_S_COMPLETE)
224 m_gss = GSS_C_NO_CONTEXT;
225 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
226 XMLString::release(&decoded);
228 XMLString::release((char**)&decoded);
237 long RemotedResponse::sendResponse(std::istream& in, long status)
243 msg.append(buf,in.gcount());
245 if (!m_output.isstruct())
246 m_output.structure();
247 m_output.addmember("response.data").string(msg.c_str());
248 m_output.addmember("response.status").integer(status);
252 void RemotedResponse::setResponseHeader(const char* name, const char* value)
254 if (!m_output.isstruct())
255 m_output.structure();
256 DDF hdrs = m_output["headers"];
258 hdrs = m_output.addmember("headers").list();
259 DDF h = DDF(name).string(value);
263 long RemotedResponse::sendRedirect(const char* url)
265 if (!m_output.isstruct())
266 m_output.structure();
267 m_output.addmember("redirect").unsafe_string(url);
268 return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED;
273 void RemotedHandler::setAddress(const char* address)
275 if (!m_address.empty())
276 throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
278 SPConfig& conf = SPConfig::getConfig();
279 if (!conf.isEnabled(SPConfig::InProcess)) {
280 ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
282 listener->regListener(m_address.c_str(),this);
284 Category::getInstance(SHIBSP_LOGCAT".Handler").info("no ListenerService available, handler remoting disabled");
288 RemotedHandler::RemotedHandler()
292 RemotedHandler::~RemotedHandler()
294 SPConfig& conf = SPConfig::getConfig();
295 ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
296 if (listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
297 listener->unregListener(m_address.c_str(),this);
300 DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
302 DDF in = DDF(m_address.c_str()).structure();
303 in.addmember("application_id").string(request.getApplication().getId());
304 in.addmember("scheme").string(request.getScheme());
305 in.addmember("hostname").unsafe_string(request.getHostname());
306 in.addmember("port").integer(request.getPort());
307 in.addmember("content_type").string(request.getContentType().c_str());
308 in.addmember("content_length").integer(request.getContentLength());
309 in.addmember("body").string(request.getRequestBody());
310 in.addmember("remote_user").string(request.getRemoteUser().c_str());
311 in.addmember("client_addr").string(request.getRemoteAddr().c_str());
312 in.addmember("method").string(request.getMethod());
313 in.addmember("uri").unsafe_string(request.getRequestURI());
314 in.addmember("url").unsafe_string(request.getRequestURL());
315 in.addmember("query").string(request.getQueryString());
319 DDF hin = in.addmember("headers").structure();
320 for (vector<string>::const_iterator h = headers->begin(); h!=headers->end(); ++h) {
321 hdr = request.getHeader(h->c_str());
323 hin.addmember(h->c_str()).unsafe_string(hdr.c_str());
329 const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
331 DDF clist = in.addmember("certificates").list();
332 for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
333 DDF x509 = DDF(nullptr).string((*x)->getDEREncodingSB().rawCharBuffer());
338 const vector<string>& xvec = request.getClientCertificates();
340 DDF clist = in.addmember("certificates").list();
341 for (vector<string>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
342 DDF x509 = DDF(nullptr).string(x->c_str());
350 const GSSRequest* gss = dynamic_cast<const GSSRequest*>(&request);
352 gss_ctx_id_t ctx = gss->getGSSContext();
353 if (ctx != GSS_C_NO_CONTEXT) {
355 gss_buffer_desc contextbuf;
356 contextbuf.length = 0;
357 contextbuf.value = nullptr;
358 OM_uint32 major = gss_export_sec_context(&minor, &ctx, &contextbuf);
359 if (major == GSS_S_COMPLETE) {
361 XMLByte* out=Base64::encode(reinterpret_cast<const XMLByte*>(contextbuf.value), contextbuf.length, &len);
364 ctx.append(reinterpret_cast<char*>(out), len);
365 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
366 XMLString::release(&out);
368 XMLString::release((char**)&out);
370 in.addmember("gss_context").string(ctx.c_str());
373 request.log(SPRequest::SPError, "error while base64-encoding GSS context");
377 request.log(SPRequest::SPError, "error while exporting GSS context");
386 pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
388 DDF h = out["headers"];
390 while (hdr.isstring()) {
391 #ifdef HAVE_STRCASECMP
392 if (!strcasecmp(hdr.name(), "Content-Type"))
394 if (!stricmp(hdr.name(), "Content-Type"))
396 request.setContentType(hdr.string());
398 request.setResponseHeader(hdr.name(), hdr.string());
403 return make_pair(true, request.sendRedirect(h.string()));
406 istringstream s(h["data"].string());
407 return make_pair(true, request.sendResponse(s, h["status"].integer()));
409 return make_pair(false,0L);
412 HTTPRequest* RemotedHandler::getRequest(DDF& in) const
415 return new RemotedRequest(in);
417 throw ConfigurationException("Cannot process message using lite version of shibsp library.");
421 HTTPResponse* RemotedHandler::getResponse(DDF& out) const
424 return new RemotedResponse(out);
426 throw ConfigurationException("Cannot process message using lite version of shibsp library.");