Removed sock name typedef, reorgd headers
[shibboleth/sp.git] / shib-target / shibrpc-server.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
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.
22  *
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
28  *
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.
33  *
34  *
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.
48  */
49
50 /*
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.
54  *
55  * Created by:  Derek Atkins <derek@ihtfp.com>
56  *
57  * $Id$
58  */
59
60 #include "internal.h"
61
62 #include "shibrpc.h"
63
64 #include <sstream>
65
66 #ifdef HAVE_LIBDMALLOCXX
67 #include <dmalloc.h>
68 #endif
69
70 #include <log4cpp/Category.hh>
71
72 using namespace std;
73 using namespace log4cpp;
74 using namespace saml;
75 using namespace shibboleth;
76 using namespace shibtarget;
77
78 static string get_threadid (const char* proc)
79 {
80   static u_long counter = 0;
81   ostringstream buf;
82   buf << "[" << counter++ << "] " << proc;
83   return buf.str();
84 }
85
86 static Category& get_category (void)
87 {
88   string ctx = "shibtarget.rpc-server";
89   return Category::getInstance(ctx);
90 }
91
92 extern "C" bool_t
93 shibrpc_ping_1_svc(int *argp, int *result, struct svc_req *rqstp)
94 {
95   *result = (*argp)+1;
96   return TRUE;
97 }
98
99 void set_rpc_status(ShibRpcError *error, ShibRpcStatus status,
100                     const char* msg, const char* origin)
101 {
102   error->status = status;
103   if (status) {
104     error->ShibRpcError_u.e.error = strdup(msg ? msg : "");
105     error->ShibRpcError_u.e.origin = strdup(origin ? origin : "");
106   }
107 }
108
109 void set_rpc_status_x(ShibRpcError *error, ShibRpcStatus status,
110                       const char* msg, const XMLCh* origin)
111 {
112   if (!status) {
113     set_rpc_status(error, status, NULL, NULL);
114     return;
115   }
116   auto_ptr_char orig(origin);
117   set_rpc_status(error, status, msg, orig.get());
118 }
119
120 extern "C" bool_t
121 shibrpc_session_is_valid_1_svc(shibrpc_session_is_valid_args_1 *argp,
122                                shibrpc_session_is_valid_ret_1 *result,
123                                struct svc_req *rqstp)
124 {
125   Category& log = get_category();
126   string ctx = get_threadid("session_is_valid");
127   saml::NDC ndc(ctx);
128
129   if (!argp || !result) {
130     log.error ("RPC Argument Error");
131     return FALSE;
132   }
133
134   memset (result, 0, sizeof (*result));
135   
136   log.debug ("checking: %s@%s (checkAddr=%s)",
137              argp->cookie.cookie, argp->cookie.client_addr,
138              argp->checkIPAddress ? "true" : "false");
139
140   // See if the cookie exists...
141   CCacheEntry *entry = g_shibTargetCCache->find(argp->cookie.cookie);
142
143   // If not, leave now..
144   if (!entry) {
145     log.debug ("Not found");
146     set_rpc_status(&result->status, SHIBRPC_NO_SESSION,
147                    "No session exists for this cookie", "");
148     return TRUE;
149   }
150
151   // TEST the session...
152   try {
153
154     // Grab the origin
155     const XMLCh* origin = entry->getStatement()->getSubject()->getNameQualifier();
156
157     // Verify the address is the same
158     if (argp->checkIPAddress) {
159       log.debug ("Checking address against %s", entry->getClientAddress());
160       if (strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
161         log.debug ("IP Address mismatch");
162
163         throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
164   "Your IP address does not match the address in the original authentication.",
165                                   origin);
166       }
167     }
168
169     // and that the session is still valid...
170     if (!entry->isSessionValid(argp->lifetime, argp->timeout)) {
171       log.debug ("Session expired");
172       throw ShibTargetException(SHIBRPC_SESSION_EXPIRED,
173                                 "Your session has expired.  Re-authenticate.",
174                                 origin);
175     }
176
177     // and now try to prefetch the attributes .. this could cause an
178     // "error", which is why we call it here.
179     try {
180       log.debug ("resource: %s", argp->application_id);
181       entry->preFetch(argp->application_id, 15);        // give a 15-second window for the RM
182
183     } catch (SAMLException &e) {
184       log.debug ("prefetch failed with a SAML Exception: %s", e.what());
185       ostringstream os;
186       os << e;
187       throw ShibTargetException(SHIBRPC_SAML_EXCEPTION, os.str(), origin);
188
189     } catch (...) {
190       log.error ("prefetch caught an unknown exception");
191       throw ShibTargetException(SHIBRPC_UNKNOWN_ERROR,
192                 "An unknown error occured while pre-fetching attributes.",
193                                 origin);
194     }
195
196   } catch (ShibTargetException &e) {
197     entry->release();
198     g_shibTargetCCache->remove (argp->cookie.cookie);
199     set_rpc_status_x(&result->status, e.which(), e.what(), e.where());
200     return TRUE;
201   }
202
203   // Ok, just release it.
204   entry->release();
205
206   // ok, we've succeeded..
207   set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
208   log.debug ("session ok");
209   return TRUE;
210 }
211
212 extern "C" bool_t
213 shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp,
214                           shibrpc_new_session_ret_1 *result, struct svc_req *rqstp)
215 {
216   Category& log = get_category();
217   string ctx = get_threadid("new_session");
218   saml::NDC ndc(ctx);
219
220   if (!argp || !result) {
221     log.error ("Invalid RPC Arguments");
222     return FALSE;
223   }
224
225   // Initialize the result structure
226   memset (result, 0, sizeof(*result));
227   result->cookie = strdup ("");
228
229   log.debug ("creating session for %s", argp->client_addr);
230   log.debug ("shire location: %s", argp->shire_location);
231
232   XMLByte* post=reinterpret_cast<XMLByte*>(argp->saml_post);
233   auto_ptr_XMLCh location(argp->shire_location);
234
235   // Pull in the Policies
236   Iterator<const XMLCh*> policies=dynamic_cast<STConfig&>(ShibTargetConfig::getConfig()).getPolicies();
237
238   // And grab the Profile
239   // XXX: Create a "Global" POSTProfile instance per location...
240   log.debug ("create the POST profile (%d policies)", policies.size());
241   ShibPOSTProfile *profile = ShibPOSTProfileFactory::getInstance(
242     ShibTargetConfig::getConfig().getMetadataProviders(),
243     ShibTargetConfig::getConfig().getTrustProviders(),
244     policies,location.get(),3600
245     );
246
247   SAMLResponse* r = NULL;
248   const SAMLAuthenticationStatement* auth_st = NULL;
249   XMLCh* origin = NULL;
250
251   try
252   {
253     try
254     {
255       // Make sure we've got a profile
256       if (!profile)
257         throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,
258                                   "Failed to obtain the profile");
259
260       // Try and accept the response...
261       log.debug ("Trying to accept the post");
262       r = profile->accept(post, &origin);
263
264       // Make sure we got a response
265       if (!r)
266         throw ShibTargetException(SHIBRPC_RESPONSE_MISSING,
267                                   "Failed to accept the response.",
268                                   origin);
269
270       // Find the SSO Assertion
271       log.debug ("Get the SSOAssertion");
272       const SAMLAssertion* ssoAssertion = profile->getSSOAssertion(*r);
273
274       // Check against the replay cache
275       log.debug ("check replay cache");
276       if (profile->checkReplayCache(*ssoAssertion) == false)
277         throw ShibTargetException(SHIBRPC_ASSERTION_REPLAYED,
278                                   "Duplicate assertion found.",
279                                   origin);
280
281       // Get the authentication statement we need.
282       log.debug ("get SSOStatement");
283       auth_st = profile->getSSOStatement(*ssoAssertion);
284
285       // Maybe verify the origin address....
286       if (argp->checkIPAddress) {
287         log.debug ("check IP Address");
288
289         // Verify the client address exists
290         const XMLCh* ip = auth_st->getSubjectIP();
291         if (!ip)
292           throw ShibTargetException(SHIBRPC_IPADDR_MISSING,
293                     "The IP Address provided by your origin site was missing.",
294                                     origin);
295         
296         log.debug ("verify client address");
297         // Verify the client address matches authentication
298         auto_ptr_char this_ip(ip);
299         if (strcmp (argp->client_addr, this_ip.get()))
300           throw ShibTargetException(SHIBRPC_IPADDR_MISMATCH,
301             "The IP address provided by your origin site did not match "
302             "your current address.  "
303             "To correct this problem you may need to bypass a local proxy server.",
304                                     origin);
305       }
306     }
307     catch (SAMLException &e)    // XXX refine this handler to catch and log different profile exceptions
308     {
309       log.error ("received SAML exception: %s", e.what());
310       ostringstream os;
311       os << e;
312       throw ShibTargetException (SHIBRPC_SAML_EXCEPTION, os.str(), origin);
313     }
314     catch (XMLException &e)
315     {
316       log.error ("received XML exception");
317       auto_ptr_char msg(e.getMessage());
318       throw ShibTargetException (SHIBRPC_XML_EXCEPTION, msg.get(), origin);
319     }
320   }
321   catch (ShibTargetException &e)
322   {
323     log.info ("FAILED: %s", e.what());
324     if (r) delete r;
325     if (origin) XMLString::release(&origin);
326     set_rpc_status_x(&result->status, e.which(), e.what(), e.where());
327     return TRUE;
328   }
329 #if 1
330   catch (...)
331   {
332     log.error ("Unknown error");
333     if (r) delete r;
334     if (origin) XMLString::release(&origin);
335     set_rpc_status(&result->status, SHIBRPC_UNKNOWN_ERROR,
336                    "An unknown exception occurred", "");
337     return TRUE;
338   }
339 #endif
340
341   // It passes all our tests -- create a new session.
342   log.info ("Creating new session");
343
344   SAMLAuthenticationStatement* as=static_cast<SAMLAuthenticationStatement*>(auth_st->clone());
345
346   // Create a new cookie
347   SAMLIdentifier id;
348   auto_ptr_char c(id);
349   const char *cookie = c.get();
350
351   // Cache this session with the cookie
352   g_shibTargetCCache->insert(cookie, as, argp->client_addr);
353   
354   // Delete the response...
355   delete r;
356
357   // Delete the origin...
358   XMLString::release(&origin);
359
360   // And let the user know.
361   if (result->cookie) free(result->cookie);
362   result->cookie = strdup(cookie);
363   set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
364
365   log.debug("new session id: %s", cookie);
366   return TRUE;
367 }
368
369 extern "C" bool_t
370 shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp,
371                         shibrpc_get_assertions_ret_1 *result, struct svc_req *rqstp)
372 {
373   Category& log = get_category();
374   string ctx = get_threadid("get_assertions");
375   saml::NDC ndc(ctx);
376
377   if (!argp || !result) {
378     log.error ("Invalid RPC arguments");
379     return FALSE;
380   }
381
382   memset (result, 0, sizeof (*result));
383
384   log.debug ("get attrs for client at %s", argp->cookie.client_addr);
385   log.debug ("cookie: %s", argp->cookie.cookie);
386   log.debug ("resource: %s", argp->application_id);
387
388   // Find this session
389   CCacheEntry* entry = g_shibTargetCCache->find(argp->cookie.cookie);
390
391   // If it does not exist, leave now..
392   if (!entry) {
393     log.error ("No Session");
394     set_rpc_status(&result->status, SHIBRPC_NO_SESSION,
395                    "getattrs Internal error: no session", "");
396     return TRUE;
397   }
398
399   // Grab the origin
400   const XMLCh* origin = entry->getStatement()->getSubject()->getNameQualifier();
401
402   // Validate the client address (again?)
403   if (argp->checkIPAddress &&
404       strcmp (argp->cookie.client_addr, entry->getClientAddress())) {
405     log.error ("IP Mismatch");
406     set_rpc_status_x(&result->status, SHIBRPC_IPADDR_MISMATCH,
407    "Your IP address does not match the address in the original authentication.",
408                      origin);
409     entry->release();
410     return TRUE;
411   }
412
413   try {
414     // grab the attributes for this resource
415     Iterator<SAMLAssertion*> iter = entry->getAssertions(argp->application_id);
416     u_int size = iter.size();
417     result->assertions.assertions_len = size;
418
419     // if we have assertions...
420     if (size) {
421
422       // Build the response section
423       ShibRpcXML* av = (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML));
424       result->assertions.assertions_val = av;
425
426       // and then serialize them all...
427       u_int i = 0;
428       while (iter.hasNext()) {
429         SAMLAssertion* as = iter.next();
430         ostringstream os;
431         os << *as;
432         av[i++].xml_string = strdup(os.str().c_str());
433       }
434     }
435   } catch (SAMLException& e) {
436     log.error ("received SAML exception: %s", e.what());
437     ostringstream os;
438     os << e;
439     set_rpc_status_x(&result->status, SHIBRPC_SAML_EXCEPTION,
440                      strdup(os.str().c_str()), origin);
441     entry->release();
442     return TRUE;
443   }
444
445   // Now grab the serialized authentication statement
446   result->auth_statement.xml_string = strdup(entry->getSerializedStatement());
447
448   entry->release();
449
450   // and let it fly
451   set_rpc_status(&result->status, SHIBRPC_OK, NULL, NULL);
452
453   log.debug ("returning");
454   return TRUE;
455 }
456
457 extern "C" int
458 shibrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
459 {
460         xdr_free (xdr_result, result);
461
462         /*
463          * Insert additional freeing code here, if needed
464          */
465
466         return 1;
467 }