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