1f41d893e2b7857aa66744d08854ded14f8af3b7
[shibboleth/cpp-sp.git] / shib-target / shib-rpcerror.cpp
1 /*
2  * shib-rpcerror.cpp -- RPC Error class
3  *
4  * Created by:  Derek Atkins <derek@ihtfp.com>
5  *
6  * $Id$
7  */
8
9 #ifndef WIN32
10 # include <unistd.h>
11 #endif
12
13 #include "shib-target.h"
14
15 #include <stdexcept>
16 #include <sstream>
17 #include <typeinfo>
18
19 #include <log4cpp/Category.hh>
20
21 using namespace std;
22 using namespace shibtarget;
23 using namespace shibboleth;
24 using namespace saml;
25
26 namespace {
27   int initializing = 0;
28   int initialized = 0;
29   const type_info* type_MalformedException = NULL;
30   const type_info* type_UnsupportedExtensionException = NULL;
31   const type_info* type_InvalidCryptoException = NULL;
32   const type_info* type_TrustException = NULL;
33   const type_info* type_BindingException = NULL;
34   const type_info* type_SOAPException = NULL;
35   const type_info* type_ContentTypeException = NULL;
36
37   const type_info* type_ProfileException = NULL;
38   const type_info* type_FatalProfileException = NULL;
39   const type_info* type_RetryableProfileException = NULL;
40   const type_info* type_ExpiredAssertionException = NULL;
41   const type_info* type_InvalidAssertionException = NULL;
42   const type_info* type_ReplayedAssertionException = NULL;
43
44   const XMLCh code_InvalidHandle[] = // InvalidHandle
45     { chLatin_I, chLatin_n, chLatin_v, chLatin_a, chLatin_l, chLatin_i, chLatin_d,
46       chLatin_H, chLatin_a, chLatin_n, chLatin_d, chLatin_l, chLatin_e, 
47       chNull
48     };
49 }
50
51 void rpcerror_init (void)
52 {
53   if (initialized)
54     return;
55
56   if (initializing++) {
57     while (!initialized);
58     return;
59   }
60
61   type_MalformedException = &typeid(MalformedException);
62   type_UnsupportedExtensionException = &typeid(UnsupportedExtensionException);
63   type_InvalidCryptoException = &typeid(InvalidCryptoException);
64   type_TrustException = &typeid(TrustException);
65   type_BindingException = &typeid(BindingException);
66   type_SOAPException = &typeid(SOAPException);
67   type_ContentTypeException = &typeid(ContentTypeException);
68
69   type_ProfileException = &typeid(ProfileException);
70   type_FatalProfileException = &typeid(FatalProfileException);
71   type_RetryableProfileException = &typeid(RetryableProfileException);
72   type_ExpiredAssertionException = &typeid(ExpiredAssertionException);
73   type_InvalidAssertionException = &typeid(InvalidAssertionException);
74   type_ReplayedAssertionException = &typeid(ReplayedAssertionException);
75
76   initialized = 1;
77 }
78
79 #define TEST_TYPE(type,str) { if (type && *type == info) return str; }
80 const char* rpcerror_exception_type(SAMLException* e)
81 {
82   if (!e)
83     return "Invalid (NULL) exception";
84
85   const type_info& info = typeid(*e);
86
87   TEST_TYPE(type_MalformedException, "Exception: XML object is malformed");
88   TEST_TYPE(type_UnsupportedExtensionException,
89             "Exception: an unsupported extention was accessed");
90   TEST_TYPE(type_InvalidCryptoException, "Exception: cryptographic check failed");
91   TEST_TYPE(type_TrustException, "Exception: trust failed");
92   TEST_TYPE(type_BindingException,
93             "Exception: an error occurred in binding to the AA");
94   TEST_TYPE(type_SOAPException, "Exception: SOAP error");
95   TEST_TYPE(type_ContentTypeException, "Exception: Content Type Failure");
96
97   TEST_TYPE(type_ProfileException, "Exception: Profile Error");
98   TEST_TYPE(type_FatalProfileException, "Exception: Fatal Profile Error");
99   TEST_TYPE(type_RetryableProfileException, "Exception: Retryable Profile Error");
100   TEST_TYPE(type_ExpiredAssertionException, "Exception: Expired Assertion");
101   TEST_TYPE(type_InvalidAssertionException, "Exception: Invalid Assertion");
102   TEST_TYPE(type_ReplayedAssertionException, "Exception: Replayed Assertion");
103
104   return "Unknown SAML Exception";
105 }
106 #undef TEST_TYPE
107
108 class shibtarget::RPCErrorPriv {
109 public:
110   RPCErrorPriv(int stat, const char* msg, const XMLCh* originSite);
111   ~RPCErrorPriv();
112
113   int           status;
114   string        error_msg;
115   xstring       origin;
116   SAMLException* except;
117 };
118
119 RPCErrorPriv::RPCErrorPriv(int stat, const char* msg, const XMLCh* originSite)
120 {
121   status = stat;
122   string ctx = "shibtarget.RPCErrorPriv";
123   log4cpp::Category& log = log4cpp::Category::getInstance(ctx);
124
125   rpcerror_init();
126
127   if (originSite) origin = originSite;
128
129   if (status == SHIBRPC_SAML_EXCEPTION) {
130     istringstream estr(msg);
131     try { 
132       except = NULL;
133       except = SAMLException::getInstance(estr);
134     } catch (SAMLException& e) {
135       log.error ("Caught SAML Exception while building the SAMLException: %s",
136                  e.what());
137       log.error ("XML: %s", msg);
138     } catch (XMLException& e) {
139       log.error ("Caught XML Exception building SAMLException: %s",
140                  e.getMessage());
141       log.error ("XML: %s", msg);
142     } catch (...) {
143       log.error ("Caught exception building SAMLException!");
144       log.error ("XML: %s", msg);
145     }
146     if (dynaptr(ContentTypeException, except)!=NULL)
147         error_msg = 
148           "We were unable to contact your identity provider and cannot grant "
149           "access at this time. Please contact your provider's help desk or "
150           "administrator so that the appropriate steps can be taken.  "
151           "Be sure to describe what you're trying to access and useful "
152           "context like the current time.";
153     else
154         error_msg = (except ? except->what() : msg);
155   } else {
156     error_msg = msg;
157     except = NULL;
158   }
159 }
160
161 RPCErrorPriv::~RPCErrorPriv()
162 {
163   if (except)
164     delete except;
165 }
166
167 RPCError::RPCError(ShibRpcError* error)
168 {
169   if (!error || !error->status)
170     init(0, "", NULL);
171   else {
172     auto_ptr<XMLCh> origin(XMLString::transcode(error->ShibRpcError_u.e.origin));
173     init(error->status, error->ShibRpcError_u.e.error, origin.get());
174   }
175 }
176
177 void RPCError::init(int stat, char const* msg, const XMLCh* origin)
178 {
179   m_priv = new RPCErrorPriv(stat,msg,origin);
180 }
181
182 RPCError::~RPCError()
183 {
184   delete m_priv;
185 }
186
187 bool RPCError::isError() { return (m_priv->status != 0); }
188
189 #define TEST_TYPE(type) { if (type && *type == info) return true; }
190 bool RPCError::isRetryable()
191 {
192   switch (m_priv->status) {
193   case SHIBRPC_NO_SESSION:
194   case SHIBRPC_SESSION_EXPIRED:
195     return true;
196
197   case SHIBRPC_SAML_EXCEPTION:
198     if (m_priv->except) {
199       const type_info& info = typeid(*m_priv->except);
200
201       TEST_TYPE(type_RetryableProfileException);
202       TEST_TYPE(type_ExpiredAssertionException);
203
204       Iterator<saml::QName> codes = m_priv->except->getCodes();
205       while (codes.hasNext()) {
206         saml::QName name = codes.next();
207
208         if (!XMLString::compareString(name.getNamespaceURI(),
209                                       shibboleth::XML::SHIB_NS)) {
210           if (!XMLString::compareString(name.getLocalName(), code_InvalidHandle)) {
211             return true;
212           }
213         }
214       }
215     }
216
217     // FALLTHROUGH
218   default:
219     return false;
220   }
221 }
222 #undef TEST_TYPE
223
224 const char* RPCError::getType()
225 {
226   switch (m_priv->status) {
227   case SHIBRPC_OK:              return "No Error";
228   case SHIBRPC_UNKNOWN_ERROR:   return "Unknown error";
229   case SHIBRPC_INTERNAL_ERROR:  return "Internal Error";
230   case SHIBRPC_XML_EXCEPTION:   return "Xerces XML Exception";
231   case SHIBRPC_SAX_EXCEPTION:   return "Xerces SAX Exception";
232   case SHIBRPC_SAML_EXCEPTION:  return rpcerror_exception_type(m_priv->except);
233
234   case SHIBRPC_NO_SESSION:      return "No Session";
235   case SHIBRPC_SESSION_EXPIRED: return "Session Expired";
236   case SHIBRPC_IPADDR_MISMATCH: return "IP Address Mismatch";
237
238   case SHIBRPC_IPADDR_MISSING:  return "IP Address Missing";
239   case SHIBRPC_RESPONSE_MISSING:        return "SAML Response Missing";
240   case SHIBRPC_ASSERTION_REPLAYED:      return "SAML Assertion Replayed";
241   default:                      return "Unknown Shibboleth RPC error";
242   }
243 }
244
245 const char* RPCError::getText()
246 {
247   return m_priv->error_msg.c_str();
248 }
249
250 const char* RPCError::getDesc()
251 {
252   if (m_priv->except) {
253     Iterator<saml::QName> i=m_priv->except->getCodes();
254     if (i.hasNext() &&
255         XMLString::compareString(L(Responder),i.next().getLocalName()))
256       return
257         "An error occurred at the target system while processing your request";
258     else
259       return "An error occurred at your origin site while processing your request";
260   } else
261     return "An error occurred processing your request";
262 }
263
264 int RPCError::getCode() { return m_priv->status; }
265
266 const char* RPCError::getOriginErrorURL()
267 {
268     const char* res="No URL Available";
269     if (!m_priv->origin.empty())
270     {
271         OriginSiteMapper mapper;
272         res=mapper.getErrorURL(m_priv->origin.c_str());
273     }
274     return res;
275 }
276
277 const char* RPCError::getOriginContactName()
278
279     const char* res="No Name Available";
280     if (!m_priv->origin.empty())
281     {
282         OriginSiteMapper mapper;
283         Iterator<const IContactInfo*> i=mapper.getContacts(m_priv->origin.c_str());
284         while (i.hasNext())
285         {
286             const IContactInfo* c=i.next();
287             if (c->getType()==IContactInfo::technical)
288             {
289                 res=c->getName();
290                 break;
291             }
292         }
293     }
294     return res;
295 }
296
297 const char* RPCError::getOriginContactEmail()
298 {
299     const char* res="No Email Available";
300     if (!m_priv->origin.empty())
301     {
302         OriginSiteMapper mapper;
303         Iterator<const IContactInfo*> i=mapper.getContacts(m_priv->origin.c_str());
304         while (i.hasNext())
305         {
306             const IContactInfo* c=i.next();
307             if (c->getType()==IContactInfo::technical)
308             {
309                 res=c->getEmail();
310                 break;
311             }
312         }
313     }
314     return res;
315 }