Rework address handling based on app/location.
[shibboleth/cpp-sp.git] / shibsp / handler / impl / RemotedHandler.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  * 
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /**
18  * RemotedHandler.cpp
19  * 
20  * Base class for handlers that need SP request/response layer to be remoted. 
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "ServiceProvider.h"
26 #include "handler/RemotedHandler.h"
27
28 #include <algorithm>
29 #include <log4cpp/Category.hh>
30 #include <saml/util/CGIParser.h>
31 #include <xmltooling/unicode.h>
32 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
33 #include <xsec/enc/XSECCryptoException.hpp>
34 #include <xsec/framework/XSECException.hpp>
35 #include <xsec/framework/XSECProvider.hpp>
36
37 using namespace shibsp;
38 using namespace opensaml;
39 using namespace xmltooling;
40 using namespace log4cpp;
41 using namespace xercesc;
42 using namespace std;
43
44 namespace shibsp {
45     class SHIBSP_DLLLOCAL RemotedRequest : public virtual opensaml::HTTPRequest 
46     {
47         DDF& m_input;
48         mutable CGIParser* m_parser;
49         mutable vector<XSECCryptoX509*> m_certs;
50     public:
51         RemotedRequest(DDF& input) : m_input(input), m_parser(NULL) {}
52         virtual ~RemotedRequest() {
53             for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
54             delete m_parser;
55         }
56
57         // GenericRequest
58         const char* getScheme() const {
59             return m_input["scheme"].string();
60         }
61         const char* getHostname() const {
62             return m_input["hostname"].string();
63         }
64         int getPort() const {
65             return m_input["port"].integer();
66         }
67         std::string getContentType() const {
68             DDF s = m_input["content_type"];
69             return s.string() ? s.string() : "";
70         }
71         long getContentLength() const {
72             return m_input["content_length"].integer();
73         }
74         const char* getRequestBody() const {
75             return m_input["body"].string();
76         }
77
78         const char* getParameter(const char* name) const;
79         std::vector<const char*>::size_type getParameters(const char* name, std::vector<const char*>& values) const;
80         
81         std::string getRemoteUser() const {
82             DDF s = m_input["remote_user"];
83             return s.string() ? s.string() : "";
84         }
85         std::string getRemoteAddr() const {
86             DDF s = m_input["client_addr"];
87             return s.string() ? s.string() : "";
88         }
89
90         const std::vector<XSECCryptoX509*>& getClientCertificates() const;
91         
92         // HTTPRequest
93         const char* getMethod() const {
94             return m_input["method"].string();
95         }
96         const char* getRequestURI() const {
97             return m_input["uri"].string();
98         }
99         const char* getRequestURL() const {
100             return m_input["url"].string();
101         }
102         const char* getQueryString() const {
103             return m_input["query"].string();
104         }
105         std::string getHeader(const char* name) const {
106             DDF s = m_input["headers"][name];
107             return s.string() ? s.string() : "";
108         }
109     };
110
111     class SHIBSP_DLLLOCAL RemotedResponse : public virtual opensaml::HTTPResponse 
112     {
113         DDF& m_output;
114     public:
115         RemotedResponse(DDF& output) : m_output(output) {}
116         virtual ~RemotedResponse() {}
117        
118         // GenericResponse
119         long sendResponse(std::istream& inputStream, long status);
120         
121         // HTTPResponse
122         void setResponseHeader(const char* name, const char* value);
123         long sendRedirect(const char* url);
124     };
125 }
126
127 const char* RemotedRequest::getParameter(const char* name) const
128 {
129     if (!m_parser)
130         m_parser=new CGIParser(*this);
131     
132     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
133     return (bounds.first==bounds.second) ? NULL : bounds.first->second;
134 }
135
136 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
137 {
138     if (!m_parser)
139         m_parser=new CGIParser(*this);
140
141     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
142     while (bounds.first!=bounds.second) {
143         values.push_back(bounds.first->second);
144         ++bounds.first;
145     }
146     return values.size();
147 }
148
149 const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
150 {
151     if (m_certs.empty()) {
152         DDF cert = m_input["certificates"].first();
153         while (cert.isstring()) {
154             try {
155                 auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
156                 x509->loadX509Base64Bin(cert.string(), cert.strlen());
157                 m_certs.push_back(x509.release());
158             }
159             catch(XSECException& e) {
160                 auto_ptr_char temp(e.getMsg());
161                 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", temp.get());
162             }
163             catch(XSECCryptoException& e) {
164                 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
165             }
166             cert = cert.next();
167         }
168     }
169     return m_certs;
170 }
171
172 long RemotedResponse::sendResponse(std::istream& in, long status)
173 {
174     string msg;
175     char buf[1024];
176     while (in) {
177         in.read(buf,1024);
178         msg.append(buf,in.gcount());
179     }
180     if (!m_output.isstruct())
181         m_output.structure();
182     m_output.addmember("response.data").string(msg.c_str());
183     m_output.addmember("response.status").integer(status);
184     return status;
185 }
186
187 void RemotedResponse::setResponseHeader(const char* name, const char* value)
188 {
189     if (!m_output.isstruct())
190         m_output.structure();
191     DDF hdrs = m_output["headers"];
192     if (hdrs.isnull())
193         hdrs = m_output.addmember("headers").structure();
194     hdrs.addmember(name).string(value);
195 }
196
197 long RemotedResponse::sendRedirect(const char* url)
198 {
199     if (!m_output.isstruct())
200         m_output.structure();
201     m_output.addmember("redirect").string(url);
202     return HTTPResponse::SAML_HTTP_STATUS_MOVED;
203 }
204
205
206 void RemotedHandler::setAddress(const char* address)
207 {
208     if (!m_address.empty())
209         throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
210     m_address = address;
211     SPConfig& conf = SPConfig::getConfig();
212     if (conf.isEnabled(SPConfig::OutOfProcess)) {
213         ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
214         if (listener)
215             listener->regListener(m_address.c_str(),this);
216         else
217             Category::getInstance(SHIBSP_LOGCAT".Handler").info("no ListenerService available, handler remoting disabled");
218     }
219 }
220
221 RemotedHandler::~RemotedHandler()
222 {
223     SPConfig& conf = SPConfig::getConfig();
224     ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
225     if (listener && conf.isEnabled(SPConfig::OutOfProcess))
226         listener->unregListener(m_address.c_str(),this);
227 }
228
229 DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
230 {
231     DDF in = DDF(m_address.c_str()).structure();
232     in.addmember("scheme").string(request.getScheme());
233     in.addmember("hostname").string(request.getHostname());
234     in.addmember("port").integer(request.getPort());
235     in.addmember("content_type").string(request.getContentType().c_str());
236     in.addmember("content_length").integer(request.getContentLength());
237     in.addmember("body").string(request.getRequestBody());
238     in.addmember("remote_user").string(request.getRemoteUser().c_str());
239     in.addmember("client_addr").string(request.getRemoteAddr().c_str());
240     in.addmember("method").string(request.getMethod());
241     in.addmember("uri").string(request.getRequestURI());
242     in.addmember("url").string(request.getRequestURL());
243     in.addmember("query").string(request.getQueryString());
244
245     if (headers) {
246         string hdr;
247         DDF hin = in.addmember("headers").structure();
248         for (vector<string>::const_iterator h = headers->begin(); h!=headers->end(); ++h) {
249             hdr = request.getHeader(h->c_str());
250             if (!hdr.empty())
251                 hin.addmember(h->c_str()).string(hdr.c_str());
252         }
253     }
254
255     if (certs) {
256         const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
257         if (!xvec.empty()) {
258             DDF clist = in.addmember("certificates").list();
259             for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
260                 DDF x509 = DDF(NULL).string((*x)->getDEREncodingSB().rawCharBuffer());
261                 clist.add(x509);
262             }
263         }
264     }
265
266     return in;
267 }
268
269 pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
270 {
271     DDF h = out["headers"];
272     h = h.first();
273     while (h.isstring()) {
274         request.setResponseHeader(h.name(), h.string());
275         h = h.next();
276     }
277     h = out["redirect"];
278     if (h.isstring())
279         return make_pair(true, request.sendRedirect(h.string()));
280     h = out["response"];
281     if (h.isstruct()) {
282         istringstream s(h["data"].string());
283         return make_pair(true, static_cast<GenericResponse&>(request).sendResponse(s, h["status"].integer()));
284     }
285     return make_pair(false,0);
286 }
287
288 HTTPRequest* RemotedHandler::getRequest(DDF& in) const
289 {
290     return new RemotedRequest(in);
291 }
292
293 HTTPResponse* RemotedHandler::getResponse(DDF& out) const
294 {
295     return new RemotedResponse(out);
296 }