Some work on SAML
[mech_eap.git] / util_saml.cpp
1 /*
2  * Copyright (c) 2010, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 /*
33  * Copyright 2001-2009 Internet2
34  * 
35  * Licensed under the Apache License, Version 2.0 (the "License");
36  * you may not use this file except in compliance with the License.
37  * You may obtain a copy of the License at
38  *
39  *     http://www.apache.org/licenses/LICENSE-2.0
40  *
41  * Unless required by applicable law or agreed to in writing, software
42  * distributed under the License is distributed on an "AS IS" BASIS,
43  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
44  * See the License for the specific language governing permissions and
45  * limitations under the License.
46  */
47
48 #include <gssapi/gssapi.h>
49 #include <gssapi/gssapi_ext.h>
50 #include "util.h"
51
52 #include <shibsp/Application.h>
53 #include <shibsp/exceptions.h>
54 #include <shibsp/SPConfig.h>
55 #include <shibsp/ServiceProvider.h>
56 #include <shibsp/attribute/Attribute.h>
57 #include <shibsp/attribute/resolver/ResolutionContext.h>
58 #include <shibsp/handler/AssertionConsumerService.h>
59 #include <shibsp/metadata/MetadataProviderCriteria.h>
60 #include <shibsp/util/SPConstants.h>
61
62 #include <saml/saml1/core/Assertions.h>
63 #include <saml/saml2/core/Assertions.h>
64 #include <saml/saml2/metadata/Metadata.h>
65 #include <xercesc/util/XMLUniDefs.hpp>
66 #include <xmltooling/XMLToolingConfig.h>
67 #include <xmltooling/util/XMLHelper.h>
68
69 using namespace shibsp;
70 using namespace opensaml::saml2md;
71 using namespace opensaml;
72 using namespace xmltooling::logging;
73 using namespace xmltooling;
74 using namespace xercesc;
75 using namespace std;
76
77 class GSSEAPResolver : public shibsp::AssertionConsumerService
78 {
79 public:
80     GSSEAPResolver(const DOMElement *e, const char *appId)
81         : shibsp::AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".GSSEAPResolver")) {
82     }
83     virtual ~GSSEAPResolver() {}
84
85     ResolutionContext* resolveAttributes (
86         const Application& application,
87         const RoleDescriptor* issuer,
88         const XMLCh* protocol,
89         const saml1::NameIdentifier* v1nameid,
90         const saml2::NameID* nameid,
91         const XMLCh* authncontext_class,
92         const XMLCh* authncontext_decl,
93         const vector<const Assertion*>* tokens
94         ) const {
95             return shibsp::AssertionConsumerService::resolveAttributes(
96                     application, issuer, protocol, v1nameid,
97                     nameid, authncontext_class, authncontext_decl, tokens
98             );
99     }
100
101 private:
102     void implementProtocol(
103         const Application& application,
104         const HTTPRequest& httpRequest,
105         HTTPResponse& httpResponse,
106         SecurityPolicy& policy,
107         const PropertySet* settings,
108         const XMLObject& xmlObject
109         ) const {
110             throw FatalProfileException("Should never be called.");
111     }
112 };
113
114 class SHIBSP_DLLLOCAL DummyContext : public ResolutionContext
115 {
116 public:
117     DummyContext(const vector<Attribute*>& attributes) : m_attributes(attributes) {
118     }
119
120     virtual ~DummyContext() {
121         for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
122     }
123
124     vector<Attribute*>& getResolvedAttributes() {
125         return m_attributes;
126     }
127     vector<Assertion*>& getResolvedAssertions() {
128         return m_tokens;
129     }
130
131 private:
132     vector<Attribute*> m_attributes;
133     static vector<Assertion*> m_tokens; // never any tokens, so just share an empty vector
134 };
135
136 struct eap_gss_saml_attr_ctx {
137     ResolutionContext *resCtx;
138     gss_buffer_desc assertion;
139 };
140
141 static OM_uint32
142 samlAllocAttrContext(OM_uint32 *minor,
143                      struct eap_gss_saml_attr_ctx **pCtx)
144 {
145     struct eap_gss_saml_attr_ctx *ctx;
146
147     ctx = (struct eap_gss_saml_attr_ctx *)GSSEAP_CALLOC(1, sizeof(*ctx));
148     if (ctx == NULL) {
149         *minor = ENOMEM;
150         return GSS_S_FAILURE;
151     }
152
153     *pCtx = ctx;
154     *minor = 0;
155     return GSS_S_COMPLETE;
156 }
157
158 static OM_uint32
159 samlImportAssertion(OM_uint32 *minor,
160                     gss_buffer_t buffer,
161                     saml2::Assertion **pAssertion)
162 {
163     *pAssertion = NULL;
164
165     try {
166         DOMDocument *doc;
167         const XMLObjectBuilder *b;
168         DOMElement *elem;
169         XMLObject *xobj;
170         string samlBuf((char *)buffer->value, buffer->length);
171         istringstream samlIn(samlBuf);
172
173         doc = XMLToolingConfig::getConfig().getParser().parse(samlIn);
174         b = XMLObjectBuilder::getDefaultBuilder();
175         elem = doc->getDocumentElement();
176         xobj = b->buildOneFromElement(elem, true);
177
178         *pAssertion = dynamic_cast<saml2::Assertion *>(xobj);
179         if (*pAssertion == NULL) {
180             /* TODO minor_status */
181             return GSS_S_BAD_NAME;
182         }
183     } catch (exception &e){
184         /* TODO minor_status */
185         return GSS_S_BAD_NAME;
186     }
187
188     *minor = 0;
189     return GSS_S_COMPLETE;
190 }
191
192 OM_uint32
193 samlDuplicateAttrContext(OM_uint32 *minor,
194                          const struct eap_gss_saml_attr_ctx *in,
195                          struct eap_gss_saml_attr_ctx **out)
196 {
197     OM_uint32 major, tmpMinor;
198     struct eap_gss_saml_attr_ctx *ctx;
199
200     major = samlAllocAttrContext(minor, &ctx);
201     if (GSS_ERROR(major))
202         goto cleanup;
203
204     major = duplicateBuffer(minor, (gss_buffer_t)&in->assertion, &ctx->assertion);
205     if (GSS_ERROR(major))
206         goto cleanup;
207
208     ctx->resCtx = new DummyContext(in->resCtx->getResolvedAttributes());
209
210 cleanup:
211     if (GSS_ERROR(major))
212         samlReleaseAttrContext(&tmpMinor, &ctx);
213
214     return major;
215 }
216
217 OM_uint32
218 samlReleaseAttrContext(OM_uint32 *minor,
219                        struct eap_gss_saml_attr_ctx **pCtx)
220 {
221     struct eap_gss_saml_attr_ctx *ctx = *pCtx;
222
223     if (ctx != NULL) {
224         delete ctx->resCtx;
225         gss_release_buffer(minor, &ctx->assertion);
226         GSSEAP_FREE(ctx);
227         *pCtx = NULL;
228     }
229
230     *minor = 0;
231     return GSS_S_COMPLETE;
232 }
233
234 OM_uint32
235 samlCreateAttrContext(OM_uint32 *minor,
236                       gss_buffer_t buffer,
237                       gss_name_t acceptorName,
238                       struct eap_gss_saml_attr_ctx **pCtx)
239 {
240     OM_uint32 major, tmpMinor;
241     struct eap_gss_saml_attr_ctx *ctx;
242     SPConfig &conf = SPConfig::getConfig();
243     ServiceProvider *sp;
244     const Application *app;
245     MetadataProvider *m;
246     gss_buffer_desc nameBuf;
247     const XMLCh *issuer = NULL;
248     saml2::NameID *subjectName = NULL;
249     saml2::Assertion *assertion;
250
251     nameBuf.length = 0;
252     nameBuf.value = NULL;
253
254     conf.setFeatures(SPConfig::Metadata             |
255                      SPConfig::Trust                |
256                      SPConfig::AttributeResolution  |
257                      SPConfig::Credentials          |
258                      SPConfig::OutOfProcess);
259     if (!conf.init())
260         return GSS_S_FAILURE;
261     if (!conf.instantiate())
262         return GSS_S_FAILURE;
263
264     sp = conf.getServiceProvider();
265     sp->lock();
266
267     major = gss_display_name(minor, acceptorName, &nameBuf, NULL);
268     if (GSS_ERROR(major))
269         goto cleanup;
270
271     app = sp->getApplication((const char *)nameBuf.value);
272     if (app == NULL) {
273         major = GSS_S_FAILURE;
274         goto cleanup;
275     }
276
277     major = samlAllocAttrContext(minor, &ctx);
278     if (GSS_ERROR(major))
279         goto cleanup;
280
281     major = duplicateBuffer(minor, buffer, &ctx->assertion);
282     if (GSS_ERROR(major))
283         goto cleanup;
284
285     major = samlImportAssertion(minor, &ctx->assertion, &assertion);
286     if (GSS_ERROR(major))
287         goto cleanup;
288
289     if (assertion->getIssuer() != NULL)
290         issuer = assertion->getIssuer()->getName();
291     if (assertion->getSubject() != NULL)
292         subjectName = assertion->getSubject()->getNameID();
293
294     try {
295         m = app->getMetadataProvider();
296         xmltooling::Locker mlocker(m);
297         MetadataProviderCriteria mc(*app, issuer,
298                                     &IDPSSODescriptor::ELEMENT_QNAME,
299                                     samlconstants::SAML20P_NS);
300         pair<const EntityDescriptor *, const RoleDescriptor *> site =
301             m->getEntityDescriptor(mc);
302         if (!site.first) {
303             auto_ptr_char temp(issuer);
304             throw MetadataException("Unable to locate metadata for IdP ($1).",
305                                     params(1,temp.get()));
306         }
307         vector<const Assertion*> tokens(1, assertion);
308         GSSEAPResolver gssResolver(NULL, (const char *)nameBuf.value);
309         ctx->resCtx = gssResolver.resolveAttributes(*app, site.second,
310                                                     samlconstants::SAML20P_NS,
311                                                     NULL, subjectName, NULL,
312                                                     NULL, &tokens);
313     } catch (exception &ex) {
314         major = GSS_S_BAD_NAME;
315         goto cleanup;
316     }
317
318     major = GSS_S_COMPLETE;
319     *pCtx = ctx;
320
321 cleanup:
322     sp->unlock();
323     conf.term();
324
325     if (GSS_ERROR(major))
326         samlReleaseAttrContext(&tmpMinor, &ctx);
327     gss_release_buffer(&tmpMinor, &nameBuf);
328
329     return major;
330 }
331
332 OM_uint32
333 samlGetAttributeTypes(OM_uint32 *minor,
334                       const struct eap_gss_saml_attr_ctx *ctx,
335                       void *data,
336                       OM_uint32 (*addAttribute)(OM_uint32 *, void *, gss_buffer_t))
337 {
338     OM_uint32 major = GSS_S_COMPLETE;
339
340     if (ctx == NULL)
341         return GSS_S_COMPLETE;
342
343     for (vector<Attribute*>::const_iterator a = ctx->resCtx->getResolvedAttributes().begin();
344         a != ctx->resCtx->getResolvedAttributes().end();
345         ++a)
346     {
347         gss_buffer_desc attribute;
348
349         attribute.value = (void *)((*a)->getId());
350         attribute.length = strlen((char *)attribute.value);
351
352         major = addAttribute(minor, data, &attribute);
353         if (GSS_ERROR(major))
354             break;
355     }
356
357     return major;
358 }
359
360 OM_uint32
361 samlGetAttribute(OM_uint32 *minor,
362                  const struct eap_gss_saml_attr_ctx *ctx,
363                  gss_buffer_t attr,
364                  int *authenticated,
365                  int *complete,
366                  gss_buffer_t value,
367                  gss_buffer_t display_value,
368                  int *more)
369 {
370     OM_uint32 major;
371     Attribute *shibAttr = NULL;
372     gss_buffer_desc buf;
373
374     if (ctx == NULL)
375         return GSS_S_UNAVAILABLE;
376
377     for (vector<Attribute *>::const_iterator a = ctx->resCtx->getResolvedAttributes().begin();
378          a != ctx->resCtx->getResolvedAttributes().end();
379          ++a) {
380         for (vector<string>::const_iterator s = (*a)->getAliases().begin();
381              s != (*a)->getAliases().end();
382              ++s) {
383             if (attr->length == strlen((*s).c_str()) &&
384                 memcmp((*s).c_str(), attr->value, attr->length) == 0) {
385                 shibAttr = *a;
386                 break;
387             }
388         }
389         if (shibAttr != NULL)
390             break;
391     }
392
393     if (shibAttr == NULL)
394         return GSS_S_UNAVAILABLE;
395
396     if (*more == -1) {
397         *more = 0;
398     } else if (*more >= (int)shibAttr->valueCount()) {
399         *more = 0;
400         return GSS_S_COMPLETE;
401     }
402
403     buf.value = (void *)shibAttr->getString(*more);
404     buf.length = strlen((char *)buf.value);
405
406     major = duplicateBuffer(minor, &buf, value);
407     if (GSS_ERROR(major))
408         return major;
409  
410     *authenticated = TRUE;
411     *complete = FALSE;
412
413     return GSS_S_COMPLETE;
414 }
415
416 OM_uint32
417 samlSetAttribute(OM_uint32 *minor,
418                  struct eap_gss_saml_attr_ctx *ctx,
419                  int complete,
420                  gss_buffer_t attr,
421                  gss_buffer_t value)
422 {
423     return GSS_S_UNAVAILABLE;
424 }
425
426 OM_uint32
427 samlGetAssertion(OM_uint32 *minor,
428                  struct eap_gss_saml_attr_ctx *ctx,
429                  gss_buffer_t buffer)
430 {
431     if (ctx == NULL)
432         return GSS_S_UNAVAILABLE;
433
434     return duplicateBuffer(minor, &ctx->assertion, buffer);
435 }
436
437 OM_uint32
438 samlExportAttrContext(OM_uint32 *minor,
439                       struct eap_gss_saml_attr_ctx *ctx,
440                       gss_buffer_t buffer)
441 {
442     GSSEAP_NOT_IMPLEMENTED;
443 }
444
445 OM_uint32
446 samlImportAttrContext(OM_uint32 *minor,
447                       gss_buffer_t buffer,
448                       struct eap_gss_saml_attr_ctx **ppCtx)
449 {
450     GSSEAP_NOT_IMPLEMENTED;
451 }