1 /* zxidpep.c - Handwritten functions for XACML Policy Enforcement Point
2 * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 * Copyright (c) 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: zxidpep.c,v 1.10 2010-01-08 02:10:09 sampo Exp $
10 *
11 * 24.8.2009, created --Sampo
12 * 10.10.2009, added zxid_az() family --Sampo
13 * 12.2.2010, added locking to lazy loading --Sampo
14 * 31.5.2010, generalized to several PEPs model --Sampo
15 * 7.9.2010, merged patches from Stijn Lievens --Sampo
16 * 10.1.2011, added TrustPDP support --Sampo
17 *
18 * See also: zxid_simple_ab_pep() in zxidpdp.c, zxidwsc.c, zxidwsp.c
19 */
20
21 #include "platform.h"
22 #include "errmac.h"
23 #include "zxid.h"
24 #include "zxidpriv.h"
25 #include "zxidconf.h"
26 #include "saml2.h"
27 #include "tas3.h"
28 #include "c/zx-const.h"
29 #include "c/zx-ns.h"
30 #include "c/zx-data.h"
31 #include "c/zx-e-data.h"
32
33 #if 0
34 #define XS_STRING "xs:string"
35 #else
36 #define XS_STRING "http://www.w3.org/2001/XMLSchema#string"
37 #endif
38
39 /*() Check for duplicate attribute in XACML attribute chain
40 * for nth occurance of named attribute.
41 *
42 * - NULL or zero length name will match any
43 * - minus one (-1) as either length field will cause strlen() to be done
44 * - the index n is one based
45 *
46 * *Arguments*
47 *
48 * xac_at_list:: List of (already existing) xac attributes to scan, potentially NULL.
49 * name_len:: Length of the attribute name, or 0 if no matching by attribute name is desired
50 * name:: attribute name to match (or 0)
51 * n:: Howmanieth instance of the matching attribute is desired. 1 means first.
52 * return:: Data structure representing the matching attribute.
53 */
54
55 /* Called by: zxid_pepmap_extract x4 */
zxid_find_xac_attribute(struct zx_xac_Attribute_s * xac_at_list,int name_len,char * name,int n)56 static struct zx_xac_Attribute_s* zxid_find_xac_attribute(struct zx_xac_Attribute_s* xac_at_list, int name_len, char* name, int n)
57 {
58 struct zx_xac_Attribute_s* at;
59 if (!name) { name_len = 0; name = ""; }
60 if (name_len == -1 && name) name_len = strlen(name);
61 if (!xac_at_list) {
62 DD("No xac attribute list when looking for attribute name(%.*s) n=%d", name_len, name, n);
63 return 0;
64 }
65 for (at = xac_at_list;
66 at;
67 at = (struct zx_xac_Attribute_s*)at->gg.g.n) {
68 if (at->gg.g.tok != zx_xac_Attribute_ELEM)
69 continue;
70 if ((name_len ? (at->AttributeId
71 && at->AttributeId->g.len == name_len
72 && !memcmp(at->AttributeId->g.s, name, name_len)) : 1)
73 ) {
74 --n;
75 if (!n)
76 return at;
77 }
78 }
79 return 0;
80 }
81
82 /*() Extract attributes from session pool according to pepmap, usually the PEPMAP
83 * configuration variable.
84 *
85 * cf:: the configuration will need to have ~PEPMAP~ and ~PDP_URL~ options
86 * set according to your situation.
87 * cgi:: if non-null, will receive error and status codes
88 * ses:: all attributes are obtained from the session. You may wish
89 * to add additional attributes that are not known by SSO.
90 * pepmap:: The map used to extract the attributes from the pool to the XACML request
91 * subj:: Value-result parameter. Linked list of subject attributes.
92 * rsrc:: Value-result parameter. Linked list of resource attributes.
93 * act:: Value-result parameter. Linked list of action attributes (usually just one attribute).
94 * env:: Value-result parameter. Linked list of environment attributes.
95 */
96
97 /* Called by: zxid_call_trustpdp, zxid_pep_az_base_soap_pepmap, zxid_pep_az_soap_pepmap */
zxid_pepmap_extract(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zxid_map * pepmap,struct zx_xac_Attribute_s ** subj,struct zx_xac_Attribute_s ** rsrc,struct zx_xac_Attribute_s ** act,struct zx_xac_Attribute_s ** env)98 static void zxid_pepmap_extract(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zxid_map* pepmap, struct zx_xac_Attribute_s** subj, struct zx_xac_Attribute_s** rsrc, struct zx_xac_Attribute_s** act, struct zx_xac_Attribute_s** env)
99 {
100 struct zxid_map* map;
101 struct zxid_attr* at;
102 struct zxid_attr* av;
103 struct zx_xac_Attribute_s* xac_at;
104 char* name;
105
106 for (at = ses?ses->at:0; at; at = at->n) {
107 map = zxid_find_map(pepmap, at->name);
108 if (!map) {
109 D("ATTR(%s)=VAL(%s): Ignored due to no matching PEPMAP", at->name, at->val);
110 continue;
111 }
112
113 if (map->rule == ZXID_MAP_RULE_DEL) {
114 D("attribute(%s) filtered out by del rule in PEPMAP", at->name);
115 continue;
116 }
117
118 at->map_val = zxid_map_val(cf, ses, 0, map, at->name, at->val);
119
120 if (!at->map_val || !at->map_val->len || !at->map_val->s) {
121 if (cf->az_opt & 0x02) {
122 INFO("attribute(%s) with empty value passed due to AZ_OPT=0x02", at->name);
123 continue;
124 } else {
125 INFO("attribute(%s) filtered out due to empty value", at->name);
126 continue;
127 }
128 }
129
130 if (map->dst && *map->dst && map->src && map->src[0] != '*') {
131 D("renaming(%s) to(%s) orig_val(%s) map_val(%.*s)", at->name, map->dst, at->val, at->map_val->len, at->map_val->s);
132 name = map->dst;
133 } else {
134 name = at->name;
135 }
136 xac_at = zxid_mk_xacml_simple_at(cf, 0,
137 zx_dup_str(cf->ctx, name),
138 zx_dup_str(cf->ctx, XS_STRING),
139 at->issuer, at->map_val);
140 if (map->ns && *map->ns) {
141 if (!strcmp(map->ns, "subj")) {
142 if (cf->az_opt & 0x04) {
143 DD("Dup allowed by AZ_OPT=0x04");
144 } else if (zxid_find_xac_attribute(*subj, -1, at->name, 1)) {
145 INFO("attribute(%s) filtered out as duplicate", at->name);
146 continue;
147 }
148 ZX_NEXT(xac_at) = (void*)*subj;
149 *subj = xac_at;
150 } else if (!strcmp(map->ns, "rsrc")) {
151 if (cf->az_opt & 0x04) {
152 DD("Dup allowed by AZ_OPT=0x04");
153 } else if (zxid_find_xac_attribute(*rsrc, -1, at->name, 1)) {
154 INFO("attribute(%s) filtered out as duplicate", at->name);
155 continue;
156 }
157 ZX_NEXT(xac_at) = (void*)*rsrc;
158 *rsrc = xac_at;
159 } else if (!strcmp(map->ns, "act")) {
160 if (cf->az_opt & 0x04) {
161 DD("Dup allowed by AZ_OPT=0x04");
162 } else if (zxid_find_xac_attribute(*act, -1, at->name, 1)) {
163 INFO("attribute(%s) filtered out as duplicate", at->name);
164 continue;
165 }
166 #if 0
167 *act = xac_at; /* there can be only one */
168 #else
169 ZX_NEXT(xac_at) = (void*)*act;
170 *act = xac_at; /* We can have multiple attributes in the action section */
171 #endif
172 } else if (!strcmp(map->ns, "env")) {
173 if (cf->az_opt & 0x04) {
174 DD("Dup allowed by AZ_OPT=0x04");
175 } else if (zxid_find_xac_attribute(*env, -1, at->name, 1)) {
176 INFO("attribute(%s) filtered out as duplicate", at->name);
177 continue;
178 }
179 ZX_NEXT(xac_at) = (void*)*env;
180 *env = xac_at;
181 } else {
182 ERR("PEPMAP unknown ns(%s). Valid values are subj, rsrc, act, and env.", map->ns);
183 }
184 } else {
185 ERR("PEPMAP entry lacks ns %p", map->ns);
186 }
187
188 for (av = at->nv; av; av = av->n) {
189 av->map_val = zxid_map_val(cf, ses, 0, map, at->name, av->val);
190 xac_at = zxid_mk_xacml_simple_at(cf, 0,
191 zx_dup_str(cf->ctx, name),
192 zx_dup_str(cf->ctx, XS_STRING),
193 at->issuer, at->map_val);
194 if (map->ns && *map->ns) {
195 if (!strcmp(map->ns, "subj")) {
196 ZX_NEXT(xac_at) = (void*)*subj;
197 *subj = xac_at;
198 } else if (!strcmp(map->ns, "rsrc")) {
199 ZX_NEXT(xac_at) = (void*)*rsrc;
200 *rsrc = xac_at;
201 } else if (!strcmp(map->ns, "act")) {
202 ERR("PEPMAP: Only one XACML action attribute allowed %d", 0);
203 } else if (!strcmp(map->ns, "env")) {
204 ZX_NEXT(xac_at) = (void*)*env;
205 *env = xac_at;
206 } else {
207 ERR("PEPMAP unknown ns(%s). Valid values are subj, rsrc, act, and env.", map->ns);
208 }
209 } else {
210 ERR("PEPMAP entry lacks ns %p", map->ns);
211 }
212 }
213 }
214
215 if (!*act) {
216 *act = zxid_mk_xacml_simple_at(cf, 0,
217 zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:1.0:action:action-id"),
218 zx_dup_str(cf->ctx, XS_STRING),
219 0,
220 zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:1.0:action:implied-action"));
221 }
222 }
223
224 /* ============== Policy Enforcement Point, Authorization Decision Query ============== */
225
226 /*() Call Policy Decision Point (PDP) to obtain an authorization decision
227 * about a contemplated action on a resource. The attributes from the session
228 * pool, as filtered by PEPMAP are fed to the PDP as inputs
229 * for the decision. The call is using XACML SAML profile over SOAP.
230 *
231 * cf:: the configuration will need to have ~PEPMAP~ and ~PDP_URL~ options
232 * set according to your situation.
233 * cgi:: if non-null, will receive error and status codes
234 * ses:: all attributes are obtained from the session. You may wish
235 * to add additional attributes that are not known by SSO.
236 * pdp_url:: URL of the PDP to contact
237 * subj:: Linked list of subject attributes.
238 * rsrc:: Linked list of resource attributes.
239 * act:: Linked list of action attributes (usually just one attribute).
240 * envi:: Linked list of environment attributes.
241 * returns:: SAML Response as data structure or null upon error.
242 */
243
244 /* Called by: zxid_call_trustpdp, zxid_pep_az_base_soap_pepmap, zxid_pep_az_soap_pepmap */
zxid_az_soap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * pdp_url,struct zx_xac_Attribute_s * subj,struct zx_xac_Attribute_s * rsrc,struct zx_xac_Attribute_s * act,struct zx_xac_Attribute_s * envi)245 static struct zx_sp_Response_s* zxid_az_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* pdp_url, struct zx_xac_Attribute_s* subj, struct zx_xac_Attribute_s* rsrc, struct zx_xac_Attribute_s* act, struct zx_xac_Attribute_s* envi)
246 {
247 X509* sign_cert;
248 EVP_PKEY* sign_pkey;
249 struct zxsig_ref refs;
250 struct zx_root_s* r;
251 struct zx_e_Header_s* hdr;
252 struct zx_e_Body_s* body;
253 struct zx_wsse_Security_s* sec;
254 struct zx_str* ss;
255 struct zx_sp_Response_s* resp;
256
257 hdr = zx_NEW_e_Header(cf->ctx,0);
258 #if 0
259 hdr->Action = zx_NEW_a_Action(cf->ctx, &hdr->gg);
260 //zx_add_content(cf->ctx, &hdr->Action->gg, zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:2.0:profile:saml2.0:v2:schema:protocol:cd-01"));
261 zx_add_content(cf->ctx, &hdr->Action->gg, zx_dup_str(cf->ctx, "urn:oasis:xacml:2.0:saml:protocol:schema:os"));
262
263 //zx_add_content(cf->ctx, &hdr->Action->gg, zx_dup_str(cf->ctx, "SAML2XACMLAuthzRequest"));
264 //zx_add_content(cf->ctx, &hdr->Action->gg, zx_dup_str(cf->ctx, "http://ws.apache.org/axis2/TestPolicyPortType/authRequestRequest"));
265 hdr->Action->mustUnderstand = zx_ref_str(cf->ctx, XML_TRUE);
266 hdr->Action->actor = zx_ref_str(cf->ctx, SOAP_ACTOR_NEXT);
267 #endif
268
269 /* Add our own token so PDP can do whatever PEP can (they are considered to be
270 * part of the same entity). This is TAS3 specific hack. */
271
272 if (!(cf->az_opt & 0x01)) {
273 sec = hdr->Security = zx_NEW_wsse_Security(cf->ctx, &hdr->gg);
274 sec->actor = zx_ref_attr(cf->ctx, &sec->gg, zx_e_actor_ATTR, SOAP_ACTOR_NEXT);
275 sec->mustUnderstand = zx_ref_attr(cf->ctx, &sec->gg, zx_e_mustUnderstand_ATTR, XML_TRUE);
276 sec->Timestamp = zx_NEW_wsu_Timestamp(cf->ctx, &sec->gg);
277 sec->Timestamp->Created = zx_NEW_wsu_Created(cf->ctx, &sec->Timestamp->gg);
278 zx_add_content(cf->ctx, &sec->Timestamp->Created->gg, zxid_date_time(cf, time(0)));
279 sec->Assertion = ses->tgta7n;
280 zx_reverse_elem_lists(&sec->gg);
281 D("tgta7n=%p", ses->tgta7n);
282 }
283 /* Prepare request according to the version */
284
285 body = zx_NEW_e_Body(cf->ctx,0);
286 if (!strcmp(cf->xasp_vers, "xac-soap")) {
287 ZX_ADD_KID(body, xac_Request, zxid_mk_xac_az(cf, &body->gg, subj, rsrc, act, envi));
288 #if 0
289 /* *** xac:Response does not have signature field */
290 if (cf->sso_soap_sign) {
291 ZERO(&refs, sizeof(refs));
292 refs.id = body->xac_Request->ID;
293 refs.canon = zx_EASY_ENC_SO_xac_Request(cf->ctx, body->xac_Request);
294 if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert az xac-soap")) {
295 body->xac_Request->Signature = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
296 zx_add_kid(&body->xac_Request->gg, &body->xac_Request->Signature->gg);
297 }
298 zx_str_free(cf->ctx, refs.canon);
299 }
300 #endif
301 } else if (!strcmp(cf->xasp_vers, "2.0-cd1")) {
302 ZX_ADD_KID(body, xaspcd1_XACMLAuthzDecisionQuery, zxid_mk_az_cd1(cf, subj, rsrc, act, envi));
303 if (cf->sso_soap_sign) {
304 ZERO(&refs, sizeof(refs));
305 refs.id = &body->xaspcd1_XACMLAuthzDecisionQuery->ID->g;
306 refs.canon = zx_easy_enc_elem_sig(cf, &body->xaspcd1_XACMLAuthzDecisionQuery->gg);
307 if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert az cd1")) {
308 body->xaspcd1_XACMLAuthzDecisionQuery->Signature
309 = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
310 zx_add_kid_after_sa_Issuer(&body->xaspcd1_XACMLAuthzDecisionQuery->gg, &body->xaspcd1_XACMLAuthzDecisionQuery->Signature->gg);
311 }
312 zx_str_free(cf->ctx, refs.canon);
313 }
314 } else {
315 ZX_ADD_KID(body, XACMLAuthzDecisionQuery, zxid_mk_az(cf, subj, rsrc, act, envi));
316 if (cf->sso_soap_sign) {
317 ZERO(&refs, sizeof(refs));
318 refs.id = &body->XACMLAuthzDecisionQuery->ID->g;
319 refs.canon = zx_easy_enc_elem_sig(cf, &body->XACMLAuthzDecisionQuery->gg);
320 if (zxid_lazy_load_sign_cert_and_pkey(cf, &sign_cert, &sign_pkey, "use sign cert az")) {
321 body->XACMLAuthzDecisionQuery->Signature
322 = zxsig_sign(cf->ctx, 1, &refs, sign_cert, sign_pkey, cf->xmldsig_sig_meth, cf->xmldsig_digest_algo);
323 zx_add_kid_after_sa_Issuer(&body->XACMLAuthzDecisionQuery->gg, &body->XACMLAuthzDecisionQuery->Signature->gg);
324 }
325 zx_str_free(cf->ctx, refs.canon);
326 }
327 }
328
329 /* Perform the network I/O for the call (connect to PDP) */
330
331 #if 0
332 //ss = zx_ref_str(cf->ctx, "https://idpdemo.tas3.eu:8443/zxididp?o=S");
333 // http://192.168.136.42:1104/axis2/services/TestPolicy.PERMISAuthzServerHttpSoap12Endpoint/
334 // http://192.168.1.27:1104/axis2/services/TestPolicy?wsdl
335 // http://192.168.1.66:1104/axis2/services/TestPolicy.PERMISAuthzServerHttpEndpoint/
336 ss = zx_ref_str(cf->ctx, "http://192.168.1.27:1104/axis2/services/TestPolicy.PERMISAuthzServerHttpEndpoint/");
337 //ss = zx_ref_str(cf->ctx, "");
338 //ss = zx_ref_str(cf->ctx, "http://192.168.1.66:1104/axis2/services/TestPolicy.PERMISAuthzServerHttpEndpoint/");
339 //ss = zx_ref_str(cf->ctx, "http://192.168.1.27:1104/axis2/services/TestPolicy.PERMISAuthzServerHttpSoap12Endpoint/");
340 #else
341 ss = zx_ref_str(cf->ctx, pdp_url);
342 #endif
343 r = zxid_soap_call_hdr_body(cf, ss, hdr, body);
344 //r = zxid_idp_soap(cf, cgi, ses, idp_meta, ZXID_MNI_SVC, body);
345 if (!r || !r->Envelope || !r->Envelope->Body || !r->Envelope->Body->Response) {
346 ERR("Missing Response or other essential element %p %p %p %p", r, r?r->Envelope:0, r && r->Envelope?r->Envelope->Body:0, r && r->Envelope && r->Envelope->Body ? r->Envelope->Body->Response:0);
347 return 0;
348 }
349
350 resp = r->Envelope->Body->Response;
351
352 /* Parse response from the PDP. */
353
354 if (!zxid_saml_ok(cf, cgi, resp->Status, "AzResp")) {
355 ERR("Response->Status no OK (%p)", resp->Status);
356 return 0;
357 }
358 if (!resp->Assertion) {
359 ERR("No Assertion in the Response (%p)", resp);
360 return 0;
361 }
362
363 return resp;
364 }
365
366 /*(i) Call Policy Decision Point (PDP) to obtain an authorization decision
367 * about a contemplated action on a resource. The attributes from the session
368 * pool, as filtered by PEPMAP are fed to the PDP as inputs
369 * for the decision. The call is using XACML SAML profile over SOAP.
370 *
371 * cf:: the configuration will need to have ~PEPMAP~
372 * set according to your situation.
373 * cgi:: if non-null, will receive error and status codes
374 * ses:: all attributes are obtained from the session. You may wish
375 * to add additional attributes that are not known by SSO.
376 * pdp_url:: URL of the PDP to contact
377 * pepmap:: The map used to extract the attributes from the pool to the XACML request
378 * returns:: 0 on error or deny (for any reason, e.g. indeterminate); in case of
379 * permit returns <xac:Response> as string, allowing the obligations to be extracted.
380 *
381 * For simpler API, see zxid_az() family of functions.
382 */
383
384 /* Called by: zxid_pep_az_soap, zxid_query_ctlpt_pdp, zxid_simple_ab_pep, zxid_simple_ses_active_cf */
zxid_pep_az_soap_pepmap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * pdp_url,struct zxid_map * pepmap,const char * lk)385 char* zxid_pep_az_soap_pepmap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* pdp_url, struct zxid_map* pepmap, const char* lk)
386 {
387 struct zx_xac_Attribute_s* subj = 0;
388 struct zx_xac_Attribute_s* rsrc = 0;
389 struct zx_xac_Attribute_s* act = 0;
390 struct zx_xac_Attribute_s* env = 0;
391 struct zx_str* ss;
392 struct zx_sp_Response_s* resp;
393 struct zx_sa_Statement_s* stmt;
394 struct zx_xasa_XACMLAuthzDecisionStatement_s* az_stmt;
395 struct zx_xasacd1_XACMLAuthzDecisionStatement_s* az_stmt_cd1;
396 struct zx_elem_s* decision = 0;
397 char* p;
398
399 if (cf->log_level>0)
400 zxlog(cf, 0, 0, 0, 0, 0, 0, ses?ZX_GET_CONTENT(ses->nameid):0, "N", "W", "AZSOAP", ses?ses->sid:0, " ");
401
402 if (!pdp_url || !*pdp_url) {
403 if (cf->az_fail_mode & (ZXID_AZ_FAIL_MODE1_MISSING_URL | ZXID_AZ_FAIL_MODE4_PERMIT_ALWAYS)) {
404 ERR("%s: No PDP_URL or PDP_CALL_URL set. Permit due to AZ_FAIL_MODE=%d",lk,cf->az_fail_mode);
405 goto fail_mode_permit;
406 } else {
407 ERR("%s: No PDP_URL or PDP_CALL_URL set. Deny. %p", lk, pdp_url);
408 return 0;
409 }
410 }
411
412 zxid_pepmap_extract(cf, cgi, ses, pepmap, &subj, &rsrc, &act, &env);
413 resp = zxid_az_soap(cf, cgi, ses, pdp_url, subj, rsrc, act, env);
414 if (!resp || !resp->Assertion) {
415 if (cf->az_fail_mode & (ZXID_AZ_FAIL_MODE2_NET_FAIL | ZXID_AZ_FAIL_MODE4_PERMIT_ALWAYS)) {
416 ERR("%s: Malformed or nonexistent authorization response from PDP(%s) (%p).\nWARNING: Permit due to AZ_FAIL_MODE=%d", lk, pdp_url, resp, cf->az_fail_mode);
417 goto fail_mode_permit;
418 } else {
419 ERR("%s: DENY due to malformed authorization response from PDP. Either no response or response lacking assertion. %p", lk, resp);
420 return 0;
421 }
422 }
423
424 az_stmt = resp->Assertion->XACMLAuthzDecisionStatement;
425 if (az_stmt && az_stmt->Response && az_stmt->Response->Result) {
426 decision = az_stmt->Response->Result->Decision;
427 if (ZX_CONTENT_EQ_CONST(decision, "Permit")) {
428 ss = zx_easy_enc_elem_opt(cf, &az_stmt->Response->gg);
429 if (!ss || !ss->len) goto nomem;
430 p = ss->s;
431 INFO("%s: PERMIT found in azstmt len=%d", lk, ss->len);
432 ZX_FREE(cf->ctx, ss);
433 return p;
434 }
435 }
436 az_stmt_cd1 = resp->Assertion->xasacd1_XACMLAuthzDecisionStatement;
437 if (az_stmt_cd1 && az_stmt_cd1->Response && az_stmt_cd1->Response->Result) {
438 decision = az_stmt_cd1->Response->Result->Decision;
439 if (ZX_CONTENT_EQ_CONST(decision, "Permit")) {
440 ss = zx_easy_enc_elem_opt(cf, &az_stmt_cd1->Response->gg);
441 if (!ss || !ss->len) goto nomem;
442 p = ss->s;
443 ZX_FREE(cf->ctx, ss);
444 INFO("%s: PERMIT found in azstmt_cd1 len=%d", lk, ss->len);
445 ZX_FREE(cf->ctx, ss);
446 return p;
447 }
448 }
449 stmt = resp->Assertion->Statement;
450 if (stmt && stmt->Response && stmt->Response->Result) { /* Response here is xac:Response */
451 decision = stmt->Response->Result->Decision;
452 if (ZX_CONTENT_EQ_CONST(decision, "Permit")) {
453 ss = zx_easy_enc_elem_opt(cf, &stmt->Response->gg);
454 if (!ss || !ss->len) goto nomem;
455 p = ss->s;
456 INFO("%s: PERMIT found in stmt len=%d", lk, ss->len);
457 ZX_FREE(cf->ctx, ss);
458 return p;
459 }
460 }
461 /*if (resp->Assertion->AuthzDecisionStatement) { }*/
462 if (decision) {
463 if (ZX_CONTENT_EQ_CONST(decision, "Deny")) {
464 if (cf->az_fail_mode & ZXID_AZ_FAIL_MODE4_PERMIT_ALWAYS) {
465 ERR("%s: DENY found in stmt.\nWARNING: Permit due to AZ_FAIL_MODE=%d",lk,cf->az_fail_mode);
466 goto fail_mode_permit;
467 } else {
468 INFO("%s: DENY found in stmt", lk);
469 return 0;
470 }
471 } else {
472 if (cf->az_fail_mode & ZXID_AZ_FAIL_MODE4_PERMIT_ALWAYS) {
473 ERR("%s: DENY due to Other(%.*s).\nWARNING: Permit due to AZ_FAIL_MODE=%d", lk, ZX_GET_CONTENT_LEN(decision), ZX_GET_CONTENT_S(decision), cf->az_fail_mode);
474 goto fail_mode_permit;
475 } else {
476 INFO("%s: DENY due to Other(%.*s) (treated as Deny) found in stmt", lk, ZX_GET_CONTENT_LEN(decision), ZX_GET_CONTENT_S(decision));
477 return 0;
478 }
479 }
480 }
481 if (cf->az_fail_mode & ZXID_AZ_FAIL_MODE4_PERMIT_ALWAYS) {
482 ERR("%s: DENY due to error or no xac:Response from PPD.\nWARNING: Permit due to AZ_FAIL_MODE=%d", lk, cf->az_fail_mode);
483 goto fail_mode_permit;
484 } else {
485 ERR("%s: DENY due to error or no xac:Response from PDP %p %p %p", lk,az_stmt,az_stmt_cd1,stmt);
486 return 0;
487 }
488 nomem:
489 ERR("%s: DENY due memory allocation error. %p", lk, ss);
490 return 0;
491 fail_mode_permit:
492 return zx_dup_cstr(cf->ctx,"<xac:Response xmlns:xac=\"urn:oasis:names:tc:xacml:2.0:context:schema:os\"><xac:Result><xac:Decision>Permit</xac:Decision><xac:Status><xac:StatusCode Value=\"urn:oasis:names:tc:xacml:1.0:status:ok\"/></xac:Status></xac:Result></xac:Response>");
493 }
494
495 /*(i) Call Policy Decision Point (PDP) to obtain an authorization decision
496 * about a contemplated action on a resource. The attributes from the session
497 * pool, as filtered by PEPMAP are fed to the PDP as inputs
498 * for the decision. The call is using XACML SAML profile over SOAP.
499 *
500 * This is similar to zxid_pep_az_soap_pepmap() with the difference that the <xac:Response>
501 * element is returned even in the deny and indeterminate cases (null
502 * is still returned if there was an error). Effectively this +base+
503 * form does not make judgement about whether <xac:Response> means
504 * permit, deny, or something else.
505 *
506 * You should use this function if the Deny message contains interesting
507 * obligations (normally it does not).
508 *
509 * cf:: the configuration will need to have ~PEPMAP~ and ~PDP_URL~ options
510 * set according to your situation.
511 * cgi:: if non-null, will receive error and status codes
512 * ses:: all attributes are obtained from the session. You may wish
513 * to add additional attributes that are not known by SSO.
514 * pdp_url:: URL of the PDP to contact
515 * pepmap:: The map used to extract the attributes from the pool to the XACML request
516 * returns:: 0 on error; in case of deny (or indeterminate, etc.) as well as
517 * permit returns <xac:Response> as string, allowing the obligations to be extracted.
518 *
519 * For simpler API, see zxid_az_base() family of functions.
520 */
521
522 /* Called by: zxid_pep_az_base_soap */
zxid_pep_az_base_soap_pepmap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * pdp_url,struct zxid_map * pepmap)523 char* zxid_pep_az_base_soap_pepmap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* pdp_url, struct zxid_map* pepmap)
524 {
525 struct zx_xac_Attribute_s* subj = 0;
526 struct zx_xac_Attribute_s* rsrc = 0;
527 struct zx_xac_Attribute_s* act = 0;
528 struct zx_xac_Attribute_s* env = 0;
529 char* res;
530 struct zx_str* ss;
531 struct zx_sp_Response_s* resp;
532 struct zx_sa_Statement_s* stmt;
533 struct zx_xasa_XACMLAuthzDecisionStatement_s* az_stmt;
534 struct zx_xasacd1_XACMLAuthzDecisionStatement_s* az_stmt_cd1;
535
536 if (cf->log_level>0)
537 zxlog(cf, 0, 0, 0, 0, 0, 0, ses?ZX_GET_CONTENT(ses->nameid):0, "N", "W", "AZSOAP", ses?ses->sid:0, " ");
538
539 if (!pdp_url || !*pdp_url) {
540 ERR("No PDP_URL or PDP_CALL_URL set. Deny. %p", pdp_url);
541 return 0;
542 }
543
544 zxid_pepmap_extract(cf, cgi, ses, pepmap, &subj, &rsrc, &act, &env);
545 resp = zxid_az_soap(cf, cgi, ses, pdp_url, subj, rsrc, act, env);
546 if (!resp || !resp->Assertion) {
547 ERR("DENY due to malformed authorization response from PDP. Either no response or response lacking assertion. %p", resp);
548 return 0;
549 }
550
551 az_stmt = resp->Assertion->XACMLAuthzDecisionStatement;
552 if (az_stmt && az_stmt->Response) {
553 ss = zx_easy_enc_elem_opt(cf, &az_stmt->Response->gg);
554 if (!ss || !ss->len)
555 return 0;
556 res = ss->s;
557 ZX_FREE(cf->ctx, ss);
558 D("azstmt(%s)", res);
559 return res;
560 }
561 az_stmt_cd1 = resp->Assertion->xasacd1_XACMLAuthzDecisionStatement;
562 if (az_stmt_cd1 && az_stmt_cd1->Response) {
563 ss = zx_easy_enc_elem_opt(cf, &az_stmt_cd1->Response->gg);
564 if (!ss || !ss->len)
565 return 0;
566 res = ss->s;
567 ZX_FREE(cf->ctx, ss);
568 D("cd1(%s)", res);
569 return res;
570 }
571 stmt = resp->Assertion->Statement;
572 if (stmt && stmt->Response) { /* Response here is xac:Response */
573 ss = zx_easy_enc_elem_opt(cf, &stmt->Response->gg);
574 if (!ss || !ss->len)
575 return 0;
576 res = ss->s;
577 ZX_FREE(cf->ctx, ss);
578 D("stmt(%s)", res);
579 return res;
580 }
581
582 D("Missing az related Response element %d",0);
583 return 0;
584 }
585
586 /*() Call Policy Decision Point (PDP) to obtain an authorization decision.
587 * Uses default PEPMAP to call zxid_pep_az_soap_pepmap(). */
588
589 /* Called by: zxid_az_cf_ses */
zxid_pep_az_soap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * pdp_url)590 char* zxid_pep_az_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* pdp_url) {
591 return zxid_pep_az_soap_pepmap(cf, cgi, ses, pdp_url, cf->pepmap, "az_soap");
592 }
593
594 /* Called by: zxid_az_base_cf_ses */
zxid_pep_az_base_soap(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,const char * pdp_url)595 char* zxid_pep_az_base_soap(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* pdp_url) {
596 return zxid_pep_az_base_soap_pepmap(cf, cgi, ses, pdp_url, cf->pepmap);
597 }
598
599 /*int zxid_az_cf_cgi_ses(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses);*/
600
601 /*(i) Call Policy Decision Point (PDP) to obtain an authorization decision
602 * about a contemplated action on a resource. The attributes from the session
603 * pool, as filtered by ~PEPMAP~ are fed to the PDP as inputs
604 * for the decision. Session object is passed in as an argument.
605 *
606 * cf:: the configuration will need to have ~PEPMAP~ and ~PDP_URL~
607 * or ~PDP_CALL_URL~ options set according to your situation.
608 * qs:: if non-null, will resceive error and status codes
609 * ses:: all attributes are obtained from the session. You may wish
610 * to add additional attributes that are not known by SSO.
611 * The session object, e.g. from zxid_get_ses()
612 * returns:: 0 on deny (for any reason, e.g. indeterminate), or string
613 * containing the obligations on permit.
614 *
615 * For simpler API, see zxid_az() family of functions.
616 */
617
618 /* Called by: zxcall_main, zxid_az_cf */
zxid_az_cf_ses(zxid_conf * cf,const char * qs,zxid_ses * ses)619 char* zxid_az_cf_ses(zxid_conf* cf, const char* qs, zxid_ses* ses)
620 {
621 zxid_cgi cgi;
622 char* ret;
623 char* url = (cf->pdp_call_url&&*cf->pdp_call_url) ? cf->pdp_call_url : cf->pdp_url;
624 D_INDENT("az: ");
625 ZERO(&cgi, sizeof(cgi));
626 /*zxid_parse_cgi(cf, &cgi, ""); DD("qs(%s) ses=%p", STRNULLCHKD(qs), ses);*/
627 if (qs && ses)
628 zxid_add_qs2ses(cf, ses, zx_dup_cstr(cf->ctx, qs), 1);
629 ret = zxid_pep_az_soap(cf, &cgi, ses, url);
630 D_DEDENT("az: ");
631 return ret;
632 }
633
634 /* Called by: zxid_az_base_cf */
zxid_az_base_cf_ses(zxid_conf * cf,const char * qs,zxid_ses * ses)635 char* zxid_az_base_cf_ses(zxid_conf* cf, const char* qs, zxid_ses* ses)
636 {
637 zxid_cgi cgi;
638 char* ret;
639 char* url = (cf->pdp_call_url&&*cf->pdp_call_url) ? cf->pdp_call_url : cf->pdp_url;
640 D_INDENT("azb: ");
641 ZERO(&cgi, sizeof(cgi));
642 /*zxid_parse_cgi(cf, &cgi, ""); DD("qs(%s) ses=%p", STRNULLCHKD(qs), ses);*/
643 if (qs && ses)
644 zxid_add_qs2ses(cf, ses, zx_dup_cstr(cf->ctx, qs), 1);
645 ret = zxid_pep_az_base_soap(cf, &cgi, ses, url);
646 D_DEDENT("azb: ");
647 return ret;
648 }
649
650 /*(i) Call Policy Decision Point (PDP) to obtain an authorization decision
651 * about a contemplated action on a resource. The attributes from the session
652 * pool, as filtered by ~PEPMAP~ are fed to the PDP as inputs
653 * for the decision. Session is identified by a session id.
654 *
655 * cf:: the configuration will need to have ~PEPMAP~ and ~PDP_URL~ options
656 * set according to your situation.
657 * qs:: if non-null, will resceive error and status codes
658 * sid:: all attributes are obtained from the session. You may wish
659 * to add additional attributes that are not known by SSO. The
660 * session id, such as returned from SSO.
661 * returns:: 0 on deny (for any reason, e.g. indeterminate), or string
662 * containing the obligations on permit.
663 *
664 * For simpler API, see zxid_az() family of functions.
665 */
666
667 /* Called by: zxid_az */
zxid_az_cf(zxid_conf * cf,const char * qs,const char * sid)668 char* zxid_az_cf(zxid_conf* cf, const char* qs, const char* sid)
669 {
670 zxid_ses ses;
671 ZERO(&ses, sizeof(ses));
672 if (sid && sid[0]) {
673 zxid_get_ses(cf, &ses, sid);
674 zxid_ses_to_pool(cf, &ses);
675 }
676 return zxid_az_cf_ses(cf, qs, &ses);
677 }
678
679 /* Called by: zxid_az_base */
zxid_az_base_cf(zxid_conf * cf,const char * qs,const char * sid)680 char* zxid_az_base_cf(zxid_conf* cf, const char* qs, const char* sid)
681 {
682 zxid_ses ses;
683 ZERO(&ses, sizeof(ses));
684 if (sid && sid[0]) {
685 zxid_get_ses(cf, &ses, sid);
686 zxid_ses_to_pool(cf, &ses);
687 }
688 return zxid_az_base_cf_ses(cf, qs, &ses);
689 }
690
691 /*() See zxid_az_cf() for description. Only difference is that the configuration
692 * is accepted as a string instead of an object. */
693
694 /* Called by: */
zxid_az(const char * conf,const char * qs,const char * sid)695 char* zxid_az(const char* conf, const char* qs, const char* sid)
696 {
697 struct zx_ctx ctx;
698 zxid_conf cf;
699 zx_reset_ctx(&ctx);
700 ZERO(&cf, sizeof(cf));
701 cf.ctx = &ctx;
702 zxid_conf_to_cf_len(&cf, -1, conf);
703 return zxid_az_cf(&cf, qs, sid);
704 }
705
706 /* Called by: */
zxid_az_base(const char * conf,const char * qs,const char * sid)707 char* zxid_az_base(const char* conf, const char* qs, const char* sid)
708 {
709 struct zx_ctx ctx;
710 zxid_conf cf;
711 zx_reset_ctx(&ctx);
712 ZERO(&cf, sizeof(cf));
713 cf.ctx = &ctx;
714 zxid_conf_to_cf_len(&cf, -1, conf);
715 return zxid_az_base_cf(&cf, qs, sid);
716 }
717
718 /*() Call Trust Policy Decision Point (PDP) to obtain a trust evaluation and scoring
719 *
720 * You should use this function if the Deny message contains interesting
721 * obligations (normally it does not).
722 *
723 * cf:: the configuration will need to have ~PEPMAP~ and ~PDP_URL~ options
724 * set according to your situation.
725 * cgi:: if non-null, will receive error and status codes
726 * ses:: all attributes are obtained from the session. You may wish
727 * to add additional attributes that are not known by SSO.
728 * pepmap:: The map used to extract the attributes from the pool to the XACML request
729 * start:: Start of query string format buffer of trust options
730 * lim:: One past end of trust option buffer
731 * returns:: 0 on error; in case of deny (or indeterminate, etc.) as well as
732 * permit returns <xac:Response> as string, allowing the obligations to be extracted.
733 */
734
735 /* Called by: zxid_di_query */
zxid_call_trustpdp(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,struct zxid_map * pepmap,const char * start,const char * lim,zxid_epr * epr)736 int zxid_call_trustpdp(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, struct zxid_map* pepmap, const char* start, const char* lim, zxid_epr* epr)
737 {
738 struct zx_xac_Attribute_s* xac_at;
739 struct zx_xac_Attribute_s* subj = 0;
740 struct zx_xac_Attribute_s* rsrc = 0;
741 struct zx_xac_Attribute_s* act = 0;
742 struct zx_xac_Attribute_s* env = 0;
743 const char* val;
744 const char* sep;
745 struct zx_str* ss;
746 struct zx_sp_Response_s* resp;
747 struct zx_sp_StatusCode_s* sc;
748 struct zx_sa_Statement_s* stmt;
749 struct zx_xasa_XACMLAuthzDecisionStatement_s* az_stmt;
750 struct zx_xasacd1_XACMLAuthzDecisionStatement_s* az_stmt_cd1;
751 struct zx_elem_s* decision;
752 struct zx_tas3_TrustRanking_s* tr;
753
754 D("option(%.*s)", ((int)(lim-start)), start);
755
756 if (cf->log_level>0)
757 zxlog(cf, 0, 0, 0, 0, 0, 0, ses?ZX_GET_CONTENT(ses->nameid):0, "N", "W", "TRUSTPDP", ses?ses->sid:0, " ");
758
759 if (!cf->trustpdp_url || !*cf->trustpdp_url) {
760 ERR("No TRUSTPDP_URL. Deny. %p", cf->trustpdp_url);
761 return 0;
762 }
763
764 #if 1
765 if (epr->Metadata && epr->Metadata->ProviderID) {
766 ss = ZX_GET_CONTENT(epr->Metadata->ProviderID);
767 D("Using ProviderID(%.*s) as resource-id", ss->len, ss->s);
768 rsrc = zxid_mk_xacml_simple_at(cf, 0,
769 zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:1.0:resource:resource-id"),
770 zx_dup_str(cf->ctx, XS_STRING),
771 0, zx_dup_zx_str(cf->ctx, ss));
772 ss = ZX_GET_CONTENT(epr->Metadata->ServiceType);
773 xac_at = zxid_mk_xacml_simple_at(cf, 0,
774 zx_dup_str(cf->ctx, "urn:tas3:servicetype"),
775 zx_dup_str(cf->ctx, XS_STRING),
776 0, zx_dup_zx_str(cf->ctx, ss));
777 ZX_NEXT(xac_at) = &rsrc->gg.g;
778 rsrc = xac_at;
779 } else {
780 ERR("EPR does not have Metadata or Metadata/ProviderID. resource-id not set %p",epr->Metadata);
781 }
782
783 if (ses->nid && *ses->nid) {
784 #if 0
785 /* Pass user as subject. This is TAS3 correct behaviour. */
786 subj = zxid_mk_xacml_simple_at(cf, 0,
787 zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:1.0:subject:subject-id"),
788 zx_dup_str(cf->ctx, XS_STRING),
789 0,
790 zx_dup_str(cf->ctx, ses->nid));
791 #else
792 /* *** Pass service to rank as subject. This is a kludge to work around Jerry's bug. */
793 ss = ZX_GET_CONTENT(epr->Metadata->ProviderID);
794 D("*** Using ProviderID(%.*s) as subject-id", ss->len, ss->s);
795 subj = zxid_mk_xacml_simple_at(cf, 0,
796 zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:1.0:subject:subject-id"),
797 zx_dup_str(cf->ctx, XS_STRING),
798 0, zx_dup_zx_str(cf->ctx, ss));
799 #endif
800 } else {
801 D("No ses->nid %p", ses->nid);
802 }
803
804 act = zxid_mk_xacml_simple_at(cf, 0,
805 zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:1.0:action:action-id"),
806 zx_dup_str(cf->ctx, XS_STRING),
807 0,
808 zx_dup_str(cf->ctx, "urn:oasis:names:tc:xacml:1.0:action:implied-action"));
809 #else
810 zxid_pepmap_extract(cf, cgi, ses, pepmap, &subj, &rsrc, &act, &env);
811 #endif
812
813 while (start < lim && (start = zx_memmem(start, (lim - start), TAS3_TRUST_INPUT_CTL1, sizeof(TAS3_TRUST_INPUT_CTL1)-1))) {
814
815 val = memchr(start+sizeof(TAS3_TRUST_INPUT_CTL1)-1, '=', lim - (start+sizeof(TAS3_TRUST_INPUT_CTL1)-1));
816 if (!val) {
817 ERR("Malformed trust option(%.*s)", ((int)(lim-start)), start);
818 break;
819 }
820 ++val;
821 sep = memchr(val, '&', lim-val);
822 if (!sep) {
823 sep = lim;
824 }
825
826 D("add trust attr(%.*s) val(%.*s)", ((int)(val-1-start)), start, ((int)(sep-val)), val);
827 xac_at = zxid_mk_xacml_simple_at(cf, 0,
828 zx_dup_len_str(cf->ctx, val-1-start, start),
829 zx_dup_str(cf->ctx, XS_STRING),
830 0, zx_dup_len_str(cf->ctx, sep-val, val));
831 ZX_NEXT(xac_at) = &env->gg.g;
832 env = xac_at;
833 start = sep;
834 }
835
836 resp = zxid_az_soap(cf, cgi, ses, cf->trustpdp_url, subj, rsrc, act, env);
837 if (!resp)
838 return 0;
839
840 az_stmt = resp->Assertion->XACMLAuthzDecisionStatement;
841 if (az_stmt && az_stmt->Response) {
842 decision = az_stmt->Response->Result->Decision;
843 } else {
844 az_stmt_cd1 = resp->Assertion->xasacd1_XACMLAuthzDecisionStatement;
845 if (az_stmt_cd1 && az_stmt_cd1->Response) {
846 decision = az_stmt_cd1->Response->Result->Decision;
847 } else {
848 stmt = resp->Assertion->Statement;
849 if (stmt && stmt->Response) { /* Response here is xac:Response */
850 decision = stmt->Response->Result->Decision;
851 } else {
852 D("Missing az related Response element %d",0);
853 return 0;
854 }
855 }
856 }
857 if (ZX_CONTENT_EQ_CONST(decision, "Permit")) {
858 D("Permit %d",0);
859 if (resp->Status && resp->Status->StatusCode && resp->Status->StatusCode->StatusCode) {
860 /* Second and further layer status codes may contain Trust Rankings */
861 for (sc = resp->Status->StatusCode->StatusCode; sc; sc = sc->StatusCode) {
862 if (!sc->Value || !sc->Value->g.len < sizeof(TAS3_TRUST_RANKING_CTL1)-1 ||!sc->Value->g.s
863 || memcmp(sc->Value->g.s, TAS3_TRUST_RANKING_CTL1,sizeof(TAS3_TRUST_RANKING_CTL1)-1))
864 continue;
865 val = memchr(sc->Value->g.s + sizeof(TAS3_TRUST_RANKING_CTL1)-1, '=', sc->Value->g.len - (sizeof(TAS3_TRUST_RANKING_CTL1)-1));
866 if (!val) {
867 ERR("Malformed trust ranking(%.*s)", sc->Value->g.len, sc->Value->g.s);
868 continue;
869 }
870
871 if (!epr->Metadata)
872 epr->Metadata = zx_NEW_a_Metadata(cf->ctx, &epr->gg);
873 if (!epr->Metadata->Trust)
874 epr->Metadata->Trust = zx_NEW_tas3_Trust(cf->ctx, &epr->Metadata->gg);
875 tr = zx_NEW_tas3_TrustRanking(cf->ctx, &epr->Metadata->Trust->gg);
876 tr->metric = zx_dup_len_attr(cf->ctx, &tr->gg, zx_metric_ATTR,
877 val - sc->Value->g.s, sc->Value->g.s);
878 ++val;
879 tr->val = zx_dup_len_attr(cf->ctx, &tr->gg, zx_val_ATTR,
880 sc->Value->g.len - (val - sc->Value->g.s), val);
881 }
882 }
883
884 INFO("PERMIT found in azstmt len=%d", ss->len);
885 ZX_FREE(cf->ctx, ss);
886 return 1;
887 } else {
888 D("Deny %d",0);
889 return 0;
890 }
891 }
892
893 /* EOF -- zxidpep.c */
894