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 log.debug ("application: %s", argp->application_id);
161 const IApplication* app=conf->getApplication(argp->application_id);
163 // Something's horribly wrong.
164 log.error("couldn't find application for session");
165 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR, "Unable to locate application for session, deleted?");
169 ISessionCacheEntry* entry = conf->getSessionCache()->find(argp->cookie.cookie,app);
171 // If not, leave now..
173 log.debug ("Not found");
174 set_rpc_status(&result->status, SHIBRPC_NO_SESSION, "No session exists for this cookie");
178 // TEST the session...
180 Metadata m(app->getMetadataProviders());
181 const IEntityDescriptor* origin=m.lookup(entry->getStatement()->getSubject()->getNameIdentifier()->getNameQualifier());
183 // Verify the address is the same
184 if (argp->checkIPAddress) {
185 log.debug ("Checking address against %s", entry->getClientAddress());
186 if (strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
187 log.debug ("IP Address mismatch");
188 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
189 "Your IP address does not match the address in the original authentication.", origin);
193 // and that the session is still valid...
194 if (!entry->isValid(argp->lifetime, argp->timeout)) {
195 log.debug ("Session expired");
196 throw ShibTargetException(SHIBRPC_SESSION_EXPIRED, "Your session has expired, must re-authenticate.", origin);
199 // and now try to prefetch the attributes .. this could cause an
200 // "error", which is why we call it here.
202 entry->preFetch(15); // give a 15-second window for the RM
204 catch (SAMLException &e) {
205 log.debug ("prefetch failed with a SAML Exception: %s", e.what());
208 throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str().c_str(), origin);
210 catch (ShibTargetException&) {
211 // These are caught and handled down below.
216 log.error ("prefetch caught an unknown exception");
217 throw ShibTargetException(SHIBRPC_UNKNOWN_ERROR,
218 "An unknown error occured while pre-fetching attributes.", origin);
222 catch (ShibTargetException &e) {
224 conf->getSessionCache()->remove(argp->cookie.cookie);
225 set_rpc_status(&result->status, e);
226 // Transaction Logging
227 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
228 stc.getTransactionLog().infoStream() <<
229 "Destroyed invalid session (ID: " <<
230 argp->cookie.cookie <<
231 ") with (applicationId: " <<
232 argp->application_id <<
233 "), request was from (ClientAddress: " <<
234 argp->cookie.client_addr <<
236 stc.releaseTransactionLog();
240 // Ok, just release it.
243 // ok, we've succeeded..
244 set_rpc_status(&result->status, SHIBRPC_OK);
245 log.debug ("session ok");
250 shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp,
251 shibrpc_new_session_ret_1 *result, struct svc_req *rqstp)
253 Category& log = get_category();
254 string ctx=get_threadid("new_session");
257 if (!argp || !result) {
258 log.error ("Invalid RPC Arguments");
262 // Initialize the result structure
263 memset (result, 0, sizeof(*result));
264 result->cookie = strdup ("");
266 log.debug ("creating session for %s", argp->client_addr);
267 log.debug ("shire location: %s", argp->shire_location);
268 log.debug ("application: %s", argp->application_id);
270 XMLByte* post=reinterpret_cast<XMLByte*>(argp->saml_post);
271 auto_ptr_XMLCh location(argp->shire_location);
273 SAMLResponse* r = NULL;
274 const SAMLAuthenticationStatement* auth_st = NULL;
275 XMLCh* origin = NULL;
277 // Access the application config.
278 IConfig* conf=ShibTargetConfig::getConfig().getINI();
280 const IApplication* app=conf->getApplication(argp->application_id);
282 // Something's horribly wrong. Flush the session.
283 log.error ("couldn't find application for session");
284 set_rpc_status(&result->status, SHIBRPC_INTERNAL_ERROR, "Unable to locate application for session, deleted?");
288 pair<bool,bool> checkReplay=pair<bool,bool>(false,false);
289 const IPropertySet* props=app->getPropertySet("Sessions");
291 checkReplay=props->getBool("checkReplay");
293 const IRoleDescriptor* role=NULL;
294 Metadata m(app->getMetadataProviders());
298 // Something's horribly wrong.
299 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,"Unable to locate application configuration, deleted?");
301 // And build the POST profile wrapper.
302 log.debug("create the POST profile");
303 ShibPOSTProfile profile(app->getMetadataProviders(),app->getRevocationProviders(),app->getTrustProviders());
307 // Try and accept the response...
308 log.debug ("Trying to accept the post");
309 r = profile.accept(post,location.get(),300,app->getAudiences(),&origin);
311 // Try and map to metadata for support purposes.
312 const IEntityDescriptor* provider=m.lookup(origin);
314 const IIDPSSODescriptor* IDP=provider->getIDPSSODescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
317 // This can't really happen, since the profile must have found a role.
319 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
320 "Unable to locate role-specific metadata for identity provider", provider);
322 // Make sure we got a response
324 throw ShibTargetException(SHIBRPC_RESPONSE_MISSING, "Failed to accept the response.", role);
326 // Find the SSO Assertion
327 log.debug ("Get the SSOAssertion");
328 const SAMLAssertion* ssoAssertion = profile.getSSOAssertion(*r,app->getAudiences());
330 // Check against the replay cache?
331 if (checkReplay.first && !checkReplay.second)
332 log.warn("replay checking is off, this is a security risk unless you're testing");
334 log.debug ("check replay cache");
335 if (!profile.checkReplayCache(*ssoAssertion))
336 throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED, "Duplicate assertion detected.", role);
339 // Get the authentication statement we need.
340 log.debug ("get SSOStatement");
341 auth_st = profile.getSSOStatement(*ssoAssertion);
343 // Maybe verify the origin address....
344 if (argp->checkIPAddress) {
345 log.debug ("check IP Address");
347 // Verify the client address exists
348 const XMLCh* ip = auth_st->getSubjectIP();
350 log.debug ("verify client address");
352 // Verify the client address matches authentication
353 auto_ptr_char this_ip(ip);
354 if (strcmp(argp->client_addr, this_ip.get()))
355 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
356 "Your client's current IP address differs from the one used when you authenticated "
357 "to your identity provider. To correct this problem, you may need to bypass a proxy server. "
358 "Please contact your local support staff or help desk for assistance.",
363 catch (SAMLException &e)
365 log.error ("caught SAML exception: %s", e.what());
368 throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str().c_str(), role);
370 catch (XMLException &e)
372 log.error ("received XML exception");
373 auto_ptr_char msg(e.getMessage());
374 throw ShibTargetException (SHIBRPC_XML_EXCEPTION, msg.get(), role);
377 catch (ShibTargetException &e) {
378 log.info ("FAILED: %s", e.what());
380 if (origin) XMLString::release(&origin);
381 set_rpc_status(&result->status, e);
386 log.error ("Unknown error");
388 if (origin) XMLString::release(&origin);
389 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR, "An unknown exception occurred");
394 // It passes all our tests -- create a new session.
395 log.info ("Creating new session");
397 SAMLAuthenticationStatement* as=static_cast<SAMLAuthenticationStatement*>(auth_st->clone());
399 // Create a new cookie
400 string cookie = conf->getSessionCache()->generateKey();
402 // Cache this session, possibly including response if attributes appear present.
403 bool attributesPushed=false;
404 Iterator<SAMLAssertion*> assertions=r->getAssertions();
405 while (!attributesPushed && assertions.hasNext()) {
406 Iterator<SAMLStatement*> statements=assertions.next()->getStatements();
407 while (!attributesPushed && statements.hasNext()) {
408 if (dynamic_cast<SAMLAttributeStatement*>(statements.next()))
409 attributesPushed=true;
412 conf->getSessionCache()->insert(cookie.c_str(), app, as, argp->client_addr, (attributesPushed ? r : NULL), role);
414 // Maybe delete the response...
415 if (!attributesPushed)
418 // And let the user know.
419 if (result->cookie) free(result->cookie);
420 result->cookie = strdup(cookie.c_str());
421 set_rpc_status(&result->status, SHIBRPC_OK);
423 log.debug("new session id: %s", cookie.c_str());
425 // Transaction Logging
426 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
427 auto_ptr_char oname(origin);
428 auto_ptr_char hname(as->getSubject()->getNameIdentifier()->getName());
429 stc.getTransactionLog().infoStream() <<
430 "New session (ID: " <<
432 ") with (applicationId: " <<
433 argp->application_id <<
434 ") for principal from (IdP: " <<
436 ") at (ClientAddress: " <<
438 ") with (NameIdentifier: " <<
442 stc.releaseTransactionLog();
444 // Delete the origin...
445 if (origin) XMLString::release(&origin);
451 shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp,
452 shibrpc_get_assertions_ret_1 *result, struct svc_req *rqstp)
454 Category& log = get_category();
455 string ctx = get_threadid("get_assertions");
458 if (!argp || !result) {
459 log.error ("Invalid RPC arguments");
463 memset (result, 0, sizeof (*result));
464 result->auth_statement.xml_string = strdup("");
466 log.debug ("get attrs for client at %s", argp->cookie.client_addr);
467 log.debug ("cookie: %s", argp->cookie.cookie);
468 log.debug ("application: %s", argp->application_id);
471 IConfig* conf=ShibTargetConfig::getConfig().getINI();
474 // Try and locate support metadata for errors we throw.
475 log.debug ("application: %s", argp->application_id);
476 const IApplication* app=conf->getApplication(argp->application_id);
478 // Something's horribly wrong.
479 log.error("couldn't find application for session");
480 set_rpc_status(&result->status, SHIBRPC_INTERNAL_ERROR, "Unable to locate application for session, deleted?");
484 ISessionCacheEntry* entry = conf->getSessionCache()->find(argp->cookie.cookie,app);
486 // If it does not exist, leave now..
488 log.error ("No Session");
489 set_rpc_status(&result->status, SHIBRPC_NO_SESSION, "getattrs Internal error: no session");
493 Metadata m(app->getMetadataProviders());
494 const IEntityDescriptor* origin=m.lookup(entry->getStatement()->getSubject()->getNameIdentifier()->getNameQualifier());
498 // Validate the client address (again?)
499 if (argp->checkIPAddress && strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
501 log.error("IP Mismatch");
502 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
503 "Your IP address does not match the address in the original authentication.", origin);
506 // grab the attributes for this resource
507 Iterator<SAMLAssertion*> iter = entry->getAssertions();
508 u_int size = iter.size();
510 // if we have assertions...
513 // Build the response section
514 ShibRpcXML* av = (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
516 // and then serialize them all...
518 while (iter.hasNext()) {
519 SAMLAssertion* as = iter.next();
522 av[i++].xml_string = strdup(os.str().c_str());
525 // Set the results, once we know we've succeeded.
526 result->assertions.assertions_len = size;
527 result->assertions.assertions_val = av;
530 catch (SAMLException &e) {
532 log.error ("caught SAML exception: %s", e.what());
535 throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str().c_str(), origin);
538 catch (ShibTargetException &e) {
540 set_rpc_status(&result->status, e);
546 log.error ("caught an unknown exception");
547 throw ShibTargetException(SHIBRPC_UNKNOWN_ERROR,
548 "An unexpected error occured while fetching attributes.", origin);
553 // Now grab the serialized authentication statement
554 free(result->auth_statement.xml_string);
555 result->auth_statement.xml_string = strdup(entry->getSerializedStatement());
560 set_rpc_status(&result->status, SHIBRPC_OK);
562 log.debug ("returning");
567 shibrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
569 xdr_free (xdr_result, result);
572 * Insert additional freeing code here, if needed