2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 * shibrpc-server.cpp -- SHIBRPC Server implementation. Originally created
52 * as shibrpc-server-stubs.c; make sure that the function
53 * prototypes here match those in shibrpc.x.
55 * Created by: Derek Atkins <derek@ihtfp.com>
66 #ifdef HAVE_LIBDMALLOCXX
70 #include <log4cpp/Category.hh>
73 using namespace log4cpp;
75 using namespace shibboleth;
76 using namespace shibtarget;
78 static string get_threadid (const char* proc)
80 static u_long counter = 0;
82 buf << "[" << counter++ << "] " << proc;
86 static Category& get_category (void)
88 string ctx = "shibtarget.rpc-server";
89 return Category::getInstance(ctx);
93 shibrpc_ping_1_svc(int *argp, int *result, struct svc_req *rqstp)
99 void set_rpc_status(ShibRpcError *error, ShibRpcStatus status,
100 const char* msg, const char* origin)
102 error->status = status;
104 error->ShibRpcError_u.e.error = strdup(msg ? msg : "");
105 error->ShibRpcError_u.e.origin = strdup(origin ? origin : "");
109 void set_rpc_status_x(ShibRpcError *error, ShibRpcStatus status,
110 const char* msg, const XMLCh* origin)
113 set_rpc_status(error, status, NULL, NULL);
116 auto_ptr_char orig(origin);
117 set_rpc_status(error, status, msg, orig.get());
121 shibrpc_session_is_valid_1_svc(shibrpc_session_is_valid_args_1 *argp,
122 shibrpc_session_is_valid_ret_1 *result,
123 struct svc_req *rqstp)
125 Category& log = get_category();
126 string ctx = get_threadid("session_is_valid");
129 if (!argp || !result) {
130 log.error ("RPC Argument Error");
134 memset (result, 0, sizeof (*result));
136 log.debug ("checking: %s@%s (checkAddr=%s)",
137 argp->cookie.cookie, argp->cookie.client_addr,
138 argp->checkIPAddress ? "true" : "false");
140 // See if the cookie exists...
141 CCacheEntry *entry = g_shibTargetCCache->find(argp->cookie.cookie);
143 // If not, leave now..
145 log.debug ("Not found");
146 set_rpc_status(&result->status, SHIBRPC_NO_SESSION,
147 "No session exists for this cookie", "");
151 // TEST the session...
155 const XMLCh* origin = entry->getStatement()->getSubject()->getNameQualifier();
157 // Verify the address is the same
158 if (argp->checkIPAddress) {
159 log.debug ("Checking address against %s", entry->getClientAddress());
160 if (strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
161 log.debug ("IP Address mismatch");
163 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
164 "Your IP address does not match the address in the original authentication.",
169 // and that the session is still valid...
170 if (!entry->isSessionValid(argp->lifetime, argp->timeout)) {
171 log.debug ("Session expired");
172 throw ShibTargetException(SHIBRPC_SESSION_EXPIRED,
173 "Your session has expired. Re-authenticate.",
177 // and now try to prefetch the attributes .. this could cause an
178 // "error", which is why we call it here.
180 log.debug ("resource: %s", argp->application_id);
181 entry->preFetch(argp->application_id, 15); // give a 15-second window for the RM
183 } catch (SAMLException &e) {
184 log.debug ("prefetch failed with a SAML Exception: %s", e.what());
187 throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str(), origin);
190 log.error ("prefetch caught an unknown exception");
191 throw ShibTargetException(SHIBRPC_UNKNOWN_ERROR,
192 "An unknown error occured while pre-fetching attributes.",
196 } catch (ShibTargetException &e) {
198 g_shibTargetCCache->remove (argp->cookie.cookie);
199 set_rpc_status_x(&result->status, e.which(), e.what(), e.where());
203 // Ok, just release it.
206 // ok, we've succeeded..
207 set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
208 log.debug ("session ok");
213 shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp,
214 shibrpc_new_session_ret_1 *result, struct svc_req *rqstp)
216 Category& log = get_category();
217 string ctx = get_threadid("new_session");
220 if (!argp || !result) {
221 log.error ("Invalid RPC Arguments");
225 // Initialize the result structure
226 memset (result, 0, sizeof(*result));
227 result->cookie = strdup ("");
229 log.debug ("creating session for %s", argp->client_addr);
230 log.debug ("shire location: %s", argp->shire_location);
232 XMLByte* post=reinterpret_cast<XMLByte*>(argp->saml_post);
233 auto_ptr_XMLCh location(argp->shire_location);
235 // Pull in the Policies
236 Iterator<const XMLCh*> policies=dynamic_cast<STConfig&>(ShibTargetConfig::getConfig()).getPolicies();
238 // And grab the Profile
239 // XXX: Create a "Global" POSTProfile instance per location...
240 log.debug ("create the POST profile (%d policies)", policies.size());
241 ShibPOSTProfile *profile = ShibPOSTProfileFactory::getInstance(
242 ShibTargetConfig::getConfig().getMetadataProviders(),
243 ShibTargetConfig::getConfig().getTrustProviders(),
244 policies,location.get(),3600
247 SAMLResponse* r = NULL;
248 const SAMLAuthenticationStatement* auth_st = NULL;
249 XMLCh* origin = NULL;
255 // Make sure we've got a profile
257 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
258 "Failed to obtain the profile");
260 // Try and accept the response...
261 log.debug ("Trying to accept the post");
262 r = profile->accept(post, &origin);
264 // Make sure we got a response
266 throw ShibTargetException(SHIBRPC_RESPONSE_MISSING,
267 "Failed to accept the response.",
270 // Find the SSO Assertion
271 log.debug ("Get the SSOAssertion");
272 const SAMLAssertion* ssoAssertion = profile->getSSOAssertion(*r);
274 // Check against the replay cache
275 log.debug ("check replay cache");
276 if (profile->checkReplayCache(*ssoAssertion) == false)
277 throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED,
278 "Duplicate assertion found.",
281 // Get the authentication statement we need.
282 log.debug ("get SSOStatement");
283 auth_st = profile->getSSOStatement(*ssoAssertion);
285 // Maybe verify the origin address....
286 if (argp->checkIPAddress) {
287 log.debug ("check IP Address");
289 // Verify the client address exists
290 const XMLCh* ip = auth_st->getSubjectIP();
292 throw ShibTargetException(SHIBRPC_IPADDR_MISSING,
293 "The IP Address provided by your origin site was missing.",
296 log.debug ("verify client address");
297 // Verify the client address matches authentication
298 auto_ptr_char this_ip(ip);
299 if (strcmp (argp->client_addr, this_ip.get()))
300 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
301 "The IP address provided by your origin site did not match "
302 "your current address. "
303 "To correct this problem you may need to bypass a local proxy server.",
307 catch (SAMLException &e) // XXX refine this handler to catch and log different profile exceptions
309 log.error ("received SAML exception: %s", e.what());
312 throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str(), origin);
314 catch (XMLException &e)
316 log.error ("received XML exception");
317 auto_ptr_char msg(e.getMessage());
318 throw ShibTargetException (SHIBRPC_XML_EXCEPTION, msg.get(), origin);
321 catch (ShibTargetException &e)
323 log.info ("FAILED: %s", e.what());
325 if (origin) XMLString::release(&origin);
326 set_rpc_status_x(&result->status, e.which(), e.what(), e.where());
332 log.error ("Unknown error");
334 if (origin) XMLString::release(&origin);
335 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR,
336 "An unknown exception occurred", "");
341 // It passes all our tests -- create a new session.
342 log.info ("Creating new session");
344 SAMLAuthenticationStatement* as=static_cast<SAMLAuthenticationStatement*>(auth_st->clone());
346 // Create a new cookie
349 const char *cookie = c.get();
351 // Cache this session with the cookie
352 g_shibTargetCCache->insert(cookie, as, argp->client_addr);
354 // Delete the response...
357 // Delete the origin...
358 XMLString::release(&origin);
360 // And let the user know.
361 if (result->cookie) free(result->cookie);
362 result->cookie = strdup(cookie);
363 set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
365 log.debug("new session id: %s", cookie);
370 shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp,
371 shibrpc_get_assertions_ret_1 *result, struct svc_req *rqstp)
373 Category& log = get_category();
374 string ctx = get_threadid("get_assertions");
377 if (!argp || !result) {
378 log.error ("Invalid RPC arguments");
382 memset (result, 0, sizeof (*result));
384 log.debug ("get attrs for client at %s", argp->cookie.client_addr);
385 log.debug ("cookie: %s", argp->cookie.cookie);
386 log.debug ("resource: %s", argp->application_id);
389 CCacheEntry* entry = g_shibTargetCCache->find(argp->cookie.cookie);
391 // If it does not exist, leave now..
393 log.error ("No Session");
394 set_rpc_status(&result->status, SHIBRPC_NO_SESSION,
395 "getattrs Internal error: no session", "");
400 const XMLCh* origin = entry->getStatement()->getSubject()->getNameQualifier();
402 // Validate the client address (again?)
403 if (argp->checkIPAddress &&
404 strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
405 log.error ("IP Mismatch");
406 set_rpc_status_x(&result->status, SHIBRPC_IPADDR_MISMATCH,
407 "Your IP address does not match the address in the original authentication.",
414 // grab the attributes for this resource
415 Iterator<SAMLAssertion*> iter = entry->getAssertions(argp->application_id);
416 u_int size = iter.size();
417 result->assertions.assertions_len = size;
419 // if we have assertions...
422 // Build the response section
423 ShibRpcXML* av = (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
424 result->assertions.assertions_val = av;
426 // and then serialize them all...
428 while (iter.hasNext()) {
429 SAMLAssertion* as = iter.next();
432 av[i++].xml_string = strdup(os.str().c_str());
435 } catch (SAMLException& e) {
436 log.error ("received SAML exception: %s", e.what());
439 set_rpc_status_x(&result->status, SHIBRPC_SAML_EXCEPTION,
440 strdup(os.str().c_str()), origin);
445 // Now grab the serialized authentication statement
446 result->auth_statement.xml_string = strdup(entry->getSerializedStatement());
451 set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
453 log.debug ("returning");
458 shibrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
460 xdr_free (xdr_result, result);
463 * Insert additional freeing code here, if needed