1 /* zxidxmltool.c - Testing tool for parsing XML
2 * Copyright (c) 2007 Symlabs (symlabs@symlabs.com), All Rights Reserved.
3 * Author: Sampo Kellomaki (sampo@iki.fi)
4 * This is confidential unpublished proprietary source code of the author.
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: zxidxmltool.c,v 1.5 2009-11-24 23:53:40 sampo Exp $
9 *
10 * 29.9.2007, started --Sampo
11 * WARNING: This code appears to be historical as of Oct-2010. --Sampo
12 */
13
14 #include <signal.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22
23 #include "errmac.h"
24 #include "zx.h"
25 #include "zxid.h"
26 #include "zxidconf.h"
27 #include "c/zxidvers.h"
28 #include "c/zx-ns.h"
29
30 CU8* help =
31 "zxidxmltool - XML Parsing - R" ZXID_REL "\n\
32 Copyright (c) 2007 Symlabs (symlabs@symlabs.com), All Rights Reserved.\n\
33 Author: Sampo Kellomaki (sampo@iki.fi)\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: zxidxmltool [options] < foo.xml\n\
39 -meta Dump our own metadata to stdout.\n\
40 -import URL Import metadata of others from URL, usually their Entity ID\n\
41 or Provider ID, aka well known location. The imported metadata\n\
42 is written to CoT cache directory.\n\
43 -C CONFPATH Path to (optional) config file, default " ZXID_CONF_PATH "\n\
44 -c OPT=VAL Override default or config file option. Only after -C, if any.\n\
45 -t SECONDS Timeout. Default: 0=no timeout.\n\
46 -k FDNUMBER File descriptor for reading symmetric key. Use 0 for stdin.\n\
47 -egd PATH Specify path of Entropy Gathering Daemon socket, default on\n\
48 Solaris: /tmp/entropy. On Linux /dev/urandom is used instead\n\
49 See http://www.lothar.com/tech/crypto/ or\n\
50 http://www.aet.tu-cottbus.de/personen/jaenicke/postfix_tls/prngd.html\n\
51 -rand PATH Location of random number seed file. On Solaris EGD is used.\n\
52 On Linux the default is /dev/urandom. See RFC1750.\n\
53 -uid UID:GID If run as root, drop privileges and assume specified uid and gid.\n\
54 -v Verbose messages.\n\
55 -q Be extra quiet.\n\
56 -d Turn on debugging.\n\
57 -license Show licensing details, including NATO C3 Agency disclaimer.\n\
58 -h This help message\n\
59 -- End of options\n";
60
61 int ak_buf_size = 0;
62 int verbose = 1;
63 int timeout = 0;
64 int gcthreshold = 0;
65 int leak_free = 0;
66 int drop_uid = 0;
67 int drop_gid = 0;
68 char* rand_path;
69 char* egd_path;
70 char symmetric_key[1024];
71 int symmetric_key_len;
72 char buf[256*1024];
73
74 /* Called by: main x8, zxbusd_main, zxbuslist_main, zxbustailf_main, zxcall_main, zxcot_main, zxdecode_main */
opt(int * argc,char *** argv,char *** env,zxid_conf * cf,zxid_cgi * cgi)75 void opt(int* argc, char*** argv, char*** env, zxid_conf* cf, zxid_cgi* cgi)
76 {
77 char* conf_path = 0;
78 if (*argc <= 1) return;
79
80 while (1) {
81 ++(*argv); --(*argc);
82
83 if (!(*argc) || ((*argv)[0][0] != '-')) break; /* normal exit from options loop */
84
85 switch ((*argv)[0][1]) {
86 case '-': if ((*argv)[0][2]) break;
87 ++(*argv); --(*argc);
88 DD("End of options by --");
89 return; /* -- ends the options */
90
91 case 'C': if ((*argv)[0][2]) break;
92 ++(*argv); --(*argc);
93 if (!(*argc)) break;
94 conf_path = **argv;
95 continue;
96
97 case 'c': if ((*argv)[0][2]) break;
98 ++(*argv); --(*argc);
99 if (!(*argc)) break;
100 if (conf_path != (char*)1) {
101 if (conf_path)
102 read_all(sizeof(buf), buf, "new conf path in opt", 1, "%s", conf_path);
103 else
104 read_all(sizeof(buf), buf, "no conf path in opt", 1, "%s" ZXID_CONF_FILE, cf->cpath);
105 zxid_parse_conf(cf, buf);
106 conf_path = (char*)1;
107 }
108 zxid_parse_conf(cf, **argv);
109 continue;
110
111 case 'd':
112 switch ((*argv)[0][2]) {
113 case '\0':
114 ++errmac_debug;
115 continue;
116 case 'i': if ((*argv)[0][3]) break;
117 ++(*argv); --(*argc);
118 if (!(*argc)) break;
119 errmac_instance = (*argv)[0];
120 continue;
121 }
122 break;
123
124 case 'e':
125 switch ((*argv)[0][2]) {
126 case 'g': if ((*argv)[0][3] != 'd' || (*argv)[0][4]) break;
127 ++(*argv); --(*argc);
128 if (!(*argc)) break;
129 egd_path = (*argv)[0];
130 continue;
131 }
132 break;
133
134 case 'i':
135 switch ((*argv)[0][2]) {
136 case 'm':
137 if (!strcmp((*argv)[0],"-import")) {
138 zxid_entity* ent;
139 ++(*argv); --(*argc);
140 if (!(*argc)) break;
141 cf->ctx->ns_tab = zx_ns_tab;
142 ent = zxid_get_meta(cf, (*argv)[0]);
143 if (ent)
144 zxid_write_ent_to_cache(cf, ent);
145 exit(0);
146 }
147 break;
148 }
149 break;
150
151 case 'k':
152 switch ((*argv)[0][2]) {
153 case '\0':
154 ++(*argv); --(*argc);
155 if (!(*argc)) break;
156 read_all_fd(atoi((*argv)[0]), symmetric_key, sizeof(symmetric_key), &symmetric_key_len);
157 D("Got %d characters of symmetric key", symmetric_key_len);
158 continue;
159 }
160 break;
161
162 case 'l':
163 switch ((*argv)[0][2]) {
164 case 'i':
165 if (!strcmp((*argv)[0],"-license")) {
166 extern char* license;
167 fprintf(stderr, license);
168 exit(0);
169 }
170 break;
171 }
172 break;
173
174 case 'm':
175 switch ((*argv)[0][2]) {
176 case 'e':
177 if (!strcmp((*argv)[0],"-meta")) {
178 cf->ctx->ns_tab = zx_ns_tab;
179 zxid_send_sp_meta(cf, cgi);
180 exit(0);
181 }
182 break;
183 }
184 break;
185
186 case 'q':
187 switch ((*argv)[0][2]) {
188 case '\0':
189 verbose = 0;
190 continue;
191 }
192 break;
193
194 case 'r':
195 switch ((*argv)[0][2]) {
196 case 'f':
197 /*AK_TS(LEAK, 0, "memory leaks enabled");*/
198 ERR("*** WARNING: You have turned memory frees to memory leaks. We will (eventually) run out of memory. Using -rf is not recommended. %d\n", 0);
199 ++leak_free;
200 continue;
201 #if 0
202 case 'e': /* -re */
203 if ((*argv)[0][3]) break;
204 ++(*argv); --(*argc);
205 if ((*argc) < 4) break;
206 sscanf((*argv)[0], "%i", &abort_funcno);
207 ++(*argv); --(*argc);
208 sscanf((*argv)[0], "%i", &abort_line);
209 ++(*argv); --(*argc);
210 sscanf((*argv)[0], "%i", &abort_error_code);
211 ++(*argv); --(*argc);
212 sscanf((*argv)[0], "%i", &abort_iter);
213 fprintf(stderr, "Will force core upon %x:%x err=%d iter=%d\n",
214 abort_funcno, abort_line, abort_error_code, abort_iter);
215 continue;
216 #endif
217 case 'g': /* -rg */
218 if ((*argv)[0][3]) break;
219 ++(*argv); --(*argc);
220 if (!(*argc)) break;
221 gcthreshold = atoi((*argv)[0]);
222 if (!gcthreshold)
223 ERR("*** WARNING: You have disabled garbage collection. This may lead to increased memory consumption for scripts that handle a lot of PDUs or run for long time. Using `-rg 0' is not recommended. %d\n", 0);
224 continue;
225 case 'a': /* -ra */
226 if ((*argv)[0][3] == 0) {
227 /*AK_TS(ASSERT_NONFATAL, 0, "assert nonfatal enabled");*/
228 #if 1
229 ERR("*** WARNING: YOU HAVE TURNED ASSERTS OFF USING -ra FLAG. THIS MEANS THAT YOU WILL NOT BE ABLE TO OBTAIN ANY SUPPORT. IF PROGRAM NOW TRIES TO ASSERT IT MAY MYSTERIOUSLY AND UNPREDICTABLY CRASH INSTEAD, AND NOBODY WILL BE ABLE TO FIGURE OUT WHAT WENT WRONG OR HOW MUCH DAMAGE MAY BE DONE. USING -ra IS NOT RECOMMENDED. %d\n", assert_nonfatal);
230 #endif
231 ++assert_nonfatal;
232 continue;
233 }
234 if (!strcmp((*argv)[0],"-rand")) {
235 ++(*argv); --(*argc);
236 if (!(*argc)) break;
237 rand_path = (*argv)[0];
238 continue;
239 }
240 break;
241 }
242 break;
243
244 case 't': if ((*argv)[0][2]) break;
245 ++(*argv); --(*argc);
246 if (!(*argc)) break;
247 timeout = atoi((*argv)[0]);
248 continue;
249
250 case 'u':
251 switch ((*argv)[0][2]) {
252 case 'i': if ((*argv)[0][3] != 'd' || (*argv)[0][4]) break;
253 ++(*argv); --(*argc);
254 if (!(*argc)) break;
255 sscanf((*argv)[0], "%i:%i", &drop_uid, &drop_gid);
256 continue;
257 }
258 break;
259
260 case 'v':
261 switch ((*argv)[0][2]) {
262 case '\0':
263 ++verbose;
264 continue;
265 }
266 break;
267
268 }
269 /* fall thru means unrecognized flag */
270 if (*argc)
271 fprintf(stderr, "Unrecognized flag `%s'\n", (*argv)[0]);
272 fprintf(stderr, help);
273 fprintf(stderr, "version=0x%06x rel(%s)\n", zxid_version(), zxid_version_str());
274 exit(3);
275 }
276 if (conf_path != (char*)1) {
277 if (conf_path)
278 read_all(sizeof(buf), buf, "conf_path in end of opt", 1, "%s", conf_path);
279 else
280 read_all(sizeof(buf), buf, "no conf_path in end of opt", 1, "%s" ZXID_CONF_FILE, cf->cpath);
281 zxid_parse_conf(cf, buf);
282 }
283 }
284
285
286 /* ============== M A I N ============== */
287
288 /* Called by: */
main(int argc,char ** argv,char ** env)289 int main(int argc, char** argv, char** env)
290 {
291 zxid_conf* cf = zxid_new_conf(ZXID_PATH);
292 zxid_ses ses;
293 zxid_cgi cgi;
294 int got;
295 char* qs;
296 char* cont_len;
297 //char* eid;
298 struct zx_str* ss;
299 zxid_entity* idp;
300
301 #if 1
302 /* Helps debugging CGI scripts if you see stderr. */
303 close(2);
304 got = open("zxid.stderr", O_WRONLY | O_CREAT | O_APPEND, 0666);
305 if (got != 2)
306 exit(2);
307 fprintf(stderr, "=================== Running ===================\n");
308 ++errmac_debug;
309 #endif
310
311 opt(&argc, &argv, &env, cf, &cgi);
312
313 /*if (stats_prefix) init_cmdline(argc, argv, env, stats_prefix);*/
314 CMDLINE("init");
315
316 #ifndef MINGW
317 /* *** all this cruft does not work on MINGW, but perhaps it should not even exist for Unix */
318 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { /* Ignore SIGPIPE */
319 perror("Init: signal ignore pipe");
320 exit(2);
321 }
322
323 /* Cause exit(3) to be called with the intent that any gcov profiling will get
324 * written to disk before we die. If not stopped with `kill -USR1' but you
325 * use plain kill instead, the profile will indicate many unexecuted (#####) lines. */
326 if (signal(SIGUSR1, exit) == SIG_ERR) {
327 perror("Init: signal USR1 exit");
328 exit(2);
329 }
330
331 /* Drop privileges, if requested. */
332
333 if (drop_gid) if (setgid(drop_gid)) { perror("Init: setgid"); exit(1); }
334 if (drop_uid) if (setuid(drop_uid)) { perror("Init: setuid"); exit(1); }
335 #endif
336
337 /* Pick up application variables from query string and post content (indicated by o=P in QS) */
338
339 ZERO(&cgi, sizeof(cgi));
340 qs = getenv("QUERY_STRING");
341 if (qs) {
342 D("QS(%s)", qs);
343 zxid_parse_cgi(cf, &cgi, qs);
344 if (cgi.op == 'P') {
345 cont_len = getenv("CONTENT_LENGTH");
346 if (cont_len) {
347 sscanf(cont_len, "%d", &got);
348 if (read_all_fd(fdstdin, buf, got, &got) == -1) {
349 perror("Trouble reading post content");
350 } else {
351 buf[got] = 0;
352 D("POST(%s) got=%d cont_len(%s)", buf, got, cont_len);
353 if (buf[0] == '<') { /* No BOM and looks XML */
354 return zxid_sp_soap_parse(cf, &cgi, &ses, got, buf);
355 }
356 if (buf[2] == '<') { /* UTF-16 BOM and looks XML */
357 return zxid_sp_soap_parse(cf, &cgi, &ses, got-2, buf+2);
358 }
359 if (buf[3] == '<') { /* UTF-8 BOM and looks XML */
360 return zxid_sp_soap_parse(cf, &cgi, &ses, got-3, buf+3);
361 }
362 zxid_parse_cgi(cf, &cgi, buf);
363 }
364 }
365 }
366 } else
367 cgi.op = 'M'; /* Bare `/zxid' as a URL means same as `/zxid?o=M' */
368
369 D("op(%c) sid(%s)", cgi.op, cgi.sid?cgi.sid:"-");
370
371 /* Check if user already has working session. */
372
373 if (cgi.sid) {
374 if (zxid_get_ses(cf, &ses, cgi.sid))
375 if (zxid_mgmt(cf, &cgi, &ses))
376 return 0;
377 }
378 ZERO(&ses, sizeof(ses));
379
380 switch (cgi.op) {
381 case 'M': /* Invoke LECP or redirect to CDC reader. */
382 if (zxid_lecp_check(cf, &cgi))
383 return 0;
384 printf("Location: %s?o=C\r\n\r\n", ZXID_CDC_URL);
385 return 0;
386 case 'C': /* CDC Read: Common Domain Cookie Reader */
387 return zxid_cdc_read(cf, &cgi);
388 case 'E': /* Return from CDC read, or start here to by-pass CDC read. */
389 if (zxid_lecp_check(cf, &cgi))
390 return 0;
391 if (zxid_cdc_check(cf, &cgi))
392 return 0;
393 break;
394 case 'L':
395 if (ss = zxid_start_sso_location(cf, cgi)) {
396 printf("%.*s", ss->len, ss->s);
397 return 0;
398 }
399 break;
400 case 'A':
401 D("Process artifact(%s)", cgi.saml_art);
402 switch (zxid_sp_deref_art(cf, &cgi, &ses)) {
403 case ZXID_REDIR_OK: return 0;
404 case ZXID_SSO_OK:
405 if (zxid_mgmt(cf, &cgi, &ses))
406 return 0;
407 }
408 break;
409 case 'P':
410 D("Process response(%s)", cgi.saml_resp);
411 switch (zxid_sp_dispatch(cf, &cgi, &ses, cgi.saml_resp)) {
412 case ZXID_REDIR_OK: return 0;
413 case ZXID_SSO_OK:
414 if (zxid_mgmt(cf, &cgi, &ses))
415 return 0;
416 }
417 break;
418 case 'Q':
419 D("Process request(%s)", cgi.saml_req);
420 switch (zxid_sp_dispatch(cf, &cgi, &ses, cgi.saml_req)) {
421 case ZXID_REDIR_OK: return 0;
422 case ZXID_SSO_OK:
423 if (zxid_mgmt(cf, &cgi, &ses))
424 return 0;
425 }
426 break;
427 case 'B': /* Metadata */
428 write_all_fd(1, "Content-Type: text/xml\r\n\r\n", sizeof("Content-Type: text/xml\r\n\r\n")-1);
429 return zxid_send_sp_meta(cf, &cgi);
430 default: D("unknown op(%c)", cgi.op);
431 }
432
433 //printf("COOKIE: foo\r\n");
434 printf("Content-Type: text/html\r\n\r\n");
435 printf("<title>ZXID SP SSO</title>" ZXID_BODY_TAG "<h1>ZXID SP Federated SSO (user NOT logged in, no session)</h1><pre>\n");
436 //if (qs) printf("QS(%s)\n", qs);
437 //if (got>0) printf("GOT(%.*s)\n", got, buf);
438 printf("</pre><form method=post action=\"zxid?o=P\">");
439 if (cgi.err)
440 printf("<p><font color=red><i>%s</i></font></p>\n", cgi.err);
441 if (cgi.msg)
442 printf("<p><i>%s</i></p>\n", cgi.msg);
443 //printf("User:<input name=user> PW:<input name=pw type=password>");
444 //printf("<input name=login value=\" Login \" type=submit>");
445
446 printf("<h3>Login Using New IdP</h3>\n");
447 printf("<i>A new IdP is one whose metadata we do not have yet. We need to know the Entity ID in order to fetch the metadata using the well known location method. You will need to ask the adminstrator of the IdP to tell you what the EntityID is.</i>\n");
448 printf("<p>IdP EntityID URL <input name=e size=100>");
449 printf("<input type=submit name=l1 value=\" Login (SAML20:Artifact) \">\n");
450 printf("<input type=submit name=l2 value=\" Login (SAML20:POST) \"><br>\n");
451
452 idp = zxid_load_cot_cache(cf);
453
454 if (idp) {
455 printf("<h3>Login Using Known IdP</h3>\n");
456 for (; idp; idp = idp->n) {
457 if (!idp->ed->IDPSSODescriptor)
458 continue;
459 printf("<input type=submit name=\"l1%s\" value=\" Login to %s (SAML20:Artifact) \">\n",
460 idp->eid, idp->eid);
461 printf("<input type=submit name=\"l2%s\" value=\" Login to %s (SAML20:POST) \">\n",
462 idp->eid, idp->eid);
463 }
464 }
465
466 #if 0
467 printf("<h3>Login Using IdP Discovered from Common Domain Cookie (CDC)</h3>\n");
468
469 printf("<input type=submit name=\"l1https://s-ps.liberty-iop.org:8881/idp.xml\" value=\" Login to test-idp3 (SAML20:Artifact) \">\n");
470 printf("<input type=submit name=\"l2https://s-ps.liberty-iop.org:8881/idp.xml\" value=\" Login to test-idp3 (SAML20:POST) \">\n");
471 #endif
472
473 printf("<h3>CoT configuration parameters your IdP may need to know</h3>\n");
474 eid = zxid_my_ent_id_cstr(cf);
475 printf("Entity ID of this SP: <a href=\"%s\">%s</a> (Click on the link to fetch SP metadata.)\n", eid, eid);
476
477 printf("<h3>Technical options (typically hidden fields on production site)</h3>\n");
478 printf("<input type=checkbox name=fc value=1 checked> Allow new federation to be created<br>\n");
479 printf("<input type=checkbox name=fp value=1> Do not allow IdP to interact (e.g. ask password) (IsPassive flag)<br>\n");
480 printf("<input type=checkbox name=ff value=1> IdP should reauthenticate user (ForceAuthn flag)<br>\n");
481
482 printf("NID Format: <select name=fn><option value=prstnt>Persistent<option value=trnsnt>Transient<option value=\"\">(none)</select><br>\n");
483 printf("Affiliation: <select name=fq><option value=\"\">(none)</select><br>\n");
484 printf("Consent: <select name=fy><option value=\"\">(empty)<option value=\"urn:liberty:consent:obtained\">obtained<option value=\"urn:liberty:consent:obtained:prior\">obtained:prior<option value=\"urn:liberty:consent:obtained:current:implicit\">obtained:current:implicit<option value=\"urn:liberty:consent:obtained:current:explicit\">obtained:current:explicit<option value=\"urn:liberty:consent:unavailable\">unavailable<option value=\"urn:liberty:consent:inapplicable\">inapplicable</select><br>\n");
485 printf("Authn Req Context: <select name=fa><option value=\"\">(none)<option value=pw>Password<option value=pwp>Password with Protected Transport<option value=clicert>TLS Client Certificate</select><br>\n");
486 printf("Matching Rule: <select name=fm><option value=exact>Exact<option value=minimum>Min<option value=maximum>Max<option value=better>Better<option value=\"\">(none)</select><br>\n");
487 /*printf("RelayState: <input name=fr value=\"rs123\"><br>\n");*/
488
489 printf("</form><hr>");
490 printf("<a href=\"http://zxid.org/\">zxid.org</a>, %s", zxid_version_str());
491 if (cgi.dbg)
492 printf("<p><form><textarea cols=100 row=10>%s</textarea></form>\n", cgi.dbg);
493 return 0;
494 }
495
496 /* EOF -- zxidxmltool.c */
497