1 /* zxcall.c  -  Web Service Client tool
2  * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3  * This is confidential unpublished proprietary source code of the author.
4  * NO WARRANTY, not even implied warranties. Contains trade secrets.
5  * Distribution prohibited unless authorized in writing.
6  * Licensed under Apache License 2.0, see file COPYING.
7  * $Id: zxcot.c,v 1.5 2009-11-29 12:23:06 sampo Exp $
8  *
9  * 27.8.2009, created --Sampo
10  */
11 
12 #include "platform.h"  /* for dirent.h */
13 
14 #include <string.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 
19 #include "platform.h"
20 #include "errmac.h"
21 #include "zx.h"
22 #include "zxid.h"
23 #include "zxidutil.h"
24 #include "zxidconf.h"
25 #include "c/zxidvers.h"
26 #include "c/zx-const.h"
27 #include "c/zx-ns.h"
28 #include "c/zx-data.h"
29 
30 char* help =
31 "zxcall  -  Web Service Client tool R" ZXID_REL "\n\
32 SAML 2.0 and ID-WSF 2.0 are standards for federated identity and web services.\n\
33 Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.\n\
34 NO WARRANTY, not even implied warranties. Licensed under Apache License v2.0\n\
35 See http://www.apache.org/licenses/LICENSE-2.0\n\
36 Send well researched bug reports to the author. Home: zxid.org\n\
37 \n\
38 Usage: zxcall [options] -s SESID -t SVCTYPE <soap_req_body.xml >soap_resp.xml\n\
39        zxcall [options] -a IDP USER:PW -t SVCTYPE <soap_req_body.xml >soap_resp.xml\n\
40        zxcall [options] -a IDP USER:PW -t SVCTYPE -nd # Discovery only\n\
41        zxcall [options] -a IDP USER:PW   # Authentication only\n\
42        zxcall [options] -s SESID -im EID # Identity Mapping to EID\n\
43        zxcall [options] -s SESID -l      # List session cache\n\
44   -c CONF          Optional configuration string (default -c CPATH=/var/zxid/)\n\
45                    Most of the configuration is read from " ZXID_CONF_PATH "\n\
46   -s SESID         Session ID referring to a directory in /var/zxid/ses\n\
47                    Use zxidhlo to do SSO and then cut and paste from there.\n\
48   -a IDP USER:PW   Use Authentication service to authenticate the user and\n\
49                    create session. IDP is IdP's Entity ID. This is alternative to -s\n\
50   -t SVCTYPE       Service Type URI. Used for discovery. Mandatory (omitting -t\n\
51                    causes authorization only mode, provided that -az was specified).\n\
52   -u EPURL         Optional endpoint URL or ProviderID. Discovery must match this.\n\
53   -di DISCOOPTS    Optional discovery options. Query string format.\n\
54   -din N           Discovery index (default: 1=pick first).\n\
55   -az AZCREDS      Optional authorization credentials. Query string format.\n\
56                    N.B. For authorization to work PDP_URL configuration option is needed.\n\
57   -im DSTEID       Map session's login identity to identity at some other SP using ID-WSF\n\
58   -nidmap DSTEID   Map session's login identity to identity at some other SP using SAML\n\
59   -e SOAPBODY      Pass SOAP body as argument (default is to read from STDIN)\n\
60   -b               In response, only return content of SOAP body, omitting Envelope and Body.\n\
61   -nd              Discovery only (you need to specify -t SVCTYPE as well)\n\
62   -n               Dryrun. Do not actually make call. Instead print it to stdout.\n\
63   -l               List EPR cache (you need to specify -s SEDID or -a as well)\n\
64   -v               Verbose messages.\n\
65   -q               Be extra quiet.\n\
66   -d               Turn on debugging.\n\
67   -dc              Dump config.\n\
68   -h               This help message\n\
69   --               End of options\n\
70 \n\
71 echo '<query>Foo</query>' | zxcall -a https://idp.tas3.eu/zxididp?o=B user:pw -t urn:x-demo-svc\n\
72 \n";
73 
74 int dryrun  = 0;
75 int verbose = 1;
76 int out_fmt = 0;
77 int din = 1;
78 int di_only = 0;
79 /* int ssos = 0;    -nssos           SSOS only (you need to specify -a IDP USER:PW as well)\n\ */
80 int listses = 0;
81 char* entid = 0;
82 char* idp   = 0;
83 char* user  = 0;
84 char* sid = 0;
85 char* svc = 0;
86 char* url = 0;
87 char* di  = 0;
88 char* az  = 0;
89 char* im_to = 0;
90 char* nidmap_to = 0;
91 char* bdy = 0;
92 zxid_conf* cf;
93 
94 /* Called by:  main x8, zxbusd_main, zxbuslist_main, zxbustailf_main, zxcall_main, zxcot_main, zxdecode_main */
opt(int * argc,char *** argv,char *** env)95 static void opt(int* argc, char*** argv, char*** env)
96 {
97   struct zx_str* ss;
98   if (*argc <= 1) return;
99 
100   while (1) {
101     ++(*argv); --(*argc);
102 
103     if (!(*argc) || ((*argv)[0][0] != '-')) break;  /* normal exit from options loop */
104 
105     switch ((*argv)[0][1]) {
106     case '-': if ((*argv)[0][2]) break;
107       ++(*argv); --(*argc);
108       DD("End of options by --");
109       return;  /* -- ends the options */
110 
111     case 'a':
112       switch ((*argv)[0][2]) {
113       case '\0':
114 	++(*argv); --(*argc);
115 	if ((*argc) < 2) break;
116 	idp = (*argv)[0];
117 	++(*argv); --(*argc);
118 	user = (*argv)[0];
119 	continue;
120       case 'z':
121 	++(*argv); --(*argc);
122 	if ((*argc) < 1) break;
123 	az = (*argv)[0];
124 	continue;
125       }
126       break;
127 
128     case 'b':
129       switch ((*argv)[0][2]) {
130       case '\0':
131 	++out_fmt;
132 	continue;
133       }
134       break;
135 
136     case 'c':
137       switch ((*argv)[0][2]) {
138       case '\0':
139 	++(*argv); --(*argc);
140 	if ((*argc) < 1) break;
141 	zxid_parse_conf(cf, (*argv)[0]);
142 	continue;
143       }
144       break;
145 
146     case 'd':
147       switch ((*argv)[0][2]) {
148       case '\0':
149 	++errmac_debug;
150 	if (errmac_debug == 2)
151 	  strncpy(errmac_instance, "\t\e[43mzxcall\e[0m", sizeof(errmac_instance));
152 	continue;
153       case 'i':
154         switch ((*argv)[0][3]) {
155 	case '\0':
156 	  ++(*argv); --(*argc);
157 	  if ((*argc) < 1) break;
158 	  di = (*argv)[0];
159 	  continue;
160 	case 'n':
161 	  ++(*argv); --(*argc);
162 	  if ((*argc) < 1) break;
163 	  sscanf((*argv)[0], "%i", &din);
164 	  continue;
165 	}
166 	break;
167       case 'c':
168 	ss = zxid_show_conf(cf);
169 	if (verbose>1) {
170 	  printf("\n======== CONF ========\n%.*s\n^^^^^^^^ CONF ^^^^^^^^\n",ss->len,ss->s);
171 	  exit(0);
172 	}
173 	fprintf(stderr, "\n======== CONF ========\n%.*s\n^^^^^^^^ CONF ^^^^^^^^\n",ss->len,ss->s);
174 	continue;
175       }
176       break;
177 
178     case 'e':
179       switch ((*argv)[0][2]) {
180       case '\0':
181 	++(*argv); --(*argc);
182 	if ((*argc) < 1) break;
183 	bdy = (*argv)[0];
184 	continue;
185       }
186       break;
187 
188     case 'i':
189       switch ((*argv)[0][2]) {
190       case 'm':
191 	++(*argv); --(*argc);
192 	if ((*argc) < 1) break;
193 	im_to = (*argv)[0];
194 	continue;
195       }
196       break;
197 
198     case 'l':
199       switch ((*argv)[0][2]) {
200       case '\0':
201 	++listses;
202 	continue;
203 #if 0
204       case 'i':
205 	if (!strcmp((*argv)[0],"-license")) {
206 	  extern char* license;
207 	  fprintf(stderr, license);
208 	  exit(0);
209 	}
210 	break;
211 #endif
212       }
213       break;
214 
215     case 'n':
216       switch ((*argv)[0][2]) {
217       case 'd':
218 	++di_only;
219 	continue;
220 #if 0
221       case 's':
222 	if (!strcmp((*argv)[0],"-nssos")) {
223 	  ++ssos;
224 	  continue;
225 	}
226 	break;
227 #endif
228       case 'i':
229 	if (!strcmp((*argv)[0],"-nidmap")) {
230 	  ++(*argv); --(*argc);
231 	  if ((*argc) < 1) break;
232 	  nidmap_to = (*argv)[0];
233 	  continue;
234 	}
235 	break;
236       case '\0':
237 	++dryrun;
238 	continue;
239       }
240       break;
241 
242     case 'q':
243       switch ((*argv)[0][2]) {
244       case '\0':
245 	verbose = 0;
246 	continue;
247       }
248       break;
249 
250     case 's':
251       switch ((*argv)[0][2]) {
252       case '\0':
253 	++(*argv); --(*argc);
254 	if ((*argc) < 1) break;
255 	sid = (*argv)[0];
256 	continue;
257       }
258       break;
259 
260     case 't':
261       switch ((*argv)[0][2]) {
262       case '\0':
263 	++(*argv); --(*argc);
264 	if ((*argc) < 1) break;
265 	svc = (*argv)[0];
266 	continue;
267       }
268       break;
269 
270     case 'u':
271       switch ((*argv)[0][2]) {
272       case '\0':
273 	++(*argv); --(*argc);
274 	if ((*argc) < 1) break;
275 	url = (*argv)[0];
276 	continue;
277       }
278       break;
279 
280     case 'v':
281       switch ((*argv)[0][2]) {
282       case '\0':
283 	++verbose;
284 	continue;
285       }
286       break;
287 
288     }
289     /* fall thru means unrecognized flag */
290     if (*argc)
291       fprintf(stderr, "Unrecognized flag `%s'\n", (*argv)[0]);
292 help:
293     if (verbose>1) {
294       printf("%s", help);
295       exit(0);
296     }
297     fprintf(stderr, "%s", help);
298     /*fprintf(stderr, "version=0x%06x rel(%s)\n", zxid_version(), zxid_version_str());*/
299     exit(3);
300   }
301   if (!sid && !idp) {
302     fprintf(stderr, "MUST specify either -s or -a\n");
303     goto help;
304   }
305 }
306 
307 
308 /*() List session and especially its EPR cache to stdout.
309  * Typical name: /var/zxid/ses/SESID/SVCTYPE,SHA1
310  *
311  * cf:: ZXID configuration object, also used for memory allocation
312  * ses:: Session object in whose EPR cache the file is searched
313  *
314  * See also: zxid_find_epr() */
315 
316 /* Called by:  zxcall_main */
zxid_print_session(zxid_conf * cf,zxid_ses * ses)317 int zxid_print_session(zxid_conf* cf, zxid_ses* ses)
318 {
319   struct zx_root_s* r;
320   int epr_len, din = 0;
321   char path[ZXID_MAX_BUF];
322   char* epr_buf;  /* MUST NOT come from stack. */
323   DIR* dir;
324   struct dirent * de;
325   zxid_epr* epr;
326   struct zx_a_Metadata_s* md;
327   struct zx_str* ss;
328 
329   D_INDENT("lstses: ");
330 
331   if (!name_from_path(path, sizeof(path), "%s" ZXID_SES_DIR "%s", cf->cpath, ses->sid)) {
332     D_DEDENT("lstses: ");
333     return 0;
334   }
335 
336   printf("SESID:  %s\nSESDIR: %s\n", ses->sid, path);
337   dir = opendir(path);
338   if (!dir) {
339     perror("opendir to find epr in session");
340     ERR("Opening session for find epr by opendir failed path(%s) sesptr=%p", path, ses);
341     D_DEDENT("lstses: ");
342     return 0;
343   }
344 
345   while (de = readdir(dir)) {
346     D("%d Considering file(%s)", din, de->d_name);
347     if (de->d_name[0] == '.')  /* . .. and "hidden" files */
348       continue;
349     if (de->d_name[strlen(de->d_name)-1] == '~')  /* Ignore backups from hand edited EPRs. */
350       continue;
351     D("%d Checking EPR content file(%s)", din, de->d_name);
352     epr_buf = read_all_alloc(cf->ctx, "lstses", 1, &epr_len,
353 			     "%s" ZXID_SES_DIR "%s/%s", cf->cpath, ses->sid, de->d_name);
354     if (!epr_buf)
355       continue;
356 
357     r = zx_dec_zx_root(cf->ctx, epr_len, epr_buf, "lstses");
358     if (!r || !r->EndpointReference) {
359       ERR("No EPR found. Failed to parse epr_buf(%.*s)", epr_len, epr_buf);
360       continue;
361     }
362     epr = r->EndpointReference;
363     ZX_FREE(cf->ctx, r);
364 
365     md = epr->Metadata;
366     if (!md || !ZX_SIMPLE_ELEM_CHK(md->ServiceType)) {
367       ERR("No Metadata %p or ServiceType. Failed to parse epr_buf(%.*s)", md, epr_len, epr_buf);
368       printf("EPR %d no service type\n", ++din);
369     } else {
370       ss = ZX_GET_CONTENT(md->ServiceType);
371       printf("EPR %d SvcType: %.*s\n", ++din, ss->len, ss->s);
372     }
373     ss = zxid_get_epr_address(cf, epr);
374     printf("  URL:         %.*s\n", ss?ss->len:0, ss?ss->s:"");
375     ss = zxid_get_epr_entid(cf, epr);
376     printf("  EntityID:    %.*s\n", ss?ss->len:0, ss?ss->s:"");
377     ss = zxid_get_epr_desc(cf, epr);
378     printf("  Description: %.*s\n", ss?ss->len:0, ss?ss->s:"");
379   }
380   ZX_FREE(cf->ctx, epr_buf);
381   closedir(dir);
382   D_DEDENT("lstses: ");
383   return 0;
384 }
385 
386 #ifndef zxcall_main
387 #define zxcall_main main
388 #endif
389 
390 /*() Web Services Client tool */
391 
392 /* Called by: */
zxcall_main(int argc,char ** argv,char ** env)393 int zxcall_main(int argc, char** argv, char** env)
394 {
395   int siz, got, n;
396   char* p;
397   struct zx_str* ss;
398   zxid_ses* ses;
399   zxid_entity* idp_meta;
400   zxid_epr* epr;
401 
402   strncpy(errmac_instance, CC_CYNY("\tzxcall"), sizeof(errmac_instance));
403   cf = zxid_new_conf_to_cf(0);
404   opt(&argc, &argv, &env);
405 
406   if (sid) {
407     D("Existing session sesid(%s)", sid);
408     ses = zxid_fetch_ses(cf, sid);
409     if (!ses) {
410       ERR("Session not found or error in session sesid(%s)", sid);
411       return 1;
412     }
413   } else {
414     D("Obtain session from authentication service(%s)", idp);
415     idp_meta = zxid_get_ent(cf, idp);
416     if (!idp_meta) {
417       ERR("IdP metadata not found and could not be fetched. idp(%s)", idp);
418       return 1;
419     }
420     for (p = user; !ONE_OF_2(*p, ':', 0); ++p) ;
421     if (*p)
422       *p++ = 0;
423     ses = zxid_as_call(cf, idp_meta, user, p);
424     if (!ses) {
425       ERR("Login using Authentication Service failed idp(%s)", idp);
426       return 1;
427     }
428     INFO("Logged in. NameID(%s) Session in %s" ZXID_SES_DIR "%s", ses->nid, cf->cpath, ses->sid);
429     sid = ses->sid;
430   }
431 
432   if (listses)
433     return zxid_print_session(cf, ses);
434 
435   if (im_to) {
436     D("ID-WSF Map to identity at eid(%s)", im_to);
437     zxid_map_identity_token(cf, ses, im_to, 0);
438     //printf("%.*s\n", ZX_GET_CONTENT_LEN(nameid), ZX_GET_CONTENT_S(nameid));
439     return 0;
440   }
441 
442   if (nidmap_to) {
443     D("SAML Map to identity at eid(%s)", nidmap_to);
444     zxid_nidmap_identity_token(cf, ses, nidmap_to, 0);
445     //printf("%.*s\n", ZX_GET_CONTENT_LEN(nameid), ZX_GET_CONTENT_S(nameid));
446     return 0;
447   }
448 
449   if (di_only) {
450     D("Discover only. svctype(%s), dindex=%d", STRNULLCHK(svc), din);
451     epr = zxid_get_epr(cf, ses, svc, url, di, 0 /*action*/, din);
452     if (!epr) {
453       ERR("Discovery failed to find any epr of service type(%s)", STRNULLCHK(svc));
454       return 3;
455     }
456     for (din = 1; ;++din) {
457       epr = zxid_get_epr(cf, ses, svc, url, di, 0 /*action*/, din);
458       if (!epr)
459 	break;
460       printf("%d. Found epr for service type(%s)\n", din, STRNULLCHK(svc));
461       ss = zxid_get_epr_desc(cf, epr);
462       printf("   Description: %.*s\n", ss?ss->len:0, ss?ss->s:"");
463       ss = zxid_get_epr_address(cf, epr);
464       printf("   EPURL:       %.*s\n", ss?ss->len:0, ss?ss->s:"");
465       ss = zxid_get_epr_entid(cf, epr);
466       printf("   EntityID:    %.*s\n", ss?ss->len:0, ss?ss->s:"");
467     }
468     return 0;
469   }
470 
471   if (svc) {
472     D("Call service svctype(%s)", svc);
473     if (!bdy) {
474       if (verbose)
475 	fprintf(stderr, "Reading SOAP request body from stdin...\n");
476       siz = 4096;
477       p = bdy = ZX_ALLOC(cf->ctx, siz);
478       while (1) {
479 	n = read_all_fd(fdstdin, p, siz+bdy-p-1, &got);
480 	if (n == -1) {
481 	  perror("reading SOAP req from stdin");
482 	  break;
483 	}
484 	p += got;
485 	if (got < siz+bdy-p-1) break;
486 	siz += 60*1024;
487 	REALLOCN(bdy, siz);
488       }
489       *p = 0;
490     }
491     if (dryrun) {
492       if (verbose)
493 	fprintf(stderr, "Dryrun. Call aborted.\n");
494       return 0;
495     }
496     if (verbose)
497       fprintf(stderr, "Calling...\n");
498 
499     ss = zxid_call(cf, ses, svc, url, di, az, bdy);
500     if (!ss || !ss->s) {
501       ERR("Call failed %p", ss);
502       return 2;
503     }
504     if (verbose)
505       fprintf(stderr, "Done. Call returned %d bytes.\n", ss->len);
506     if (out_fmt) {
507       p = zxid_extract_body(cf, ss->s);
508       printf("%s", p);
509     } else
510       printf("%.*s", ss->len, ss->s);
511   } else if (az) {
512     D("Call Az(%s)", az);
513     if (dryrun) {
514       if (verbose)
515 	fprintf(stderr, "Dryrun. zxid_az() aborted.\n");
516       return 0;
517     }
518     if (zxid_az_cf_ses(cf, az, ses)) {
519       if (verbose)
520 	fprintf(stderr, "Permit.\n");
521       return 0;
522     } else {
523       if (verbose)
524 	fprintf(stderr, "Deny.\n");
525       return 1;
526     }
527   } else {
528     D("Neither service type (-t) nor -az supplied. Performed only authentication. %d",0);
529     if (verbose)
530       fprintf(stderr, "Authentication only. Session created.\n");
531     printf("%s", sid);
532   }
533   return 0;
534 }
535 
536 /* EOF  --  zxcall.c */
537