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