1 /* zxidecp.c - Handwritten functions for implementing Enhanced Client Proxy and SP
2 * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 * Copyright (c) 2006-2008 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4 * Author: Sampo Kellomaki (sampo@iki.fi)
5 * This is confidential unpublished proprietary source code of the author.
6 * NO WARRANTY, not even implied warranties. Contains trade secrets.
7 * Distribution prohibited unless authorized in writing.
8 * Licensed under Apache License 2.0, see file COPYING.
9 * $Id: zxidecp.c,v 1.10 2008-10-08 03:56:55 sampo Exp $
10 *
11 * 12.8.2006, created --Sampo
12 * 16.1.2007, split from zxidlib.c --Sampo
13 * 7.10.2008, added documentation --Sampo
14 * 14.3.2010, reformed eid presentation --Sampo
15 *
16 * See: zxid_sp_soap_dispatch() in zxidslo.c for handling PAOS response
17 *
18 * If you do not know what PAOS, ECP or LECP means, you should read SAML2 Profiles specification.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include "platform.h"
24 #include "errmac.h"
25 #include "zxid.h"
26 #include "zxidpriv.h"
27 #include "zxidconf.h"
28 #include "saml2.h"
29 #include "c/zx-const.h"
30 #include "c/zx-paos-data.h"
31
32 extern char **environ;
33
34 /*() Generate SOAP headers for use with PAOS carried SAML2 ECP profile AuthnRequest.
35 *
36 * If you do not know what PAOS, ECP or LECP means, you should read [SAML2bind] specification. */
37
38 /* Called by: zxid_lecp_check */
zxid_mk_paos_Request_hdr(zxid_conf * cf)39 static struct zx_paos_Request_s* zxid_mk_paos_Request_hdr(zxid_conf* cf)
40 {
41 struct zx_paos_Request_s* hdr= zx_NEW_paos_Request(cf->ctx,0);
42 hdr->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
43 hdr->actor = zx_ref_attr(cf->ctx, &hdr->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
44 hdr->service = zx_ref_attr(cf->ctx, &hdr->gg, zx_service_ATTR, SAML2_SSO_ECP);
45 hdr->responseConsumerURL = zx_attrf(cf->ctx, &hdr->gg, zx_responseConsumerURL_ATTR, "%s?o=P", cf->burl);
46 /*hdr->messageID = zx_ref_str(cf->ctx, "1"); OPTIONAL */
47 return hdr;
48 }
49
50 /*() Build IDPList of IDPEntry(s) from the IdPs know to us at the moment (our CoT).
51 * Can be used for ECP and IdP proxying.
52 *
53 * cf:: ZXID configuration object, used to locate the CoT directory (PATH
54 * configuration option) and for memory allocation
55 * binding:: The SSO protocol binding the qualifying IdPs MUST support, or 0 if anything goes
56 * return:: IdP list data structure or 0 on failure */
57
58 /* Called by: zxid_mk_ecp_Request_hdr */
zxid_mk_idp_list(zxid_conf * cf,char * binding)59 static struct zx_sp_IDPList_s* zxid_mk_idp_list(zxid_conf* cf, char* binding)
60 {
61 zxid_entity* idp;
62 struct zx_md_SingleSignOnService_s* sso_svc;
63 struct zx_sp_IDPList_s* idp_list;
64 struct zx_sp_IDPEntry_s* idp_entry;
65
66 idp = zxid_load_cot_cache(cf);
67 if (!idp)
68 return 0;
69
70 idp_list = zx_NEW_sp_IDPList(cf->ctx,0);
71 for (; idp; idp = idp->n) {
72 D("IDPList consider idp(%s)", idp->eid);
73 if (!idp->ed->IDPSSODescriptor)
74 continue;
75 for (sso_svc = idp->ed->IDPSSODescriptor->SingleSignOnService;
76 sso_svc;
77 sso_svc = (struct zx_md_SingleSignOnService_s*)sso_svc->gg.g.n) {
78 if (sso_svc->gg.g.tok != zx_md_SingleSignOnService_ELEM)
79 continue;
80 if (sso_svc->Binding && !memcmp(binding, sso_svc->Binding->g.s, sso_svc->Binding->g.len))
81 break;
82 }
83 if (!sso_svc) {
84 D("Entity(%s) does not have any IdP SSO Service with binding(%s)", idp->eid, binding);
85 continue; /* Not eligible IdP, next one please. */
86 }
87
88 idp_list->IDPEntry = idp_entry = zx_NEW_sp_IDPEntry(cf->ctx, &idp_list->gg);
89 idp_entry->ProviderID = zx_ref_attr(cf->ctx, &idp_entry->gg, zx_ProviderID_ATTR, idp->eid);
90 idp_entry->Name = zx_ref_attr(cf->ctx, &idp_entry->gg, zx_Name_ATTR, idp->dpy_name);
91 idp_entry->Loc = sso_svc->Location;
92 }
93 return idp_list;
94 }
95
96 /*() Generate headers for use with Liberty ID-FF 1.2 LECP carried AuthnRequest.
97 *
98 * If you do not know what PAOS, ECP or LECP means, you should read [SAML2bind] specification. */
99
100 /* Called by: zxid_lecp_check */
zxid_mk_ecp_Request_hdr(zxid_conf * cf)101 static struct zx_ecp_Request_s* zxid_mk_ecp_Request_hdr(zxid_conf* cf)
102 {
103 struct zx_ecp_Request_s* hdr= zx_NEW_ecp_Request(cf->ctx,0);
104 hdr->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
105 hdr->actor = zx_ref_attr(cf->ctx, &hdr->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
106 /*hdr->IsPassive = zx_ref_attr(cf->ctx, &hdr->gg, zx_IsPassive_ATTR, XML_TRUE); OPTIONAL, default=? */
107 hdr->ProviderName = zxid_my_ent_id_attr(cf, &hdr->gg, zx_ProviderName_ATTR); /* *** Friendly name? */
108 hdr->Issuer = zxid_my_issuer(cf, &hdr->gg);
109 hdr->IDPList = zxid_mk_idp_list(cf, SAML2_SOAP);
110 return hdr;
111 }
112
113 /* ============== LECP or ECP ============== */
114
115 /*() Check for ECP indications in HTTP request headers and initiate
116 * PAOS based Single Sign On, i.e AuthnRequest. This is part of the
117 * SAML2 Enhanced Client Proxy profile.
118 *
119 * Limitation:: Current (2008) code only works in CGI environment due to
120 * reliance on environment variables. (*** fixme)
121 *
122 * If you do not know what PAOS, ECP or LECP means, you should read [SAML2bind] specification. */
123
124 /* Called by: covimp_test, main x4, zxid_simple_no_ses_cf x2 */
zxid_lecp_check(zxid_conf * cf,zxid_cgi * cgi)125 struct zx_str* zxid_lecp_check(zxid_conf* cf, zxid_cgi* cgi)
126 {
127 struct zx_e_Envelope_s* se;
128 struct zx_str* env;
129 struct zx_str* req;
130 char* le;
131 #if 0
132 char** pp;
133 for (pp = environ; *pp; ++pp) /* Debug envirnment problems, e.g. mini_httpd does not pass HTTP_PAOS, unless patched. */
134 D("ENV(%s)", *pp);
135 #endif
136
137 le = getenv("HTTP_PAOS");
138 if (!le) {
139 le = getenv("HTTP_LIBERTY_ENABLED");
140 if (!le) {
141 D("Neither ECP nor LECP request %d", 0);
142 return 0; /* No ECP/LECP detected, return to normal processing. */
143 }
144 D("LECP detected HTTP_LIBERTY_ENABLED(%s) (*** NOT IMPLEMENTED)", le);
145 /* *** start ID-FF 1.2 LECP processing */
146 return 0; /* ars = ...; */
147 }
148 D("ECP detected HTTP_PAOS(%s)", le);
149 if (!strstr(le, SAML2_SSO_ECP))
150 return 0;
151
152 /* SAML 2.0 ECP: Create PAOS request to be sent in HTTP response. */
153
154 se = zx_NEW_e_Envelope(cf->ctx,0);
155 se->Body = zx_NEW_e_Body(cf->ctx, &se->gg);
156 se->Body->AuthnRequest = zxid_mk_authn_req(cf, cgi);
157 se->Header = zx_NEW_e_Header(cf->ctx, &se->gg);
158 se->Header->Request = zxid_mk_paos_Request_hdr(cf);
159 se->Header->ecp_Request = zxid_mk_ecp_Request_hdr(cf);
160 env = zx_easy_enc_elem_opt(cf, &se->gg);
161 req = zx_strf(cf->ctx,
162 "Cache-Control: no-cache, no-store, must-revalidate, private" CRLF
163 "Pragma: no-cache" CRLF
164 "Content-Type: " PAOS_CONTENT CRLF /* content type only specified for ECP */
165 "Content-Length: %d" CRLF2
166 "%.*s", env->len, env->len, env->s);
167 zx_str_free(cf->ctx, env);
168 return req;
169 }
170
171 /* EOF -- zxidecp.c */
172