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