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