1 /* zxidhrxmlwsc.c  -  ID-SIS HR-XML WSC
2  * Copyright (c) 2007-2009 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: zxidhrxmlwsc.c,v 1.12 2009-11-29 12:23:06 sampo Exp $
9  *
10  * 19.6.2007, created --Sampo
11  *
12  * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
13  *           README-zxid, section 10 "zxid_simple() API"
14  */
15 
16 #include "platform.h"
17 
18 #include <string.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 
26 #include <zx/errmac.h>
27 #include <zx/zxid.h>      /* ZXID main API, including zxid_simple(). */
28 #include <zx/zxidpriv.h>
29 #include <zx/zxidutil.h>
30 #include <zx/zxidconf.h>  /* Default and compile-time configuration options. */
31 #include <zx/wsf.h>
32 #include <zx/c/zxidvers.h>
33 #include <zx/c/zx-ns.h>
34 #include <zx/c/zx-e-data.h>
35 
36 char* help =
37 "zxidhrxmlwsc  -  SAML 2.0 SP + WSC CGI - R" ZXID_REL "\n\
38 SAML 2.0 is a standard for federated identity and Single Sign-On.\n\
39 Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.\n\
40 Copyright (c) 2007-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.\n\
41 Author: Sampo Kellomaki (sampo@iki.fi)\n\
42 NO WARRANTY, not even implied warranties. Licensed under Apache License v2.0\n\
43 See http://www.apache.org/licenses/LICENSE-2.0\n\
44 Send well-researched bug reports to the author. Home: zxid.org\n\
45 \n\
46 Usage: zxidhrxmlwsc [options]   (when used as CGI, no options can be supplied)\n\
47   -h               This help message\n\
48   --               End of options\n";
49 
50 #define HRXMLOP_CREATE 1
51 #define HRXMLOP_QUERY  2
52 #define HRXMLOP_MODIFY 3
53 #define HRXMLOP_DELETE 4
54 
55 struct hrxml_cgi {
56   int op;
57   char* select;
58   char* data;
59 };
60 
61 /* Called by:  main */
hrxml_parse_cgi(struct hrxml_cgi * cgi,char * qs)62 int hrxml_parse_cgi(struct hrxml_cgi* cgi, char* qs)
63 {
64   char *n, *v;
65   D("START qs(%s)", qs);
66   while (qs && *qs) {
67     qs = zxid_qs_nv_scan(qs, &n, &v, 1);
68     if (!n)
69       n = "NULL_NAME_ERRX";
70 
71 
72     printf("<input name=hrxmlselect value=\"\"><br>\n");
73     printf("<p>HR-XML Data<br><textarea name=hrxmldata cols=100 rows=5>%.*s</textarea>\n", 0, "");
74 
75     switch (n[0]) {
76     case 'h':
77       if (!strcmp(n, "hrxmlcreate")) {	cgi->op = HRXMLOP_CREATE;	break;      }
78       if (!strcmp(n, "hrxmlquery")) {	cgi->op = HRXMLOP_QUERY;	break;      }
79       if (!strcmp(n, "hrxmlmodify")) {	cgi->op = HRXMLOP_MODIFY;	break;      }
80       if (!strcmp(n, "hrxmldelete")) {	cgi->op = HRXMLOP_DELETE;	break;      }
81       if (!strcmp(n, "hrxmlselect")) {	cgi->select = v;	break;      }
82       if (!strcmp(n, "hrxmldata")) {	cgi->data = v;	break;      }
83       /* fall thru */
84     default:  break; //D("Unknown CGI field(%s) val(%s)", n, v);
85     }
86   }
87   //D("END cgi=%p cgi->eid=%p eid(%s) op(%c) magic=%x", cgi, cgi->eid,cgi->eid, cgi->op, cgi->magic);
88   return 0;
89 }
90 
91 /* ============== M A I N ============== */
92 
93 #define ZXIDHLO "zxidhrxmlwsc"
94 #define CONF "PATH=/var/zxid/"
95 
96 /* Called by: */
main(int argc,char ** argv)97 int main(int argc, char** argv)
98 {
99   struct zx_ctx ctx;
100   zxid_conf cfs;
101   struct hrxml_cgi cgi;
102   zxid_conf* cf;
103   zxid_ses sess;
104   zxid_ses* ses;
105   struct zx_root_s* r;
106   struct zx_e_Envelope_s* env = 0;
107   zxid_epr* epr;
108   struct zx_str* ss;
109   char* p;
110   char* sid;
111   char* nid;
112   char* res;
113   char* hrxml_resp = 0;
114   char* qs;
115   char* qs2;
116   char buf[64*1024];
117   char urlbuf[256];
118   int got, cl=0;
119 
120   qs = getenv("CONTENT_LENGTH");
121   if (qs)
122     sscanf(qs, "%d", &cl);
123 
124   if (cl) {
125     read_all_fd(fdstdin, buf, MIN(cl, sizeof(buf)-1), &got);
126     buf[got] = 0;
127     qs2 = buf;
128   } else {
129     qs2 = getenv("QUERY_STRING");
130     if (!qs2)
131       qs2 = "";
132     cl = strlen(qs2);
133   }
134   qs = strdup(qs2);
135 #if 1
136   /* Helps debugging CGI scripts if you see stderr. */
137   close(2);
138   if (open("tmp/zxid.stderr", O_WRONLY | O_CREAT | O_APPEND, 0666) != 2)
139     exit(2);
140   fprintf(stderr, "=================== Running ===================\n");
141   errmac_debug = 1;
142 #endif
143 
144   if (argc > 1) {
145     fprintf(stderr, "This is a CGI script (written in C). No arguments are accepted.\n%s", help);
146     exit(1);
147   }
148 
149 #if 1
150   zx_reset_ctx(&ctx);
151   ZERO(&cfs, sizeof(cfs));
152   cfs.ctx = &ctx;
153   cf = &cfs;
154   zxid_conf_to_cf_len(cf, -1, CONF);
155 #else
156   cf = zxid_new_conf_to_cf(CONF);
157 #endif
158 
159   /* Dynamic construction of URL configuration parameter */
160 
161 #if 0
162 #define PROTO_STR "https://"
163 #else
164 #define PROTO_STR "http://"
165 #endif
166 
167   strcpy(urlbuf, PROTO_STR);
168   p = urlbuf + sizeof(PROTO_STR)-1;
169   res = getenv("HTTP_HOST");
170   strcpy(p, res);
171   p+=strlen(res);
172   res = getenv("SCRIPT_NAME");
173   strcpy(p, res);
174   p+=strlen(res);
175   if (p > urlbuf + sizeof(urlbuf))
176     exit(1);
177   zxid_url_set(cf, urlbuf);
178 
179   res = zxid_simple_cf(cf, cl, qs2, 0, 0x1fff);
180   switch (res[0]) {
181   default:
182     ERR("Unknown zxid_simple() response(%s)", res);
183   case 'd': break; /* Logged in case */
184   }
185 
186   /* Parse the LDIF to figure out session ID and the federated ID */
187 
188   sid = strstr(res, "sesid: ");
189   nid = strstr(res, "idpnid: ");
190   if (sid) {
191     sid += sizeof("sesid: ") - 1;
192     p = strchr(sid, '\n');
193     if (p)
194       *p = 0;  /* nul termination */
195   }
196   if (nid) {
197     nid += sizeof("idpnid: ") - 1;
198     p = strchr(nid, '\n');
199     if (p)
200       *p = 0;  /* nul termination */
201   }
202 
203   DD("HERE qs(%s)", qs);
204   ZERO(&cgi, sizeof(cgi));
205   hrxml_parse_cgi(&cgi, qs);
206 
207   ses = &sess;
208   zxid_get_ses(cf, ses, sid);
209 
210   D("HERE cgi.op=%d qs(%s) sid(%s)", cgi.op, qs, sid);
211 
212   switch (cgi.op) {
213 
214   case HRXMLOP_CREATE:
215     D("Here %d", 0);
216     epr = zxid_get_epr(cf, ses, zx_xmlns_idhrxml, 0, 0, 0, 1);
217     if (!epr) {
218       ERR("EPR could not be discovered %d", 0);
219       break;
220     }
221     D("Here %p", epr);
222 
223     env = zx_NEW_e_Envelope(cf->ctx,0);
224     env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
225     env->Body = zx_NEW_e_Body(cf->ctx, &env->gg);
226     env->Body->idhrxml_Create = zx_NEW_idhrxml_Create(cf->ctx, &env->Body->gg);
227     env->Body->idhrxml_Create->CreateItem = zx_NEW_idhrxml_CreateItem(cf->ctx, &env->Body->idhrxml_Create->gg);
228     env->Body->idhrxml_Create->CreateItem->NewData = zx_NEW_idhrxml_NewData(cf->ctx, &env->Body->idhrxml_Create->CreateItem->gg);
229 
230     /* Parse the XML from the form field into data structure and include it as NewData. */
231 
232     r = zx_dec_zx_root(cf->ctx, strlen(cgi.data), cgi.data, "hrxml wsc");
233     if (!r || !r->Candidate) {
234       ERR("No hrxml:Candidate tag found in form field hrxmldata(%s)", cgi.data);
235       hrxml_resp = "No hrxml:Candidate tag found in form field hrxmldata.";
236       break;
237     }
238     env->Body->idhrxml_Create->CreateItem->NewData->Candidate = r->Candidate;
239 
240     D("Here %p", epr);
241     env = zxid_wsc_call(cf, ses, epr, env, 0);
242     if (!env || env == (void*)ZXID_REDIR_OK || !env->Body) {
243       ERR("Web services call failed %p", env);
244       break;
245     }
246     D("Here %p", epr);
247     if (!env->Body->idhrxml_CreateResponse) {
248       ERR("There was no result %p", env->Body);
249       break;
250     }
251     if (!memcmp(env->Body->idhrxml_CreateResponse->Status->code->g.s, "OK", 2)) {
252       hrxml_resp = "Create OK.";
253     } else {
254       hrxml_resp = "Create Failed.";
255       D("Non OK status(%.*s)", env->Body->idhrxml_CreateResponse->Status->code->g.len, env->Body->idhrxml_CreateResponse->Status->code->g.s);
256     }
257     D("Here %p", epr);
258     break;
259 
260   case HRXMLOP_QUERY:
261     epr = zxid_get_epr(cf, ses, zx_xmlns_idhrxml, 0, 0, 0, 1);
262     if (!epr) {
263       ERR("EPR could not be discovered %d", 0);
264       break;
265     }
266     env = zx_NEW_e_Envelope(cf->ctx,0);
267     env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
268     env->Body = zx_NEW_e_Body(cf->ctx, &env->gg);
269     env->Body->idhrxml_Query = zx_NEW_idhrxml_Query(cf->ctx, &env->Body->gg);
270     env->Body->idhrxml_Query->QueryItem = zx_NEW_idhrxml_QueryItem(cf->ctx, &env->Body->idhrxml_Query->gg);
271     env->Body->idhrxml_Query->QueryItem->Select
272       = zx_ref_elem(cf->ctx, &env->Body->idhrxml_Query->QueryItem->gg, zx_idhrxml_Select_ELEM, cgi.select);
273 
274     env = zxid_wsc_call(cf, ses, epr, env, 0);
275     D("HERE env=%p", env);
276     if (!env || env == (void*)ZXID_REDIR_OK || !env->Body) {
277       ERR("Web services call failed %d", 0);
278       break;
279     }
280     if (!env->Body->idhrxml_QueryResponse) {
281       ERR("There was no result %p", env->Body);
282       break;
283     }
284     if (!memcmp(env->Body->idhrxml_QueryResponse->Status->code->g.s, "OK", 2)) {
285       if (!env->Body->idhrxml_QueryResponse->Data) {
286 	hrxml_resp = "No data in otherwise successful response.";
287 	ERR("There was no data %p", env->Body);
288 	break;
289       }
290       if (!env->Body->idhrxml_QueryResponse->Data->Candidate) {
291 	hrxml_resp = "No Candidate in otherwise successful response.";
292 	ERR("There was no candidate %p", env->Body);
293 	break;
294       }
295       ss = zx_easy_enc_elem_opt(cf, &env->Body->idhrxml_QueryResponse->Data->Candidate->gg);
296       hrxml_resp = ss->s;
297     } else {
298       hrxml_resp = "Query Failed.";
299       D("Non OK status(%.*s)", env->Body->idhrxml_QueryResponse->Status->code->g.len, env->Body->idhrxml_QueryResponse->Status->code->g.s);
300     }
301     break;
302 
303   case HRXMLOP_MODIFY:
304     ss = zxid_callf(cf, ses, zx_xmlns_idhrxml, 0, 0, 0, "<idhrxml:Modify><idhrxml:ModifyItem><idhrxml:Select>%s</idhrxml:Select><idhrxml:NewData>%s</idhrxml:NewData></idhrxml:ModifyItem></idhrxml:Modify>", cgi.select, cgi.data);
305     //ZXID_CHK_STATUS(env, idhrxml_ModifyResponse, hrxml_resp = "Modify failed"; break);
306     //hrxml_resp = "Modify OK";
307     hrxml_resp = ss->s;
308     break;
309 
310   case HRXMLOP_DELETE:
311     epr = zxid_get_epr(cf, ses, zx_xmlns_idhrxml, 0, 0, 0, 1);
312     if (!epr) {
313       ERR("EPR could not be discovered %d", 0);
314       break;
315     }
316     //env = zxid_new_envf(cf, "<idhrxml:Delete><idhrxml:DeleteItem><idhrxml:Select>%s</idhrxml:Select></idhrxml:DeleteItem></idhrxml:Delete>", cgi.select);
317     env = zxid_wsc_call(cf, ses, epr, env, 0);
318     D("HERE env=%p", env);
319     if (!env || env == (void*)ZXID_REDIR_OK || !env->Body) {
320       ERR("Web services call failed %p", env);
321       break;
322     }
323     if (!env->Body->idhrxml_DeleteResponse) {
324       ERR("There was no result %p", env->Body);
325       break;
326     }
327     if (!memcmp(env->Body->idhrxml_DeleteResponse->Status->code->g.s, "OK", 2)) {
328       hrxml_resp = "Delete OK.";
329     } else {
330       hrxml_resp = "Delete Failed.";
331       D("Non OK status(%.*s)", env->Body->idhrxml_DeleteResponse->Status->code->g.len, env->Body->idhrxml_DeleteResponse->Status->code->g.s);
332     }
333     break;
334   }
335 
336   /* Render protected content page. You should replace this
337    * with your own content, or establishment of your own session
338    * and then redirect to your own content. Whatever makes sense. */
339 
340   printf("Content-Type: text/html\r\n\r\n");
341   printf("<title>ZXID HELLO SP Mgmt</title>" ZXID_BODY_TAG "<h1>ZXID HELLO SP Management (user logged in, session active)</h1><pre>\n");
342   printf("</pre><form method=post action=\"" ZXIDHLO "?o=P\">");
343   //if (err) printf("<p><font color=red><i>%s</i></font></p>\n", err);
344   //if (msg) printf("<p><i>%s</i></p>\n", msg);
345   if (sid) {
346     printf("<input type=hidden name=s value=\"%s\">", sid);
347     printf("<input type=submit name=gl value=\" Local Logout \">\n");
348     printf("<input type=submit name=gr value=\" Single Logout (Redir) \">\n");
349     printf("<input type=submit name=gs value=\" Single Logout (SOAP) \">\n");
350     printf("<input type=submit name=gt value=\" Defederate (Redir) \">\n");
351     printf("<input type=submit name=gu value=\" Defederate (SOAP) \"><br>\n");
352     printf("sid(%s) nid(%s) <a href=\"" ZXIDHLO "?s=%s\">Reload</a>", sid, nid?nid:"?!?", sid);
353     printf("<hr><h1>ID-SIS HR-XML Client</h1>\n");
354     printf("<input type=submit name=hrxmlcreate value=\" Create \"><br>\n");
355     printf("<input type=submit name=hrxmlquery value=\" Query \">\n");
356     printf(" Select: <input name=hrxmlselect value=\"\"><br>\n");
357     printf("<input type=submit name=hrxmlmodify value=\" Modify \"><br>\n");
358     printf("<input type=submit name=hrxmldelete value=\" Delete \"><br>\n");
359     printf("<p>HR-XML Data<br><textarea name=hrxmldata cols=100 rows=5>%s</textarea>\n",
360 	   "<hrxml:Candidate xmlns:hrxml=\"http://ns.hr-xml.org/2007-04-15\"></hrxml:Candidate>");
361 
362     printf("<p>HR-XML Response<br><textarea name=hrxmlresp cols=100 rows=5>%s</textarea>\n",
363 	   hrxml_resp?hrxml_resp:"");
364   }
365 
366   printf("</form><hr>\n");
367   printf("<a href=\"http://zxid.org/\">zxid.org</a>, %s", zxid_version_str());
368   return 0;
369 }
370 
371 /* EOF  --  zxidhlowsf.c */
372