1 /* zxid.c  -  CGI binary for SAML 2 SP
2  * Copyright (c) 2006 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: zxid.c,v 1.42 2009-11-24 23:53:40 sampo Exp $
9  *
10  * 15.4.2006, started work over Easter holiday --Sampo
11  * 22.4.2006, added more options over the weekend --Sampo
12  * 28.5.2006, adopted structure from s5066d --Sampo
13  * 30.9.2006, added signature verification --Sampo
14  *
15  * This file contains option processing, configuration, and main().
16  *
17  * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
18  *
19  * WARNING: This file is outdated. See zxidhlo.c instead.
20  */
21 
22 #include "platform.h"
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 //#include <sys/wait.h>
30 //#include <pthread.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 //#include <netdb.h>
34 
35 #ifdef USE_CURL
36 #include <curl/curl.h>
37 #endif
38 
39 #include <zx/errmac.h>
40 #include <zx/zx.h>
41 #include <zx/zxid.h>
42 #include <zx/zxidpriv.h>
43 #include <zx/zxidutil.h>
44 #include <zx/zxidconf.h>
45 #include <zx/c/zxidvers.h>
46 #include <zx/c/zx-ns.h>
47 #include <zx/c/zx-md-data.h>
48 
49 char* help =
50 "zxid  -  SAML 2.0 SP CGI - R" ZXID_REL "\n\
51 SAML 2.0 is a standard for federated identity and Single Sign-On.\n\
52 Copyright (c) 2006 Symlabs (symlabs@symlabs.com), All Rights Reserved.\n\
53 Author: Sampo Kellomaki (sampo@iki.fi)\n\
54 NO WARRANTY, not even implied warranties. Licensed under Apache License v2.0\n\
55 See http://www.apache.org/licenses/LICENSE-2.0\n\
56 Send well researched bug reports to the author. Home: zxid.org\n\
57 \n\
58 Usage: zxid [options]   (when used as CGI, no options can be supplied)\n\
59   -meta            Dump our own metadata to stdout.\n\
60   -import URL      Import metadata of others from URL, usually their Entity ID\n\
61                    or Provider ID, aka well known location. The imported metadata\n\
62                    is written to CoT cache directory.\n\
63   -fileimport FILE Import metadata of others from file.\n\
64   -C CONFPATH      Path to (optional) config file, default " ZXID_CONF_PATH "\n\
65   -c OPT=VAL       Override default or config file option. Only after -C, if any.\n\
66   -t SECONDS       Timeout. Default: 0=no timeout.\n\
67   -k FDNUMBER      File descriptor for reading symmetric key. Use 0 for stdin.\n\
68   -egd PATH        Specify path of Entropy Gathering Daemon socket, default on\n\
69                    Solaris: /tmp/entropy. On Linux /dev/urandom is used instead\n\
70                    See http://www.lothar.com/tech/crypto/ or\n\
71                    http://www.aet.tu-cottbus.de/personen/jaenicke/postfix_tls/prngd.html\n\
72   -rand PATH       Location of random number seed file. On Solaris EGD is used.\n\
73                    On Linux the default is /dev/urandom. See RFC1750.\n\
74   -uid UID:GID     If run as root, drop privileges and assume specified uid and gid.\n\
75   -v               Verbose messages.\n\
76   -q               Be extra quiet.\n\
77   -d               Turn on debugging.\n\
78   -license         Show licensing details, including NATO C3 Agency disclaimer.\n\
79   -h               This help message\n\
80   --               End of options\n";
81 
82 int ak_buf_size = 0;
83 int verbose = 1;
84 int timeout = 0;
85 int gcthreshold = 0;
86 int leak_free = 0;
87 int drop_uid = 0;
88 int drop_gid = 0;
89 char* rand_path;
90 char* egd_path;
91 char  symmetric_key[1024];
92 int symmetric_key_len;
93 char buf[32*1024];
94 
95 /* N.B. This options processing is a skeleton. In reality CGI scripts do not have
96  * an opportunity to process any options. */
97 
98 /* 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)99 void opt(int* argc, char*** argv, char*** env, zxid_conf* cf, zxid_cgi* cgi)
100 {
101   char* conf_path = 0;
102   if (*argc <= 1) return;
103 
104   while (1) {
105     ++(*argv); --(*argc);
106 
107     if (!(*argc) || ((*argv)[0][0] != '-')) break;  /* normal exit from options loop */
108 
109     switch ((*argv)[0][1]) {
110     case '-': if ((*argv)[0][2]) break;
111       ++(*argv); --(*argc);
112       DD("End of options by --");
113       return;  /* -- ends the options */
114 
115     case 'C': if ((*argv)[0][2]) break;
116       ++(*argv); --(*argc);
117       if (!(*argc)) break;
118       conf_path = **argv;
119       continue;
120 
121     case 'c': if ((*argv)[0][2]) break;
122       ++(*argv); --(*argc);
123       if (!(*argc)) break;
124       if (conf_path != (char*)1) {
125 	if (conf_path)
126 	  read_all(sizeof(buf), buf, "new conf path in opt", 1, "%s", conf_path);
127 	else
128 	  read_all(sizeof(buf), buf, "no conf path in opt", 1, "%s" ZXID_CONF_FILE, cf->cpath);
129 	zxid_parse_conf(cf, buf);
130 	conf_path = (char*)1;
131       }
132       zxid_parse_conf(cf, **argv);
133       continue;
134 
135     case 'd':
136       switch ((*argv)[0][2]) {
137       case '\0':
138 	++errmac_debug;
139 	continue;
140       case 'i':  if ((*argv)[0][3]) break;
141 	++(*argv); --(*argc);
142 	if (!(*argc)) break;
143 	strcpy(errmac_instance, (*argv)[0]);
144 	continue;
145       }
146       break;
147 
148     case 'e':
149       switch ((*argv)[0][2]) {
150       case 'g': if ((*argv)[0][3] != 'd' || (*argv)[0][4]) break;
151 	++(*argv); --(*argc);
152 	if (!(*argc)) break;
153 	egd_path = (*argv)[0];
154 	continue;
155       }
156       break;
157 
158     case 'i':
159       switch ((*argv)[0][2]) {
160       case 'm':
161 	if (!strcmp((*argv)[0],"-import")) {
162 	  zxid_entity* ent;
163 	  ++(*argv); --(*argc);
164 	  if (!(*argc)) break;
165 	  cf->ctx->ns_tab = zx_ns_tab;
166 	  ent = zxid_get_meta(cf, (*argv)[0]);
167 	  if (ent)
168 	    zxid_write_ent_to_cache(cf, ent);
169 	  exit(0);
170 	}
171 	break;
172       }
173       break;
174 #if 0
175     case 'f':
176       switch ((*argv)[0][2]) {
177       case 'i':
178 	if (!strcmp((*argv)[0],"-fileimport")) {
179 	  zxid_entity* ent;
180 	  ++(*argv); --(*argc);
181 	  if (!(*argc)) break;
182 	  cf->ctx->ns_tab = zx_ns_tab;
183 	  ent = zxid_get_meta(cf, (*argv)[0]);
184 	  if (ent)
185 	    zxid_write_ent_to_cache(cf, ent);
186 	  exit(0);
187 	}
188 	break;
189       }
190       break;
191 #endif
192 #ifndef MINGW
193     case 'k':
194       switch ((*argv)[0][2]) {
195       case '\0':
196 	++(*argv); --(*argc);
197 	if (!(*argc)) break;
198 	read_all_fd((fdtype)atoi((*argv)[0]), symmetric_key, sizeof(symmetric_key), &symmetric_key_len);
199 	D("Got %d characters of symmetric key", symmetric_key_len);
200 	continue;
201       }
202       break;
203 #endif
204 
205     case 'l':
206       switch ((*argv)[0][2]) {
207       case 'i':
208 	if (!strcmp((*argv)[0],"-license")) {
209 	  extern char* license;
210 	  fprintf(stderr, "%s", license);
211 	  exit(0);
212 	}
213 	break;
214       }
215       break;
216 
217     case 'm':
218       switch ((*argv)[0][2]) {
219       case 'e':
220 	if (!strcmp((*argv)[0],"-meta")) {
221 	  cf->ctx->ns_tab = zx_ns_tab;
222 	  zxid_send_sp_meta(cf, cgi);
223 	  exit(0);
224 	}
225 	break;
226       }
227       break;
228 
229     case 'q':
230       switch ((*argv)[0][2]) {
231       case '\0':
232 	verbose = 0;
233 	continue;
234       }
235       break;
236 
237     case 'r':
238       switch ((*argv)[0][2]) {
239       case 'f':
240 	/*AK_TS(LEAK, 0, "memory leaks enabled");*/
241 	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);
242 	++leak_free;
243 	continue;
244 #if 0
245       case 'e':  /* -re */
246 	if ((*argv)[0][3]) break;
247 	++(*argv); --(*argc);
248 	if ((*argc) < 4) break;
249 	sscanf((*argv)[0], "%i", &abort_funcno);
250 	++(*argv); --(*argc);
251 	sscanf((*argv)[0], "%i", &abort_line);
252 	++(*argv); --(*argc);
253 	sscanf((*argv)[0], "%i", &abort_error_code);
254 	++(*argv); --(*argc);
255 	sscanf((*argv)[0], "%i", &abort_iter);
256 	fprintf(stderr, "Will force core upon %x:%x err=%d iter=%d\n",
257 		abort_funcno, abort_line, abort_error_code, abort_iter);
258 	continue;
259 #endif
260       case 'g':  /* -rg */
261 	if ((*argv)[0][3]) break;
262 	++(*argv); --(*argc);
263 	if (!(*argc)) break;
264 	gcthreshold = atoi((*argv)[0]);
265 	if (!gcthreshold)
266 	  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);
267 	continue;
268       case 'a': /* -ra */
269 	if ((*argv)[0][3] == 0) {
270 	  /*AK_TS(ASSERT_NONFATAL, 0, "assert nonfatal enabled");*/
271 #if 1
272 	  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);
273 #endif
274 	  ++assert_nonfatal;
275 	  continue;
276 	}
277 	if (!strcmp((*argv)[0],"-rand")) {
278 	  ++(*argv); --(*argc);
279 	  if (!(*argc)) break;
280 	  rand_path = (*argv)[0];
281 	  continue;
282 	}
283 	break;
284       }
285       break;
286 
287     case 't': if ((*argv)[0][2]) break;
288       ++(*argv); --(*argc);
289       if (!(*argc)) break;
290       timeout = atoi((*argv)[0]);
291       continue;
292 
293     case 'u':
294       switch ((*argv)[0][2]) {
295       case 'i': if ((*argv)[0][3] != 'd' || (*argv)[0][4]) break;
296 	++(*argv); --(*argc);
297 	if (!(*argc)) break;
298 	sscanf((*argv)[0], "%i:%i", &drop_uid, &drop_gid);
299 	continue;
300       }
301       break;
302 
303     case 'v':
304       switch ((*argv)[0][2]) {
305       case '\0':
306 	++verbose;
307 	continue;
308       }
309       break;
310 
311     }
312     /* fall thru means unrecognized flag */
313     if (*argc)
314       fprintf(stderr, "Unrecognized flag `%s'\n", (*argv)[0]);
315     fprintf(stderr, "%s", help);
316     fprintf(stderr, "version=0x%06x rel(%s)\n", zxid_version(), zxid_version_str());
317     exit(3);
318   }
319   if (conf_path != (char*)1) {
320     if (conf_path)
321       read_all(sizeof(buf), buf, "conf_path in end of opt", 1, "%s", conf_path);
322     else
323       read_all(sizeof(buf), buf, "no conf_path in end of opt", 1, "%szxid.conf", cf->cpath);
324     zxid_parse_conf(cf, buf);
325   }
326 }
327 
328 /* ============== Management Screen ============== */
329 
330 /* This screen is only invoked if session is active. Zero return
331  * value causes the login screen to be rendered. */
332 
333 /* Called by:  main x7 */
zxid_mgmt(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses)334 int zxid_mgmt(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
335 {
336   struct zx_str* ss;
337   D("op(%c)", cgi->op);
338   switch (cgi->op) {
339   case 'l':
340     zxid_del_ses(cf, ses);
341     cgi->msg = "Local logout Ok. Session terminated.";
342     return 0;  /* Simply abandon local session. Falls thru to login screen. */
343   case 'r':
344     ss = zxid_sp_slo_redir(cf, cgi, ses);
345     zxid_del_ses(cf, ses);
346     printf("%.*s", ss->len, ss->s);
347     zx_str_free(cf->ctx, ss);
348     fflush(stdout);
349     return 1;  /* Redirect already happened. Do not show login screen. */
350   case 's':
351     zxid_sp_slo_soap(cf, cgi, ses);
352     zxid_del_ses(cf, ses);
353     cgi->msg = "SP Initiated logout (SOAP). Session terminated.";
354     return 0;  /* Falls thru to login screen. */
355   case 't':
356     ss = zxid_sp_mni_redir(cf, cgi, ses, 0);
357     printf("%.*s", ss->len, ss->s);
358     zx_str_free(cf->ctx, ss);
359     fflush(stdout);
360     return 1;  /* Redirect already happened. Do not show login screen. */
361   case 'u':
362     zxid_sp_mni_soap(cf, cgi, ses, 0);
363     cgi->msg = "SP Initiated defederation (SOAP).";
364     break;     /* Defederation does not have to mean SLO */
365   case 'P':
366   case 'Q':
367     ss = zxid_sp_dispatch(cf, cgi, ses);
368     switch (ss->s[0]) {
369     case 'K': return 0;
370     case 'L':
371       printf("%.*s", ss->len, ss->s);
372       zx_str_free(cf->ctx, ss);
373       fflush(stdout);
374       return 1;
375     }
376     break;
377   }
378 
379   //printf("COOKIE: foo\r\n");
380   printf("Content-Type: text/html\r\n\r\n");
381   printf("<title>ZXID SP Mgmt</title>" ZXID_BODY_TAG "<h1>ZXID SP Management (user logged in, session active)</h1><pre>\n");
382   //if (qs) printf("QS(%s)\n", qs);
383   //if (got>0) printf("GOT(%.*s)\n", got, buf);
384   printf("</pre><form method=post action=\"zxid?o=P\">");
385   if (cgi->err)
386     printf("<p><font color=red><i>%s</i></font></p>\n", cgi->err);
387   if (cgi->msg)
388     printf("<p><i>%s</i></p>\n", cgi->msg);
389   //printf("User:<input name=user> PW:<input name=pw type=password>");
390   //printf("<input name=login value=\" Login \" type=submit>");
391   printf("<input type=hidden name=s value=\"%s\">", ses->sid);
392   printf("<input type=submit name=gl value=\" Local Logout \">\n");
393   printf("<input type=submit name=gr value=\" Single Logout (Redir) \">\n");
394   printf("<input type=submit name=gs value=\" Single Logout (SOAP) \">\n");
395   printf("<input type=submit name=gt value=\" Defederate (Redir) \">\n");
396   printf("<input type=submit name=gu value=\" Defederate (SOAP) \">\n");
397 
398   printf("<h3>Technical options (typically hidden fields on production site)</h3>\n");
399 
400   printf("sid(%s) nid(%s) <a href=\"zxid?s=%s\">Reload</a>", ses->sid, ses->nid, ses->sid);
401 
402   printf("</form><hr>");
403   printf("<a href=\"http://zxid.org/\">zxid.org</a>, %s", zxid_version_str());
404   if (cgi->dbg)
405     printf("<p><form><textarea cols=100 row=10>%s</textarea></form>\n", cgi->dbg);
406   return 1;
407 }
408 
409 /* ============== M A I N ============== */
410 
411 /* Called by: */
main(int argc,char ** argv,char ** env)412 int main(int argc, char** argv, char** env)
413 {
414   zxid_conf* cf = zxid_new_conf(ZXID_PATH);
415   zxid_ses ses;
416   zxid_cgi cgi;
417   int got;
418   char* qs;
419   char* cont_len;
420   struct zx_str* ss;
421   char* eid;
422   zxid_entity* idp;
423 
424 #if 1
425   /* Helps debugging CGI scripts if you see stderr. */
426   close(2);
427   got = open("zxid.stderr", O_WRONLY | O_CREAT | O_APPEND, 0666);
428   if (got != 2)
429     exit(2);
430   fprintf(stderr, "=================== Running ===================\n");
431   ++errmac_debug;
432   zxid_set_opt(cf, 6, 0);
433 #endif
434   cf->nosig_fatal = 0;  // *** For SimpleSign the signature is checked at other level
435 
436   opt(&argc, &argv, &env, cf, &cgi);
437 
438   /*if (stats_prefix) init_cmdline(argc, argv, env, stats_prefix);*/
439   CMDLINE("init");
440 
441 #ifndef MINGW
442   /* *** all this cruft does not work on MINGW, but perhaps it should not even exist for Unix */
443   if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {   /* Ignore SIGPIPE */
444     perror("Init: signal ignore pipe");
445     exit(2);
446   }
447 
448   /* Cause exit(3) to be called with the intent that any gcov profiling will get
449    * written to disk before we die. If not stopped with `kill -USR1' but you
450    * use plain kill instead, the profile will indicate many unexecuted (#####) lines. */
451   if (signal(SIGUSR1, exit) == SIG_ERR) {
452     perror("Init: signal USR1 exit");
453     exit(2);
454   }
455 
456   /* Drop privileges, if requested. */
457 
458   if (drop_gid) if (setgid(drop_gid)) { perror("Init: setgid"); exit(1); }
459   if (drop_uid) if (setuid(drop_uid)) { perror("Init: setuid"); exit(1); }
460 #endif
461 
462   /* Pick up application variables from query string and post content (indicated by o=P in QS) */
463 
464   ZERO(&cgi, sizeof(cgi));
465   qs = getenv("QUERY_STRING");
466   if (qs) {
467     D("QS(%s)", qs);
468     zxid_parse_cgi(cf, &cgi, qs);
469     if (cgi.op == 'P') {
470       cont_len = getenv("CONTENT_LENGTH");
471       if (cont_len) {
472 	sscanf(cont_len, "%d", &got);
473 	if (read_all_fd(fdstdin, buf, got, &got) == -1) {
474 	  perror("Trouble reading post content");
475 	} else {
476 	  buf[got] = 0;
477 	  D("POST(%s) got=%d cont_len(%s)", buf, got, cont_len);
478 	  if (buf[0] == '<') {  /* No BOM and looks XML */
479 	    return zxid_sp_soap_parse(cf, &cgi, &ses, got, buf);
480 	  }
481 	  if (buf[2] == '<') {  /* UTF-16 BOM and looks XML */
482 	    return zxid_sp_soap_parse(cf, &cgi, &ses, got-2, buf+2);
483 	  }
484 	  if (buf[3] == '<') {  /* UTF-8 BOM and looks XML */
485 	    return zxid_sp_soap_parse(cf, &cgi, &ses, got-3, buf+3);
486 	  }
487 	  zxid_parse_cgi(cf, &cgi, buf);
488 	}
489       }
490     }
491   } else
492     cgi.op = 'M';  /* Bare `/zxid' as a URL means same as `/zxid?o=M' */
493 
494   D("op(%c) sid(%s)", cgi.op, cgi.sid?cgi.sid:"-");
495 
496   /* Check if user already has working session. */
497 
498   if (cgi.sid) {
499     if (zxid_get_ses(cf, &ses, cgi.sid))
500       if (zxid_mgmt(cf, &cgi, &ses))
501 	return 0;
502   }
503   ZERO(&ses, sizeof(ses));
504 
505   switch (cgi.op) {
506   case 'M':  /* Invoke LECP or redirect to CDC reader. */
507     if (zxid_lecp_check(cf, &cgi))
508       return 0;
509     printf("Location: %s?o=C\r\n\r\n", ZXID_CDC_URL);
510     return 0;
511   case 'C':  /* CDC Read: Common Domain Cookie Reader */
512     if (zxid_cdc_read(cf, &cgi))
513       return 0;
514     return 1;
515   case 'E':  /* Return from CDC read, or start here to by-pass CDC read. */
516     if (zxid_lecp_check(cf, &cgi))
517       return 0;
518     if (zxid_cdc_check(cf, &cgi))
519       return 0;
520     break;
521   case 'L':
522     if (ss = zxid_start_sso_location(cf, cgi)) {
523       printf("%.*s", ss->len, ss->s);
524       return 0;
525     }
526     break;
527   case 'A':
528     D("Process artifact(%s)", cgi.saml_art);
529     switch (zxid_sp_deref_art(cf, &cgi, &ses)) {
530     case ZXID_REDIR_OK: return 0;
531     case ZXID_SSO_OK:
532       if (zxid_mgmt(cf, &cgi, &ses))
533 	return 0;
534     }
535     break;
536   case 'P':
537   case 'Q':
538     DD("Process response(%s)", cgi.saml_resp);
539     ss = zxid_sp_dispatch(cf, &cgi, &ses);
540     switch (ss->s[0]) {
541     case 'L':
542       printf("%.*s", ss->len, ss->s);
543       zx_str_free(cf->ctx, ss);
544       fflush(stdout);
545       return 0;
546     case 'O':
547       if (zxid_mgmt(cf, &cgi, &ses))
548 	return 0;
549     }
550     break;
551   case 'B':  /* Metadata */
552     write_all_fd(fdstdout, "Content-Type: text/xml\r\n\r\n", sizeof("Content-Type: text/xml\r\n\r\n")-1);
553     return zxid_send_sp_meta(cf, &cgi);
554   default: D("unknown op(%c)", cgi.op);
555   }
556 
557   //printf("COOKIE: foo\r\n");
558   printf("Content-Type: text/html\r\n\r\n");
559   printf("<title>ZXID SP SSO</title>" ZXID_BODY_TAG "<h1>ZXID SP Federated SSO (user NOT logged in, no session)</h1><pre>\n");
560   //if (qs) printf("QS(%s)\n", qs);
561   //if (got>0) printf("GOT(%.*s)\n", got, buf);
562   printf("</pre><form method=post action=\"zxid?o=P\">");
563   if (cgi.err)
564     printf("<p><font color=red><i>%s</i></font></p>\n", cgi.err);
565   if (cgi.msg)
566     printf("<p><i>%s</i></p>\n", cgi.msg);
567   //printf("User:<input name=user> PW:<input name=pw type=password>");
568   //printf("<input name=login value=\" Login \" type=submit>");
569 
570   printf("<h3>Login Using New IdP</h3>\n");
571   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");
572   printf("<p>IdP EntityID URL <input name=e size=100>");
573   printf("<input type=submit name=l1 value=\" Login (SAML20:Artifact) \">\n");
574   printf("<input type=submit name=l2 value=\" Login (SAML20:POST) \"><br>\n");
575 
576   idp = zxid_load_cot_cache(cf);
577 
578   if (idp) {
579     printf("<h3>Login Using Known IdP</h3>\n");
580     for (; idp; idp = idp->n) {
581       if (!idp->ed->IDPSSODescriptor)
582 	continue;
583       printf("<input type=submit name=\"l0%s\" value=\" Login to %s (SAML20:any) \">\n",
584 	     idp->eid, idp->eid);
585       printf("<input type=submit name=\"l1%s\" value=\" Login to %s (SAML20:Artifact) \">\n",
586 	     idp->eid, idp->eid);
587       printf("<input type=submit name=\"l2%s\" value=\" Login to %s (SAML20:POST) \">\n",
588 	     idp->eid, idp->eid);
589       printf("<input type=submit name=\"l5%s\" value=\" Login to %s (SAML20:SimpleSign) \">\n",
590 	     idp->eid, idp->eid);
591     }
592   }
593 
594 #if 0
595   printf("<h3>Login Using IdP Discovered from Common Domain Cookie (CDC)</h3>\n");
596 
597   printf("<input type=submit name=\"l1https://s-ps.liberty-iop.org:8881/idp.xml\" value=\" Login to test-idp3 (SAML20:Artifact) \">\n");
598   printf("<input type=submit name=\"l2https://s-ps.liberty-iop.org:8881/idp.xml\" value=\" Login to test-idp3 (SAML20:POST) \">\n");
599 #endif
600 
601   printf("<h3>CoT configuration parameters your IdP may need to know</h3>\n");
602   eid = zxid_my_ent_id_cstr(cf);
603   printf("Entity ID of this SP: <a href=\"%s\">%s</a> (Click on the link to fetch SP metadata.)\n", eid, eid);
604 
605   printf("<h3>Technical options (typically hidden fields on production site)</h3>\n");
606   printf("<input type=checkbox name=fc value=1 checked> Allow new federation to be created<br>\n");
607   printf("<input type=checkbox name=fp value=1> Do not allow IdP to interact (e.g. ask password) (IsPassive flag)<br>\n");
608   printf("<input type=checkbox name=ff value=1> IdP should reauthenticate user (ForceAuthn flag)<br>\n");
609 
610   printf("NID Format: <select name=fn><option value=prstnt>Persistent<option value=trnsnt>Transient<option value=\"\">(none)</select><br>\n");
611   printf("Affiliation: <select name=fq><option value=\"\">(none)</select><br>\n");
612   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");
613   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");
614   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");
615   printf("RelayState: <input name=fr value=\"rs123\"><br>\n");
616 
617   printf("</form><hr>");
618   printf("<a href=\"http://zxid.org/\">zxid.org</a>, %s", zxid_version_str());
619   if (cgi.dbg)
620     printf("<p><form><textarea cols=100 row=10>%s</textarea></form>\n", cgi.dbg);
621   return 0;
622 }
623 
624 /* EOF  --  zxid.c */
625