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_2_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 shibrpc_get_session_2_svc(
126 shibrpc_get_session_args_2 *argp,
127 shibrpc_get_session_ret_2 *result,
128 struct svc_req *rqstp
131 Category& log = get_category();
132 string ctx = get_threadid("session_is_valid");
135 if (!argp || !result) {
136 log.error ("RPC Argument Error");
140 memset (result, 0, sizeof (*result));
141 result->auth_statement.xml_string = strdup("");
143 log.debug ("checking: %s@%s (checkAddr=%s)",
144 argp->cookie, argp->client_addr, argp->checkIPAddress ? "true" : "false");
146 // See if the session exists...
148 IConfig* conf=ShibTargetConfig::getConfig().getINI();
150 log.debug ("application: %s", argp->application_id);
151 const IApplication* app=conf->getApplication(argp->application_id);
153 // Something's horribly wrong.
154 log.error("couldn't find application for session");
155 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR, "Unable to locate application for session, deleted?");
159 ISessionCacheEntry* entry = conf->getSessionCache()->find(argp->cookie,app);
161 // If not, leave now..
163 log.debug ("Not found");
164 set_rpc_status(&result->status, SHIBRPC_NO_SESSION, "No session exists for this key value");
168 // TEST the session...
170 Metadata m(app->getMetadataProviders());
171 const IEntityDescriptor* origin=m.lookup(entry->getAuthnStatement()->getSubject()->getNameIdentifier()->getNameQualifier());
173 // Verify the address is the same
174 if (argp->checkIPAddress) {
175 log.debug ("Checking address against %s", entry->getClientAddress());
176 if (strcmp (argp->client_addr, entry->getClientAddress())) {
177 log.debug ("IP Address mismatch");
178 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
179 "Your IP address does not match the address recorded at the time the session was established.", origin);
183 // and that the session is still valid...
184 if (!entry->isValid(argp->lifetime, argp->timeout)) {
185 log.debug ("Session expired");
186 throw ShibTargetException(SHIBRPC_SESSION_EXPIRED, "Your session has expired, and you must re-authenticate.", origin);
190 // Now grab the serialized authentication statement
192 os << *(entry->getAuthnStatement());
193 free(result->auth_statement.xml_string);
194 result->auth_statement.xml_string = strdup(os.str().c_str());
196 // grab the attributes for this session
197 Iterator<SAMLAssertion*> iter = entry->getAssertions();
198 u_int size = iter.size();
200 // if we have assertions...
202 // Build the response section
203 ShibRpcXML* av = (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
205 // and then serialize them all...
207 while (iter.hasNext()) {
208 SAMLAssertion* as = iter.next();
211 av[i++].xml_string = strdup(os2.str().c_str());
214 // Set the results, once we know we've succeeded.
215 result->assertions.assertions_len = size;
216 result->assertions.assertions_val = av;
219 catch (SAMLException &e) {
220 log.error ("caught SAML exception: %s", e.what());
223 throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str().c_str(), origin);
226 catch (ShibTargetException &e) {
228 log.error ("FAILED: %s", e.what());
229 conf->getSessionCache()->remove(argp->cookie);
230 set_rpc_status(&result->status, e);
231 // Transaction Logging
232 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
233 stc.getTransactionLog().infoStream() <<
234 "Destroyed invalid session (ID: " <<
236 ") with (applicationId: " <<
237 argp->application_id <<
238 "), request was from (ClientAddress: " <<
241 stc.releaseTransactionLog();
247 log.error ("Unknown exception");
248 conf->getSessionCache()->remove(argp->cookie);
249 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR, "An unknown exception occurred");
250 // Transaction Logging
251 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
252 stc.getTransactionLog().infoStream() <<
253 "Destroyed invalid session (ID: " <<
255 ") with (applicationId: " <<
256 argp->application_id <<
257 "), request was from (ClientAddress: " <<
260 stc.releaseTransactionLog();
265 // Ok, just release it.
268 // ok, we've succeeded..
269 set_rpc_status(&result->status, SHIBRPC_OK);
270 log.debug ("session ok");
275 shibrpc_new_session_2_svc(
276 shibrpc_new_session_args_2 *argp,
277 shibrpc_new_session_ret_2 *result,
278 struct svc_req *rqstp
281 Category& log = get_category();
282 string ctx=get_threadid("new_session");
285 if (!argp || !result) {
286 log.error ("Invalid RPC Arguments");
290 // Initialize the result structure
291 memset (result, 0, sizeof(*result));
292 result->cookie = strdup ("");
293 result->target = strdup ("");
295 log.debug ("creating session for %s", argp->client_addr);
296 log.debug ("recipient: %s", argp->recipient);
297 log.debug ("application: %s", argp->application_id);
299 auto_ptr_XMLCh recipient(argp->recipient);
301 SAMLResponse* r = NULL;
302 const SAMLAuthenticationStatement* auth_st = NULL;
303 XMLCh* origin = NULL;
305 // Access the application config.
306 IConfig* conf=ShibTargetConfig::getConfig().getINI();
308 const IApplication* app=conf->getApplication(argp->application_id);
310 // Something's horribly wrong. Flush the session.
311 log.error ("couldn't find application for session");
312 set_rpc_status(&result->status, SHIBRPC_INTERNAL_ERROR, "Unable to locate application for session, deleted?");
316 // TODO: Sub in call to getReplayCache() as the determinant.
317 // For now, we always have a cache and use the flag...
318 pair<bool,bool> checkReplay=pair<bool,bool>(false,false);
319 const IPropertySet* props=app->getPropertySet("Sessions");
321 checkReplay=props->getBool("checkReplay");
323 const IRoleDescriptor* role=NULL;
324 Metadata m(app->getMetadataProviders());
325 SAMLBrowserProfile::BrowserProfileResponse bpr;
329 // Something's horribly wrong.
330 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,"Unable to locate application configuration, deleted?");
334 auto_ptr<SAMLBrowserProfile::ArtifactMapper> artifactMapper(app->getArtifactMapper());
336 // Try and run the profile.
337 log.debug ("Executing browser profile...");
338 bpr=app->getBrowserProfile()->receive(
342 SAMLBrowserProfile::Post, // For now, we only handle POST.
343 (!checkReplay.first || checkReplay.second) ? conf->getReplayCache() : NULL,
347 // Try and map to metadata for support purposes.
348 const IEntityDescriptor* provider=m.lookup(origin);
350 const IIDPSSODescriptor* IDP=provider->getIDPSSODescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
353 // This can't really happen, since the profile must have found a role.
355 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
356 "Unable to locate role-specific metadata for identity provider", provider);
358 // Maybe verify the origin address....
359 if (argp->checkIPAddress) {
360 log.debug ("verify client address");
362 // Verify the client address exists
363 const XMLCh* ip = bpr.authnStatement->getSubjectIP();
365 // Verify the client address matches authentication
366 auto_ptr_char this_ip(ip);
367 if (strcmp(argp->client_addr, this_ip.get()))
368 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
369 "Your client's current IP address differs from the one used when you authenticated "
370 "to your identity provider. To correct this problem, you may need to bypass a proxy server. "
371 "Please contact your local support staff or help desk for assistance.",
376 // Verify condition(s) on authentication assertion.
377 // Attribute assertions get filtered later, essentially just like an AAP.
378 Iterator<SAMLCondition*> conditions=bpr.assertion->getConditions();
379 while (conditions.hasNext()) {
380 SAMLCondition* cond=conditions.next();
381 const SAMLAudienceRestrictionCondition* ac=dynamic_cast<const SAMLAudienceRestrictionCondition*>(cond);
385 log.error("Unrecognized Condition in authentication assertion (%s), tossing it.",os.str().c_str());
386 throw FatalProfileException("Unable to start session due to unrecognized condition in authentication assertion.");
388 else if (!ac->eval(app->getAudiences())) {
391 log.error("Unacceptable AudienceRestrictionCondition in authentication assertion (%s), tossing it.",os.str().c_str());
392 throw FatalProfileException("Unable to start session due to unacceptable AudienceRestrictionCondition in authentication assertion.");
396 catch (ReplayedAssertionException& e) {
397 // Specific case where we have an error code.
399 // Try and map to metadata for support purposes.
400 const IEntityDescriptor* provider=m.lookup(origin);
402 const IIDPSSODescriptor* IDP=provider->getIDPSSODescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
406 throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED, e.what(), role);
408 catch (SAMLException& e) {
409 log.error ("caught SAML exception: %s", e.what());
413 // Try and map to metadata for support purposes.
414 const IEntityDescriptor* provider=m.lookup(origin);
416 const IIDPSSODescriptor* IDP=provider->getIDPSSODescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
420 throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str().c_str(), role);
423 catch (ShibTargetException& e) {
424 log.error ("FAILED: %s", e.what());
426 if (origin) XMLString::release(&origin);
427 set_rpc_status(&result->status, e);
432 log.error ("Unknown error");
434 if (origin) XMLString::release(&origin);
435 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR, "An unknown exception occurred");
440 // It passes all our tests -- create a new session.
441 log.info ("Creating new session");
443 // Create a new session key.
444 string cookie = conf->getSessionCache()->generateKey();
446 // Are attributes present?
447 bool attributesPushed=false;
448 Iterator<SAMLAssertion*> assertions=bpr.response->getAssertions();
449 while (!attributesPushed && assertions.hasNext()) {
450 Iterator<SAMLStatement*> statements=assertions.next()->getStatements();
451 while (!attributesPushed && statements.hasNext()) {
452 if (dynamic_cast<SAMLAttributeStatement*>(statements.next()))
453 attributesPushed=true;
457 // Insertion into cache might fail.
458 SAMLAuthenticationStatement* as=NULL;
460 as=static_cast<SAMLAuthenticationStatement*>(bpr.authnStatement->clone());
461 // TODO: we need to extract the Issuer and propagate that around as the origin site along
462 // with the statement and attribute assertions.
463 conf->getSessionCache()->insert(
468 (attributesPushed ? bpr.response : NULL),
472 catch (SAMLException& e) {
473 log.error ("caught SAML exception during cache insertion: %s", e.what());
478 if (origin) XMLString::release(&origin);
479 ShibTargetException ex(SHIBRPC_SAML_EXCEPTION, os.str().c_str(), role);
480 set_rpc_status(&result->status, ex);
485 log.error ("caught unknown exception during cache insertion");
488 if (origin) XMLString::release(&origin);
489 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR, "An unknown exception occurred");
494 // And let the user know.
495 if (result->cookie) free(result->cookie);
496 if (result->target) free(result->target);
497 result->cookie = strdup(cookie.c_str());
498 result->target = strdup(bpr.TARGET.c_str());
499 set_rpc_status(&result->status, SHIBRPC_OK);
501 // Maybe delete the response...
502 if (!attributesPushed)
505 log.debug("new session id: %s", cookie.c_str());
507 // Transaction Logging
508 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
509 auto_ptr_char oname(origin);
510 auto_ptr_char hname(as->getSubject()->getNameIdentifier()->getName());
511 stc.getTransactionLog().infoStream() <<
512 "New session (ID: " <<
514 ") with (applicationId: " <<
515 argp->application_id <<
516 ") for principal from (IdP: " <<
518 ") at (ClientAddress: " <<
520 ") with (NameIdentifier: " <<
524 stc.releaseTransactionLog();
526 // Delete the origin...
527 if (origin) XMLString::release(&origin);
533 shibrpc_prog_2_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
535 xdr_free (xdr_result, result);
538 * Insert additional freeing code here, if needed