1 /* zxumacall.c  -  UMA Client and Debugging Tool
2  * Copyright (c) 2014 Synergetics NV, All Rights Reserved.
3  * Author: Sampo Kellomaki (sampo@synergetics.be)
4  * This is confidential unpublished proprietary source code.
5  * NO WARRANTY, not even implied warranties. Contains trade secrets.
6  * Distribution prohibited unless authorized in writing.
7  * Licensed under Apache License 2.0, see file COPYING.
8  * $Id: zxcot.c,v 1.5 2009-11-29 12:23:06 sampo Exp $
9  *
10  * 9.10.2014, created --Sampo
11  */
12 
13 #include "platform.h"  /* for dirent.h */
14 
15 #include <string.h>
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <errno.h>
19 
20 #include "platform.h"
21 #include "errmac.h"
22 #include "zx.h"
23 #include "zxid.h"
24 #include "zxidutil.h"
25 #include "zxidconf.h"
26 #include "c/zxidvers.h"
27 #include "c/zx-const.h"
28 #include "c/zx-ns.h"
29 #include "c/zx-data.h"
30 #include "saml2.h"
31 
32 char* help =
33 "zxumacall  - UMA Client and Debugging tool R" ZXID_REL "\n\
34 UMA - User Managed Access and OAUTH2.0 are standards for access authorization of web services.\n\
35 Copyright (c) 2014 Synergetics NV, All Rights Reserved.\n\
36 Author: Sampo Kellomaki (sampo@synergetics.be)\n\
37 NO WARRANTY, not even implied warranties. Licensed under Apache License v2.0\n\
38 See http://www.apache.org/licenses/LICENSE-2.0\n\
39 Send well researched bug reports to the author. Home: zxid.org\n\
40 \n\
41 Usage: zxumacall [options] -s SESID -t SVCTYPE <req >resp\n\
42        zxumacall [options] -a IDP USER:PW -t SVCTYPE <req >resp\n\
43        zxumacall [options] -ua IDP UAT    -t SVCTYPE <req >resp\n\
44        zxumacall [options] -a IDP USER:PW -t SVCTYPE -nd # Discovery only\n\
45        zxumacall [options] -a IDP USER:PW   # Authentication only\n\
46        zxumacall [options] -dynclireg az_entity # Dynamic Client Registration\n\
47        zxumacall [options] -s SESID -im EID # Identity Mapping to EID\n\
48        zxumacall [options] -s SESID -l      # List session cache\n\
49   -c CONF          Optional configuration string (default -c CPATH=/var/zxid/)\n\
50                    Most of the configuration is read from " ZXID_CONF_PATH "\n\
51   -s SESID         Session ID referring to a directory in /var/zxid/ses\n\
52                    Use zxidhlo to do SSO and then cut and paste from there.\n\
53   -a IDP USER:PW   Use Authentication service to authenticate the user and\n\
54                    create session. IDP is IdP's Entity ID. This is alternative to -s\n\
55   -t SVCTYPE       Service Type URI. Used for discovery. Mandatory (omitting -t\n\
56                    causes authorization only mode, provided that -az was specified).\n\
57   -dynclireg       Invoke Dynamic Client Registration client to call Az Server's client registration endpoint\n\
58   -swstmt file     (Optional) File containing signed Software Statement for dynreg\n\
59   -iat IAT         (Optional) Initial Access Token for dynamic client registration\n\
60   -ua IDP UAT      (Optional) Token for _uma_authn query string passed to OpenID-Connect server\n\
61   -client_id ID    client_id (same as returned by dynamic client registration)\n\
62   -client_secret SS  client_secret (same as returned by dynamic client registration)\n\
63   -rr NAME ICON_URI SCOPE TYPE Perform OAUTH2 Resource Set Registration\n\
64   -u EPURL         Optional endpoint URL or ProviderID. Discovery must match this.\n\
65   -di DISCOOPTS    Optional discovery options. Query string format.\n\
66   -din N           Discovery index (default: 1=pick first).\n\
67   -az AZCREDS      Optional authorization credentials. Query string format.\n\
68                    N.B. For authorization to work PDP_URL configuration option is needed.\n\
69   -im DSTEID       Map session's login identity to identity at some other SP using ID-WSF\n\
70   -nidmap DSTEID   Map session's login identity to identity at some other SP using SAML\n\
71   -e ODY           Pass request body as argument (default is to read from STDIN)\n\
72   -b               In response, only return content of response body, omitting headers.\n\
73   -nd              Discovery only (you need to specify -t SVCTYPE as well)\n\
74   -n               Dryrun. Do not actually make call. Instead print it to stdout.\n\
75   -l               List EPR cache (you need to specify -s SEDID or -a as well)\n\
76   -v               Verbose messages.\n\
77   -q               Be extra quiet.\n\
78   -d               Turn on debugging.\n\
79   -dc              Dump config.\n\
80   -h               This help message\n\
81   --               End of options\n\
82 \n\
83 echo '<query>Foo</query>' | zxcall -a https://idp.tas3.eu/zxididp?o=B user:pw -t urn:x-demo-svc\n\
84 \n";
85 
86 int dryrun  = 0;
87 int verbose = 1;
88 int out_fmt = 0;
89 int din = 1;
90 int di_only = 0;
91 /* int ssos = 0;    -nssos           SSOS only (you need to specify -a IDP USER:PW as well)\n\ */
92 int listses = 0;
93 int dynclireg = 0;
94 extern char* iat;          /* see zxidoauth.c */
95 extern char* _uma_authn;   /* see zxidoauth.c */
96 char* swstmt = 0;
97 char* client_id;
98 char* client_secret;
99 char* rsrc_name = 0;
100 char* rsrc_icon_uri = 0;
101 char* rsrc_scope_url = 0;
102 char* rsrc_type = 0;
103 char* entid = 0;
104 char* idp   = 0;
105 char* user  = 0;
106 char* sid = 0;
107 char* svc = 0;
108 char* url = 0;
109 char* di  = 0;
110 char* az  = 0;
111 char* im_to = 0;
112 char* nidmap_to = 0;
113 char* bdy = 0;
114 zxid_conf* cf;
115 
116 /* Called by:  main x8, zxbusd_main, zxbuslist_main, zxbustailf_main, zxcall_main, zxcot_main, zxdecode_main */
opt(int * argc,char *** argv,char *** env)117 static void opt(int* argc, char*** argv, char*** env)
118 {
119   struct zx_str* ss;
120   if (*argc <= 1) return;
121 
122   while (1) {
123     ++(*argv); --(*argc);
124 
125     if (!(*argc) || ((*argv)[0][0] != '-')) break;  /* normal exit from options loop */
126 
127     switch ((*argv)[0][1]) {
128     case '-': if ((*argv)[0][2]) break;
129       ++(*argv); --(*argc);
130       DD("End of options by --");
131       return;  /* -- ends the options */
132 
133     case 'a':
134       switch ((*argv)[0][2]) {
135       case '\0':
136 	++(*argv); --(*argc);
137 	if ((*argc) < 2) break;
138 	idp = (*argv)[0];
139 	++(*argv); --(*argc);
140 	user = (*argv)[0];
141 	continue;
142       case 'z':
143 	++(*argv); --(*argc);
144 	if ((*argc) < 1) break;
145 	az = (*argv)[0];
146 	continue;
147       }
148       break;
149 
150     case 'b':
151       switch ((*argv)[0][2]) {
152       case '\0':
153 	++out_fmt;
154 	continue;
155       }
156       break;
157 
158     case 'c':
159       switch ((*argv)[0][2]) {
160       case '\0':
161 	++(*argv); --(*argc);
162 	if ((*argc) < 1) break;
163 	zxid_parse_conf(cf, (*argv)[0]);
164 	continue;
165       case 'l':
166 	if (!strcmp((*argv)[0],"-client_id")) {
167 	  ++(*argv); --(*argc);
168 	  if ((*argc) < 1) break;
169 	  client_id = (*argv)[0];
170 	  continue;
171 	}
172 	if (!strcmp((*argv)[0],"-client_secret")) {
173 	  ++(*argv); --(*argc);
174 	  if ((*argc) < 1) break;
175 	  client_secret = (*argv)[0];
176 	  continue;
177 	}
178 	break;
179       }
180       break;
181 
182     case 'd':
183       switch ((*argv)[0][2]) {
184       case '\0':
185 	++errmac_debug;
186 	if (errmac_debug == 2)
187 	  strncpy(errmac_instance, "\t\e[43mzxuma\e[0m", sizeof(errmac_instance));
188 	continue;
189       case 'i':
190         switch ((*argv)[0][3]) {
191 	case '\0':
192 	  ++(*argv); --(*argc);
193 	  if ((*argc) < 1) break;
194 	  di = (*argv)[0];
195 	  continue;
196 	case 'n':
197 	  ++(*argv); --(*argc);
198 	  if ((*argc) < 1) break;
199 	  sscanf((*argv)[0], "%i", &din);
200 	  continue;
201 	}
202 	break;
203       case 'c':
204 	ss = zxid_show_conf(cf);
205 	if (verbose>1) {
206 	  printf("\n======== CONF ========\n%.*s\n^^^^^^^^ CONF ^^^^^^^^\n",ss->len,ss->s);
207 	  exit(0);
208 	}
209 	fprintf(stderr, "\n======== CONF ========\n%.*s\n^^^^^^^^ CONF ^^^^^^^^\n",ss->len,ss->s);
210 	continue;
211       case 'y':
212 	if (!strcmp((*argv)[0],"-dynclireg")) {
213 	  ++(*argv); --(*argc);
214 	  ++dynclireg;
215 	  continue;
216 	}
217 	break;
218       }
219       break;
220 
221     case 'e':
222       switch ((*argv)[0][2]) {
223       case '\0':
224 	++(*argv); --(*argc);
225 	if ((*argc) < 1) break;
226 	bdy = (*argv)[0];
227 	continue;
228       }
229       break;
230 
231     case 'i':
232       switch ((*argv)[0][2]) {
233       case 'm':
234 	++(*argv); --(*argc);
235 	if ((*argc) < 1) break;
236 	im_to = (*argv)[0];
237 	continue;
238       case 'a':
239 	switch ((*argv)[0][3]) {
240 	case 't':
241 	  ++(*argv); --(*argc);
242 	  if ((*argc) < 1) break;
243 	  iat = (*argv)[0];
244 	  continue;
245 	}
246 	break;
247       }
248       break;
249 
250     case 'l':
251       switch ((*argv)[0][2]) {
252       case '\0':
253 	++listses;
254 	continue;
255 #if 0
256       case 'i':
257 	if (!strcmp((*argv)[0],"-license")) {
258 	  extern char* license;
259 	  fprintf(stderr, license);
260 	  exit(0);
261 	}
262 	break;
263 #endif
264       }
265       break;
266 
267     case 'n':
268       switch ((*argv)[0][2]) {
269       case 'd':
270 	++di_only;
271 	continue;
272 #if 0
273       case 's':
274 	if (!strcmp((*argv)[0],"-nssos")) {
275 	  ++ssos;
276 	  continue;
277 	}
278 	break;
279 #endif
280       case 'i':
281 	if (!strcmp((*argv)[0],"-nidmap")) {
282 	  ++(*argv); --(*argc);
283 	  if ((*argc) < 1) break;
284 	  nidmap_to = (*argv)[0];
285 	  continue;
286 	}
287 	break;
288       case '\0':
289 	++dryrun;
290 	continue;
291       }
292       break;
293 
294     case 'q':
295       switch ((*argv)[0][2]) {
296       case '\0':
297 	verbose = 0;
298 	continue;
299       }
300       break;
301 
302     case 'r':
303       switch ((*argv)[0][2]) {
304       case 'r':
305 	++(*argv); --(*argc);
306 	if ((*argc) < 4) break;
307 	rsrc_name = (*argv)[0];
308 	rsrc_icon_uri = (*argv)[1];
309 	rsrc_scope_url = (*argv)[2];
310 	rsrc_type = (*argv)[3];
311 	++(*argv); --(*argc);
312 	++(*argv); --(*argc);
313 	++(*argv); --(*argc);
314 	continue;
315       }
316       break;
317 
318     case 's':
319       switch ((*argv)[0][2]) {
320       case '\0':
321 	++(*argv); --(*argc);
322 	if ((*argc) < 1) break;
323 	sid = (*argv)[0];
324 	continue;
325       }
326       break;
327 
328     case 't':
329       switch ((*argv)[0][2]) {
330       case '\0':
331 	++(*argv); --(*argc);
332 	if ((*argc) < 1) break;
333 	svc = (*argv)[0];
334 	continue;
335       }
336       break;
337 
338     case 'u':
339       switch ((*argv)[0][2]) {
340       case '\0':
341 	++(*argv); --(*argc);
342 	if ((*argc) < 1) break;
343 	url = (*argv)[0];
344 	continue;
345       case 'a':
346 	++(*argv); --(*argc);
347 	if ((*argc) < 2) break;
348 	idp = (*argv)[0];
349 	_uma_authn = (*argv)[1];
350 	++(*argv); --(*argc);
351 	continue;
352       }
353       break;
354 
355     case 'v':
356       switch ((*argv)[0][2]) {
357       case '\0':
358 	++verbose;
359 	continue;
360       }
361       break;
362 
363     }
364     /* fall thru means unrecognized flag */
365     if (*argc)
366       fprintf(stderr, "Unrecognized flag `%s'\n", (*argv)[0]);
367 help:
368     if (verbose>1) {
369       printf("%s", help);
370       exit(0);
371     }
372     fprintf(stderr, "%s", help);
373     /*fprintf(stderr, "version=0x%06x rel(%s)\n", zxid_version(), zxid_version_str());*/
374     exit(3);
375   }
376 #if 0
377   if (!sid && !idp) {
378     fprintf(stderr, "MUST specify either -s or -a\n");
379     goto help;
380   }
381 #endif
382 }
383 
384 
385 /*() List session and especially its EPR cache to stdout.
386  * Typical name: /var/zxid/ses/SESID/SVCTYPE,SHA1
387  *
388  * cf:: ZXID configuration object, also used for memory allocation
389  * ses:: Session object in whose EPR cache the file is searched
390  *
391  * See also: zxid_find_epr() */
392 
393 /* Called by:  zxcall_main */
zxid_print_session(zxid_conf * cf,zxid_ses * ses)394 int zxid_print_session(zxid_conf* cf, zxid_ses* ses)
395 {
396   struct zx_root_s* r;
397   int epr_len, din = 0;
398   char path[ZXID_MAX_BUF];
399   char* epr_buf;  /* MUST NOT come from stack. */
400   DIR* dir;
401   struct dirent * de;
402   zxid_epr* epr;
403   struct zx_a_Metadata_s* md;
404   struct zx_str* ss;
405 
406   D_INDENT("lstses: ");
407 
408   if (!name_from_path(path, sizeof(path), "%s" ZXID_SES_DIR "%s", cf->cpath, ses->sid)) {
409     D_DEDENT("lstses: ");
410     return 0;
411   }
412 
413   printf("SESID:  %s\nSESDIR: %s\n", ses->sid, path);
414   dir = opendir(path);
415   if (!dir) {
416     perror("opendir to find epr in session");
417     ERR("Opening session for find epr by opendir failed path(%s) sesptr=%p", path, ses);
418     D_DEDENT("lstses: ");
419     return 0;
420   }
421 
422   while (de = readdir(dir)) {
423     D("%d Considering file(%s)", din, de->d_name);
424     if (de->d_name[0] == '.')  /* . .. and "hidden" files */
425       continue;
426     if (de->d_name[strlen(de->d_name)-1] == '~')  /* Ignore backups from hand edited EPRs. */
427       continue;
428     D("%d Checking EPR content file(%s)", din, de->d_name);
429     epr_buf = read_all_alloc(cf->ctx, "lstses", 1, &epr_len,
430 			     "%s" ZXID_SES_DIR "%s/%s", cf->cpath, ses->sid, de->d_name);
431     if (!epr_buf)
432       continue;
433 
434     r = zx_dec_zx_root(cf->ctx, epr_len, epr_buf, "lstses");
435     if (!r || !r->EndpointReference) {
436       ERR("No EPR found. Failed to parse epr_buf(%.*s)", epr_len, epr_buf);
437       continue;
438     }
439     epr = r->EndpointReference;
440     ZX_FREE(cf->ctx, r);
441 
442     md = epr->Metadata;
443     if (!md || !ZX_SIMPLE_ELEM_CHK(md->ServiceType)) {
444       ERR("No Metadata %p or ServiceType. Failed to parse epr_buf(%.*s)", md, epr_len, epr_buf);
445       printf("EPR %d no service type\n", ++din);
446     } else {
447       ss = ZX_GET_CONTENT(md->ServiceType);
448       printf("EPR %d SvcType: %.*s\n", ++din, ss->len, ss->s);
449     }
450     ss = zxid_get_epr_address(cf, epr);
451     printf("  URL:         %.*s\n", ss?ss->len:0, ss?ss->s:"");
452     ss = zxid_get_epr_entid(cf, epr);
453     printf("  EntityID:    %.*s\n", ss?ss->len:0, ss?ss->s:"");
454     ss = zxid_get_epr_desc(cf, epr);
455     printf("  Description: %.*s\n", ss?ss->len:0, ss?ss->s:"");
456   }
457   ZX_FREE(cf->ctx, epr_buf);
458   closedir(dir);
459   D_DEDENT("lstses: ");
460   return 0;
461 }
462 
463 #if 0
464 char* zxid_scan_quoted(zxid_conf* cf, char* p, char** val, const char* res, comma_expected)
465 {
466   char quote;
467   char* p;
468   if (ONE_OF_2(*val, '"', '\'')) {
469     quote = *val;
470     ++val;
471     p = strchr(val, quote);
472     if (!p) {
473       ERR("Mismatched quote res(%s)", res);
474       return 0;
475     }
476     val = zx_dup_len_cstr(cf->ctx, val, p-val);
477     ++p;
478     if (comma_expected) {
479       if (*p != ',') {
480 	ERR("Comma expected res(%s)", res);
481 	return 0;
482       }
483     }
484     ++p;
485   } else {
486     p = strchr(val, ',');
487     if (comma_expected) {
488       if (!p) {
489 	ERR("Comma expected res(%s)", res);
490 	return 0;
491       }
492     } else {
493 
494     }
495     val = zx_dup_len_cstr(cf->ctx, val, p-val);
496     ++p;
497   }
498   return val;
499 
500   // *** buggy
501 }
502 #endif
503 
504 /*(i) Make a HTTP POST call given payload string.
505  * The call is protected by UMA.
506  *
507  * cf:: ZXID configuration object, see zxid_new_conf()
508  * ses:: Session object that contains the EPR cache
509  * svctype:: URI (often the namespace URI) specifying the kind of service we
510  *     wish to call. Used for EPR lookup or discovery.
511  * url:: (Optional) If provided, this argument has to match either
512  *     the ProviderID, EntityID, or actual service endpoint URL.
513  * di_opt:: (Optional) Additional discovery options for selecting the
514  *     service, query string format
515  * az_cred:: (Optional) Additional authorization credentials or
516  *     attributes, query string format. These credentials will be populated
517  *     to the attribute pool in addition to the ones obtained from SSO and
518  *     other sources. Then a PDP is called to get an authorization decision
519  *     (as well as obligations we pledge to support). See also PEPMAP
520  *     configuration option. This implementes generalized (application
521  *     independent) Requestor Out and Requestor In PEPs. To implement
522  *     application dependent PEP features you should call zxid_az() directly.
523  * req:: request payload as string
524  * return:: SOAP Envelope of the response, as a string. You can parse this
525  *     string to obtain all returned SOAP headers as well as the Body and its
526  *     content. NULL on failure. ses->curflt and/or ses->curstatus contain
527  *     more detailed error information. */
528 
529 /* Called by:  zxcall_main, zxid_callf */
zxid_uma_call(zxid_conf * cf,zxid_ses * ses,const char * svctype,const char * url,const char * di_opt,const char * az_cred,const char * req)530 struct zx_str* zxid_uma_call(zxid_conf* cf, zxid_ses* ses, const char* svctype, const char* url, const char* di_opt, const char* az_cred, const char* req)
531 {
532   long rc;
533   struct zx_str* res;
534   char* p;
535   char* realm;
536   char* host_id;
537   char* as_uri;
538   char* error;
539   char* ticket = 0;
540   char* azhdr = 0;
541   /* *** try
542 GET /umaprotected/resource HTTP/1.1
543 
544 HTTP/1.1 401 Unauthorized
545    WWW-Authenticate: UMA realm="example",
546     host_id="photoz.example.com",
547     as_uri="https://as.example.com"
548 
549 *** obtain RPT by calling AS with AAT
550 
551 GET /umaprotected/resource HTTP/1.1
552 Authorization: Bearer RPT
553 
554 (see ResourceServer registers the desired permissions)
555 
556 HTTP/1.1 403 Forbidden
557    WWW-Authenticate: UMA realm="example",
558     host_id="photoz.example.com",
559     as_uri="https://as.example.com",
560     error="insufficient_scope"
561 
562 {
563 "ticket": "016f84e8-f9b9-11e0-bd6f-0021cc6004de"
564 }
565 
566 OR
567 
568 HTTP/1.1 200 OK
569 
570 *** ResourceServer registers the desired permissions
571 
572 POST /host/scope_reg_uri/photoz.example.com HTTP/1.1
573 Content-Type: application/json
574 Host: as.example.com
575 
576 {
577   "resource_set_id": "112210f47de98100",
578   "scopes": [
579       "http://photoz.example.com/dev/actions/view",
580       "http://photoz.example.com/dev/actions/all"
581   ]
582 }
583 
584 HTTP/1.1 201 Created
585 Content-Type: application/json
586 Location: https://as.example.com/permreg/host/photoz.example.com/5454345rdsaa4543
587 ...
588 
589 {
590 "ticket": "016f84e8-f9b9-11e0-bd6f-0021cc6004de"
591 }
592 
593 *** With ticket, enhance the RPT
594 
595   */
596 
597   while (1) {
598     curl_easy_setopt(cf->curl, CURLOPT_HEADER, 1);
599     res = zxid_http_cli(cf, -1, url, -1, req, "application/json", azhdr, 0x01);
600     if (!res) {
601       ERR("Call to url(%s) failed", url);
602       return 0;
603     }
604     curl_easy_getinfo(cf->curl, CURLINFO_RESPONSE_CODE, &rc);
605     switch (rc) {
606     case 200:
607       p = strstr(res->s, CRLF CRLF);  /* find where headers end */
608       if (!p) {
609 	ERR("Failed to find empty line separating headers from the body(%s)", res->s);
610 	return res;
611       }
612       p = zx_dup_cstr(cf->ctx, p + sizeof(CRLF CRLF)-1);
613       ZX_FREE(cf->ctx, res->s);
614       res->s = p;
615       return res;
616     case 401:
617 #if 0
618       p = strstr(res->s, "WWW-Authenticate: UMA realm=");
619       if (!p) {
620 	ERR("Failed to find WWW-Authenticate header or it is not of uma type res(%s)", res->s);
621 	return res;
622       }
623       p = p + sizeof("WWW-Authenticate: UMA realm=")+1;
624       p = zxid_scan_quoted(cf, p, &realm);
625 
626       p = strstr(p, "host_id=");
627       p = p + sizeof("host_id=")+1;
628       p = zxid_scan_quoted(cf, p, &host_id);
629 #else
630       if (sscanf(res->s, "WWW-Authenticate: UMA realm=\"%m[^\"]\" , host_id=\"%m[^\"]\", as_uri=\"%m[^\"]\"",
631 		  &realm, &host_id, &as_uri) != 3) {
632 	ERR("Failed to find WWW-Authenticate header or it is not correctly formatted for UMA. res(%s)", res->s);
633 	return res;
634       }
635 #endif
636 
637       // Call AS to get RPT
638       zxid_oauth_call_rpt_endpoint(cf, ses, host_id, as_uri);
639       azhdr = zx_alloc_sprintf(cf->ctx, 0, "Authorization: Bearer %s", ses->rpt);
640       break;
641 
642     case 403:
643       if (sscanf(res->s, "WWW-Authenticate: UMA realm=\"%m[^\"]\" , host_id=\"%m[^\"]\", as_uri=\"%m[^\"]\", error=\"%m[^\"]\"",
644 		  &realm, &host_id, &as_uri, &error) != 4) {
645 	ERR("Failed to find WWW-Authenticate header or it is not correctly formatted for UMA. res(%s)", res->s);
646 	return res;
647       }
648       if (strcmp(error, "insufficient_scope")) {
649 	ERR("Expected error insufficient_scope, got(%s)", error);
650 	return res;
651       }
652       p = strstr(res->s, CRLF CRLF);  /* find where headers end */
653       if (!p) {
654 	ERR("Failed to find empty line separating headers from the body(%s)", res->s);
655 	return res;
656       }
657       ticket = zx_json_extract_dup(cf->ctx, res->s, "\"ticket\"");
658 
659       // Call AS to get more perms
660       zxid_oauth_call_az_endpoint(cf, ses, host_id, as_uri, ticket);
661       azhdr = zx_alloc_sprintf(cf->ctx, 0, "Authorization: Bearer %s", ses->rpt);
662       break;
663 
664     default:
665       ERR("Unexpected HTTP response %ld", rc);
666       return 0;
667     }
668   }
669 }
670 
671 #ifndef zxumacall_main
672 #define zxumacall_main main
673 #endif
674 
675 /*() Web Services Client tool */
676 
677 /* Called by: */
zxumacall_main(int argc,char ** argv,char ** env)678 int zxumacall_main(int argc, char** argv, char** env)
679 {
680   int siz, got, n;
681   char* p;
682   struct zx_str* ss;
683   zxid_ses* ses = 0;
684   zxid_entity* idp_meta;
685   zxid_epr* epr;
686 
687   strncpy(errmac_instance, CC_CYNY("\tzxuma"), sizeof(errmac_instance));
688   cf = zxid_new_conf_to_cf(0);
689   opt(&argc, &argv, &env);
690 
691   if (dynclireg) {
692     ses = zxid_alloc_ses(cf);
693     ss = zxid_oauth_dynclireg_client(cf, 0, ses, url);
694     printf("%*s", ss->len, ss->s);
695     return 0;
696   }
697 
698   if (sid) {
699     D("Existing session sesid(%s)", sid);
700     ses = zxid_fetch_ses(cf, sid);
701     if (!ses) {
702       ERR("Session not found or error in session sesid(%s)", sid);
703       return 1;
704     }
705   } else {
706     D("Obtain session from authentication service(%s)", idp);
707     idp_meta = zxid_get_ent(cf, idp);
708     if (!idp_meta) {
709       ERR("IdP metadata not found and could not be fetched. idp(%s)", idp);
710       return 1;
711     }
712     if (user) {
713       for (p = user; !ONE_OF_2(*p, ':', 0); ++p) ;
714       if (*p)
715 	*p++ = 0;
716       ses = zxid_as_call(cf, idp_meta, user, p);
717     } else if (_uma_authn) {
718       ses = zxid_alloc_ses(cf);
719       zxid_oidc_as_call(cf, ses, idp_meta, _uma_authn);
720     }
721     if (!ses) {
722       ERR("Login using Authentication Service failed idp(%s)", idp);
723       return 1;
724     }
725     INFO("Logged in. Session in %s" ZXID_SES_DIR "%s\n%s", cf->cpath, STRNULLCHKD(ses->sid),STRNULLCHKD(ses->access_token));
726   }
727 
728   if (rsrc_name) {
729     if (!ses->client_secret) {
730       if (!ses)
731 	ses = zxid_alloc_ses(cf);
732       if (client_id) {
733 	ses->client_id = client_id;
734 	ses->client_secret = client_secret;
735       } else
736 	zxid_oauth_dynclireg_client(cf, 0, ses, url);
737     }
738     zxid_oauth_rsrcreg_client(cf, 0, ses, url, rsrc_name, rsrc_icon_uri, rsrc_scope_url, rsrc_type);
739     return 0;
740   }
741 
742   if (listses)
743     return zxid_print_session(cf, ses);
744 
745   if (im_to) {
746     D("ID-WSF Map to identity at eid(%s)", im_to);
747     zxid_map_identity_token(cf, ses, im_to, 0);
748     //printf("%.*s\n", ZX_GET_CONTENT_LEN(nameid), ZX_GET_CONTENT_S(nameid));
749     return 0;
750   }
751 
752   if (nidmap_to) {
753     D("SAML Map to identity at eid(%s)", nidmap_to);
754     zxid_nidmap_identity_token(cf, ses, nidmap_to, 0);
755     //printf("%.*s\n", ZX_GET_CONTENT_LEN(nameid), ZX_GET_CONTENT_S(nameid));
756     return 0;
757   }
758 
759   if (di_only) {
760     D("Discover only. svctype(%s), dindex=%d", STRNULLCHK(svc), din);
761     epr = zxid_get_epr(cf, ses, svc, url, di, 0 /*action*/, din);
762     if (!epr) {
763       ERR("Discovery failed to find any epr of service type(%s)", STRNULLCHK(svc));
764       return 3;
765     }
766     for (din = 1; ;++din) {
767       epr = zxid_get_epr(cf, ses, svc, url, di, 0 /*action*/, din);
768       if (!epr)
769 	break;
770       printf("%d. Found epr for service type(%s)\n", din, STRNULLCHK(svc));
771       ss = zxid_get_epr_desc(cf, epr);
772       printf("   Description: %.*s\n", ss?ss->len:0, ss?ss->s:"");
773       ss = zxid_get_epr_address(cf, epr);
774       printf("   EPURL:       %.*s\n", ss?ss->len:0, ss?ss->s:"");
775       ss = zxid_get_epr_entid(cf, epr);
776       printf("   EntityID:    %.*s\n", ss?ss->len:0, ss?ss->s:"");
777     }
778     return 0;
779   }
780 
781   if (svc) {
782     D("Call service svctype(%s)", svc);
783     if (!bdy) {
784       if (verbose)
785 	fprintf(stderr, "Reading request body from stdin...\n");
786       siz = 4096;
787       p = bdy = ZX_ALLOC(cf->ctx, siz);
788       while (1) {
789 	n = read_all_fd(fdstdin, p, siz+bdy-p-1, &got);
790 	if (n == -1) {
791 	  perror("reading SOAP req from stdin");
792 	  break;
793 	}
794 	p += got;
795 	if (got < siz+bdy-p-1) break;
796 	siz += 60*1024;
797 	REALLOCN(bdy, siz);
798       }
799       *p = 0;
800     }
801     if (dryrun) {
802       if (verbose)
803 	fprintf(stderr, "Dryrun. Call aborted.\n");
804       return 0;
805     }
806     if (verbose)
807       fprintf(stderr, "Calling...\n");
808 
809 #if 1
810     ss = zxid_uma_call(cf, ses, svc, url, di, az, bdy);
811     printf("%.*s", ss->len, ss->s);
812 #else
813     ss = zxid_call(cf, ses, svc, url, di, az, bdy);
814     if (!ss || !ss->s) {
815       ERR("Call failed %p", ss);
816       return 2;
817     }
818     if (verbose)
819       fprintf(stderr, "Done. Call returned %d bytes.\n", ss->len);
820     if (out_fmt) {
821       p = zxid_extract_body(cf, ss->s);
822       printf("%s", p);
823     } else
824       printf("%.*s", ss->len, ss->s);
825 #endif
826   } else if (az) {
827     D("Call Az(%s)", az);
828     if (dryrun) {
829       if (verbose)
830 	fprintf(stderr, "Dryrun. zxid_az() aborted.\n");
831       return 0;
832     }
833     if (zxid_az_cf_ses(cf, az, ses)) {
834       if (verbose)
835 	fprintf(stderr, "Permit.\n");
836       return 0;
837     } else {
838       if (verbose)
839 	fprintf(stderr, "Deny.\n");
840       return 1;
841     }
842   } else {
843     D("Neither service type (-t) nor -az supplied. Performed only authentication. %d",0);
844     if (verbose)
845       fprintf(stderr, "Authentication only.\n");
846   }
847   return 0;
848 }
849 
850 /* EOF  --  zxcall.c */
851