SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / shibsp / handler / impl / RemotedHandler.cpp
1 /**
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.
6  *
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
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  */
20
21 /**
22  * RemotedHandler.cpp
23  * 
24  * Base class for handlers that need SP request/response layer to be remoted. 
25  */
26
27 #include "internal.h"
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"
34
35 #include <algorithm>
36 #include <boost/scoped_ptr.hpp>
37 #include <xmltooling/unicode.h>
38 #include <xercesc/util/Base64.hpp>
39
40 #ifndef SHIBSP_LITE
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>
46 #endif
47
48 #ifdef HAVE_GSSAPI_NAMINGEXTS
49 # ifdef SHIBSP_HAVE_GSSMIT
50 #  include <gssapi/gssapi_ext.h>
51 # endif
52 #endif
53
54 using namespace shibsp;
55 using namespace opensaml;
56 using namespace xmltooling;
57 using namespace xercesc;
58 using namespace boost;
59 using namespace std;
60
61 #ifndef SHIBSP_LITE
62 namespace shibsp {
63     class SHIBSP_DLLLOCAL RemotedRequest : 
64 #ifdef SHIBSP_HAVE_GSSAPI
65         public GSSRequest,
66 #endif
67         public HTTPRequest
68     {
69         DDF& m_input;
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;
75 #endif
76     public:
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)
80 #endif
81         {
82         }
83
84         virtual ~RemotedRequest() {
85             for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
86 #ifdef SHIBSP_HAVE_GSSAPI
87             OM_uint32 minor;
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);
92 #endif
93         }
94
95         // GenericRequest
96         const char* getScheme() const {
97             return m_input["scheme"].string();
98         }
99         bool isSecure() const {
100             return HTTPRequest::isSecure();
101         }
102         const char* getHostname() const {
103             return m_input["hostname"].string();
104         }
105         int getPort() const {
106             return m_input["port"].integer();
107         }
108         std::string getContentType() const {
109             DDF s = m_input["content_type"];
110             return s.string() ? s.string() : "";
111         }
112         long getContentLength() const {
113             return m_input["content_length"].integer();
114         }
115         const char* getRequestBody() const {
116             return m_input["body"].string();
117         }
118
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;
121         
122         std::string getRemoteUser() const {
123             DDF s = m_input["remote_user"];
124             return s.string() ? s.string() : "";
125         }
126         std::string getRemoteAddr() const {
127             DDF s = m_input["client_addr"];
128             return s.string() ? s.string() : "";
129         }
130
131         const std::vector<XSECCryptoX509*>& getClientCertificates() const;
132         
133 #ifdef SHIBSP_HAVE_GSSAPI
134         // GSSRequest
135         gss_ctx_id_t getGSSContext() const;
136         gss_name_t getGSSName() const;
137 #endif
138
139         // HTTPRequest
140         const char* getMethod() const {
141             return m_input["method"].string();
142         }
143         const char* getRequestURI() const {
144             return m_input["uri"].string();
145         }
146         const char* getRequestURL() const {
147             return m_input["url"].string();
148         }
149         const char* getQueryString() const {
150             return m_input["query"].string();
151         }
152         std::string getHeader(const char* name) const {
153             DDF s = m_input["headers"][name];
154             return s.string() ? s.string() : "";
155         }
156     };
157
158     class SHIBSP_DLLLOCAL RemotedResponse : public virtual HTTPResponse 
159     {
160         DDF& m_output;
161     public:
162         RemotedResponse(DDF& output) : m_output(output) {}
163         virtual ~RemotedResponse() {}
164        
165         // GenericResponse
166         long sendResponse(std::istream& inputStream, long status);
167         
168         // HTTPResponse
169         void setResponseHeader(const char* name, const char* value);
170         long sendRedirect(const char* url);
171     };
172 }
173
174 const char* RemotedRequest::getParameter(const char* name) const
175 {
176     if (!m_parser)
177         m_parser.reset(new CGIParser(*this));
178     
179     pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
180     return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
181 }
182
183 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
184 {
185     if (!m_parser)
186         m_parser.reset(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
196 const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
197 {
198     if (m_certs.empty()) {
199         DDF certs = m_input["certificates"];
200         DDF cert = certs.first();
201         while (cert.string()) {
202             try {
203                 auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
204                 if (strstr(cert.string(), "BEGIN"))
205                     x509->loadX509PEM(cert.string(), cert.strlen());
206                 else
207                     x509->loadX509Base64Bin(cert.string(), cert.strlen());
208                 m_certs.push_back(x509.get());
209                 x509.release();
210             }
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());
214             }
215             catch(XSECCryptoException& e) {
216                 Category::getInstance(SHIBSP_LOGCAT ".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
217             }
218             cert = certs.next();
219         }
220     }
221     return m_certs;
222 }
223
224 #ifdef SHIBSP_HAVE_GSSAPI
225 gss_ctx_id_t RemotedRequest::getGSSContext() const
226 {
227     if (m_gssctx == GSS_C_NO_CONTEXT) {
228         const char* encoded = m_input["gss_context"].string();
229         if (encoded) {
230             xsecsize_t x;
231             XMLByte* decoded = Base64::decode(reinterpret_cast<const XMLByte*>(encoded), &x);
232             if (decoded) {
233                 gss_buffer_desc importbuf;
234                 importbuf.length = x;
235                 importbuf.value = decoded;
236                 OM_uint32 minor;
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);
242 #else
243                 XMLString::release((char**)&decoded);
244 #endif
245             }
246         }
247     }
248     return m_gssctx;
249 }
250
251 gss_name_t RemotedRequest::getGSSName() const
252 {
253     if (m_gssname == GSS_C_NO_NAME) {
254         const char* encoded = m_input["gss_name"].string();
255         if (encoded) {
256             xsecsize_t x;
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);
264 #else
265             major = gss_import_name(&minor, &importbuf, GSS_C_NT_EXPORT_NAME, &m_gssname);
266 #endif
267             if (major != GSS_S_COMPLETE)
268                 m_gssname = GSS_C_NO_NAME;
269 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
270             XMLString::release(&decoded);
271 #else
272             XMLString::release((char**)&decoded);
273 #endif
274         }
275
276         if (m_gssname == GSS_C_NO_NAME) {
277             gss_ctx_id_t ctx = getGSSContext();
278              if (ctx != GSS_C_NO_CONTEXT) {
279                  OM_uint32 minor;
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;
283              }
284          }
285     }
286     return m_gssname;
287 }
288 #endif
289
290 long RemotedResponse::sendResponse(std::istream& in, long status)
291 {
292     string msg;
293     char buf[1024];
294     while (in) {
295         in.read(buf, 1024);
296         msg.append(buf, in.gcount());
297     }
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);
302     return status;
303 }
304
305 void RemotedResponse::setResponseHeader(const char* name, const char* value)
306 {
307     if (!m_output.isstruct())
308         m_output.structure();
309     DDF hdrs = m_output["headers"];
310     if (hdrs.isnull())
311         hdrs = m_output.addmember("headers").list();
312     DDF h = DDF(name).string(value);
313     hdrs.add(h);
314 }
315
316 long RemotedResponse::sendRedirect(const char* url)
317 {
318     if (!m_output.isstruct())
319         m_output.structure();
320     m_output.addmember("redirect").unsafe_string(url);
321     return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED;
322 }
323
324 #endif
325
326 void RemotedHandler::setAddress(const char* address)
327 {
328     if (!m_address.empty())
329         throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
330     m_address = address;
331     SPConfig& conf = SPConfig::getConfig();
332     if (!conf.isEnabled(SPConfig::InProcess)) {
333         ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
334         if (listener)
335             listener->regListener(m_address.c_str(), this);
336         else
337             Category::getInstance(SHIBSP_LOGCAT ".Handler").info("no ListenerService available, handler remoting disabled");
338     }
339 }
340
341 set<string> RemotedHandler::m_remotedHeaders;
342
343 RemotedHandler::RemotedHandler()
344 {
345 }
346
347 RemotedHandler::~RemotedHandler()
348 {
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);
353 }
354
355 void RemotedHandler::addRemotedHeader(const char* header)
356 {
357     m_remotedHeaders.insert(header);
358 }
359
360 DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
361 {
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());
376
377     if (headers || !m_remotedHeaders.empty()) {
378         string hdr;
379         DDF hin = in.addmember("headers").structure();
380         if (headers) {
381             for (vector<string>::const_iterator h = headers->begin(); h != headers->end(); ++h) {
382                 hdr = request.getHeader(h->c_str());
383                 if (!hdr.empty())
384                     hin.addmember(h->c_str()).unsafe_string(hdr.c_str());
385             }
386         }
387         for (set<string>::const_iterator hh = m_remotedHeaders.begin(); hh != m_remotedHeaders.end(); ++hh) {
388             hdr = request.getHeader(hh->c_str());
389             if (!hdr.empty())
390                 hin.addmember(hh->c_str()).unsafe_string(hdr.c_str());
391         }
392     }
393
394     if (certs) {
395 #ifndef SHIBSP_LITE
396         const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
397         if (!xvec.empty()) {
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());
401                 clist.add(x509);
402             }
403         }
404 #else
405         const vector<string>& xvec = request.getClientCertificates();
406         if (!xvec.empty()) {
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());
410                 clist.add(x509);
411             }
412         }
413 #endif
414     }
415
416 #ifdef SHIBSP_HAVE_GSSAPI
417     const GSSRequest* gss = dynamic_cast<const GSSRequest*>(&request);
418     if (gss) {
419         gss_ctx_id_t ctx = gss->getGSSContext();
420         if (ctx != GSS_C_NO_CONTEXT) {
421             OM_uint32 minor;
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) {
425                 xsecsize_t len = 0;
426                 XMLByte* out = Base64::encode(reinterpret_cast<const XMLByte*>(contextbuf.value), contextbuf.length, &len);
427                 gss_release_buffer(&minor, &contextbuf);
428                 if (out) {
429                     string ctx;
430                     ctx.append(reinterpret_cast<char*>(out), len);
431 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
432                     XMLString::release(&out);
433 #else
434                     XMLString::release((char**)&out);
435 #endif
436                     in.addmember("gss_context").string(ctx.c_str());
437                 }
438                 else {
439                     request.log(SPRequest::SPError, "error while base64-encoding GSS context");
440                 }
441             }
442             else {
443                 request.log(SPRequest::SPError, "error while exporting GSS context");
444             }
445         }
446 #ifdef HAVE_GSSAPI_NAMINGEXTS
447         else {
448             gss_name_t name = gss->getGSSName();
449             if (name != GSS_C_NO_NAME) {
450                 OM_uint32 minor;
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) {
454                     xsecsize_t len = 0;
455                     XMLByte* out = Base64::encode(reinterpret_cast<const XMLByte*>(namebuf.value), namebuf.length, &len);
456                     gss_release_buffer(&minor, &namebuf);
457                     if (out) {
458                         string nm;
459                         nm.append(reinterpret_cast<char*>(out), len);
460 #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
461                         XMLString::release(&out);
462 #else
463                         XMLString::release((char**)&out);
464 #endif
465                         in.addmember("gss_name").string(nm.c_str());
466                     }
467                     else {
468                         request.log(SPRequest::SPError, "error while base64-encoding GSS name");
469                     }
470                 }
471                 else {
472                     request.log(SPRequest::SPError, "error while exporting GSS name");
473                 }
474             }
475         }
476 #endif
477     }
478 #endif
479
480     return in;
481 }
482
483 pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
484 {
485     DDF h = out["headers"];
486     DDF hdr = h.first();
487     while (hdr.isstring()) {
488 #ifdef HAVE_STRCASECMP
489         if (!strcasecmp(hdr.name(), "Content-Type"))
490 #else
491         if (!stricmp(hdr.name(), "Content-Type"))
492 #endif
493             request.setContentType(hdr.string());
494         else
495             request.setResponseHeader(hdr.name(), hdr.string());
496         hdr = h.next();
497     }
498     h = out["redirect"];
499     if (h.isstring())
500         return make_pair(true, request.sendRedirect(h.string()));
501     h = out["response"];
502     if (h.isstruct()) {
503         istringstream s(h["data"].string());
504         return make_pair(true, request.sendResponse(s, h["status"].integer()));
505     }
506     return make_pair(false, 0L);
507 }
508
509 HTTPRequest* RemotedHandler::getRequest(DDF& in) const
510 {
511 #ifndef SHIBSP_LITE
512     return new RemotedRequest(in);
513 #else
514     throw ConfigurationException("Cannot process message using lite version of shibsp library.");
515 #endif
516 }
517
518 HTTPResponse* RemotedHandler::getResponse(DDF& out) const
519 {
520 #ifndef SHIBSP_LITE
521     return new RemotedResponse(out);
522 #else
523     throw ConfigurationException("Cannot process message using lite version of shibsp library.");
524 #endif
525 }