1 /* zxidepr.c  -  Handwritten functions for client side EPR and bootstrap handling
2  * Copyright (c) 2012-2014 Synergetics NV (sampo@synergetics.be), All Rights Reserved.
3  * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * Copyright (c) 2007-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
5  * Author: Sampo Kellomaki (sampo@iki.fi)
6  * This is confidential unpublished proprietary source code of the author.
7  * NO WARRANTY, not even implied warranties. Contains trade secrets.
8  * Distribution prohibited unless authorized in writing.
9  * Licensed under Apache License 2.0, see file COPYING.
10  * $Id: zxidepr.c,v 1.19 2009-11-29 12:23:06 sampo Exp $
11  *
12  * 5.2.2007,  created --Sampo
13  * 7.10.2008, added documentation --Sampo
14  * 22.4.2012, fixed folding EPR names (to avoid folding comma) --Sampo
15  * 7.12.2013, added EPR ranking --Sampo
16  * 27.5.2014, improved nth progessing in zxid_find_epr() --Sampo
17  *
18  * See also: zxidsimp.c (attributes to LDIF), and zxida7n.c (general attribute querying)
19  *
20  * N.B. Like session storage, the epr cache makes case preserving assumption about
21  * underlying filesystem. Case insensitive filesystem will insignificantly increase
22  * chances of naming collitions.
23  *
24  * See also zxiddi.c for discovery server code.
25  */
26 
27 #include "platform.h"  /* for dirent.h */
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <errno.h>
35 
36 #include "errmac.h"
37 #include "zxid.h"
38 #include "zxidpriv.h"
39 #include "zxidutil.h"
40 #include "zxidconf.h"
41 #include "saml2.h"
42 #include "c/zx-ns.h"
43 #include "c/zx-a-data.h"
44 
45 /*() Fold service type (or any URN or URL) to file name. Typically
46  * a service is represented by cached EPR file in session directory
47  * or in /var/zxid/dimd/ directory in the IdP case. This file name
48  * will have comma separated structure:
49  *   FOLDEDSVCTYP,RANKKEY,NICE_SHA1_OF_CONTENTS
50  * This function computes the first component of the comma separated structure. */
51 
52 /* Called by:  zxid_di_query, zxid_find_epr, zxid_nice_sha1, zxid_reg_svc */
zxid_fold_svc(char * svctyp,int len)53 void zxid_fold_svc(char* svctyp, int len)
54 {
55   for (; *svctyp && len; ++svctyp, --len)
56     if (ONE_OF_6(*svctyp, ':','/',',','?','&','='))
57       *svctyp = '_';
58 }
59 
60 /*() Compute (and fold) unique nice sha1 name according to NAME,SHA1
61  *
62  * This name format is designed to ensure unique name, while
63  * maintainting human (=sysadmin) readability. This is
64  * useful in the common case where WSC wants to call a specific type of web service.
65  *
66  * cf::  ZXID configuration object, also used for memory allocation
67  * buf:: result parameter. The buffer, which must have been allocated, will be
68  *     modified to have the path. The path will be nul terminated.
69  * buf_len:: The length of the buf (including nul termination), usually sizeof(buf)
70  * name:: Often Service type name or SP Entity ID
71  * cont:: content of EPR or the SP EntityID, used to compute sha1 hash that becomes part
72  *     of the file name
73  * ign_prefix:: How many characters to ignore from beginning of name: 0 or 7 (http://)
74  * return:: 0 on success (the real return value is returned via ~buf~ result parameter) */
75 
76 /* Called by: */
zxid_nice_sha1(zxid_conf * cf,char * buf,int buf_len,struct zx_str * name,struct zx_str * cont,int ign_prefix)77 int zxid_nice_sha1(zxid_conf* cf, char* buf, int buf_len, struct zx_str* name, struct zx_str* cont, int ign_prefix)
78 {
79   int len = MAX(name->len - ign_prefix, 0);
80   char sha1_cont[28];
81   sha1_safe_base64(sha1_cont, cont->len, cont->s);
82   sha1_cont[27] = 0;
83   snprintf(buf, buf_len, "%.*s,%s", len, name->s+ign_prefix, sha1_cont);
84   buf[buf_len-1] = 0; /* must terminate manually as on win32 termination is not guaranteed */
85   zxid_fold_svc(buf, len);
86   return 0;
87 }
88 
89 /*() Compute (and fold) unique EPR name according to /var/zxid/ses/SESID/SVC,RANK,SHA1
90  *
91  * This name format is designed to ensure unique name for each EPR, while
92  * also making it easy to determine the service type and rank from the name. This is
93  * useful in the common case where WSC wants to call a specific type of web service.
94  *
95  * cf::  ZXID configuration object, also used for memory allocation
96  * dir:: Directory, such as "ses/"
97  * sid:: Session ID whose EPR cache the file is/will be located
98  * buf:: result parameter. The buffer, which must have been allocated, will be
99  *     modified to have the path. The path will be nul terminated.
100  * buf_len:: The length of the buf (including nul termination), usually sizeof(buf)
101  * svc:: Service type name
102  * rank:: Ranking key, used to ensure ordering of EPRs in discovery
103  * cont:: content of EPR, used to compute sha1 hash that becomes part of the file name
104  * return:: 0 on success (the real return value is returned via ~buf~ result parameter)
105  *
106  * N.B. This function relies on specific, ANSI documented, functioning
107  * of snprintf(3) library function. Unfortunately, it has been found that
108  * on some platforms this function only works correctly in the 'C' locale. If
109  * you suspect this to be the case, you may want to try
110  *
111  *    export LANG=C
112  *
113  * especially if you get errors about multibyte characters. */
114 
115 /* Called by:  zxid_cache_epr, zxid_snarf_eprs_from_ses */
zxid_epr_path(zxid_conf * cf,char * dir,char * sid,char * buf,int buf_len,struct zx_str * svc,int rank,struct zx_str * cont)116 int zxid_epr_path(zxid_conf* cf, char* dir, char* sid, char* buf, int buf_len, struct zx_str* svc, int rank, struct zx_str* cont)
117 {
118   int len = svc->len;
119   char sha1_cont[28];
120   sha1_safe_base64(sha1_cont, cont->len, cont->s);
121   sha1_cont[27] = 0;
122 
123   len = snprintf(buf, buf_len, "%s%s%s/", cf->cpath, dir, sid);
124   if (len <= 0) {
125     platform_broken_snprintf(len, __FUNCTION__, buf_len, "%s%s%s/");
126     if (buf && buf_len > 0)
127       buf[0] = 0;
128     return 1;
129   }
130   buf[buf_len-1] = 0; /* must terminate manually as on win32 termination is not guaranteed */
131   buf += len;
132   buf_len -= len;
133 
134   if (buf_len < svc->len + 1 + 4 + 1 + sizeof(sha1_cont)) {
135     ERR("buf too short buf_len=%ld need=%ld svc(%.*s)", (long)buf_len, svc->len + 1 + 4 + 1 + sizeof(sha1_cont), svc->len, svc->s);
136     return 1;
137   }
138   memcpy(buf, svc->s, svc->len);
139   zxid_fold_svc(buf, svc->len);
140   buf += svc->len;
141   buf_len -= svc->len;
142 
143   len = snprintf(buf, buf_len, ",%04d,%s", rank, sha1_cont);
144   if (len <= 0) {
145     platform_broken_snprintf(len, __FUNCTION__, buf_len, ",%04d,%s");
146     if (buf && buf_len > 0)
147       buf[0] = 0;
148     return 1;
149   }
150   buf[buf_len-1] = 0; /* must terminate manually as on win32 termination is not guaranteed */
151   return 0;
152 }
153 
154 /*() Serialize EPR data structure to XML and write it to session's EPR cache under
155  * file name that is both unique and indicates the service type and ranking.
156  *
157  * cf:: ZXID configuration object, also used for memory allocation
158  * ses:: Session object in whose EPR cache the file will be located
159  * epr:: XML data structure representing the EPR
160  * return:: 1 on success, 0 on failure
161  *
162  * Known bug:: If an EPR is meant to substitute a previously discovered
163  *     one, it will not as the content (and possibly rank) will be different,
164  *     thus causing the new EPR to have different name so it will not overwrite
165  *     the old one. Perhaps the simple sha1 hash of the content is not the
166  *     right solution. Better sha1 the svctype+eid+epurl? */
167 
168 /* Called by:  main, zxid_get_epr, zxid_snarf_eprs */
zxid_cache_epr(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr,int rank)169 int zxid_cache_epr(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, int rank)
170 {
171   fdtype fd;
172   struct zx_str* ss;
173   char path[ZXID_MAX_BUF];
174 
175   if (!ses || !ses->sid || !ses->sid[0]) {
176     ERR("Valid session required %p", ses);
177     return 0;
178   }
179   if (!epr || !epr->Metadata || !epr->Metadata->ServiceType) {
180     ERR("EPR is not a ID-WSF 2.0 Bootstrap: no Metadata %p", epr);
181     return 0;
182   }
183   ss = zx_easy_enc_elem_opt(cf, &epr->gg);
184   if (!ss) {
185     ERR("Encoding EndpointReference failed %p", epr);
186     return 0;
187   }
188 
189   // *** respect rank and detect cache duplicates
190   zxid_epr_path(cf, ZXID_SES_DIR, ses->sid, path, sizeof(path),
191 		ZX_GET_CONTENT(epr->Metadata->ServiceType), rank, ss);
192   //fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0666);
193   fd = open_fd_from_path(O_CREAT | O_WRONLY | O_TRUNC, 0666, "zxid_cache_epr", 1, "%s", path);
194   if (fd == BADFD) {
195     perror("open for write cache_epr");
196     ERR("EPR path(%s) creation failed", path);
197   } else if (write_all_fd(fd, ss->s, ss->len) == -1) {
198     perror("Trouble writing EPR");
199   }
200   close_file(fd, (const char*)__FUNCTION__);
201   zx_str_free(cf->ctx, ss);
202   return 1;
203 }
204 
205 /*() Look into attribute statements of a SSO assertion and extract anything
206  * that looks like EPR, storing results in the session for later reference.
207  *
208  * cf:: ZXID configuration object, also used for memory allocation
209  * ses:: Session object in whose EPR cache will be populated
210  *
211  * N.B. This approach ignores the official attribute names totally. Anything
212  * that looks like an EPR and that is strcturally in right place will work.
213  * Typical name /var/zxid/ses/SESID/SVCTYPE,SHA1 */
214 
215 /* Called by:  zxid_as_call_ses, zxid_snarf_eprs_from_ses */
zxid_snarf_eprs(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr)216 void zxid_snarf_eprs(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr)
217 {
218   struct zx_str* ss;
219   struct zx_str* urlss;
220   int wsf20 = 0;
221   if (!epr)
222     return;
223   for (; epr; epr = (zxid_epr*)epr->gg.g.n) {
224     if (epr->gg.g.tok != zx_a_EndpointReference_ELEM)
225       continue;
226     ss = ZX_GET_CONTENT(epr->Metadata->ServiceType);
227     urlss = ZX_GET_CONTENT(epr->Address);
228     D("%d: EPR svc(%.*s) url(%.*s)", wsf20, ss?ss->len:0, ss?ss->s:"", urlss?urlss->len:0, urlss?urlss->s:"");
229     if (zxid_cache_epr(cf, ses, epr, wsf20)) {
230       ++wsf20;
231       D("%d: EPR cached svc(%.*s) url(%.*s)", wsf20, ss?ss->len:0, ss?ss->s:"", urlss?urlss->len:0, urlss?urlss->s:"");
232     }
233   }
234   D("TOTAL wsf20 EPRs snarfed: %d", wsf20);
235 }
236 
237 /*() Look into attribute statements of a SSO assertion and extract anything
238  * that looks like EPR, storing results in the session for later reference.
239  *
240  * cf:: ZXID configuration object, also used for memory allocation
241  * ses:: Session object in whose EPR cache will be populated
242  *
243  * N.B. This approach ignores the official attribute names totally. Anything
244  * that looks like an EPR and that is structurally in right place will work.
245  * Typical name /var/zxid/ses/SESID/SVCTYPE,SHA1 */
246 
247 /* Called by:  zxid_sp_anon_finalize, zxid_sp_sso_finalize, zxid_wsc_valid_re_env, zxid_wsp_validate_env */
zxid_snarf_eprs_from_ses(zxid_conf * cf,zxid_ses * ses)248 void zxid_snarf_eprs_from_ses(zxid_conf* cf, zxid_ses* ses)
249 {
250   struct zx_sa_AttributeStatement_s* as;
251   struct zx_sa_Attribute_s* at;
252   struct zx_sa_AttributeValue_s* av;
253   int wsf11 = 0;
254 
255   D_INDENT("snarf_eprs: ");
256   zxid_get_ses_sso_a7n(cf, ses);
257   if (ses->a7n) {
258     for (as = ses->a7n->AttributeStatement; as;
259 	 as = (struct zx_sa_AttributeStatement_s*)as->gg.g.n) {
260       if (as->gg.g.tok != zx_sa_AttributeStatement_ELEM)
261 	continue;
262       for (at = as->Attribute; at; at = (struct zx_sa_Attribute_s*)at->gg.g.n) {
263 	if (at->gg.g.tok != zx_sa_Attribute_ELEM)
264 	  continue;
265 	for (av = at->AttributeValue; av; av = (struct zx_sa_AttributeValue_s*)av->gg.g.n) {
266 	  if (av->gg.g.tok != zx_sa_AttributeValue_ELEM)
267 	    continue;
268 	  zxid_snarf_eprs(cf, ses, av->EndpointReference);
269 	  if (av->ResourceOffering) {
270 	    ++wsf11;
271 	    D("Detected wsf11 resource offering. %d", wsf11);
272 #if 0
273 	    ss = zx_easy_enc_elem_opt(cf, &av->ResourceOffering->gg);
274 	    zxid_epr_path(cf, ZXID_SES_DIR, ses->sid, path, sizeof(path),
275 			  ZX_GET_CONTENT(av->EndpointReference->Metadata->ServiceType), ss);
276 	    fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0666);
277 	    if (fd == -1) {
278 	      perror("open for write epr");
279 	      ERR("EPR path(%s) creation failed", path);
280 	    } else if (write_all_fd(fd, ss->s, ss->len) == -1) {
281 	      perror("Trouble writing EPR");
282 	      close__file(fd, __FUNCTION__);
283 	    }
284 	    zx_str_free(cf->ctx, ss);
285 #endif
286 	  }
287 	}
288       }
289     }
290   }
291 #if 0
292   if (ses->a7n12) {
293     for (as = ses->a7n->AttributeStatement; as;
294 	 as = (struct zx_sa11_AttributeStatement_s*)as->gg.g.n) {
295       if (as->gg.g.tok != zx_sa11_AttributeStatement_ELEM)
296 	continue;
297       for (at = as->Attribute; at; at = (struct zx_sa11_Attribute_s*)at->gg.g.n) {
298 	if (at->gg.g.tok != zx_sa11_Attribute_ELEM)
299 	  continue;
300 	for (av = at->AttributeValue; av; av = (struct zx_sa11_AttributeValue_s*)av->gg.g.n) {
301 	  if (av->gg.g.tok != zx_sa11_AttributeValue_ELEM)
302 	    continue;
303 	}
304       }
305     }
306   }
307 #endif
308   D_DEDENT("snarf_eprs: ");
309 }
310 
311 /*() Search the EPRs cached under the session for a match. First the directory is searched
312  * for files whose name starts by service type. These files are opened and parsed
313  * as EPR and further checks are made. The nth match is returned. 1 means first.
314  * Typical filename: /var/zxid/ses/SESID/SVCTYPE,RANK,SHA1
315  *
316  * cf:: ZXID configuration object, also used for memory allocation
317  * ses:: Session object in whose EPR cache the file is searched
318  * svc:: Service type (usually a URN)
319  * url:: (Optional) If provided, this argument has to match either
320  *     the ProviderID, EntityID, or actual service endpoint URL.
321  * di_opt:: (Optional) Additional discovery options for selecting the service, query string format
322  * action:: (Optional) The action, or method, that must be invocable on the service (default: any)
323  * n:: How manieth matching instance is returned. 1 means first
324  * return:: EPR data structure (or linked list of EPRs) on success, 0 on failure
325  *
326  * See also: zxid_print_session() in zxcall.c and zxid_di_query() in zxiddi.c */
327 
328 /* Called by:  main x2, zxid_get_epr x3 */
zxid_find_epr(zxid_conf * cf,zxid_ses * ses,const char * svc,const char * url,const char * di_opt,const char * action,int nth)329 zxid_epr* zxid_find_epr(zxid_conf* cf, zxid_ses* ses, const char* svc, const char* url, const char* di_opt, const char* action, int nth)
330 {
331   struct zx_root_s* r;
332   struct zx_str* ss;
333   struct zx_str* pi;
334   int len, epr_len, iter = 0;
335   char path[ZXID_MAX_BUF];
336   char* epr_buf;  /* MUST NOT come from stack. */
337   DIR* dir;
338   struct dirent * de;
339   zxid_epr* found = 0;  /* List of found eligible EPRs for sorting and nth selection */
340   zxid_epr* epr = 0;
341   zxid_epr* nxt;
342   struct zx_a_Metadata_s* md = 0;
343   D_INDENT("find_epr: ");
344 
345   if (!svc || !*svc) {
346     /* *** Relax this to allow discovery of multiple or all service types */
347     ERR("Must supply service type %p", svc);
348     D_DEDENT("find_epr: ");
349     return 0;
350   }
351 
352   if (!name_from_path(path, sizeof(path), "%s" ZXID_SES_DIR "%s", cf->cpath, ses->sid)) {
353     D_DEDENT("find_epr: ");
354     return 0;
355   }
356 
357   D("Looking in session dir(%s) svc(%s) pses=%p", path, svc, ses);
358   dir = opendir(path);
359   if (!dir) {
360     ERR("Opening session for find epr by opendir failed path(%s): %d %s; euid=%d egid=%d (sesptr=%p)", path, errno, STRERROR(errno), geteuid(), getegid(), ses);
361     D_DEDENT("find_epr: ");
362     return 0;
363   }
364 
365   len = strlen(svc);
366   len = MIN(len, sizeof(path)-1);
367   memcpy(path, svc, len);
368   path[len] = 0;
369   zxid_fold_svc(path, len);
370   D("%d Folded path prefix(%.*s) len=%d", iter, len, path, len);
371 
372   for (; de = readdir(dir); ++iter) {
373     D("%d Considering file(%s)", iter, de->d_name);
374     if (de->d_name[0] == '.')  /* . .. and "hidden" files */
375       continue;
376     if (de->d_name[strlen(de->d_name)-1] == '~')  /* Ignore backups from hand edited EPRs. */
377       continue;
378     if (memcmp(de->d_name, path, len) || de->d_name[len] != ',')
379       continue;
380     D("%d Checking EPR content file(%s)", iter, de->d_name);
381     epr_buf = read_all_alloc(cf->ctx, "find_epr", 1, &epr_len,
382 			     "%s" ZXID_SES_DIR "%s/%s", cf->cpath, ses->sid, de->d_name);
383     if (!epr_buf)
384       continue;
385 
386     r = zx_dec_zx_root(cf->ctx, epr_len, epr_buf, "find epr");
387     if (!r || !r->EndpointReference) {
388       ERR("No EPR found. Failed to parse epr_buf(%.*s)", epr_len, epr_buf);
389       ZX_FREE(cf->ctx, epr_buf);
390       continue;
391     }
392     epr = r->EndpointReference;
393     ZX_FREE(cf->ctx, r);
394     if (!ZX_SIMPLE_ELEM_CHK(epr->Address)) {
395       ERR("The EPR does not have <Address> element. Rejected. %p", epr->Address);
396       goto next_file;
397     }
398     /* *** add ID-WSF 1.1 handling */
399     md = epr->Metadata;
400     if (!md || !ZX_SIMPLE_ELEM_CHK(md->ServiceType)) {
401       ERR("No Metadata %p or ServiceType. Failed to parse epr_buf(%.*s)", md, epr_len, epr_buf);
402       goto next_file;
403     }
404     ss = ZX_GET_CONTENT(md->ServiceType);
405     if (!ss || len != ss->len || memcmp(svc, ss->s, len)) {
406       D("%d Internal svctype(%.*s) does not match desired(%s). Reject.", iter, ss?ss->len:0, ss?ss->s:"", svc);
407       goto next_file;
408     }
409 
410     ss = ZX_GET_CONTENT(epr->Address);
411     if (url && (!ss || strlen(url) != ss->len || memcmp(url, ss->s, ss->len))) {
412       pi = md?ZX_GET_CONTENT(md->ProviderID):0;
413       if (pi && (strlen(url) != pi->len || memcmp(url, pi->s, pi->len))) {
414 	D("%d ProviderID(%.*s) or endpoint URL(%.*s) does not match desired url(%s). Reject.", iter, pi->len, pi->s, ss?ss->len:0, ss?ss->s:"", url);
415 	goto next_file;
416       }
417     }
418 
419     /* *** Evaluate di_opt */
420 
421     /* *** Evaluate action */
422 
423     /* Add to the front of the list of the eligible candidates. */
424     D("%d Add to candidate set svc(%s) url(%.*s)", iter, svc, ZX_GET_CONTENT_LEN(epr->Address), ZX_GET_CONTENT_S(epr->Address));
425     zxid_di_set_rankKey_if_needed(cf, epr->Metadata, iter, de);
426     epr->gg.g.n = &found->gg.g;
427     found = epr;
428     continue;
429 
430 next_file:
431     zx_free_elem(cf->ctx, &epr->gg, 0);
432     ZX_FREE(cf->ctx, epr_buf);
433     continue;
434   }
435   closedir(dir);
436 
437   if (!found) {
438     D_DEDENT("find_epr: ");
439     return 0;
440   }
441 
442   epr = zxid_di_sort_eprs(cf, found);
443   found = 0;
444   for (iter=1; epr; ++iter, epr = nxt) {
445     nxt = (zxid_epr*)epr->gg.g.n;
446     if (iter == nth) {
447       epr->gg.g.n = 0;
448       found = epr;
449     } else {
450       zx_free_elem(cf->ctx, &epr->gg, 0);  /* not returned, better free it! */
451       /* ZX_FREE(cf->ctx, epr_buf);  *** this pointer is already lost. No way to free. Bummer! */
452     }
453   }
454 
455   if (!found) {
456     D("nth=%d beyond available result set iter=%d", nth, iter);
457     D_DEDENT("find_epr: ");
458     return 0;
459   }
460 
461   D("%d/%d Found svc(%s) epurl(%.*s)", nth, iter, svc, ZX_GET_CONTENT_LEN(found->Address), ZX_GET_CONTENT_S(found->Address));
462   D_DEDENT("find_epr: ");
463   return found;
464 }
465 
466 /*() Discover an EPR over the net.
467  *
468  * cf:: ZXID configuration object, also used for memory allocation
469  * ses:: Session object in whose EPR cache the file will be searched
470  * svc:: Service type (usually the namespace URN)
471  * url:: (Optional) If provided, this argument has to match either
472  *     the ProviderID, EntityID, or actual service endpoint URL.
473  * di_opt:: (Optional) Additional discovery options for selecting the service, query string format
474  * action:: (Optional) The action, or method, that must be invocable on the service
475  * return:: EPR data structure on success, 0 on failure (no discovery EPR in cache, or
476  *     not found by the discovery service). If more than one were found, a linked list
477  *     of EPRs is returned.
478  */
479 
zxid_discover_epr(zxid_conf * cf,zxid_ses * ses,const char * svc,const char * url,const char * di_opt,const char * action)480 zxid_epr* zxid_discover_epr(zxid_conf* cf, zxid_ses* ses, const char* svc, const char* url, const char* di_opt, const char* action)
481 {
482   int wsf20 = 0;
483   struct zx_str* ss;
484   struct zx_str* urlss;
485   struct zx_e_Envelope_s* env;
486   zxid_epr* epr;
487 
488   D_INDENT("di: ");
489   INFO("Discovering svc(%s)...", STRNULLCHK(svc));
490   env = zx_NEW_e_Envelope(cf->ctx,0);
491   env->Body = zx_NEW_e_Body(cf->ctx, &env->gg);
492   env->Body->Query = zxid_mk_di_query(cf, &env->Body->gg, svc, url, di_opt, 0);
493   if (ses->deleg_di_epr) {
494     epr = ses->deleg_di_epr;
495     D("Using delegated discovery EPR %p", epr);
496   } else {
497     epr = zxid_find_epr(cf, ses, zx_xmlns_di, 0, 0, 0, 1);
498     if (!epr) {
499       ERR("EPR for svc(%s) not found in cache and no discovery EPR in cache, thus no way to discover the svc.", STRNULLCHK(svc));
500       D_DEDENT("di: ");
501       return 0;
502     }
503   }
504   env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
505   env = zxid_wsc_call(cf, ses, epr, env, 0);
506   if (!env || env == (void*)ZXID_REDIR_OK || !env->Body || !env->Body->QueryResponse) {
507     ERR("Discovery call failed: No di:QueryResponse seen env=%p body=%p", env, env?env->Body:0);
508     D_DEDENT("di: ");
509     return 0;
510   }
511   D("HERE %p", env);
512   for (epr = env->Body->QueryResponse->EndpointReference;
513        epr;
514        epr = (zxid_epr*)ZX_NEXT(epr)) {
515     if (epr->gg.g.tok != zx_a_EndpointReference_ELEM)
516       continue;
517     ss = ZX_GET_CONTENT(epr->Metadata->ServiceType);
518     urlss = ZX_GET_CONTENT(epr->Address);
519     D("%d: EPR svc(%.*s) url(%.*s)", wsf20, ss?ss->len:0, ss?ss->s:"", urlss?urlss->len:0, urlss?urlss->s:"");
520     if (zxid_cache_epr(cf, ses, epr, wsf20)) {
521       ++wsf20;
522       D("%d: EPR cached svc(%.*s) url(%.*s)", wsf20, ss?ss->len:0, ss?ss->s:"", urlss?urlss->len:0, urlss?urlss->s:"");
523     }
524   }
525   epr = env->Body->QueryResponse->EndpointReference;
526   if (!epr)
527     ERR("No end point discovered for svc(%s)", STRNULLCHK(svc));
528   D("TOTAL wsf20 EPRs discovered: %d for svc(%s)", wsf20, STRNULLCHK(svc));
529   D_DEDENT("di: ");
530   return epr;
531 }
532 
533 /*(i) First search epr cache, and if miss, go discover an EPR over the net.
534  * This is the main work horse for a WSCs wishing to call WSPs via EPR.
535  *
536  * cf:: ZXID configuration object, also used for memory allocation
537  * ses:: Session object in whose EPR cache the file will be searched
538  * svc:: Service type (usually the namespace URN)
539  * url:: (Optional) If provided, this argument has to match either
540  *     the ProviderID, EntityID, or actual service endpoint URL.
541  * di_opt:: (Optional) Additional discovery options for selecting the service, query string format
542  * action:: (Optional) The action, or method, that must be invocable on the service (default: any)
543  * nth:: How manieth matching instance is returned. 1 means first. n>1 assumes
544  *     all EPRs are already in cache and prevents querying Discovery Service.
545  *     0 forces re-querying Discovery service. If nth is larger than number of entries
546  *     in the cache, then return null (0) - this allows one to first call with nth==0 to refresh
547  *     the cache and then to iterate over it. As a legacy compliance feature, if nth==1
548  *     and there is nothing in the cache, then discovery query is made anyway
549  *     to see if something could be found.
550  * return:: EPR data structure on success, 0 on failure (no discovery EPR in cache, or
551  *     not found by the discovery service). If more than one were found, a linked list
552  *     of EPRs is returned.
553  *
554  * See also:: zxid_get_epr_address() for extracting URL as a string
555  */
556 
557 /* Called by:  main x5, zxcall_main x2, zxid_call, zxid_map_identity_token, zxid_nidmap_identity_token, zxid_show_protected_content_setcookie */
zxid_get_epr(zxid_conf * cf,zxid_ses * ses,const char * svc,const char * url,const char * di_opt,const char * action,int nth)558 zxid_epr* zxid_get_epr(zxid_conf* cf, zxid_ses* ses, const char* svc, const char* url, const char* di_opt, const char* action, int nth)
559 {
560   zxid_epr* epr;
561 
562   if (nth > 0) {
563     epr = zxid_find_epr(cf, ses, svc, url, di_opt, action, nth);
564     if (epr)
565       return epr;
566     if (nth > 1)
567       return 0;  /* Do not discover any more */
568     /* nth == 1 and no-epr-in-cache-case: fall thru */
569     D("nth=%d fallthru", nth);
570   }
571   zxid_discover_epr(cf, ses, svc, url, di_opt, action);
572   /* We need to call zxid_find_epr() to ensure the order is always same. */
573   epr = zxid_find_epr(cf, ses, svc, url, di_opt, action, nth);
574   return epr;
575 }
576 
577 /*() Accessor function for extracting endpoint address URL. */
578 
579 /* Called by:  zxcall_main, zxid_print_session, zxid_show_protected_content_setcookie */
zxid_get_epr_address(zxid_conf * cf,zxid_epr * epr)580 struct zx_str* zxid_get_epr_address(zxid_conf* cf, zxid_epr* epr) {
581   if (!epr)
582     return 0;
583   return ZX_GET_CONTENT(epr->Address);
584 }
585 
586 /*() Accessor function for extracting endpoint ProviderID. */
587 
588 /* Called by:  zxcall_main, zxid_print_session */
zxid_get_epr_entid(zxid_conf * cf,zxid_epr * epr)589 struct zx_str* zxid_get_epr_entid(zxid_conf* cf, zxid_epr* epr) {
590   if (!epr || !epr->Metadata || !epr->Metadata->ProviderID) {
591     D("Missing epr=%p epr->Metadata=%p or epr->Metadata->ProviderID", epr, epr?epr->Metadata:0);
592     return 0;
593   }
594   D("epr->Metadata->ProviderID=%p", epr->Metadata->ProviderID);
595   return ZX_GET_CONTENT(epr->Metadata->ProviderID);
596 }
597 
598 /*() Accessor function for extracting endpoint Description (Abstract). */
599 
600 /* Called by:  zxcall_main, zxid_print_session */
zxid_get_epr_desc(zxid_conf * cf,zxid_epr * epr)601 struct zx_str* zxid_get_epr_desc(zxid_conf* cf, zxid_epr* epr) {
602   if (!epr || !epr->Metadata)
603     return 0;
604   return ZX_GET_CONTENT(epr->Metadata->Abstract);
605 }
606 
607 /*() Accessor function for extracting endpoint TAS3 Trust scores. */
608 
609 /* Called by: */
zxid_get_epr_tas3_trust(zxid_conf * cf,zxid_epr * epr)610 struct zx_str* zxid_get_epr_tas3_trust(zxid_conf* cf, zxid_epr* epr) {
611   if (!epr || !epr->Metadata || !epr->Metadata->Trust)
612     return 0;
613   return zx_easy_enc_elem_sig(cf, &epr->Metadata->Trust->gg);
614 }
615 
616 /*() Accessor function for extracting security mechanism ID. */
617 
618 /* Called by: */
zxid_get_epr_secmech(zxid_conf * cf,zxid_epr * epr)619 struct zx_str* zxid_get_epr_secmech(zxid_conf* cf, zxid_epr* epr) {
620   struct zx_elem_s* secmech;
621   if (!epr || !epr->Metadata)
622     return 0;
623   if (!epr->Metadata->SecurityContext
624       || (secmech = epr->Metadata->SecurityContext->SecurityMechID)) {
625     ERR("Null EPR or EPR is missing Metadata, SecurityContext or SecurityMechID. %p", epr);
626     return 0;
627   }
628   return ZX_GET_CONTENT(secmech);
629 }
630 
631 /*() Set security mechanism ID.
632  *
633  * WARNING! Usually security mechanism ID is set by the
634  * discovery process. Do not manipulate it unless you
635  * know what you are doing. If security mechanism requires
636  * a token, you need to arrange it separately, either via
637  * discovery (recommended) or using zxid_set_epr_token() (if
638  * you know what you are doing). */
639 
640 /* Called by: */
zxid_set_epr_secmech(zxid_conf * cf,zxid_epr * epr,const char * secmec)641 void zxid_set_epr_secmech(zxid_conf* cf, zxid_epr* epr, const char* secmec) {
642   if (!epr) {
643     ERR("Null EPR. %p", epr);
644     return;
645   }
646   if (!epr->Metadata)
647     epr->Metadata = zx_NEW_a_Metadata(cf->ctx, &epr->gg);
648   if (!epr->Metadata->SecurityContext)
649     epr->Metadata->SecurityContext = zx_NEW_di_SecurityContext(cf->ctx, &epr->Metadata->gg);
650   if (secmec) {
651     epr->Metadata->SecurityContext->SecurityMechID
652       = zx_dup_elem(cf->ctx, &epr->Metadata->SecurityContext->gg, zx_di_SecurityMechID_ELEM, secmec);
653     INFO("SecurityMechID set to(%s)", secmec);
654   } else {
655     epr->Metadata->SecurityContext->SecurityMechID
656       = zx_dup_elem(cf->ctx, &epr->Metadata->SecurityContext->gg, zx_di_SecurityMechID_ELEM, 0);
657     INFO("SecurityMechID set null %d", 0);
658   }
659 }
660 
661 /*() Accessor function for extracting endpoint's (SAML2 assertion) token. */
662 
663 /* Called by: */
zxid_get_epr_token(zxid_conf * cf,zxid_epr * epr)664 zxid_tok* zxid_get_epr_token(zxid_conf* cf, zxid_epr* epr) {
665   if (!epr || !epr->Metadata || !epr->Metadata->SecurityContext) {
666     ERR("Null EPR or EPR is missing Metadata or SecurityContext. %p", epr);
667     return 0;
668   }
669   return epr->Metadata->SecurityContext->Token;
670 }
671 
672 /*() Set endpoint's (SAML2 assertion) token.
673  *
674  * WARNING! Generally you should not call this function. Instead
675  * you should use discovery to obtain a token properly targeted
676  * to the destination of the EPR. This includes correct audience
677  * restriction, correct name id, and possible encryption of the
678  * token so that only destination can open it. Perticular things
679  * you should NOT do: just copy SSO token and pass it to web service
680  * call (the audience restriction will be wrong); just copy
681  * token that was received on WSP interface and use it on WSC interface. */
682 
683 /* Called by: */
zxid_set_epr_token(zxid_conf * cf,zxid_epr * epr,zxid_tok * tok)684 void zxid_set_epr_token(zxid_conf* cf, zxid_epr* epr, zxid_tok* tok) {
685   if (!epr) {
686     ERR("Null EPR. %p", epr);
687     return;
688   }
689   if (!epr->Metadata)
690     epr->Metadata = zx_NEW_a_Metadata(cf->ctx, &epr->gg);
691   if (!epr->Metadata->SecurityContext)
692     epr->Metadata->SecurityContext = zx_NEW_di_SecurityContext(cf->ctx, &epr->Metadata->gg);
693   epr->Metadata->SecurityContext->Token = tok;
694   INFO("EPR token set %p", tok);
695 }
696 
697 /*() Constructor for "blank" EPR. Such EPR lacks security context so it is
698  * not directly usable for identity web service calls. However, it could
699  * be useful as a building block, or for non-identity web service.
700  * Also id, actor, and mustUnderstand fields need to be filled in by
701  * other means (we may eventually have defaults for some of these). */
702 
703 /* Called by: */
zxid_new_epr(zxid_conf * cf,char * address,char * desc,char * entid,char * svctype)704 zxid_epr* zxid_new_epr(zxid_conf* cf, char* address, char* desc, char* entid, char* svctype)
705 {
706   zxid_epr* epr = zx_NEW_a_EndpointReference(cf->ctx,0);
707   if (address) {
708     epr->Address = zx_NEW_a_Address(cf->ctx, &epr->gg);
709     zx_add_content(cf->ctx, &epr->Address->gg, zx_dup_str(cf->ctx, address));
710   }
711   if (desc || entid || svctype) {
712     epr->Metadata = zx_NEW_a_Metadata(cf->ctx, &epr->gg);
713     if (desc)
714       epr->Metadata->Abstract
715 	= zx_dup_elem(cf->ctx, &epr->Metadata->gg, zx_di_Abstract_ELEM, desc);
716     if (entid)
717       epr->Metadata->ProviderID
718 	= zx_dup_elem(cf->ctx, &epr->Metadata->gg, zx_di_ProviderID_ELEM, entid);
719     if (svctype)
720       epr->Metadata->ServiceType
721 	= zx_dup_elem(cf->ctx, &epr->Metadata->gg, zx_di_ServiceType_ELEM, svctype);
722   }
723   return epr;
724 }
725 
726 /*() Returns delegated discovery EPR, such as someone else's discovery epr. */
727 /* Called by: */
zxid_get_delegated_discovery_epr(zxid_conf * cf,zxid_ses * ses)728 zxid_epr* zxid_get_delegated_discovery_epr(zxid_conf* cf, zxid_ses* ses)
729 {
730   return ses->deleg_di_epr;
731 }
732 
733 /*(i) Allows explicit control over which Discovery Service is used, such
734  * as selecting somebody else's Discovery Service. This allows delegated
735  * access. */
736 
737 /* Called by: */
zxid_set_delegated_discovery_epr(zxid_conf * cf,zxid_ses * ses,zxid_epr * epr)738 void zxid_set_delegated_discovery_epr(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr)
739 {
740   ses->deleg_di_epr = epr;
741 }
742 
743 /*() Get session's call invokation token. */
744 
745 /* Called by: */
zxid_get_call_invoktok(zxid_conf * cf,zxid_ses * ses)746 zxid_tok* zxid_get_call_invoktok(zxid_conf* cf, zxid_ses* ses) {
747   if (!ses) {
748     ERR("Null session. %p", ses);
749     return 0;
750   }
751   return ses->call_invoktok;
752 }
753 
754 /*() Set session's call invokation token. */
755 
756 /* Called by: */
zxid_set_call_invoktok(zxid_conf * cf,zxid_ses * ses,zxid_tok * tok)757 void zxid_set_call_invoktok(zxid_conf* cf, zxid_ses* ses, zxid_tok* tok) {
758   if (!ses) {
759     ERR("Null session. %p", ses);
760     return;
761   }
762   ses->call_invoktok = tok;
763 }
764 
765 /*() Get session's call target token. */
766 
767 /* Called by: */
zxid_get_call_tgttok(zxid_conf * cf,zxid_ses * ses)768 zxid_tok* zxid_get_call_tgttok(zxid_conf* cf, zxid_ses* ses) {
769   if (!ses) {
770     ERR("Null session. %p", ses);
771     return 0;
772   }
773   return ses->call_tgttok;
774 }
775 
776 /*() Set session's call target token. */
777 
778 /* Called by: */
zxid_set_call_tgttok(zxid_conf * cf,zxid_ses * ses,zxid_tok * tok)779 void zxid_set_call_tgttok(zxid_conf* cf, zxid_ses* ses, zxid_tok* tok) {
780   if (!ses) {
781     ERR("Null session. %p", ses);
782     return;
783   }
784   ses->call_tgttok = tok;
785 }
786 
787 /*() Serialize an EPR. */
788 
789 /* Called by: */
zxid_epr2str(zxid_conf * cf,zxid_epr * epr)790 struct zx_str* zxid_epr2str(zxid_conf* cf, zxid_epr* epr) {
791   if (!epr) {
792     ERR("NULL EPR. %p", epr);
793     return 0;
794   }
795   return zx_easy_enc_elem_sig(cf, &epr->gg);
796 }
797 
798 /*() Serialize a token. */
799 
800 /* Called by: */
zxid_token2str(zxid_conf * cf,zxid_tok * tok)801 struct zx_str* zxid_token2str(zxid_conf* cf, zxid_tok* tok) {
802   if (!tok) {
803     ERR("NULL Token. %p", tok);
804     return 0;
805   }
806   if (!tok)
807     return 0;
808   return zx_easy_enc_elem_sig(cf, &tok->gg);
809 }
810 
811 /*() Parse string into token. */
812 
813 /* Called by: */
zxid_str2token(zxid_conf * cf,struct zx_str * ss)814 zxid_tok* zxid_str2token(zxid_conf* cf, struct zx_str* ss) {
815   struct zx_root_s* r;
816   zxid_tok* tok;
817 
818   if (!ss || !ss->len || !ss->s)
819     return 0;
820 
821   r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "decode token");
822   if (!r) {
823     ERR("Failed to parse token buf(%.*s)", ss->len, ss->s);
824     zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "C", "BADXML", 0, "bad token");
825     return 0;
826   }
827   if (r->Token)
828     return r->Token;
829   tok = zx_NEW_sec_Token(cf->ctx,0);
830   tok->Assertion = r->Assertion;
831   tok->EncryptedAssertion = r->EncryptedAssertion;
832   tok->sa11_Assertion = r->sa11_Assertion;
833   tok->ff12_Assertion = r->ff12_Assertion;
834   return tok;
835 }
836 
837 /*() Serialize an assertion. */
838 
839 /* Called by: */
zxid_a7n2str(zxid_conf * cf,zxid_a7n * a7n)840 struct zx_str* zxid_a7n2str(zxid_conf* cf, zxid_a7n* a7n) {
841   if (!a7n)
842     return 0;
843   return zx_easy_enc_elem_sig(cf, &a7n->gg);
844 }
845 
846 /*() Parse string into assertion. */
847 
848 /* Called by: */
zxid_str2a7n(zxid_conf * cf,struct zx_str * ss)849 zxid_a7n* zxid_str2a7n(zxid_conf* cf, struct zx_str* ss) {
850   struct zx_root_s* r;
851 
852   if (!ss || !ss->len || !ss->s)
853     return 0;
854 
855   r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "decode a7n");
856   if (!r) {
857     ERR("Failed to parse assertion buf(%.*s)", ss->len, ss->s);
858     zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "C", "BADXML", 0, "bad a7n");
859     return 0;
860   }
861   return r->Assertion;
862 }
863 
864 /*() Serialize a NameID. */
865 
866 /* Called by: */
zxid_nid2str(zxid_conf * cf,zxid_nid * nid)867 struct zx_str* zxid_nid2str(zxid_conf* cf, zxid_nid* nid) {
868   if (!nid)
869     return 0;
870   return zx_easy_enc_elem_sig(cf, &nid->gg);
871 }
872 
873 /*() Parse string into NameID. */
874 
875 /* Called by: */
zxid_str2nid(zxid_conf * cf,struct zx_str * ss)876 zxid_nid* zxid_str2nid(zxid_conf* cf, struct zx_str* ss) {
877   struct zx_root_s* r;
878 
879   if (!ss || !ss->len || !ss->s)
880     return 0;
881 
882   r = zx_dec_zx_root(cf->ctx, ss->len, ss->s, "decode nid");
883   if (!r) {
884     ERR("Failed to parse NameID buf(%.*s)", ss->len, ss->s);
885     zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "C", "BADXML", 0, "bad nid");
886     return 0;
887   }
888   return r->NameID;
889 }
890 
891 /* ---------- Session field accessor functions ---------- */
892 
893 /*() Get session's invoker nameid. */
894 
895 /* Called by: */
zxid_get_nameid(zxid_conf * cf,zxid_ses * ses)896 zxid_nid* zxid_get_nameid(zxid_conf* cf, zxid_ses* ses) {
897   if (!ses)
898     return 0;
899   return ses->nameid;
900 }
901 
902 /*() Set session's invoker nameid. */
903 
904 /* Called by: */
zxid_set_nameid(zxid_conf * cf,zxid_ses * ses,zxid_nid * nid)905 void zxid_set_nameid(zxid_conf* cf, zxid_ses* ses, zxid_nid* nid) {
906   if (!ses)
907     return;
908   ses->nameid = nid;
909 }
910 
911 /*() Get session's target nameid. */
912 
913 /* Called by: */
zxid_get_tgtnameid(zxid_conf * cf,zxid_ses * ses)914 zxid_nid* zxid_get_tgtnameid(zxid_conf* cf, zxid_ses* ses) {
915   if (!ses)
916     return 0;
917   return ses->tgtnameid;
918 }
919 
920 /*() Set session's target nameid. */
921 
922 /* Called by: */
zxid_set_tgtnameid(zxid_conf * cf,zxid_ses * ses,zxid_nid * nid)923 void zxid_set_tgtnameid(zxid_conf* cf, zxid_ses* ses, zxid_nid* nid) {
924   if (!ses)
925     return;
926   ses->tgtnameid = nid;
927 }
928 
929 /*() Get session's invoker assertion. */
930 
931 /* Called by: */
zxid_get_a7n(zxid_conf * cf,zxid_ses * ses)932 zxid_a7n* zxid_get_a7n(zxid_conf* cf, zxid_ses* ses) {
933   if (!ses)
934     return 0;
935   return ses->a7n;
936 }
937 
938 /*() Set session's invoker assertion. */
939 
940 /* Called by: */
zxid_set_a7n(zxid_conf * cf,zxid_ses * ses,zxid_a7n * a7n)941 void zxid_set_a7n(zxid_conf* cf, zxid_ses* ses, zxid_a7n* a7n) {
942   if (!ses)
943     return;
944   ses->a7n = a7n;
945 }
946 
947 /*() Get session's target assertion. */
948 
949 /* Called by: */
zxid_get_tgta7n(zxid_conf * cf,zxid_ses * ses)950 zxid_a7n* zxid_get_tgta7n(zxid_conf* cf, zxid_ses* ses) {
951   if (!ses)
952     return 0;
953   return ses->tgta7n;
954 }
955 
956 /*() Set session's target assertion. */
957 
958 /* Called by: */
zxid_set_tgta7n(zxid_conf * cf,zxid_ses * ses,zxid_a7n * a7n)959 void zxid_set_tgta7n(zxid_conf* cf, zxid_ses* ses, zxid_a7n* a7n) {
960   if (!ses)
961     return;
962   ses->tgta7n = a7n;
963 }
964 
965 /* EOF  --  zxidepr.c */
966