1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20 
21 /**
22  * AssertionLookup.cpp
23  *
24  * Handler for looking up assertions in the SessionCache.
25  */
26 
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "Application.h"
30 #include "ServiceProvider.h"
31 #include "SessionCache.h"
32 #include "SPRequest.h"
33 #include "handler/RemotedHandler.h"
34 #include "handler/SecuredHandler.h"
35 
36 #include <boost/scoped_ptr.hpp>
37 
38 #ifndef SHIBSP_LITE
39 # include <saml/exceptions.h>
40 # include <saml/Assertion.h>
41 # include <xmltooling/util/XMLHelper.h>
42 using namespace opensaml;
43 #endif
44 
45 using namespace shibspconstants;
46 using namespace shibsp;
47 using namespace xmltooling;
48 using namespace boost;
49 using namespace std;
50 
51 namespace shibsp {
52 
53 #if defined (_MSC_VER)
54     #pragma warning( push )
55     #pragma warning( disable : 4250 )
56 #endif
57 
58     class SHIBSP_API AssertionLookup : public SecuredHandler, public RemotedHandler
59     {
60     public:
61         AssertionLookup(const DOMElement* e, const char* appId);
~AssertionLookup()62         virtual ~AssertionLookup() {}
63 
64         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
65         void receive(DDF& in, ostream& out);
66 
getType() const67         const char* getType() const {
68             return "AssertionLookup";
69         }
70 
71     private:
72         pair<bool,long> processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
73     };
74 
75 #if defined (_MSC_VER)
76     #pragma warning( pop )
77 #endif
78 
AssertionLookupFactory(const pair<const DOMElement *,const char * > & p,bool)79     Handler* SHIBSP_DLLLOCAL AssertionLookupFactory(const pair<const DOMElement*,const char*>& p, bool)
80     {
81         return new AssertionLookup(p.first, p.second);
82     }
83 
84 };
85 
AssertionLookup(const DOMElement * e,const char * appId)86 AssertionLookup::AssertionLookup(const DOMElement* e, const char* appId)
87     : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT ".Handler.AssertionLookup"), "exportACL", "127.0.0.1 ::1")
88 {
89     pair<bool,const char*> prop = getString("Location");
90     if (!prop.first)
91         throw ConfigurationException("AssertionLookup handler requires Location property.");
92     string address(appId);
93     if (*prop.second != '/')
94         address += '/';
95     address += prop.second;
96     setAddress(address.c_str());
97 }
98 
run(SPRequest & request,bool isHandler) const99 pair<bool,long> AssertionLookup::run(SPRequest& request, bool isHandler) const
100 {
101     // Check ACL in base class.
102     pair<bool,long> ret = SecuredHandler::run(request, isHandler);
103     if (ret.first)
104         return ret;
105 
106     try {
107         if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
108             // When out of process, we run natively and directly process the message.
109             return processMessage(request.getApplication(), request, request);
110         }
111         else {
112             // When not out of process, we remote all the message processing.
113             DDF out,in = wrap(request);
114             DDFJanitor jin(in), jout(out);
115 
116             out = send(request, in);
117             return unwrap(request, out);
118         }
119     }
120     catch (std::exception& ex) {
121         m_log.error("error while processing request: %s", ex.what());
122         istringstream msg("Assertion Lookup Failed");
123         return make_pair(true, request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
124     }
125 }
126 
receive(DDF & in,ostream & out)127 void AssertionLookup::receive(DDF& in, ostream& out)
128 {
129     // Find application.
130     const char* aid = in["application_id"].string();
131     const Application* app = aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
132     if (!app) {
133         // Something's horribly wrong.
134         m_log.error("couldn't find application (%s) for assertion lookup", aid ? aid : "(missing)");
135         throw ConfigurationException("Unable to locate application for assertion lookup, deleted?");
136     }
137 
138     // Unpack the request.
139     scoped_ptr<HTTPRequest> req(getRequest(*app, in));
140     //m_log.debug("found %d client certificates", req->getClientCertificates().size());
141 
142     // Wrap a response shim.
143     DDF ret(nullptr);
144     DDFJanitor jout(ret);
145     scoped_ptr<HTTPResponse> resp(getResponse(*app, ret));
146 
147     // Since we're remoted, the result should either be a throw, a false/0 return,
148     // which we just return as an empty structure, or a response/redirect,
149     // which we capture in the facade and send back.
150     processMessage(*app, *req, *resp);
151     out << ret;
152 }
153 
processMessage(const Application & application,HTTPRequest & httpRequest,HTTPResponse & httpResponse) const154 pair<bool,long> AssertionLookup::processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const
155 {
156 #ifndef SHIBSP_LITE
157     const char* key = httpRequest.getParameter("key");
158     const char* ID = httpRequest.getParameter("ID");
159     if (!key || !*key || !ID || !*ID) {
160         m_log.error("assertion lookup request failed, missing required parameters");
161         throw FatalProfileException("Missing key or ID parameters.");
162     }
163 
164     m_log.debug("processing assertion lookup request (session: %s, assertion: %s)", key, ID);
165 
166     SessionCache* cache = application.getServiceProvider().getSessionCache();
167     if (!cache) {
168         m_log.error("session cache does not support extended API");
169         throw FatalProfileException("Session cache does not support assertion lookup.");
170     }
171 
172     // The cache will either silently pass a session or nullptr back, or throw an exception out.
173     Session* session = cache->find(application, key);
174     if (!session) {
175         m_log.error("valid session (%s) not found for assertion lookup", key);
176         throw FatalProfileException("Session key not found.");
177     }
178 
179     Locker locker(session, false);
180 
181     const Assertion* assertion = session->getAssertion(ID);
182     if (!assertion) {
183         m_log.error("assertion (%s) not found in session (%s)", ID, key);
184         throw FatalProfileException("Assertion not found.");
185     }
186 
187     stringstream s;
188     s << *assertion;
189     httpResponse.setContentType("application/samlassertion+xml");
190     return make_pair(true, httpResponse.sendResponse(s));
191 #else
192     return make_pair(false, 0L);
193 #endif
194 }
195