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 return Category::getInstance("shibtarget.rpc-server");
92 shibrpc_ping_1_svc(int *argp, int *result, struct svc_req *rqstp)
98 // Functions to map errors into IDL-defined status structure
100 void set_rpc_status(ShibRpcError *error, ShibRpcStatus status, const char* msg=NULL)
102 error->status = status;
104 error->ShibRpcError_u.e.error = strdup(msg ? msg : "");
105 error->ShibRpcError_u.e.provider = strdup("");
106 error->ShibRpcError_u.e.url = strdup("");
107 error->ShibRpcError_u.e.contact = strdup("");
108 error->ShibRpcError_u.e.email = strdup("");
112 void set_rpc_status(ShibRpcError *error, ShibTargetException& exc)
114 error->status = exc.which();
116 error->ShibRpcError_u.e.error = strdup(exc.what() ? exc.what() : "");
117 error->ShibRpcError_u.e.provider = strdup(exc.syswho() ? exc.syswho() : "");
118 error->ShibRpcError_u.e.url = strdup(exc.where() ? exc.where() : "");
119 error->ShibRpcError_u.e.contact = strdup(exc.who() ? exc.who() : "");
120 error->ShibRpcError_u.e.email = strdup(exc.how() ? exc.how() : "");
125 void set_rpc_status_x(ShibRpcError *error, ShibRpcStatus status,
126 const char* msg=NULL, const XMLCh* origin=NULL)
129 set_rpc_status(error, status);
132 auto_ptr_char orig(origin);
133 set_rpc_status(error, status, msg, orig.get());
138 shibrpc_session_is_valid_1_svc(shibrpc_session_is_valid_args_1 *argp,
139 shibrpc_session_is_valid_ret_1 *result,
140 struct svc_req *rqstp)
142 Category& log = get_category();
143 string ctx = get_threadid("session_is_valid");
146 if (!argp || !result) {
147 log.error ("RPC Argument Error");
151 memset (result, 0, sizeof (*result));
153 log.debug ("checking: %s@%s (checkAddr=%s)",
154 argp->cookie.cookie, argp->cookie.client_addr,
155 argp->checkIPAddress ? "true" : "false");
157 // See if the cookie exists...
158 IConfig* conf=ShibTargetConfig::getConfig().getINI();
160 ISessionCacheEntry* entry = conf->getSessionCache()->find(argp->cookie.cookie);
162 // If not, leave now..
164 log.debug ("Not found");
165 set_rpc_status(&result->status, SHIBRPC_NO_SESSION, "No session exists for this cookie");
169 // TEST the session...
172 // Try and locate support metadata for errors we throw.
173 log.debug ("application: %s", argp->application_id);
174 const IApplication* app=conf->getApplication(argp->application_id);
176 // Something's horribly wrong. Flush the session.
177 throw ShibTargetException(SHIBRPC_NO_SESSION,"Unable to locate application for session, deleted?");
179 Metadata m(app->getMetadataProviders());
180 const IProvider* origin=m.lookup(entry->getStatement()->getSubject()->getNameIdentifier()->getNameQualifier());
182 // Verify the address is the same
183 if (argp->checkIPAddress) {
184 log.debug ("Checking address against %s", entry->getClientAddress());
185 if (strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
186 log.debug ("IP Address mismatch");
187 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
188 "Your IP address does not match the address in the original authentication.", origin);
192 // and that the session is still valid...
193 if (!entry->isValid(argp->lifetime, argp->timeout)) {
194 log.debug ("Session expired");
195 throw ShibTargetException(SHIBRPC_SESSION_EXPIRED, "Your session has expired, must re-authenticate.", origin);
198 // and now try to prefetch the attributes .. this could cause an
199 // "error", which is why we call it here.
201 entry->preFetch(15); // give a 15-second window for the RM
203 catch (SAMLException &e) {
204 log.debug ("prefetch failed with a SAML Exception: %s", e.what());
207 throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str().c_str(), origin);
209 catch (ShibTargetException&) {
210 // These are caught and handled down below.
215 log.error ("prefetch caught an unknown exception");
216 throw ShibTargetException(SHIBRPC_UNKNOWN_ERROR,
217 "An unknown error occured while pre-fetching attributes.", origin);
221 catch (ShibTargetException &e) {
223 conf->getSessionCache()->remove(argp->cookie.cookie);
224 set_rpc_status(&result->status, e);
225 // Transaction Logging
226 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
227 stc.getTransactionLog().infoStream() <<
228 "Destroyed invalid session (ID: " <<
229 argp->cookie.cookie <<
230 ") with (applicationId: " <<
231 argp->application_id <<
232 "), request was from (ClientAddress: " <<
233 argp->cookie.client_addr <<
235 stc.releaseTransactionLog();
239 // Ok, just release it.
242 // ok, we've succeeded..
243 set_rpc_status(&result->status, SHIBRPC_OK);
244 log.debug ("session ok");
249 shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp,
250 shibrpc_new_session_ret_1 *result, struct svc_req *rqstp)
252 Category& log = get_category();
253 string ctx=get_threadid("new_session");
256 if (!argp || !result) {
257 log.error ("Invalid RPC Arguments");
261 // Initialize the result structure
262 memset (result, 0, sizeof(*result));
263 result->cookie = strdup ("");
265 log.debug ("creating session for %s", argp->client_addr);
266 log.debug ("shire location: %s", argp->shire_location);
267 log.debug ("application: %s", argp->application_id);
269 XMLByte* post=reinterpret_cast<XMLByte*>(argp->saml_post);
270 auto_ptr_XMLCh location(argp->shire_location);
272 SAMLResponse* r = NULL;
273 const SAMLAuthenticationStatement* auth_st = NULL;
274 XMLCh* origin = NULL;
276 // Access the application config.
277 IConfig* conf=ShibTargetConfig::getConfig().getINI();
279 const IApplication* app=conf->getApplication(argp->application_id);
281 // Something's horribly wrong. Flush the session.
282 log.error ("couldn't find application for session");
283 set_rpc_status(&result->status, SHIBRPC_INTERNAL_ERROR, "Unable to locate application for session, deleted?");
287 pair<bool,bool> checkReplay=pair<bool,bool>(false,false);
288 const IPropertySet* props=app->getPropertySet("Sessions");
290 checkReplay=props->getBool("checkReplay");
295 // Something's horribly wrong.
296 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,"Unable to locate application configuration, deleted?");
298 // And build the POST profile wrapper.
299 log.debug("create the POST profile");
300 ShibPOSTProfile profile(app->getMetadataProviders(),app->getRevocationProviders(),app->getTrustProviders());
302 const IProviderRole* role=NULL;
305 // Try and accept the response...
306 log.debug ("Trying to accept the post");
307 r = profile.accept(post,location.get(),300,app->getAudiences(),&origin);
309 // Try and map to metadata for support purposes.
310 Metadata m(app->getMetadataProviders());
311 const IProvider* provider=m.lookup(origin);
313 Iterator<const IProviderRole*> roles=provider->getRoles();
314 while (!role && roles.hasNext()) {
315 const IProviderRole* _r=roles.next();
316 if (dynamic_cast<const IIDPProviderRole*>(_r) && _r->hasSupport(Constants::SHIB_NS))
320 // This can't really happen, since the profile must have found a role.
322 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
323 "Unable to locate role-specific metadata for identity provider", provider);
325 // Make sure we got a response
327 throw ShibTargetException(SHIBRPC_RESPONSE_MISSING, "Failed to accept the response.", role);
329 // Find the SSO Assertion
330 log.debug ("Get the SSOAssertion");
331 const SAMLAssertion* ssoAssertion = profile.getSSOAssertion(*r,app->getAudiences());
333 // Check against the replay cache?
334 if (checkReplay.first && !checkReplay.second)
335 log.warn("replay checking is off, this is a security risk unless you're testing");
337 log.debug ("check replay cache");
338 if (!profile.checkReplayCache(*ssoAssertion))
339 throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED, "Duplicate assertion detected.", role);
342 // Get the authentication statement we need.
343 log.debug ("get SSOStatement");
344 auth_st = profile.getSSOStatement(*ssoAssertion);
346 // Maybe verify the origin address....
347 if (argp->checkIPAddress) {
348 log.debug ("check IP Address");
350 // Verify the client address exists
351 const XMLCh* ip = auth_st->getSubjectIP();
353 log.debug ("verify client address");
355 // Verify the client address matches authentication
356 auto_ptr_char this_ip(ip);
357 if (strcmp(argp->client_addr, this_ip.get()))
358 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
359 "Your client's current IP address differs from the one used when you authenticated "
360 "to your identity provider. To correct this problem, you may need to bypass a proxy server. "
361 "Please contact your local support staff or help desk for assistance.",
366 catch (SAMLException &e)
368 log.error ("caught SAML exception: %s", e.what());
371 throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str().c_str(), role);
373 catch (XMLException &e)
375 log.error ("received XML exception");
376 auto_ptr_char msg(e.getMessage());
377 throw ShibTargetException (SHIBRPC_XML_EXCEPTION, msg.get(), role);
380 catch (ShibTargetException &e) {
381 log.info ("FAILED: %s", e.what());
383 if (origin) XMLString::release(&origin);
384 set_rpc_status(&result->status, e);
389 log.error ("Unknown error");
391 if (origin) XMLString::release(&origin);
392 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR, "An unknown exception occurred");
397 // It passes all our tests -- create a new session.
398 log.info ("Creating new session");
400 SAMLAuthenticationStatement* as=static_cast<SAMLAuthenticationStatement*>(auth_st->clone());
402 // Create a new cookie
403 string cookie = conf->getSessionCache()->generateKey();
405 // Cache this session, possibly including response if attributes appear present.
406 bool attributesPushed=false;
407 Iterator<SAMLAssertion*> assertions=r->getAssertions();
408 while (!attributesPushed && assertions.hasNext()) {
409 Iterator<SAMLStatement*> statements=assertions.next()->getStatements();
410 while (!attributesPushed && statements.hasNext()) {
411 if (dynamic_cast<SAMLAttributeStatement*>(statements.next()))
412 attributesPushed=true;
415 conf->getSessionCache()->insert(cookie.c_str(), app, as, argp->client_addr, (attributesPushed ? r : NULL));
417 // Maybe delete the response...
418 if (!attributesPushed)
421 // And let the user know.
422 if (result->cookie) free(result->cookie);
423 result->cookie = strdup(cookie.c_str());
424 set_rpc_status(&result->status, SHIBRPC_OK);
426 log.debug("new session id: %s", cookie.c_str());
428 // Transaction Logging
429 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
430 auto_ptr_char oname(origin);
431 auto_ptr_char hname(as->getSubject()->getNameIdentifier()->getName());
432 stc.getTransactionLog().infoStream() <<
433 "New session (ID: " <<
435 ") with (applicationId: " <<
436 argp->application_id <<
437 ") for principal from (IdP: " <<
439 ") at (ClientAddress: " <<
441 ") with (NameIdentifier: " <<
445 stc.releaseTransactionLog();
447 // Delete the origin...
448 if (origin) XMLString::release(&origin);
454 shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp,
455 shibrpc_get_assertions_ret_1 *result, struct svc_req *rqstp)
457 Category& log = get_category();
458 string ctx = get_threadid("get_assertions");
461 if (!argp || !result) {
462 log.error ("Invalid RPC arguments");
466 memset (result, 0, sizeof (*result));
468 log.debug ("get attrs for client at %s", argp->cookie.client_addr);
469 log.debug ("cookie: %s", argp->cookie.cookie);
470 log.debug ("application: %s", argp->application_id);
473 IConfig* conf=ShibTargetConfig::getConfig().getINI();
475 ISessionCacheEntry* entry = conf->getSessionCache()->find(argp->cookie.cookie);
477 // If it does not exist, leave now..
479 log.error ("No Session");
480 set_rpc_status(&result->status, SHIBRPC_NO_SESSION, "getattrs Internal error: no session");
484 // Try and locate support metadata for errors we throw.
485 log.debug ("application: %s", argp->application_id);
486 const IApplication* app=conf->getApplication(argp->application_id);
488 // Something's horribly wrong. Flush the session.
489 log.error ("couldn't find application for session");
490 set_rpc_status(&result->status, SHIBRPC_NO_SESSION, "Unable to locate application for session, deleted?");
494 Metadata m(app->getMetadataProviders());
495 const IProvider* origin=m.lookup(entry->getStatement()->getSubject()->getNameIdentifier()->getNameQualifier());
499 // Validate the client address (again?)
500 if (argp->checkIPAddress && strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
502 log.error("IP Mismatch");
503 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
504 "Your IP address does not match the address in the original authentication.", origin);
507 // grab the attributes for this resource
508 Iterator<SAMLAssertion*> iter = entry->getAssertions();
509 u_int size = iter.size();
510 result->assertions.assertions_len = size;
512 // if we have assertions...
515 // Build the response section
516 ShibRpcXML* av = (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
517 result->assertions.assertions_val = av;
519 // and then serialize them all...
521 while (iter.hasNext()) {
522 SAMLAssertion* as = iter.next();
525 av[i++].xml_string = strdup(os.str().c_str());
529 catch (SAMLException &e) {
531 log.error ("caught SAML exception: %s", e.what());
534 throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str().c_str(), origin);
537 catch (ShibTargetException &e) {
539 set_rpc_status(&result->status, e);
545 log.error ("caught an unknown exception");
546 throw ShibTargetException(SHIBRPC_UNKNOWN_ERROR,
547 "An unexpected error occured while fetching attributes.", origin);
552 // Now grab the serialized authentication statement
553 result->auth_statement.xml_string = strdup(entry->getSerializedStatement());
558 set_rpc_status(&result->status, SHIBRPC_OK);
560 log.debug ("returning");
565 shibrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
567 xdr_free (xdr_result, result);
570 * Insert additional freeing code here, if needed