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