shib-ccache.cpp:
[shibboleth/sp.git] / shib-target / shibrpc-server.cpp
1 /*
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.
5  *
6  * Created by:  Derek Atkins <derek@ihtfp.com>
7  *
8  * $Id$
9  */
10
11 #include "shibrpc.h"
12 #include "shib-target.h"
13
14 #include <log4cpp/Category.hh>
15 #include <sstream>
16
17 #ifdef HAVE_LIBDMALLOCXX
18 #include <dmalloc.h>
19 #endif
20
21 using namespace std;
22 using namespace saml;
23 using namespace shibboleth;
24 using namespace shibtarget;
25
26 static std::string get_threadid (const char* proc)
27 {
28   static u_long counter = 0;
29   ostringstream buf;
30   buf << "[" << counter++ << "] " << proc;
31   return buf.str();
32 }
33
34 static log4cpp::Category& get_category (void)
35 {
36   string ctx = "shibtarget.rpc-server";
37   return log4cpp::Category::getInstance(ctx);
38 }
39
40 extern "C" bool_t
41 shibrpc_ping_1_svc(int *argp, int *result, struct svc_req *rqstp)
42 {
43   *result = (*argp)+1;
44   return TRUE;
45 }
46
47 extern "C" bool_t
48 shibrpc_session_is_valid_1_svc(shibrpc_session_is_valid_args_1 *argp,
49                                shibrpc_session_is_valid_ret_1 *result,
50                                struct svc_req *rqstp)
51 {
52   log4cpp::Category& log = get_category();
53   string ctx = get_threadid("session_is_valid");
54   saml::NDC ndc(ctx);
55
56   if (!argp || !result) {
57     log.error ("RPC Argument Error");
58     return FALSE;
59   }
60
61   memset (result, 0, sizeof (*result));
62   
63   log.debug ("checking: %s@%s (checkAddr=%s)",
64              argp->cookie.cookie, argp->cookie.client_addr,
65              argp->checkIPAddress ? "true" : "false");
66
67   // See if the cookie exists...
68   CCacheEntry *entry = g_shibTargetCCache->find(argp->cookie.cookie);
69
70   // If not, leave now..
71   if (!entry) {
72     log.debug ("Not found");
73     result->status = SHIBRPC_NO_SESSION;
74     result->error_msg = strdup("No session exists for this cookie");
75     return TRUE;
76   }
77
78   // Verify the address is the same
79   if (argp->checkIPAddress) {
80     log.debug ("Checking address against %s", entry->getClientAddress());
81     if (strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
82       log.debug ("IP Address mismatch");
83       result->status = SHIBRPC_IPADDR_MISMATCH;
84       result->error_msg = 
85         strdup ("Your IP address does not match the address in the original authentication.");
86       entry->release();
87       g_shibTargetCCache->remove (argp->cookie.cookie);
88       return TRUE;
89     }
90   }
91
92   // and that the session is still valid...
93   if (!entry->isSessionValid(argp->lifetime, argp->timeout)) {
94     log.debug ("Session expired");
95     result->status = SHIBRPC_SESSION_EXPIRED;
96     result->error_msg = strdup ("Your session has expired.  Re-authenticate.");
97     entry->release();
98     g_shibTargetCCache->remove (argp->cookie.cookie);
99     return TRUE;
100   }
101
102   entry->release();
103
104   // ok, we've succeeded..
105   result->status = SHIBRPC_OK;
106   result->error_msg = strdup("");
107   log.debug ("session ok");
108   return TRUE;
109 }
110
111 extern "C" bool_t
112 shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp,
113                           shibrpc_new_session_ret_1 *result, struct svc_req *rqstp)
114 {
115   log4cpp::Category& log = get_category();
116   string ctx = get_threadid("new_session");
117   saml::NDC ndc(ctx);
118
119   if (!argp || !result) {
120     log.error ("Invalid RPC Arguments");
121     return FALSE;
122   }
123
124   // Initialize the result structure
125   memset (result, 0, sizeof(*result));
126   result->cookie = strdup ("");
127
128   log.debug ("creating session for %s", argp->client_addr);
129   log.debug ("shire location: %s", argp->shire_location);
130
131   XMLByte* post=reinterpret_cast<XMLByte*>(argp->saml_post);
132   auto_ptr<XMLCh> location(XMLString::transcode(argp->shire_location));
133
134   // Pull in the Policies
135   static const XMLCh* clubShib[] = {shibboleth::Constants::POLICY_CLUBSHIB};
136   ArrayIterator<const XMLCh*> policies(clubShib);
137
138   // And grab the Profile
139   // XXX: Create a "Global" POSTProfile instance per location...
140   log.debug ("create the POST profile (%d policies)", policies.size());
141   ShibPOSTProfile *profile =
142     ShibPOSTProfileFactory::getInstance(policies,
143                                         location.get(),
144                                         3600);
145
146   SAMLResponse* r = NULL;
147   SAMLAuthenticationStatement* auth_st = NULL;
148
149   try
150   {
151     try
152     {
153       // Make sure we've got a profile
154       if (!profile)
155         throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
156                                   "Failed to obtain the profile");
157
158       // Try and accept the response...
159       log.debug ("Trying to accept the post");
160       r = profile->accept(post);
161
162       // Make sure we got a response
163       if (!r)
164         throw ShibTargetException(SHIBRPC_RESPONSE_MISSING,
165                                   "Failed to accept the response.");
166
167       // Find the SSO Assertion
168       log.debug ("Get the SSOAssertion");
169       SAMLAssertion* ssoAssertion = profile->getSSOAssertion(*r);
170
171       // Check against the replay cache
172       log.debug ("check replay cache");
173       if (profile->checkReplayCache(*ssoAssertion) == false)
174         throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED,
175                                   "Duplicate assertion found.");
176
177       // Get the authentication statement we need.
178       log.debug ("get SSOStatement");
179       auth_st = profile->getSSOStatement(*ssoAssertion);
180
181       // Maybe verify the origin address....
182       if (argp->checkIPAddress) {
183         log.debug ("check IP Address");
184
185         // Verify the client address exists
186         const XMLCh* ip = auth_st->getSubjectIP();
187         if (!ip)
188           throw ShibTargetException(SHIBRPC_IPADDR_MISSING,
189                                     "The IP Address provided by your origin site was missing.");
190         
191         log.debug ("verify client address");
192         // Verify the client address matches authentication
193         auto_ptr<char> this_ip(XMLString::transcode(ip));
194         if (strcmp (argp->client_addr, this_ip.get()))
195           throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
196                                     "The IP address provided by your origin site did not match your current address.  To correct this problem you may need to bypass a local proxy server.");
197       }
198     }
199     catch (SAMLException &e)    // XXX refine this handler to catch and log different profile exceptions
200     {
201       log.error ("received SAML exception: %s", e.what());
202       ostringstream os;
203       os << e;
204       throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str());
205     }
206     catch (XMLException &e)
207     {
208       log.error ("received XML exception");
209       auto_ptr<char> msg(XMLString::transcode(e.getMessage()));
210       throw ShibTargetException (SHIBRPC_XML_EXCEPTION, msg.get());
211     }
212   }
213   catch (ShibTargetException &e)
214   {
215     log.info ("FAILED: %s", e.what());
216     if (r) delete r;
217     result->status = e.which();
218     result->error_msg = strdup(e.what());
219     return TRUE;
220   }
221 #if 0
222   catch (...)
223   {
224     log.error ("Unknown error");
225     if (r) delete r;
226     result->status = SHIBRPC_UNKNOWN_ERROR;
227     result->error_msg = strdup("An unknown exception occurred");
228     return TRUE;
229   }
230 #endif
231
232   // It passes all our tests -- create a new session.
233   log.info ("Creating new session");
234
235   SAMLAuthenticationStatement* as=static_cast<SAMLAuthenticationStatement*>(auth_st->clone());
236
237   // Create a new cookie
238   SAMLIdentifier id;
239   auto_ptr<char> c(XMLString::transcode(id));
240   char *cookie = c.get();
241
242   // Cache this session with the cookie
243   g_shibTargetCCache->insert(cookie, as, argp->client_addr);
244
245   // Delete the response...
246   delete r;
247
248   // And let the user know.
249   free (result->cookie);
250   result->cookie = strdup(cookie);
251   result->status = SHIBRPC_OK;
252   result->error_msg = strdup("");
253
254   log.debug ("new session id: %s", cookie);
255   return TRUE;
256 }
257
258 extern "C" bool_t
259 shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp,
260                         shibrpc_get_assertions_ret_1 *result, struct svc_req *rqstp)
261 {
262   log4cpp::Category& log = get_category();
263   string ctx = get_threadid("get_assertions");
264   saml::NDC ndc(ctx);
265
266   if (!argp || !result) {
267     log.error ("Invalid RPC arguments");
268     return FALSE;
269   }
270
271   memset (result, 0, sizeof (*result));
272
273   log.debug ("get attrs for client at %s", argp->cookie.client_addr);
274   log.debug ("cookie: %s", argp->cookie.cookie);
275   log.debug ("resource: %s", argp->url);
276
277   // Find this session
278   CCacheEntry* entry = g_shibTargetCCache->find(argp->cookie.cookie);
279
280   // If it does not exist, leave now..
281   if (!entry) {
282     log.error ("No Session");
283     result->status = SHIBRPC_NO_SESSION;
284     result->error_msg = strdup("getattrs Internal error: no session");
285     return TRUE;
286   }
287
288   // Validate the client address (again?)
289   if (argp->checkIPAddress &&
290       strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
291     log.error ("IP Mismatch");
292     result->status = SHIBRPC_IPADDR_MISMATCH;
293     result->error_msg =
294       strdup("Your IP address does not match the address in the original authentication.");
295     entry->release();
296     return TRUE;
297   }
298
299   try {
300     // grab the attributes for this resource
301     Resource resource(argp->url);
302     Iterator<SAMLAssertion*> iter = entry->getAssertions(resource);
303     u_int size = iter.size();
304     result->assertions.assertions_len = size;
305
306     // if we have assertions...
307     if (size) {
308
309       // Build the response section
310       ShibRpcXML* av =
311         (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
312       result->assertions.assertions_val = av;
313
314       // and then serialize them all...
315       u_int i = 0;
316       while (iter.hasNext()) {
317         SAMLAssertion* as = iter.next();
318         ostringstream os;
319         os << *as;
320         av[i++].xml_string = strdup(os.str().c_str());
321       }
322     }
323   } catch (SAMLException& e) {
324     log.error ("received SAML exception: %s", e.what());
325     ostringstream os;
326     os << e;
327     result->status = SHIBRPC_SAML_EXCEPTION;
328     result->error_msg = strdup(os.str().c_str());
329     entry->release();
330     return TRUE;
331   }
332
333   entry->release();
334
335   // and let it fly
336   result->status = SHIBRPC_OK;
337   result->error_msg = strdup("");
338
339   log.debug ("returning");
340   return TRUE;
341 }
342
343 extern "C" int
344 shibrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
345 {
346         xdr_free (xdr_result, result);
347
348         /*
349          * Insert additional freeing code here, if needed
350          */
351
352         return 1;
353 }