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