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       g_shibTargetCCache->remove (argp->cookie.cookie);
87       return TRUE;
88     }
89   }
90
91   // and that the session is still valid...
92   if (!entry->isSessionValid(argp->lifetime, argp->timeout)) {
93     log.debug ("Session expired");
94     result->status = SHIBRPC_SESSION_EXPIRED;
95     result->error_msg = strdup ("Your session has expired.  Re-authenticate.");
96     g_shibTargetCCache->remove (argp->cookie.cookie);
97     return TRUE;
98   }
99
100   // ok, we've succeeded..
101   result->status = SHIBRPC_OK;
102   result->error_msg = strdup("");
103   log.debug ("session ok");
104   return TRUE;
105 }
106
107 extern "C" bool_t
108 shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp,
109                           shibrpc_new_session_ret_1 *result, struct svc_req *rqstp)
110 {
111   log4cpp::Category& log = get_category();
112   string ctx = get_threadid("new_session");
113   saml::NDC ndc(ctx);
114
115   if (!argp || !result) {
116     log.error ("Invalid RPC Arguments");
117     return FALSE;
118   }
119
120   // Initialize the result structure
121   memset (result, 0, sizeof(*result));
122   result->cookie = strdup ("");
123
124   log.debug ("creating session for %s", argp->client_addr);
125   log.debug ("shire location: %s", argp->shire_location);
126
127   XMLByte* post=reinterpret_cast<XMLByte*>(argp->saml_post);
128   auto_ptr<XMLCh> location(XMLString::transcode(argp->shire_location));
129
130   // Pull in the Policies
131   static const XMLCh* clubShib[] = {shibboleth::Constants::POLICY_CLUBSHIB};
132   ArrayIterator<const XMLCh*> policies(clubShib);
133
134   // And grab the Profile
135   // XXX: Create a "Global" POSTProfile instance per location...
136   log.debug ("create the POST profile (%d policies)", policies.size());
137   ShibPOSTProfile *profile =
138     ShibPOSTProfileFactory::getInstance(policies,
139                                         location.get(),
140                                         3600);
141
142   SAMLResponse* r = NULL;
143   SAMLAuthenticationStatement* auth_st = NULL;
144
145   try
146   {
147     try
148     {
149       // Make sure we've got a profile
150       if (!profile)
151         throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
152                                   "Failed to obtain the profile");
153
154       // Try and accept the response...
155       log.debug ("Trying to accept the post");
156       r = profile->accept(post);
157
158       // Make sure we got a response
159       if (!r)
160         throw ShibTargetException(SHIBRPC_RESPONSE_MISSING,
161                                   "Failed to accept the response.");
162
163       // Find the SSO Assertion
164       log.debug ("Get the SSOAssertion");
165       SAMLAssertion* ssoAssertion = profile->getSSOAssertion(*r);
166
167       // Check against the replay cache
168       log.debug ("check replay cache");
169       if (profile->checkReplayCache(*ssoAssertion) == false)
170         throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED,
171                                   "Duplicate assertion found.");
172
173       // Get the authentication statement we need.
174       log.debug ("get SSOStatement");
175       auth_st = profile->getSSOStatement(*ssoAssertion);
176
177       // Maybe verify the origin address....
178       if (argp->checkIPAddress) {
179         log.debug ("check IP Address");
180
181         // Verify the client address exists
182         const XMLCh* ip = auth_st->getSubjectIP();
183         if (!ip)
184           throw ShibTargetException(SHIBRPC_IPADDR_MISSING,
185                                     "The IP Address provided by your origin site was missing.");
186         
187         log.debug ("verify client address");
188         // Verify the client address matches authentication
189         auto_ptr<char> this_ip(XMLString::transcode(ip));
190         if (strcmp (argp->client_addr, this_ip.get()))
191           throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
192                                     "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.");
193       }
194     }
195     catch (SAMLException &e)    // XXX refine this handler to catch and log different profile exceptions
196     {
197       log.error ("received SAML exception: %s", e.what());
198       ostringstream os;
199       os << e;
200       throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str());
201     }
202     catch (XMLException &e)
203     {
204       log.error ("received XML exception");
205       auto_ptr<char> msg(XMLString::transcode(e.getMessage()));
206       throw ShibTargetException (SHIBRPC_XML_EXCEPTION, msg.get());
207     }
208   }
209   catch (ShibTargetException &e)
210   {
211     log.info ("FAILED: %s", e.what());
212     if (r) delete r;
213     result->status = e.which();
214     result->error_msg = strdup(e.what());
215     return TRUE;
216   }
217 #if 0
218   catch (...)
219   {
220     log.error ("Unknown error");
221     if (r) delete r;
222     result->status = SHIBRPC_UNKNOWN_ERROR;
223     result->error_msg = strdup("An unknown exception occurred");
224     return TRUE;
225   }
226 #endif
227
228   // It passes all our tests -- create a new session.
229   log.info ("Creating new session");
230
231   SAMLAuthenticationStatement* as=static_cast<SAMLAuthenticationStatement*>(auth_st->clone());
232
233   // Create a new cookie
234   SAMLIdentifier id;
235   auto_ptr<char> c(XMLString::transcode(id));
236   char *cookie = c.get();
237
238   // Cache this session with the cookie
239   g_shibTargetCCache->insert(cookie, as, argp->client_addr);
240
241   // Delete the response...
242   delete r;
243
244   // And let the user know.
245   free (result->cookie);
246   result->cookie = strdup(cookie);
247   result->status = SHIBRPC_OK;
248   result->error_msg = strdup("");
249
250   log.debug ("new session id: %s", cookie);
251   return TRUE;
252 }
253
254 extern "C" bool_t
255 shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp,
256                         shibrpc_get_assertions_ret_1 *result, struct svc_req *rqstp)
257 {
258   log4cpp::Category& log = get_category();
259   string ctx = get_threadid("get_assertions");
260   saml::NDC ndc(ctx);
261
262   if (!argp || !result) {
263     log.error ("Invalid RPC arguments");
264     return FALSE;
265   }
266
267   memset (result, 0, sizeof (*result));
268
269   log.debug ("get attrs for client at %s", argp->cookie.client_addr);
270   log.debug ("cookie: %s", argp->cookie.cookie);
271   log.debug ("resource: %s", argp->url);
272
273   // Find this session
274   CCacheEntry* entry = g_shibTargetCCache->find(argp->cookie.cookie);
275
276   // If it does not exist, leave now..
277   if (!entry) {
278     log.error ("No Session");
279     result->status = SHIBRPC_NO_SESSION;
280     result->error_msg = strdup("getattrs Internal error: no session");
281     return TRUE;
282   }
283
284   // Validate the client address (again?)
285   if (argp->checkIPAddress &&
286       strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
287     log.error ("IP Mismatch");
288     result->status = SHIBRPC_IPADDR_MISMATCH;
289     result->error_msg =
290       strdup("Your IP address does not match the address in the original authentication.");
291     return TRUE;
292   }
293
294   try {
295     // grab the attributes for this resource
296     Resource resource(argp->url);
297     Iterator<SAMLAssertion*> iter = entry->getAssertions(resource);
298     u_int size = iter.size();
299     result->assertions.assertions_len = size;
300
301     // if we have assertions...
302     if (size) {
303
304       // Build the response section
305       ShibRpcXML* av =
306         (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
307       result->assertions.assertions_val = av;
308
309       // and then serialize them all...
310       u_int i = 0;
311       while (iter.hasNext()) {
312         SAMLAssertion* as = iter.next();
313         ostringstream os;
314         os << *as;
315         av[i++].xml_string = strdup(os.str().c_str());
316       }
317     }
318   } catch (SAMLException& e) {
319     log.error ("received SAML exception: %s", e.what());
320     ostringstream os;
321     os << e;
322     result->status = SHIBRPC_SAML_EXCEPTION;
323     result->error_msg = strdup(os.str().c_str());
324     return TRUE;
325   }
326
327   // and let it fly
328   result->status = SHIBRPC_OK;
329   result->error_msg = strdup("");
330
331   log.debug ("returning");
332   return TRUE;
333 }
334
335 extern "C" int
336 shibrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
337 {
338         xdr_free (xdr_result, result);
339
340         /*
341          * Insert additional freeing code here, if needed
342          */
343
344         return 1;
345 }