Support for GSS-API contexts attached to requests.
[shibboleth/cpp-sp.git] / shibsp / handler / impl / RemotedHandler.cpp
index 238f1e3..a843751 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2010 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 #include "internal.h"
-#include "Application.h"
 #include "exceptions.h"
+#include "Application.h"
+#include "GSSRequest.h"
 #include "ServiceProvider.h"
+#include "SPRequest.h"
 #include "handler/RemotedHandler.h"
 
 #include <algorithm>
 #include <xmltooling/unicode.h>
+#include <xercesc/util/Base64.hpp>
 
 #ifndef SHIBSP_LITE
 # include "util/CGIParser.h"
@@ -45,16 +48,35 @@ using namespace std;
 
 #ifndef SHIBSP_LITE
 namespace shibsp {
-    class SHIBSP_DLLLOCAL RemotedRequest : public virtual HTTPRequest 
+    class SHIBSP_DLLLOCAL RemotedRequest : 
+#ifdef HAVE_GSSAPI
+        public virtual GSSRequest,
+#endif
+        public virtual HTTPRequest
     {
         DDF& m_input;
         mutable CGIParser* m_parser;
         mutable vector<XSECCryptoX509*> m_certs;
+#ifdef HAVE_GSSAPI
+        mutable gss_ctx_id_t m_gss;
+#endif
     public:
-        RemotedRequest(DDF& input) : m_input(input), m_parser(NULL) {}
+        RemotedRequest(DDF& input) : m_input(input), m_parser(nullptr)
+#ifdef HAVE_GSSAPI
+            , m_ctx(GSS_C_NO_CONTEXT)
+#endif
+        {
+        }
+
         virtual ~RemotedRequest() {
             for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
             delete m_parser;
+#ifdef HAVE_GSSAPI
+            if (m_ctx != GSS_C_NO_CONTEXT) {
+                OM_uint32 minor;
+                gss_delete_sec_context(&minor, &m_ctx, GSS_C_NO_BUFFER);
+            }
+#endif
         }
 
         // GenericRequest
@@ -92,6 +114,11 @@ namespace shibsp {
 
         const std::vector<XSECCryptoX509*>& getClientCertificates() const;
         
+#ifdef HAVE_GSSAPI
+        // GSSRequest
+        gss_ctx_id_t getGSSContext() const;
+#endif
+
         // HTTPRequest
         const char* getMethod() const {
             return m_input["method"].string();
@@ -133,7 +160,7 @@ const char* RemotedRequest::getParameter(const char* name) const
         m_parser=new CGIParser(*this);
     
     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
-    return (bounds.first==bounds.second) ? NULL : bounds.first->second;
+    return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
 }
 
 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
@@ -176,6 +203,34 @@ const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() cons
     return m_certs;
 }
 
+#ifdef HAVE_GSSAPI
+gss_ctx_id_t RemotedRequest::getGSSContext() const
+{
+    if (m_ctx == GSS_C_NO_CONTEXT) {
+        const char* encoded = m_input["gss_context"];
+        if (encoded) {
+            xsecsize_t x;
+            XMLByte* decoded=Base64::decode(reinterpret_cast<const XMLByte*>(encoded), &x);
+            if (decoded) {
+                gss_buffer_desc importbuf;
+                importbuf.length = x;
+                importbuf.data = decoded;
+                OM_uint32 minor;
+                OM_uint32 major = gss_import_sec_context(&minor, &importbuf, &m_ctx);
+                if (major != GSS_S_COMPLETE)
+                    m_ctx = GSS_C_NO_CONTEXT;
+#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
+                XMLString::release(&decoded);
+#else
+                XMLString::release((char**)&decoded);
+#endif
+            }
+        }
+    }
+    return m_ctx;
+}
+#endif
+
 long RemotedResponse::sendResponse(std::istream& in, long status)
 {
     string msg;
@@ -197,15 +252,16 @@ void RemotedResponse::setResponseHeader(const char* name, const char* value)
         m_output.structure();
     DDF hdrs = m_output["headers"];
     if (hdrs.isnull())
-        hdrs = m_output.addmember("headers").structure();
-    hdrs.addmember(name).string(value);
+        hdrs = m_output.addmember("headers").list();
+    DDF h = DDF(name).string(value);
+    hdrs.add(h);
 }
 
 long RemotedResponse::sendRedirect(const char* url)
 {
     if (!m_output.isstruct())
         m_output.structure();
-    m_output.addmember("redirect").string(url);
+    m_output.addmember("redirect").unsafe_string(url);
     return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED;
 }
 
@@ -226,6 +282,10 @@ void RemotedHandler::setAddress(const char* address)
     }
 }
 
+RemotedHandler::RemotedHandler()
+{
+}
+
 RemotedHandler::~RemotedHandler()
 {
     SPConfig& conf = SPConfig::getConfig();
@@ -239,7 +299,7 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers
     DDF in = DDF(m_address.c_str()).structure();
     in.addmember("application_id").string(request.getApplication().getId());
     in.addmember("scheme").string(request.getScheme());
-    in.addmember("hostname").string(request.getHostname());
+    in.addmember("hostname").unsafe_string(request.getHostname());
     in.addmember("port").integer(request.getPort());
     in.addmember("content_type").string(request.getContentType().c_str());
     in.addmember("content_length").integer(request.getContentLength());
@@ -247,8 +307,8 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers
     in.addmember("remote_user").string(request.getRemoteUser().c_str());
     in.addmember("client_addr").string(request.getRemoteAddr().c_str());
     in.addmember("method").string(request.getMethod());
-    in.addmember("uri").string(request.getRequestURI());
-    in.addmember("url").string(request.getRequestURL());
+    in.addmember("uri").unsafe_string(request.getRequestURI());
+    in.addmember("url").unsafe_string(request.getRequestURL());
     in.addmember("query").string(request.getQueryString());
 
     if (headers) {
@@ -257,7 +317,7 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers
         for (vector<string>::const_iterator h = headers->begin(); h!=headers->end(); ++h) {
             hdr = request.getHeader(h->c_str());
             if (!hdr.empty())
-                hin.addmember(h->c_str()).string(hdr.c_str());
+                hin.addmember(h->c_str()).unsafe_string(hdr.c_str());
         }
     }
 
@@ -267,7 +327,7 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers
         if (!xvec.empty()) {
             DDF clist = in.addmember("certificates").list();
             for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
-                DDF x509 = DDF(NULL).string((*x)->getDEREncodingSB().rawCharBuffer());
+                DDF x509 = DDF(nullptr).string((*x)->getDEREncodingSB().rawCharBuffer());
                 clist.add(x509);
             }
         }
@@ -276,13 +336,47 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers
         if (!xvec.empty()) {
             DDF clist = in.addmember("certificates").list();
             for (vector<string>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
-                DDF x509 = DDF(NULL).string(x->c_str());
+                DDF x509 = DDF(nullptr).string(x->c_str());
                 clist.add(x509);
             }
         }
 #endif
     }
 
+#ifdef HAVE_GSSAPI
+    const GSSRequest* gss = dynamic_cast<const GSSRequest*>(&request);
+    if (gss) {
+        gss_ctx_id_t ctx = gss->getGSSContext();
+        if (ctx != GSS_C_NO_CONTEXT) {
+            OM_uint32 minor;
+            gss_buffer_desc contextbuf;
+            contextbuf.length = 0;
+            contextbuf.value = nullptr;
+            OM_uint32 major = gss_export_sec_context(&minor, &ctx, &contextbuf);
+            if (major == GSS_S_COMPLETE) {
+                xsecsize_t len=0;
+                XMLByte* out=Base64::encode(reinterpret_cast<const XMLByte*>(contextbuf.value), contextbuf.length, &len);
+                if (out) {
+                    string ctx;
+                    ctx.append(reinterpret_cast<char*>(out), len);
+#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE
+                    XMLString::release(&out);
+#else
+                    XMLString::release((char**)&out);
+#endif
+                    in.addmember("gss_context").string(ctx.c_str());
+                }
+                else {
+                    request.log(SPRequest::SPError, "error while base64-encoding GSS context");
+                }
+            }
+            else {
+                request.log(SPRequest::SPError, "error while exporting GSS context");
+            }
+        }
+    }
+#endif
+
     return in;
 }