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