1 /* zxidloc.c  -  Handwritten functions implementing service locator (based on metadata)
2  * Copyright (c) 2006-2008 Symlabs (symlabs@symlabs.com), All Rights Reserved.
3  * Author: Sampo Kellomaki (sampo@iki.fi)
4  * This is confidential unpublished proprietary source code of the author.
5  * NO WARRANTY, not even implied warranties. Contains trade secrets.
6  * Distribution prohibited unless authorized in writing.
7  * Licensed under Apache License 2.0, see file COPYING.
8  * $Id: zxidloc.c,v 1.13 2010-01-08 02:10:09 sampo Exp $
9  *
10  * 12.8.2006, created --Sampo
11  * 16.1.2007, split from zxidlib.c --Sampo
12  * 7.10.2008, added documentation --Sampo
13  */
14 
15 #include <string.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include "platform.h"
19 #include "errmac.h"
20 #include "zxid.h"
21 #include "zxidutil.h"
22 #include "zxidconf.h"
23 #include "saml2.h"
24 #include "c/zx-data.h"
25 
26 /* err_res = "ERR-metadata-does-not-have-url-for-binding";*/
27 
28 /* ============== IdP Service Locator ============= */
29 
30 /* *** figure out a way to leverage commonality. */
31 
32 /*() Raw computation of IdP URL given service type, binding, and whether operation is a
33  * request. See zxid_idp_loc() for full description. */
34 
35 /* Called by:  zxid_idp_loc x3, zxid_slo_resp_redir, zxid_sp_dispatch */
zxid_idp_loc_raw(zxid_conf * cf,zxid_cgi * cgi,zxid_entity * idp_meta,int svc_type,char * binding,int req)36 struct zx_str* zxid_idp_loc_raw(zxid_conf* cf, zxid_cgi* cgi,
37 				zxid_entity* idp_meta, int svc_type, char* binding, int req)
38 {
39   struct zx_str* loc;
40   struct zx_md_SingleLogoutService_s* slo_svc;
41   struct zx_md_ManageNameIDService_s* mni_svc;
42 
43   if (!idp_meta || !idp_meta->eid || !idp_meta->ed->IDPSSODescriptor) {
44     ERR("Entity(%s) does not have IdP SSO Descriptor (metadata problem)", idp_meta?STRNULLCHKQ(idp_meta->eid):"-");
45     return 0;
46   }
47 
48   switch (svc_type) {
49   case ZXID_SLO_SVC:
50     for (slo_svc = idp_meta->ed->IDPSSODescriptor->SingleLogoutService;
51 	 slo_svc;
52 	 slo_svc = (struct zx_md_SingleLogoutService_s*)slo_svc->gg.g.n) {
53       if (slo_svc->gg.g.tok != zx_md_SingleLogoutService_ELEM)
54 	continue;
55       if (slo_svc->Binding  && !memcmp(binding, slo_svc->Binding->g.s, slo_svc->Binding->g.len)
56 	  /*&& svc->index && !memcmp(end_pt_ix, svc->index->s, svc->index->len)*/
57 	  && slo_svc->Location)
58 	break;
59     }
60     if (!slo_svc)
61       break;
62     loc = req ? &slo_svc->Location->g : (slo_svc->ResponseLocation ? &slo_svc->ResponseLocation->g : &slo_svc->Location->g);
63     if (!loc)
64       break;
65     return loc;
66   case ZXID_MNI_SVC:
67     for (mni_svc = idp_meta->ed->IDPSSODescriptor->ManageNameIDService;
68 	 mni_svc;
69 	 mni_svc = (struct zx_md_ManageNameIDService_s*)mni_svc->gg.g.n) {
70       if (mni_svc->gg.g.tok != zx_md_ManageNameIDService_ELEM)
71 	continue;
72       if (mni_svc->Binding  && !memcmp(binding, mni_svc->Binding->g.s, mni_svc->Binding->g.len)
73 	  /*&& svc->index && !memcmp(end_pt_ix, svc->index->s, svc->index->len)*/
74 	  && mni_svc->Location)
75 	break;
76     }
77     if (!mni_svc)
78       break;
79     loc = req ? &mni_svc->Location->g : (mni_svc->ResponseLocation ? &mni_svc->ResponseLocation->g : &mni_svc->Location->g);
80     if (!loc)
81       break;
82     return loc;
83   }
84 
85   ERR("IdP Entity(%s) does not have any %d service with binding(%s) (metadata problem)", idp_meta->eid, svc_type, binding);
86   return 0;
87 }
88 
89 /*() SAML2 service locator. Given desired service, like SLO or MNI, and possibly binding,
90  * locate the appropriate service descriptor from the IdP metadata.
91  *
92  * cf:: ZXID configuration object, used for preferences and for memory allocation
93  * cgi:: May contain CGI variables that further indicate preference. Often specified
94  *     as 0 (no preference).
95  * ses:: Session object, which may be used to remember historical events, such as
96  *     binding of SSO transaction, that may act as preferences for binding. The
97  *     session MUST have assertion.
98  * idp_meta:: Metadata for the IdP
99  * svc_type:: The desired service, indicated as URN
100  * binding:: preferred binding URN, or 0 if no preference. In that case the built in
101  *     preference is used, or if that is indifferent, then first applicable metadata
102  *     item is picked. If IdP only supports one binding 0 will match that. If nonzero,
103  *     then the IdP metadata MUST have exactly matching entry or else 0 is returned.
104  * return:: URL for accessing the service or 0 upon failure
105  *
106  * *Limitation:* If binding is not specified, it may be ambiguous what binding the returned
107  * URL relates to. Generally the decision will have been taken prior to calling
108  * this function. */
109 
110 /* Called by:  zxid_idp_soap, zxid_sp_mni_redir, zxid_sp_slo_redir */
zxid_idp_loc(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,zxid_entity * idp_meta,int svc_type,char * binding)111 struct zx_str* zxid_idp_loc(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses,
112 			    zxid_entity* idp_meta, int svc_type, char* binding)
113 {
114   zxid_get_ses_sso_a7n(cf, ses);
115 
116   if (ses->a7n) {
117     return zxid_idp_loc_raw(cf, cgi, idp_meta, svc_type, binding, 1);
118   }
119   if (ses->a7n11) {
120     ERR("Not implemented: obtaining location from SAML 1.1 assetion %d", 0);
121     //return zxid_idp_loc_raw(cf, cgi, ses->a7n->Issuer, svc_type, binding, 1);
122   }
123   if (ses->a7n12) {
124     ERR("Not implemented: obtaining location from ID-FF 1.2 type SAML 1.1 assetion %d", 0);
125     //return zxid_idp_loc_raw(cf, cgi, ses->a7n->Issuer, svc_type, binding, 1);
126   }
127 
128   ERR("Session sid(%s) appears to lack SSO assertion.", ses->sid);
129   return 0;
130 }
131 
132 /*() Deternine URL for SOAP binding to given service and perform a SOAP call.
133  *
134  * cf:: ZXID configuration object
135  * cgi:: CGI variables that may influence determination of end point. Or 0 if no preference.
136  * ses:: Session information that may influence the choice of the end point. The
137  *     session MUST have asserion.
138  * idp_meta:: Metadata for the IdP
139  * svc_type:: The desired service, indicated as URN
140  * body:: XML data structure for the SOAP call <Body> element payload
141  * return:: XML data structure for Body element of the SOAP call response. */
142 
143 /* Called by:  zxid_az_soap, zxid_sp_mni_soap, zxid_sp_slo_soap */
zxid_idp_soap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,zxid_entity * idp_meta,int svc_type,struct zx_e_Body_s * body)144 struct zx_root_s* zxid_idp_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses,
145 				zxid_entity* idp_meta, int svc_type, struct zx_e_Body_s* body)
146 {
147   struct zx_root_s* r;
148   struct zx_str* loc = zxid_idp_loc(cf, cgi, ses, idp_meta, svc_type, SAML2_SOAP);
149   if (!loc)
150     return 0;
151   r = zxid_soap_call_hdr_body(cf, loc, 0, body);
152   zx_str_free(cf->ctx, loc);
153   return r;
154 }
155 
156 /* ============== SP Service Locator ============= */
157 
158 /* *** figure out a way to leverage commonality. */
159 
160 /* Called by:  zxid_idp_sso */
zxid_sp_loc_by_index_raw(zxid_conf * cf,zxid_cgi * cgi,zxid_entity * sp_meta,int svc_type,struct zx_str * ix,int * binding)161 struct zx_str* zxid_sp_loc_by_index_raw(zxid_conf* cf, zxid_cgi* cgi,
162 					zxid_entity* sp_meta, int svc_type,
163 					struct zx_str* ix, int* binding)
164 {
165   struct zx_str* loc;
166   struct zx_md_AssertionConsumerService_s* acs_svc;
167 
168   if (!sp_meta || !sp_meta->eid || !sp_meta->ed->SPSSODescriptor) {
169     ERR("Entity(%s) does not have SP SSO Descriptor (metadata problem)", sp_meta?STRNULLCHKQ(sp_meta->eid):"-");
170     return 0;
171   }
172 
173   switch (svc_type) {
174   case ZXID_ACS_SVC:
175     for (acs_svc = sp_meta->ed->SPSSODescriptor->AssertionConsumerService;
176 	 acs_svc;
177 	 acs_svc = (struct zx_md_AssertionConsumerService_s*)acs_svc->gg.g.n) {
178       if (acs_svc->gg.g.tok != zx_md_AssertionConsumerService_ELEM)
179 	continue;
180       if (acs_svc->index && ix->len == acs_svc->index->g.len
181 	  && !memcmp(ix->s, acs_svc->index->g.s, ix->len)
182 	  && acs_svc->Location)
183 	break;
184     }
185     if (!acs_svc)
186       break;
187     loc = &acs_svc->Location->g;
188     if (!loc)
189       break;
190     *binding = zxid_protocol_binding_map_saml2(&acs_svc->Binding->g);
191     return loc;
192   }
193 
194   ERR("SP Entity(%s) does not have any %d service with index(%.*s) (metadata problem)", sp_meta->eid, svc_type, ix->len, ix->s);
195   *binding = 0;
196   return 0;
197 }
198 
199 
200 /*() Raw computation of SP URL given service type, binding, and whether operation is a
201  * request. See zxid_sp_loc() for full description.
202  *
203  * return:: URL for the protocol end point, or 0 on failure */
204 
205 /* Called by:  zxid_idp_dispatch, zxid_idp_sso x2, zxid_oauth2_az_server_sso, zxid_slo_resp_redir, zxid_sp_loc x3 */
zxid_sp_loc_raw(zxid_conf * cf,zxid_cgi * cgi,zxid_entity * sp_meta,int svc_type,char * binding,int req)206 struct zx_str* zxid_sp_loc_raw(zxid_conf* cf, zxid_cgi* cgi, zxid_entity* sp_meta, int svc_type, char* binding, int req)
207 {
208   struct zx_str* loc;
209   struct zx_md_SingleLogoutService_s* slo_svc;
210   struct zx_md_ManageNameIDService_s* mni_svc;
211   struct zx_md_AssertionConsumerService_s* acs_svc;
212 
213   if (!sp_meta || !sp_meta->eid || !sp_meta->ed->SPSSODescriptor) {
214     ERR("Entity(%s) does not have SP SSO Descriptor (metadata problem)", sp_meta?STRNULLCHKQ(sp_meta->eid):"-");
215     return 0;
216   }
217 
218   switch (svc_type) {
219   case ZXID_SLO_SVC:
220     for (slo_svc = sp_meta->ed->SPSSODescriptor->SingleLogoutService;
221 	 slo_svc;
222 	 slo_svc = (struct zx_md_SingleLogoutService_s*)slo_svc->gg.g.n) {
223       if (slo_svc->gg.g.tok != zx_md_SingleLogoutService_ELEM)
224 	continue;
225       if (slo_svc->Binding  && !memcmp(binding, slo_svc->Binding->g.s, slo_svc->Binding->g.len)
226 	  /*&& svc->index && !memcmp(end_pt_ix, svc->index->s, svc->index->len)*/
227 	  && slo_svc->Location)
228 	break;
229     }
230     if (!slo_svc)
231       break;
232     loc = req ? &slo_svc->Location->g : (slo_svc->ResponseLocation ? &slo_svc->ResponseLocation->g : &slo_svc->Location->g);
233     if (!loc)
234       break;
235     return loc;
236   case ZXID_MNI_SVC:
237     for (mni_svc = sp_meta->ed->SPSSODescriptor->ManageNameIDService;
238 	 mni_svc;
239 	 mni_svc = (struct zx_md_ManageNameIDService_s*)mni_svc->gg.g.n) {
240       if (mni_svc->gg.g.tok != zx_md_ManageNameIDService_ELEM)
241 	continue;
242       if (mni_svc->Binding  && !memcmp(binding, mni_svc->Binding->g.s, mni_svc->Binding->g.len)
243 	  /*&& svc->index && !memcmp(end_pt_ix, svc->index->s, svc->index->len)*/
244 	  && mni_svc->Location)
245 	break;
246     }
247     if (!mni_svc)
248       break;
249     loc = req ? &mni_svc->Location->g : (mni_svc->ResponseLocation ? &mni_svc->ResponseLocation->g : &mni_svc->Location->g);
250     if (!loc)
251       break;
252     return loc;
253   case ZXID_ACS_SVC:
254     for (acs_svc = sp_meta->ed->SPSSODescriptor->AssertionConsumerService;
255 	 acs_svc;
256 	 acs_svc = (struct zx_md_AssertionConsumerService_s*)acs_svc->gg.g.n) {
257       if (acs_svc->gg.g.tok != zx_md_AssertionConsumerService_ELEM)
258 	continue;
259       if (acs_svc->Binding  && !memcmp(binding, acs_svc->Binding->g.s, acs_svc->Binding->g.len)
260 	  /*&& svc->index && !memcmp(end_pt_ix, svc->index->s, svc->index->len)*/
261 	  && acs_svc->Location)
262 	break;
263     }
264     if (!acs_svc)
265       break;
266     loc = &acs_svc->Location->g;
267     if (!loc)
268       break;
269     return loc;
270   }
271 
272   ERR("SP Entity(%s) does not have any %d service with binding(%s) (metadata problem)", sp_meta->eid, svc_type, binding);
273   return 0;
274 }
275 
276 /*() SAML2 service locator for SP. Given desired service, like SLO or MNI, and possibly binding,
277  * locate the appropriate service descriptor from the Sp metadata.
278  *
279  * cf:: ZXID configuration object, used for preferences and for memory allocation
280  * cgi:: May contain CGI variables that further indicate preference. Often specified
281  *     as 0 (no preference).
282  * ses:: Session object, which may be used to remember historical events, such as
283  *     binding of SSO transaction, that may act as preferences for binding. The
284  *     session MUST have assertion.
285  * sp_meta:: Metadata for the Sp
286  * svc_type:: The desired service, indicated as URN
287  * binding:: preferred binding URN, or 0 if no preference. In that case the built in
288  *     preference is used, or if that is indifferent, then first applicable metadata
289  *     item is picked. If Sp only supports one binding 0 will match that. If nonzero,
290  *     then the Sp metadata MUST have exactly matching entry or else 0 is returned.
291  * return:: URL for accessing the service or 0 upon failure
292  *
293  * *Limitation:* If binding is not specified, it may be ambiguous what binding the returned
294  * URL relates to. Generally the decision will have been taken prior to calling
295  * this function. */
296 
297 /* Called by:  zxid_sp_soap */
zxid_sp_loc(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,zxid_entity * sp_meta,int svc_type,char * binding)298 struct zx_str* zxid_sp_loc(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, zxid_entity* sp_meta, int svc_type, char* binding)
299 {
300   zxid_get_ses_sso_a7n(cf, ses);
301 
302   if (ses->a7n) {
303     return zxid_sp_loc_raw(cf, cgi, sp_meta, svc_type, binding, 1);
304   }
305   if (ses->a7n11) {
306     ERR("Not implemented: obtaining location from SAML 1.1 assetion %d", 0);
307     //return zxid_sp_loc_raw(cf, cgi, ses->a7n->Issuer, svc_type, binding, 1);
308   }
309   if (ses->a7n12) {
310     ERR("Not implemented: obtaining location from ID-FF 1.2 type SAML 1.1 assetion %d", 0);
311     //return zxid_sp_loc_raw(cf, cgi, ses->a7n->Issuer, svc_type, binding, 1);
312   }
313 
314   ERR("Session sid(%s) appears to lack SSO assertion.", ses->sid);
315   return 0;
316 }
317 
318 /*() Deternine URL for SOAP binding to given service on SP and perform a SOAP call.
319  *
320  * cf:: ZXID configuration object
321  * cgi:: CGI variables that may influence determination of end point. Or 0 if no preference.
322  * ses:: Session information that may influence the choice of the end point. The
323  *     session MUST have asserion.
324  * sp_meta:: Metadata for the Sp
325  * svc_type:: The desired service, indicated as URN
326  * body:: XML data structure for the SOAP call <Body> element payload
327  * return:: XML data structure for Body element of the SOAP call response. */
328 
329 /* Called by: */
zxid_sp_soap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,zxid_entity * sp_meta,int svc_type,struct zx_e_Body_s * body)330 struct zx_root_s* zxid_sp_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, zxid_entity* sp_meta, int svc_type, struct zx_e_Body_s* body)
331 {
332   struct zx_root_s* r;
333   struct zx_str* loc = zxid_sp_loc(cf, cgi, ses, sp_meta, svc_type, SAML2_SOAP);
334   if (!loc)
335     return 0;
336   r = zxid_soap_call_hdr_body(cf, loc, 0, body);
337   zx_str_free(cf->ctx, loc);
338   return r;
339 }
340 
341 /* EOF  --  zxidloc.c */
342