Branch data Line data Source code
1 : : /* zxidwsc.c - Handwritten nitty-gritty functions for Liberty ID-WSF Web Services Client
2 : : * Copyright (c) 2009-2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 : : * Copyright (c) 2007-2009 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: zxidwsc.c,v 1.19 2010-01-08 02:10:09 sampo Exp $
10 : : *
11 : : * 7.1.2007, created --Sampo
12 : : * 7.10.2008, added documentation --Sampo
13 : : * 7.1.2010, added WSC signing --Sampo
14 : : * 31.5.2010, added WSC sig validation and PDP calls --Sampo
15 : : */
16 : :
17 : : #include "platform.h" /* needed on Win32 for pthread_mutex_lock() et al. */
18 : :
19 : : #include "errmac.h"
20 : : #include "zxid.h"
21 : : #include "zxidpriv.h"
22 : : #include "zxidutil.h"
23 : : #include "zxidconf.h"
24 : : #include "saml2.h"
25 : : #include "wsf.h"
26 : : #include "c/zx-const.h"
27 : : #include "c/zx-ns.h"
28 : : #include "c/zx-data.h"
29 : : #include "c/zx-e-data.h"
30 : :
31 : : /*() WSC response validation work horse. This check the ID-WSF [SOAPbind2] specified
32 : : * criteria, as well as additional criteria and calls PDP, if configured.
33 : : *
34 : : * cf:: ZXID configuration object, see zxid_new_conf()
35 : : * ses:: Session object, used for attributes passed to az, and for recording errors
36 : : * az_cred:: (Optional) Additional authorization credentials or
37 : : * attributes, query string format. These credentials will be populated
38 : : * to the attribute pool in addition to the ones obtained from token and
39 : : * other sources. Then a PDP is called to get an authorization
40 : : * decision (matching obligations we support to those in the request,
41 : : * and obligations pleged by caller to those we insist on). See
42 : : * also PEPMAP configuration option. This implements generalized
43 : : * (application independent) Responder In PEP. To implement
44 : : * application dependent PEP features you should call zxid_az() directly.
45 : : * env:: Entire SOAP envelope as a data structure
46 : : * enve:: SOAP envelope as string
47 : : * return:: 1 on success, 0 on validation failure. Exact reason of the failure is
48 : : * available from ses->curflt and ses->curstatus.
49 : : *
50 : : * See also: zxid_wsp_validate() */
51 : :
52 : : /* Called by: zxid_call_epr, zxid_wsc_valid_resp */
53 : : static int zxid_wsc_valid_re_env(zxid_conf* cf, zxid_ses* ses, const char* az_cred, struct zx_e_Envelope_s* env, const char* enve)
54 : 20 : {
55 : 20 : int n_refs = 0;
56 : : struct zxsig_ref refs[ZXID_N_WSF_SIGNED_HEADERS];
57 : : struct timeval ourts;
58 : 20 : struct timeval srcts = {0,501000};
59 : : zxid_entity* wsc_meta;
60 : : struct zx_wsse_Security_s* sec;
61 : : struct zx_e_Header_s* hdr;
62 : : struct zx_str* issuer;
63 : : struct zx_str* logpath;
64 : : struct zx_str* relto;
65 : : struct zx_str ss;
66 : : zxid_cgi cgi;
67 : :
68 : 20 : GETTIMEOFDAY(&ourts, 0);
69 : 20 : zxid_set_fault(cf, ses, 0);
70 : 20 : zxid_set_tas3_status(cf, ses, 0);
71 : :
72 [ - + ]: 20 : if (!env) {
73 [ # # ]: 0 : ERR("No <e:Envelope> found. enve(%s)", STRNULLCHK(enve));
74 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No SOAP Envelope found.", "IDStarMsgNotUnderstood", 0, 0, 0));
75 : 0 : return 0;
76 : : }
77 : 20 : hdr = env->Header;
78 [ - + ]: 20 : if (!hdr) {
79 : 0 : ERR("No <e:Header> found. %d", 0);
80 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No SOAP Header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
81 : 0 : return 0;
82 : : }
83 [ + - + - : 20 : if (!ZX_SIMPLE_ELEM_CHK(hdr->MessageID)) {
+ - + - +
- - + ]
84 : 0 : ERR("No <a:MessageID> found. %d", 0);
85 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No MessageID header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
86 : 0 : return 0;
87 : : }
88 [ + - + - : 20 : relto = ZX_GET_CONTENT(hdr->RelatesTo);
+ - ]
89 [ + - + - ]: 40 : if (relto && relto->len) {
90 [ + - ]: 20 : if (ses->wsc_msgid) {
91 [ + - + - ]: 40 : if (strlen(ses->wsc_msgid) == relto->len
92 : : && !memcmp(ses->wsc_msgid, relto->s, relto->len)) {
93 [ + + - + ]: 20 : D("RelatesTo check OK %d",1);
94 : : } else {
95 : : /* N.B. [SOAPBinding2] p.27, ll.818-822 indicates RelatesTo checking as SHOULD. */
96 [ # # ]: 0 : if (cf->relto_fatal) {
97 : 0 : ERR("<a:RelatesTo> (%.*s) does not match request msgid(%s).", relto->len, relto->s, ses->wsc_msgid);
98 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "RelatesTo in response does not match request MessageID.", "InvalidRefToMsgID", 0, 0, 0));
99 : 0 : return 0;
100 : : } else {
101 : 0 : INFO("<a:RelatesTo> (%.*s) does not match request msgid(%s), but configured to ignore this error (RELTO_FATAL=0).", relto->len, relto->s, ses->wsc_msgid);
102 : : }
103 : : }
104 : : } else {
105 : 0 : INFO("Session does not have wsc_msgid. Skipping <a:RelatesTo> check. %d",0);
106 : : }
107 : : } else {
108 [ # # ]: 0 : if (cf->relto_fatal) {
109 : 0 : ERR("No <a:RelatesTo> found. %d", 0);
110 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No RelatesTo header found in reply.", "IDStarMsgNotUnderstood", 0, 0, 0));
111 : 0 : return 0;
112 : : } else {
113 : 0 : INFO("No <a:RelatesTo> found, but configured to ignore this (RELTO_FATAL=0). %d", 0);
114 [ # # # # : 0 : D("No RelTo OK enve(%s)", STRNULLCHK(enve));
# # ]
115 : : }
116 : : }
117 : :
118 [ + - - + : 20 : if (!hdr->Sender || !hdr->Sender->providerID && !hdr->Sender->affiliationID) {
# # ]
119 : 0 : ERR("No <b:Sender> found (or missing providerID or affiliationID). %p", hdr->Sender);
120 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No b:Sender header found (or missing providerID or affiliationID).", "IDStarMsgNotUnderstood", 0, 0, 0));
121 : 0 : return 0;
122 : : }
123 : 20 : issuer = &hdr->Sender->providerID->g;
124 : :
125 : : /* Validate message signature (*** add Issuer trusted check, CA validation, etc.) */
126 : :
127 [ - + ]: 20 : if (!(sec = hdr->Security)) {
128 : 0 : ERR("No <wsse:Security> found. %d", 0);
129 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No wsse:Security header found.", "IDStarMsgNotUnderstood", 0, 0, 0));
130 : 0 : return 0;
131 : : }
132 : :
133 [ + - + - : 20 : if (!sec->Signature || !sec->Signature->SignedInfo || !sec->Signature->SignedInfo->Reference) {
- + ]
134 : 0 : ses->sigres = ZXSIG_NO_SIG;
135 [ # # ]: 0 : if (cf->wsp_nosig_fatal) {
136 : 0 : ERR("No Security/Signature found. %p", sec->Signature);
137 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No wsse:Security/ds:Signature found.", TAS3_STATUS_NOSIG, 0, 0, 0));
138 : 0 : return 0;
139 : : } else {
140 : 0 : INFO("No Security/Signature found, but configured to ignore this problem (WSP_NOSIG_FATAL=0). %p", sec->Signature);
141 : : }
142 : : }
143 : :
144 : 20 : wsc_meta = zxid_get_ent_ss(cf, issuer);
145 [ - + ]: 20 : if (!wsc_meta) {
146 : 0 : ses->sigres = ZXSIG_NO_SIG;
147 [ # # ]: 0 : if (cf->nosig_fatal) {
148 : 0 : INFO("Unable to find SAML metadata for Sender(%.*s), but configured to ignore this problem (NOSIG_FATAL=0).", issuer->len, issuer->s);
149 : : } else {
150 : 0 : ERR("Unable to find SAML metadata for Sender(%.*s).", issuer->len, issuer->s);
151 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No unable to find SAML metadata for sender.", "ProviderIDNotValid", 0, 0, 0));
152 : 0 : return 0;
153 : : }
154 : : }
155 : :
156 : 20 : ZERO(refs, sizeof(refs));
157 : 20 : n_refs = zxid_hunt_sig_parts(cf, n_refs, refs, sec->Signature->SignedInfo->Reference, hdr, env->Body);
158 : : /* *** Consider adding BDY and STR */
159 [ + - ]: 20 : ses->sigres = zxsig_validate(cf->ctx, wsc_meta?wsc_meta->sign_cert:0, sec->Signature, n_refs, refs);
160 : 20 : zxid_sigres_map(ses->sigres, &cgi.sigval, &cgi.sigmsg);
161 [ + - - + ]: 20 : if (cf->sig_fatal && ses->sigres) {
162 : 0 : ERR("Fail due to failed message signature sigres=%d", ses->sigres);
163 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "Message signature did not validate.", TAS3_STATUS_BADSIG, 0, 0, 0));
164 : 0 : return 0;
165 : : }
166 : :
167 [ - + ]: 20 : if (!zxid_timestamp_chk(cf, ses, sec->Timestamp, &ourts, &srcts, TAS3_PEP_RS_IN, "e:Server"))
168 : 0 : return 0;
169 : :
170 [ + - ]: 20 : if (hdr->UsageDirective) {
171 [ + - + - : 40 : if (hdr->UsageDirective->Obligation && ZX_GET_CONTENT(hdr->UsageDirective->Obligation->AttributeAssignment)) {
+ - + - ]
172 [ + - + - : 20 : ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(hdr->UsageDirective->Obligation->AttributeAssignment));
+ - ]
173 [ + + - + ]: 20 : D("Found TAS3 UsageDirective with obligation(%s)", ses->rcvd_usagedir);
174 [ # # # # : 0 : } else if (ZX_GET_CONTENT(hdr->UsageDirective)) {
# # ]
175 [ # # # # : 0 : ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(hdr->UsageDirective));
# # ]
176 [ # # # # ]: 0 : D("Found unknown UsageDirective(%s)", ses->rcvd_usagedir);
177 : : } else {
178 : 0 : ERR("UsageDirective empty or not understood. %p", hdr->UsageDirective->Dict);
179 : : }
180 : : }
181 : :
182 : 20 : zxid_ses_to_pool(cf, ses);
183 : 20 : zxid_snarf_eprs_from_ses(cf, ses); /* Harvest attributes and bootstrap(s) */
184 : :
185 [ - + # # : 20 : if (hdr->Status && hdr->Status->code
# # # # #
# ]
186 : : && (hdr->Status->code->g.len != 2
187 : : || hdr->Status->code->g.s[0] != 'O'
188 : : || hdr->Status->code->g.s[1] != 'K')) {
189 : 0 : ERR("TAS3 or app level error code(%.*s)", hdr->Status->code->g.len, hdr->Status->code->g.s);
190 : 0 : return 0;
191 : : }
192 : :
193 : : /* Call Rs-In PDP */
194 : :
195 [ - + ]: 20 : if (!zxid_localpdp(cf, ses)) {
196 : 0 : ERR("RSIN4 Deny by local PDP %d",0);
197 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Client", "Response denied by WSC local policy", TAS3_STATUS_DENY, 0, 0, 0));
198 : 0 : return 0;
199 [ + - + - ]: 20 : } else if (cf->pdp_url && *cf->pdp_url) {
200 : : //zxid_add_attr_to_pool(cf, ses, "Action", zx_dup_str(cf->ctx, "access"));
201 [ - + ]: 20 : if (!zxid_pep_az_soap_pepmap(cf, 0, ses, cf->pdp_url, cf->pepmap_rsin)) {
202 : 0 : ERR("RSIN4 Deny %d", 0);
203 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Client", "Response denied by WSC policy at PDP", TAS3_STATUS_DENY, 0, 0, 0));
204 : 0 : return 0;
205 : : }
206 : : }
207 : :
208 : : /* *** execute (or store for future execution) the obligations. */
209 : :
210 : 20 : ss.s = (char*)enve;
211 : 20 : ss.len = strlen(enve);
212 [ + - + - : 20 : logpath = zxlog_path(cf, issuer, ZX_GET_CONTENT(hdr->MessageID),
+ - ]
213 : : ZXLOG_RELY_DIR, ZXLOG_MSG_KIND, 1);
214 [ - + ]: 20 : if (zxlog_dup_check(cf, logpath, "validate response")) {
215 [ # # ]: 0 : if (cf->dup_msg_fatal) {
216 : 0 : zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate response dup err");
217 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "Duplicate Message.", "DuplicateMsg", 0, 0, 0));
218 : 0 : return 0;
219 : : } else {
220 : 0 : INFO("Duplicate message detected, but configured to ignore this (DUP_MSG_FATAL=0). %d",0);
221 : : }
222 : : }
223 : 20 : zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate response");
224 [ + + + - : 20 : zxlog(cf, &ourts, &srcts, 0, issuer, 0, ses->a7n?&ses->a7n->ID->g:0, ZX_GET_CONTENT(ses->nameid), "N", "K", "VALID", logpath->s, 0);
+ - + + ]
225 : 20 : return 1;
226 : : }
227 : :
228 : : /*() Prepare some headers for WSC call */
229 : :
230 : : /* Called by: zxid_wsc_call */
231 : : static int zxid_wsc_prep(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_e_Envelope_s* env)
232 : 38 : {
233 : : zxid_tok* tok;
234 : : struct zx_e_Header_s* hdr;
235 [ - + ]: 38 : if (!zxid_wsf_decor(cf, ses, env, 0))
236 : 0 : return 0;
237 : 38 : hdr = env->Header;
238 : :
239 [ + - - + : 38 : if (ses->call_tgttok || ses->call_invoktok && epr && epr->Metadata && epr->Metadata->SecurityContext && epr->Metadata->SecurityContext->Token) {
# # # # #
# # # ]
240 [ # # ]: 0 : if (ses->call_tgttok) {
241 [ # # # # ]: 0 : D("TargetIdentity: Explicit specification of ses->call_tgttok %d",0);
242 : 0 : tok = ses->call_tgttok;
243 : : } else {
244 [ # # # # ]: 0 : D("TargetIdentity: Using token from EPR due to specification of ses->call_invoktok %d",0);
245 : 0 : tok = epr->Metadata->SecurityContext->Token;
246 : : }
247 : 0 : hdr->TargetIdentity = zx_NEW_b_TargetIdentity(cf->ctx, &hdr->gg);
248 : 0 : hdr->TargetIdentity->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->TargetIdentity->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
249 : 0 : hdr->TargetIdentity->actor = zx_ref_attr(cf->ctx, &hdr->TargetIdentity->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
250 [ # # ]: 0 : if (tok->EncryptedAssertion) {
251 : 0 : ZX_ADD_KID(hdr->TargetIdentity, EncryptedAssertion, tok->EncryptedAssertion);
252 [ # # ]: 0 : } else if (tok->Assertion) {
253 : 0 : ZX_ADD_KID(hdr->TargetIdentity, Assertion, tok->Assertion);
254 : : } else {
255 : 0 : ERR("No <sa:EncryptedAssertion> or <sa:Assertion> found in <sec:Token> %p", tok);
256 : : }
257 : : } /* else this is just implied by the sec mech */
258 : :
259 : : #if 1
260 : : /* Mandatory for a request. */
261 : 38 : hdr->ReplyTo = zx_NEW_a_ReplyTo(cf->ctx, &hdr->gg);
262 : : /*hdr->ReplyTo->Address = zxid_mk_addr(cf, zx_strf(cf->ctx, "%s?o=P", cf->url));*/
263 : 38 : hdr->ReplyTo->Address = zxid_mk_addr(cf, &hdr->ReplyTo->gg, zx_dup_str(cf->ctx, A_ANON));
264 : 38 : hdr->ReplyTo->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->ReplyTo->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
265 : 38 : hdr->ReplyTo->actor = zx_ref_attr(cf->ctx, &hdr->ReplyTo->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
266 : : #endif
267 : :
268 : 38 : hdr->To = zx_NEW_a_To(cf->ctx, &hdr->gg);
269 [ + - + - : 38 : zx_add_content(cf->ctx, &hdr->To->gg, ZX_GET_CONTENT(epr->Address));
+ - ]
270 : 38 : hdr->To->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->To->gg, zx_e_mustUnderstand_ATTR,XML_TRUE);
271 : 38 : hdr->To->actor = zx_ref_attr(cf->ctx, &hdr->To->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
272 : :
273 : : #if 0
274 : : /* Omission means to use same address as ReplyTo */
275 : : hdr->FaultTo = zx_NEW_a_FaultTo(cf->ctx, &hdr->gg);
276 : : hdr->FaultTo->Address = zx_mk_addr(cf->ctx, &hdr->FaultTo->gg, zx_strf(cf->ctx, "%s?o=P", cf->url));
277 : : hdr->FaultTo->mustUnderstand = zx_ref_attr(cf->ctx, &hdr->FaultTo->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
278 : : hdr->FaultTo->actor = zx_ref_attr(cf->ctx, &hdr->FaultTo->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
279 : : #endif
280 : :
281 : 38 : zxid_attach_sol1_usage_directive(cf, ses, env, TAS3_PLEDGE, cf->wsc_localpdp_obl_pledge);
282 : 38 : zx_reverse_elem_lists(&hdr->gg);
283 : 38 : return 1;
284 : : }
285 : :
286 : : /* Called by: zxid_wsc_prep_secmech x2 */
287 : : static void zxid_choose_sectok(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_wsse_Security_s* sec)
288 : 38 : {
289 : : zxid_tok* tok;
290 [ - + ]: 38 : if (ses->call_invoktok) {
291 [ # # # # ]: 0 : D("Security Token: Explicit specification of ses->call_invoktok %d",0);
292 : 0 : tok = ses->call_invoktok;
293 : : } else {
294 [ + - + - : 76 : if (epr && epr->Metadata && epr->Metadata->SecurityContext && epr->Metadata->SecurityContext->Token) {
+ - + - ]
295 [ + + - + ]: 38 : D("Security Token: Using token from EPR Metadata %d",0);
296 : 38 : tok = epr->Metadata->SecurityContext->Token;
297 : : } else {
298 : 0 : ERR("Security Token: No SecurityContext in EPR Metadata %p",epr);
299 : 0 : return;
300 : : }
301 : : }
302 [ + - ]: 38 : if (tok->EncryptedAssertion) {
303 : 38 : sec->EncryptedAssertion = tok->EncryptedAssertion;
304 : 38 : zx_add_kid_before(&sec->gg, zx_wsu_Timestamp_ELEM, &sec->EncryptedAssertion->gg);
305 [ # # ]: 0 : } else if (tok->Assertion) {
306 : 0 : sec->Assertion = tok->Assertion;
307 : 0 : zx_add_kid_before(&sec->gg, zx_wsu_Timestamp_ELEM, &sec->Assertion->gg);
308 : : } else
309 : 0 : ERR("No <sa:EncryptedAssertion> or <sa:Assertion> found in <sec:Token> %p", tok);
310 : : }
311 : :
312 : : /*() Perform security mechanism related processing for a WSC call */
313 : :
314 : : /* Called by: zxid_wsc_call */
315 : : static int zxid_wsc_prep_secmech(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_e_Envelope_s* env)
316 : 38 : {
317 : : int secmech;
318 : : struct zx_wsse_Security_s* sec;
319 : : struct zx_wsse_SecurityTokenReference_s* str;
320 : : struct zx_e_Header_s* hdr;
321 : :
322 [ + - - + ]: 38 : if (!epr || !env) {
323 : 0 : ERR("MUST supply epr %p and envelope as arguments", epr);
324 : 0 : return 0;
325 : : }
326 : :
327 : 38 : hdr = env->Header;
328 : 38 : zx_add_content(cf->ctx, &hdr->MessageID->gg, zxid_mk_id(cf, "urn:M", ZXID_ID_BITS));
329 : 38 : sec = hdr->Security;
330 [ + - + - : 38 : if (!sec || !sec->Timestamp || !sec->Timestamp->Created) {
- + ]
331 : 0 : ERR("MUST supply wsse:Security and Timestamp %p", sec);
332 : 0 : return 0;
333 : : }
334 : 38 : zx_add_content(cf->ctx, &sec->Timestamp->Created->gg, zxid_date_time(cf, time(0)));
335 : :
336 : : /* Clear away any credentials from previous iteration, if any. *** clear kids list, too */
337 : 38 : sec->Signature = 0;
338 : 38 : sec->BinarySecurityToken = 0;
339 : 38 : sec->SecurityTokenReference = 0;
340 : 38 : sec->Assertion = 0;
341 : 38 : sec->EncryptedAssertion = 0;
342 : 38 : sec->sa11_Assertion = 0;
343 : 38 : sec->ff12_Assertion = 0;
344 : :
345 : : /* Sign all Headers that have Id set. See wsc_sign_sec_mech() */
346 : 38 : secmech = zxid_map_sec_mech(epr);
347 [ - + - - : 38 : switch (secmech) {
- - ]
348 : : case ZXID_SEC_MECH_NULL:
349 [ # # # # ]: 0 : D("secmech null %d", secmech);
350 : 0 : break;
351 : : case ZXID_SEC_MECH_BEARER:
352 : 38 : zxid_choose_sectok(cf, ses, epr, sec);
353 : 38 : str = sec->SecurityTokenReference = zx_NEW_wsse_SecurityTokenReference(cf->ctx, 0);
354 : 38 : zx_add_kid_before(&sec->gg, zx_wsu_Timestamp_ELEM, &str->gg);
355 : 38 : str->KeyIdentifier = zx_NEW_wsse_KeyIdentifier(cf->ctx, &str->gg);
356 : 38 : str->KeyIdentifier->ValueType = zx_ref_attr(cf->ctx, &str->KeyIdentifier->gg, zx_ValueType_ATTR, SAMLID_TOK_PROFILE);
357 [ - + ]: 38 : if (sec->Assertion)
358 : 0 : zx_add_content(cf->ctx, &str->KeyIdentifier->gg, &sec->Assertion->ID->g);
359 : : /* *** In case of encrypted assertion, how is the KeyIdentifier populated? */
360 : :
361 : 38 : zxid_wsf_sign(cf, cf->wsc_sign, sec, str, hdr, env->Body);
362 [ + + - + ]: 38 : D("secmech bearer %d", secmech);
363 : 38 : break;
364 : : case ZXID_SEC_MECH_SAML:
365 : 0 : zxid_choose_sectok(cf, ses, epr, sec);
366 : : /* *** Sign SEC, MID, TO, ACT (if any) */
367 : 0 : zxid_wsf_sign(cf, cf->wsc_sign, sec, 0, hdr, env->Body);
368 [ # # # # ]: 0 : D("secmech saml hok %d", secmech);
369 : 0 : break;
370 : : case ZXID_SEC_MECH_X509:
371 : : /* *** Sign SEC, MID, TO, ACT (if any) */
372 : 0 : zxid_wsf_sign(cf, cf->wsc_sign, sec, 0, hdr, env->Body);
373 [ # # # # ]: 0 : D("secmech x509 %d", secmech);
374 : 0 : break;
375 : : case ZXID_SEC_MECH_PEERS:
376 : : /* *** ? */
377 [ # # # # ]: 0 : D("secmech peers %d", secmech);
378 : 0 : break;
379 : : default:
380 : 0 : ERR("Unknown secmech %d", secmech);
381 : 0 : return 0;
382 : : }
383 : 38 : return 1;
384 : : }
385 : :
386 : : /*(i) zxid_wsc_call() implements the main low level ID-WSF web service call
387 : : * logic, including preparation of SOAP headers, use of sec mech (e.g.
388 : : * preparation of wsse:Security header and signing of appropriate components
389 : : * of the message), and sequencing of the call. In particular, it is
390 : : * possible that WSP requests user interaction and thus the caller web
391 : : * application will need to perform a redirect and then later call this
392 : : * function again to continue the web service call after interaction.
393 : : *
394 : : * env (rather than Body) is taken as argument so that caller can prepare
395 : : * additional SOAP headers at will before calling this function. This function
396 : : * will add Liberty ID-WSF specific SOAP headers. */
397 : :
398 : : /* Called by: main x9, zxid_call_epr, zxid_get_epr, zxid_map_identity_token, zxid_nidmap_identity_token */
399 : : struct zx_e_Envelope_s* zxid_wsc_call(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, struct zx_e_Envelope_s* env, char** ret_enve)
400 : 37 : {
401 : : int i, res;
402 : : struct zx_str* code;
403 : : struct zx_str* str;
404 : : struct zx_str* actor;
405 : : struct zx_root_s* root;
406 : : struct zx_e_Fault_s* flt;
407 : :
408 : 37 : D_INDENT("wsc_call: ");
409 : :
410 [ - + ]: 37 : if (!zxid_wsc_prep(cf, ses, epr, env)) {
411 : 0 : D_DEDENT("wsc_call: ");
412 : 0 : return 0;
413 : : }
414 : :
415 [ + - ]: 37 : for (i=0; i < cf->max_soap_retry; ++i) {
416 [ - + ]: 37 : if (!zxid_wsc_prep_secmech(cf, ses, epr, env)) {
417 : 0 : D_DEDENT("wsc_call: ");
418 : 0 : return 0;
419 : : }
420 [ + - + - : 37 : ses->wsc_msgid = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(env->Header->MessageID));
+ - ]
421 : :
422 [ + - + - : 37 : root = zxid_soap_call_raw(cf, ZX_GET_CONTENT(epr->Address), env, ret_enve);
+ - ]
423 [ + + + - : 37 : if (!root || !root->Envelope || !root->Envelope->Body) {
- + ]
424 : 2 : ERR("soap call returned empty or seriously flawed response %p", root);
425 : 2 : D_DEDENT("wsc_call: ");
426 : 2 : return 0;
427 : : }
428 : 35 : flt = root->Envelope->Body->Fault;
429 [ - + ]: 35 : if (flt) {
430 [ # # # # : 0 : code = ZX_GET_CONTENT(flt->faultcode);
# # ]
431 [ # # # # : 0 : str = ZX_GET_CONTENT(flt->faultstring);
# # ]
432 [ # # # # : 0 : actor = ZX_GET_CONTENT(flt->faultactor);
# # ]
433 [ # # # # : 0 : D("SOAP Fault(%.*s) string(%.*s) actor(%.*s)", code?code->len:1, code?code->s:"?", str?str->len:1, str?str->s:"?", actor?actor->len:1, actor?actor->s:"?");
# # # # #
# # # # #
# # ]
434 : 0 : D_DEDENT("wsc_call: ");
435 : 0 : return 0;
436 : : }
437 : :
438 : : //res = zxid_wsf_analyze_result_headers(cf, ret);
439 : 35 : res = ZXID_OK;
440 [ + - - ]: 35 : switch (res) {
441 : : case ZXID_OK:
442 : 35 : D_DEDENT("wsc_call: ");
443 : 35 : return root->Envelope;
444 : : #if 0
445 : : case ZXID_NEW_CRED:
446 : : break;
447 : : case ZXID_EP_MOVE: // ***
448 : : break;
449 : : case ZXID_EP_UPDATE:
450 : : break;
451 : : #endif
452 : : case ZXID_REDIR_OK:
453 [ # # # # ]: 0 : D("Redirection requested (e.g. Interaction Service) %d", 0);
454 : 0 : D_DEDENT("wsc_call: ");
455 : 0 : return (void*)ZXID_REDIR_OK;
456 : : default:
457 : 0 : ERR("Unknown result code: %d", res);
458 : 0 : D_DEDENT("wsc_call: ");
459 : 0 : return 0;
460 : : }
461 : : }
462 : 0 : ERR("Number of soap call retries exhausted max_soap_retry=%d", cf->max_soap_retry);
463 : 0 : D_DEDENT("wsc_call: ");
464 : 0 : return 0;
465 : : }
466 : :
467 : : static char zx_env_body_open[] = "<e:Envelope xmlns:e=\""zx_xmlns_e"\"><e:Header></e:Header><e:Body>";
468 : : static char zx_env_body_close[] = "</e:Body></e:Envelope>";
469 : : #if 0
470 : : static char zx_env_open[] = "<e:Envelope xmlns:e=\""zx_xmlns_e"\"><e:Header></e:Header>";
471 : : static char zx_env_close[] = "</e:Envelope>";
472 : : #endif
473 : :
474 : : /*() Convenience helper function to parse SOAP Envelope input string.
475 : : * If the specified envelope is incomplete, it is completed.
476 : : *
477 : : * If the string starts by "<e:Envelope", then string
478 : : * should be a complete SOAP envelope including <e:Header> and <e:Body> parts.
479 : : * If the string starts by "<e:Body", then the <e:Envelope> and <e:Header> are
480 : : * automatically added. If the string starts by neither of the above (be
481 : : * careful to use the "e:" as namespace prefix), then it is assumed to be the
482 : : * payload content of the <e:Body> and the rest of the SOAP envelope is added.
483 : : */
484 : :
485 : : /* Called by: zxid_call_epr, zxid_wsc_prepare_call, zxid_wsc_valid_resp, zxid_wsp_decorate */
486 : : struct zx_e_Envelope_s* zxid_add_env_if_needed(zxid_conf* cf, const char* enve)
487 : 39 : {
488 : : struct zx_e_Envelope_s* env;
489 : : struct zx_root_s* r;
490 : : #if 0
491 : : #endif
492 : 39 : r = zx_dec_zx_root(cf->ctx, strlen(enve), enve, "add_env");
493 [ - + ]: 39 : if (!r) {
494 : 0 : ERR("Malformed XML enve(%s)", enve);
495 : 0 : return 0;
496 : : }
497 : 39 : env = r->Envelope;
498 [ + + ]: 39 : if (env) {
499 [ - + ]: 1 : if (!env->Body)
500 : 0 : env->Body = zx_NEW_e_Body(cf->ctx, &env->gg);
501 [ - + ]: 1 : if (!env->Header)
502 : 0 : env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
503 [ - + ]: 38 : } else if (r->Body) {
504 : 0 : env = zx_NEW_e_Envelope(cf->ctx,0);
505 : 0 : ZX_ADD_KID(env, Body, r->Body);
506 [ # # ]: 0 : if (r->Header)
507 : 0 : ZX_ADD_KID(env, Header, r->Header);
508 : : else
509 : 0 : env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
510 : : } else { /* Resort to stringwise attempt to add envelope. */
511 : 38 : ZX_FREE(cf->ctx, r);
512 [ - + ]: 38 : if (!memcmp(enve, "<?xml ", sizeof("<?xml ")-1)) { /* Ignore common, but unnecessary decl. */
513 [ # # # # : 0 : for (enve += sizeof("<?xml "); *enve && !(enve[0] == '?' && enve[1] == '>'); ++enve) ;
# # ]
514 [ # # ]: 0 : if (*enve)
515 : 0 : enve += 2;
516 : : }
517 : : /* Must be just payload */
518 : 38 : enve = zx_alloc_sprintf(cf->ctx, 0, "%s%s%s", zx_env_body_open, enve, zx_env_body_close);
519 : 38 : r = zx_dec_zx_root(cf->ctx, strlen(enve), enve, "add_env2");
520 [ - + ]: 38 : if (!r) {
521 : 0 : ERR("Malformed XML enve(%s)", enve);
522 : 0 : return 0;
523 : : }
524 : 38 : env = r->Envelope;
525 : : }
526 : 39 : ZX_FREE(cf->ctx, r);
527 [ - + ]: 39 : if (!env)
528 : 0 : ERR("No <e:Envelope> found in input argument. enve(%s)", enve);
529 : 39 : return env;
530 : : }
531 : :
532 : : /* ----------------------------------------
533 : : * Simplify writing WSCs */
534 : :
535 : : /*() Make a SOAP call given XML payload for SOAP <e:Envelope> or <e:Body> content,
536 : : * specified by the string. Assumes the EPR has already been discovered.
537 : : * This is sometimes useful in prediscovered or delegated use cases, but
538 : : * normally you should be using zxid_call() and let the discovery
539 : : * take its course. */
540 : :
541 : : /* Called by: zxid_call, zxid_callf_epr */
542 : : struct zx_str* zxid_call_epr(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* enve)
543 : 19 : {
544 : : char* ret_enve;
545 : : struct zx_str* ret;
546 : : struct zx_e_Envelope_s* env;
547 : :
548 [ + - + - : 19 : if (!cf || !ses || !enve) {
- + ]
549 : 0 : ERR("Missing mandatory arguments ses=%p", ses);
550 : 0 : return 0;
551 : : }
552 : :
553 : 19 : D_INDENT("call: ");
554 : 19 : env = zxid_add_env_if_needed(cf, enve);
555 [ - + ]: 19 : if (!env) {
556 : 0 : D_DEDENT("call: ");
557 : 0 : return 0;
558 : : }
559 : :
560 : : /* Call Rq-Out PDP */
561 : :
562 [ - + ]: 19 : if (!zxid_localpdp(cf, ses)) {
563 : 0 : ERR("RQOUT1 Deny by local PDP %d",0);
564 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_OUT, "e:Client", "Request denied by WSC local policy", TAS3_STATUS_DENY, 0, 0, 0));
565 : 0 : D_DEDENT("call: ");
566 : 0 : return 0;
567 [ + - + - ]: 19 : } else if (cf->pdp_url && *cf->pdp_url) {
568 : : //zxid_add_attr_to_pool(cf, ses, "Action", zx_dup_str(cf->ctx, "access"));
569 [ - + ]: 19 : if (!zxid_pep_az_soap_pepmap(cf, 0, ses, cf->pdp_url, cf->pepmap_rqout)) {
570 : 0 : ERR("RQOUT1 Deny %d", 0);
571 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_OUT, "e:Client", "Request denied by WSC policy", TAS3_STATUS_DENY, 0, 0, 0));
572 : 0 : D_DEDENT("call: ");
573 : 0 : return 0;
574 : : }
575 : : }
576 : :
577 : : /* *** add usage directives */
578 : :
579 : 19 : env = zxid_wsc_call(cf, ses, epr, env, &ret_enve);
580 [ - + ]: 19 : if (zxid_wsc_valid_re_env(cf, ses, az_cred, env, ret_enve) != 1) {
581 : 0 : D_DEDENT("call: ");
582 : 0 : return 0;
583 : : }
584 : :
585 : 19 : ret = zx_easy_enc_elem_opt(cf, &env->gg);
586 : 19 : D_DEDENT("call: ");
587 : 19 : return ret;
588 : : }
589 : :
590 : : /*() Call web service, printf style. See zxid_call() for more documentation.
591 : : * Normally you should be calling zxid_callf() instead. */
592 : :
593 : : /* Called by: */
594 : : struct zx_str* zxid_callf_epr(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* env_f, ...)
595 : 0 : {
596 : : char* s;
597 : : va_list ap;
598 : 0 : va_start(ap, env_f);
599 : 0 : s = zx_alloc_vasprintf(cf->ctx, 0, env_f, ap);
600 : 0 : va_end(ap);
601 : 0 : return zxid_call_epr(cf, ses, epr, az_cred, s);
602 : : }
603 : :
604 : : /*(i) Make a SOAP call given XML payload for SOAP <e:Envelope> or <e:Body> content,
605 : : * specified by the string. This is your WSC work horse for calling almost any kind
606 : : * of web service. Simple and intuitive specification of XML as string: no need
607 : : * to build complex data structures.
608 : : *
609 : : * If the string starts by "<e:Envelope", then string
610 : : * should be a complete SOAP envelope including <e:Header> and <e:Body> parts. This
611 : : * allows caller to specify custom SOAP headers, in addition to the ones
612 : : * that the underlying zxid_wsc_call() will add. Usually the payload service
613 : : * will be passed as the contents of the body. If the string starts by
614 : : * "<e:Body", then the <e:Envelope> and <e:Header> are automatically added. If
615 : : * the string starts by neither of the above (be careful to use the "e:" as
616 : : * namespace prefix), then it is assumed to be the payload content of the <e:Body>
617 : : * and the rest of the SOAP envelope is added.
618 : : *
619 : : * cf:: ZXID configuration object, see zxid_new_conf()
620 : : * ses:: Session object that contains the EPR cache
621 : : * svctype:: URI (often the namespace URI) specifying the kind of service we
622 : : * wish to call. Used for EPR lookup or discovery.
623 : : * url:: (Optional) If provided, this argument has to match either
624 : : * the ProviderID, EntityID, or actual service endpoint URL.
625 : : * di_opt:: (Optional) Additional discovery options for selecting the
626 : : * service, query string format
627 : : * az_cred:: (Optional) Additional authorization credentials or
628 : : * attributes, query string format. These credentials will be populated
629 : : * to the attribute pool in addition to the ones obtained from SSO and
630 : : * other sources. Then a PDP is called to get an authorization decision
631 : : * (as well as obligations we pledge to support). See also PEPMAP
632 : : * configuration option. This implementes generalized (application
633 : : * independent) Requestor Out and Requestor In PEPs. To implement
634 : : * application dependent PEP features you should call zxid_az() directly.
635 : : * env:: XML payload
636 : : * return:: SOAP Envelope of the response, as a string. You can parse this
637 : : * string to obtain all returned SOAP headers as well as the Body and its
638 : : * content. NULL on failure. ses->curflt and/or ses->curstatus contain
639 : : * more detailed error information. */
640 : :
641 : : /* Called by: zxcall_main, zxid_callf */
642 : : struct zx_str* zxid_call(zxid_conf* cf, zxid_ses* ses, const char* svctype, const char* url, const char* di_opt, const char* az_cred, const char* enve)
643 : 20 : {
644 : : zxid_epr* epr;
645 : :
646 [ + - - + ]: 20 : if (!cf || !ses) {
647 : 0 : ERR("Missing mandatory arguments ses=%p", ses);
648 : 0 : return 0;
649 : : }
650 : :
651 : 20 : epr = zxid_get_epr(cf, ses, svctype, url, di_opt, 0 /*Action*/, 1);
652 [ + + ]: 20 : if (!epr) {
653 : 1 : ERR("EPR could not be discovered for svctype(%s)", svctype);
654 : 1 : return 0;
655 : : }
656 : :
657 : 19 : return zxid_call_epr(cf, ses, epr, az_cred, enve);
658 : : }
659 : :
660 : : /*() Call web service, printf style. See zxid_call() for more documentation. */
661 : :
662 : : /* Called by: main */
663 : : struct zx_str* zxid_callf(zxid_conf* cf, zxid_ses* ses, const char* svctype, const char* url, const char* di_opt, const char* az_cred, const char* env_f, ...)
664 : 0 : {
665 : : char* s;
666 : : va_list ap;
667 : 0 : va_start(ap, env_f);
668 : 0 : s = zx_alloc_vasprintf(cf->ctx, 0, env_f, ap);
669 : 0 : va_end(ap);
670 : 0 : return zxid_call(cf, ses, svctype, url, di_opt, az_cred, s);
671 : : }
672 : :
673 : : /*(i) Prepare a SOAP call given XML payload for SOAP <e:Envelope> or <e:Body> content,
674 : : * specified by the string. Usually you should use zxid_call(), but if you want
675 : : * to control the steps yourself or use your own http client, this function
676 : : * may be useful.
677 : : *
678 : : * If the string starts by "<e:Envelope", then string
679 : : * should be a complete SOAP envelope including <e:Header> and <e:Body> parts. This
680 : : * allows caller to specify custom SOAP headers, in addition to the ones
681 : : * that the underlying zxid_wsc_call() will add. Usually the payload service
682 : : * will be passed as the contents of the body. If the string starts by
683 : : * "<e:Body", then the <e:Envelope> and <e:Header> are automatically added. If
684 : : * the string starts by neither of the above (be careful to use the "e:" as
685 : : * namespace prefix), then it is assumed to be the payload content of the <e:Body>
686 : : * and the rest of the SOAP envelope is added.
687 : : *
688 : : * cf:: ZXID configuration object, see zxid_new_conf()
689 : : * ses:: Session object that contains the EPR cache
690 : : * epr:: End point to call. From zxid_get_epr().
691 : : * az_cred:: (Optional) Additional authorization credentials or
692 : : * attributes, query string format. These credentials will be populated
693 : : * to the attribute pool in addition to the ones obtained from SSO and
694 : : * other sources. Then a PDP is called to get an authorization decision
695 : : * (as well as obligations we pledge to support). See also PEPMAP
696 : : * configuration option. This implementes generalized (application
697 : : * independent) Requestor Out and Requestor In PEPs. To implement
698 : : * application dependent PEP features you should call zxid_az() directly.
699 : : * env:: XML payload as a string
700 : : * return:: SOAP Envelope ready to be sent to the WSP. You can pass this to HTTP client. */
701 : :
702 : : /* Called by: zxid_wsc_prepare_callf */
703 : : struct zx_str* zxid_wsc_prepare_call(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* enve)
704 : 1 : {
705 : : struct zx_str* ret;
706 : : struct zx_e_Envelope_s* env;
707 : :
708 [ + - + - : 1 : if (!cf || !ses || !enve) {
- + ]
709 : 0 : ERR("Missing mandatory arguments ses=%p", ses);
710 : 0 : return 0;
711 : : }
712 : 1 : D_INDENT("prep: ");
713 : 1 : env = zxid_add_env_if_needed(cf, enve);
714 [ - + ]: 1 : if (!env) {
715 : 0 : D_DEDENT("prep: ");
716 : 0 : return 0;
717 : : }
718 : :
719 : : //*** Needs thought and development
720 : :
721 [ - + ]: 1 : if (!zxid_wsc_prep(cf, ses, epr, env)) {
722 : 0 : D_DEDENT("prep: ");
723 : 0 : return 0;
724 : : }
725 [ - + ]: 1 : if (!zxid_wsc_prep_secmech(cf, ses, epr, env)) {
726 : 0 : D_DEDENT("prep: ");
727 : 0 : return 0;
728 : : }
729 [ + - + - : 1 : ses->wsc_msgid = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(env->Header->MessageID));
+ - ]
730 : :
731 : : /* Call Rq-Out PDP */
732 : :
733 [ - + ]: 1 : if (!zxid_localpdp(cf, ses)) {
734 : 0 : ERR("RQOUT1 Deny by local PDP %d",0);
735 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_OUT, "e:Client", "Request denied by WSC local policy", TAS3_STATUS_DENY, 0, 0, 0));
736 : 0 : D_DEDENT("prep: ");
737 : 0 : return 0;
738 [ + - + - ]: 1 : } else if (cf->pdp_url && *cf->pdp_url) {
739 : : //zxid_add_attr_to_pool(cf, ses, "Action", zx_dup_str(cf->ctx, "access"));
740 [ - + ]: 1 : if (!zxid_pep_az_soap_pepmap(cf, 0, ses, cf->pdp_url, cf->pepmap_rqout)) {
741 : 0 : ERR("RQOUT1 Deny %d", 0);
742 : 0 : zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RQ_IN, "e:Client", "Request denied by WSC policy", TAS3_STATUS_DENY, 0, 0, 0));
743 : 0 : D_DEDENT("prep: ");
744 : 0 : return 0;
745 : : }
746 : : }
747 : :
748 : : /* *** add usage directives */
749 : :
750 : 1 : ret = zx_easy_enc_elem_opt(cf, &env->gg);
751 : 1 : D_DEDENT("prep: ");
752 : 1 : return ret;
753 : : }
754 : :
755 : : /*() Prepare a web service call, printf style.
756 : : * See zxid_wsc_prepare_call() for more documentation. */
757 : :
758 : : /* Called by: */
759 : : struct zx_str* zxid_wsc_prepare_callf(zxid_conf* cf, zxid_ses* ses, zxid_epr* epr, const char* az_cred, const char* env_f, ...)
760 : 0 : {
761 : : char* s;
762 : : va_list ap;
763 : 0 : va_start(ap, env_f);
764 : 0 : s = zx_alloc_vasprintf(cf->ctx, 0, env_f, ap);
765 : 0 : va_end(ap);
766 : 0 : return zxid_wsc_prepare_call(cf, ses, epr, az_cred, s);
767 : : }
768 : :
769 : : /*(i) Validate a response to web service call. Return: 1=valid. */
770 : :
771 : : /* Called by: */
772 : : int zxid_wsc_valid_resp(zxid_conf* cf, zxid_ses* ses, const char* az_cred, const char* enve)
773 : 1 : {
774 : : int ret;
775 : : struct zx_e_Envelope_s* env;
776 : :
777 [ + - + - : 1 : if (!cf || !ses || !enve) {
- + ]
778 : 0 : ERR("Missing mandatory arguments ses=%p enve=%p", ses, enve);
779 : 0 : return 0;
780 : : }
781 : :
782 : 1 : D_INDENT("valid: ");
783 : 1 : env = zxid_add_env_if_needed(cf, enve);
784 : 1 : ret = zxid_wsc_valid_re_env(cf, ses, az_cred, env, enve);
785 : 1 : D_DEDENT("valid: ");
786 : 1 : return ret;
787 : : }
788 : :
789 : : /* EOF -- zxidwsc.c */
|