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