2 * shibrpc-server.cpp -- SHIBRPC Server implementation. Originally created
3 * as shibrpc-server-stubs.c; make sure that the function
4 * prototypes here match those in shibrpc.x.
6 * Created by: Derek Atkins <derek@ihtfp.com>
11 #include "shib-target.h"
13 #include "ccache-utils.h"
15 #include <log4cpp/Category.hh>
18 #ifdef HAVE_LIBDMALLOCXX
24 using namespace shibboleth;
25 using namespace shibtarget;
27 static std::string get_threadid (const char* proc)
29 static u_long counter = 0;
31 buf << "[" << counter++ << "] " << proc;
35 static log4cpp::Category& get_category (void)
37 string ctx = "shibtarget.rpc-server";
38 return log4cpp::Category::getInstance(ctx);
42 shibrpc_ping_1_svc(int *argp, int *result, struct svc_req *rqstp)
48 void set_rpc_status(ShibRpcError *error, ShibRpcStatus status,
49 const char* msg, const char* origin)
51 error->status = status;
53 error->ShibRpcError_u.e.error = strdup(msg);
54 error->ShibRpcError_u.e.origin = strdup(origin);
58 void set_rpc_status_x(ShibRpcError *error, ShibRpcStatus status,
59 const char* msg, const XMLCh* origin)
62 return set_rpc_status(error, status, NULL, NULL);
63 auto_ptr<char> orig(XMLString::transcode(origin));
64 return set_rpc_status(error, status, msg, orig.get());
68 shibrpc_session_is_valid_1_svc(shibrpc_session_is_valid_args_1 *argp,
69 shibrpc_session_is_valid_ret_1 *result,
70 struct svc_req *rqstp)
72 log4cpp::Category& log = get_category();
73 string ctx = get_threadid("session_is_valid");
76 if (!argp || !result) {
77 log.error ("RPC Argument Error");
81 memset (result, 0, sizeof (*result));
83 log.debug ("checking: %s@%s (checkAddr=%s)",
84 argp->cookie.cookie, argp->cookie.client_addr,
85 argp->checkIPAddress ? "true" : "false");
87 // See if the cookie exists...
88 CCacheEntry *entry = g_shibTargetCCache->find(argp->cookie.cookie);
90 // If not, leave now..
92 log.debug ("Not found");
93 set_rpc_status(&result->status, SHIBRPC_NO_SESSION,
94 "No session exists for this cookie", "");
98 // TEST the session...
102 const XMLCh* origin = entry->getStatement()->getSubject()->getNameQualifier();
104 // Verify the address is the same
105 if (argp->checkIPAddress) {
106 log.debug ("Checking address against %s", entry->getClientAddress());
107 if (strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
108 log.debug ("IP Address mismatch");
110 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
111 "Your IP address does not match the address in the original authentication.",
116 // and that the session is still valid...
117 if (!entry->isSessionValid(argp->lifetime, argp->timeout)) {
118 log.debug ("Session expired");
119 throw ShibTargetException(SHIBRPC_SESSION_EXPIRED,
120 "Your session has expired. Re-authenticate.",
124 // and now try to prefetch the attributes .. this could cause an
125 // "error", which is why we call it here.
127 log.debug ("resource: %s", argp->url);
128 Resource r(argp->url);
129 entry->preFetch(r,15); // give a 15-second window for the RM
131 } catch (SAMLException &e) {
132 log.debug ("prefetch failed with a SAML Exception: %s", e.what());
135 throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str(), origin);
138 log.error ("prefetch caught an unknown exception");
139 throw ShibTargetException(SHIBRPC_UNKNOWN_ERROR,
140 "An unknown error occured while pre-fetching attributes.",
144 } catch (ShibTargetException &e) {
146 g_shibTargetCCache->remove (argp->cookie.cookie);
147 set_rpc_status_x(&result->status, e.which(), e.what(), e.where());
151 // Ok, just release it.
154 // ok, we've succeeded..
155 set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
156 log.debug ("session ok");
161 shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp,
162 shibrpc_new_session_ret_1 *result, struct svc_req *rqstp)
164 log4cpp::Category& log = get_category();
165 string ctx = get_threadid("new_session");
168 if (!argp || !result) {
169 log.error ("Invalid RPC Arguments");
173 // Initialize the result structure
174 memset (result, 0, sizeof(*result));
175 result->cookie = strdup ("");
177 log.debug ("creating session for %s", argp->client_addr);
178 log.debug ("shire location: %s", argp->shire_location);
180 XMLByte* post=reinterpret_cast<XMLByte*>(argp->saml_post);
181 auto_ptr<XMLCh> location(XMLString::transcode(argp->shire_location));
183 // Pull in the Policies
184 Iterator<const XMLCh*> policies=ShibTargetConfig::getConfig().getPolicies();
186 // And grab the Profile
187 // XXX: Create a "Global" POSTProfile instance per location...
188 log.debug ("create the POST profile (%d policies)", policies.size());
189 ShibPOSTProfile *profile =
190 ShibPOSTProfileFactory::getInstance(policies,
194 SAMLResponse* r = NULL;
195 const SAMLAuthenticationStatement* auth_st = NULL;
196 XMLCh* origin = NULL;
202 // Make sure we've got a profile
204 throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
205 "Failed to obtain the profile");
207 // Try and accept the response...
208 log.debug ("Trying to accept the post");
209 r = profile->accept(post, &origin);
211 // Make sure we got a response
213 throw ShibTargetException(SHIBRPC_RESPONSE_MISSING,
214 "Failed to accept the response.",
217 // Find the SSO Assertion
218 log.debug ("Get the SSOAssertion");
219 const SAMLAssertion* ssoAssertion = profile->getSSOAssertion(*r);
221 // Check against the replay cache
222 log.debug ("check replay cache");
223 if (profile->checkReplayCache(*ssoAssertion) == false)
224 throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED,
225 "Duplicate assertion found.",
228 // Get the authentication statement we need.
229 log.debug ("get SSOStatement");
230 auth_st = profile->getSSOStatement(*ssoAssertion);
232 // Maybe verify the origin address....
233 if (argp->checkIPAddress) {
234 log.debug ("check IP Address");
236 // Verify the client address exists
237 const XMLCh* ip = auth_st->getSubjectIP();
239 throw ShibTargetException(SHIBRPC_IPADDR_MISSING,
240 "The IP Address provided by your origin site was missing.",
243 log.debug ("verify client address");
244 // Verify the client address matches authentication
245 auto_ptr<char> this_ip(XMLString::transcode(ip));
246 if (strcmp (argp->client_addr, this_ip.get()))
247 throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
248 "The IP address provided by your origin site did not match "
249 "your current address. "
250 "To correct this problem you may need to bypass a local proxy server.",
254 catch (SAMLException &e) // XXX refine this handler to catch and log different profile exceptions
256 log.error ("received SAML exception: %s", e.what());
259 throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str(), origin);
261 catch (XMLException &e)
263 log.error ("received XML exception");
264 auto_ptr<char> msg(XMLString::transcode(e.getMessage()));
265 throw ShibTargetException (SHIBRPC_XML_EXCEPTION, msg.get(), origin);
268 catch (ShibTargetException &e)
270 log.info ("FAILED: %s", e.what());
272 if (origin) delete origin;
273 set_rpc_status_x(&result->status, e.which(), e.what(), e.where());
279 log.error ("Unknown error");
281 if (origin) delete origin;
282 set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR,
283 "An unknown exception occurred", "");
288 // It passes all our tests -- create a new session.
289 log.info ("Creating new session");
291 SAMLAuthenticationStatement* as=static_cast<SAMLAuthenticationStatement*>(auth_st->clone());
293 // Create a new cookie
295 auto_ptr<char> c(XMLString::transcode(id));
296 char *cookie = c.get();
298 // Cache this session with the cookie
299 g_shibTargetCCache->insert(cookie, as, argp->client_addr);
301 // Delete the response...
304 // Delete the origin...
305 XMLString::release(&origin);
307 // And let the user know.
308 if (result->cookie) free(result->cookie);
309 result->cookie = strdup(cookie);
310 set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
312 log.debug("new session id: %s", cookie);
317 shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp,
318 shibrpc_get_assertions_ret_1 *result, struct svc_req *rqstp)
320 log4cpp::Category& log = get_category();
321 string ctx = get_threadid("get_assertions");
324 if (!argp || !result) {
325 log.error ("Invalid RPC arguments");
329 memset (result, 0, sizeof (*result));
331 log.debug ("get attrs for client at %s", argp->cookie.client_addr);
332 log.debug ("cookie: %s", argp->cookie.cookie);
333 log.debug ("resource: %s", argp->url);
336 CCacheEntry* entry = g_shibTargetCCache->find(argp->cookie.cookie);
338 // If it does not exist, leave now..
340 log.error ("No Session");
341 set_rpc_status(&result->status, SHIBRPC_NO_SESSION,
342 "getattrs Internal error: no session", "");
347 const XMLCh* origin = entry->getStatement()->getSubject()->getNameQualifier();
349 // Validate the client address (again?)
350 if (argp->checkIPAddress &&
351 strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
352 log.error ("IP Mismatch");
353 set_rpc_status_x(&result->status, SHIBRPC_IPADDR_MISMATCH,
354 "Your IP address does not match the address in the original authentication.",
361 // grab the attributes for this resource
362 Resource resource(argp->url);
363 Iterator<SAMLAssertion*> iter = entry->getAssertions(resource);
364 u_int size = iter.size();
365 result->assertions.assertions_len = size;
367 // if we have assertions...
370 // Build the response section
372 (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
373 result->assertions.assertions_val = av;
375 // and then serialize them all...
377 while (iter.hasNext()) {
378 SAMLAssertion* as = iter.next();
381 av[i++].xml_string = strdup(os.str().c_str());
384 } catch (SAMLException& e) {
385 log.error ("received SAML exception: %s", e.what());
388 set_rpc_status_x(&result->status, SHIBRPC_SAML_EXCEPTION,
389 strdup(os.str().c_str()), origin);
394 // Now grab the serialized authentication statement
395 result->auth_statement.xml_string = strdup(entry->getSerializedStatement());
400 set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
402 log.debug ("returning");
407 shibrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
409 xdr_free (xdr_result, result);
412 * Insert additional freeing code here, if needed