1 /* zxidhrxmlwsp.c  -  ID-SIS HR-XML WSP
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: zxidhrxmlwsp.c,v 1.14 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/zxidutil.h>
29 #include <zx/zxidconf.h>  /* Default and compile-time configuration options. */
30 #include <zx/wsf.h>
31 #include <zx/c/zxidvers.h>
32 #include <zx/c/zx-ns.h>
33 #include <zx/c/zx-data.h>
34 
35 char* help =
36 "zxidhrxmlwsp  -  SAML 2.0 WSP CGI - R" ZXID_REL "\n\
37 SAML 2.0 is a standard for federated identity and Single Sign-On.\n\
38 Copyright (c) 2007-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.\n\
39 Author: Sampo Kellomaki (sampo@iki.fi)\n\
40 NO WARRANTY, not even implied warranties. Licensed under Apache License v2.0\n\
41 See http://www.apache.org/licenses/LICENSE-2.0\n\
42 Send well-researched bug reports to the author. Home: zxid.org\n\
43 \n\
44 Usage: zxidhrxmlwsp [options]   (when used as CGI, no options can be supplied)\n\
45   -h               This help message\n\
46   --               End of options\n";
47 
48 /* ============== M A I N ============== */
49 
50 #define ZXIDHLO "zxidhrxmlwsp"
51 #define CONF "PATH=/var/zxid/"
52 
53 /* Called by: */
main(int argc,char ** argv)54 int main(int argc, char** argv)
55 {
56   struct zx_ctx ctx;
57   zxid_conf cfs;
58   zxid_conf* cf;
59   zxid_ses sess;
60   zxid_ses* ses = &sess;
61   struct zx_root_s* r;
62   //struct zx_e_Envelope_s* env;
63   //zxid_epr* epr;
64   struct zx_str* ss;
65   //char* sid;
66   char* nid;
67   char* p;
68   char* res;
69   char buf[256*1024];  /* *** should figure the size dynamically */
70   char urlbuf[256];
71   int got, cl=0;
72   fdtype fd;
73   char* qs;
74   char* qs2;
75   ZERO(ses, sizeof(zxid_ses));
76 
77 #if 1
78   /* Helps debugging CGI scripts if you see stderr. */
79   close(2);
80   if (open("/var/tmp/zxid.stderr", O_WRONLY | O_CREAT | O_APPEND, 0666) != 2)
81     exit(2);
82   fprintf(stderr, "=================== Running idhrxml wsp ===================\n");
83   errmac_debug = 2;
84 #endif
85 #if 1
86   strncpy(errmac_instance, "\t\e[45mhrxml_wsp\e[0m", sizeof(errmac_instance));
87 #else
88   strncpy(errmac_instance, "\thrxml_wsp", sizeof(errmac_instance));
89 #endif
90 
91   qs = getenv("CONTENT_LENGTH");
92   if (qs)
93     sscanf(qs, "%d", &cl);
94 
95   if (cl) {
96     read_all_fd(fdstdin, buf, MIN(cl, sizeof(buf)-1), &got);
97     buf[got] = 0;
98     qs2 = buf;
99   } else {
100     qs2 = getenv("QUERY_STRING");
101     if (!qs2)
102       qs2 = "";
103     cl = strlen(qs2);
104   }
105   qs = strdup(qs2);
106   D_XML_BLOB(0, "HRXML IN", -2, qs);
107 
108   if (argc > 1) {
109     fprintf(stderr, "This is a CGI script (written in C). No arguments are accepted.\n%s", help);
110     exit(1);
111   }
112 
113 #if 1
114   zx_reset_ctx(&ctx);
115   ZERO(&cfs, sizeof(cfs));
116   cfs.ctx = &ctx;
117   cf = &cfs;
118   zxid_conf_to_cf_len(cf, -1, CONF);
119 #else
120   cf = zxid_new_conf_to_cf(CONF);
121 #endif
122 
123   /* Dynamic construction of URL configuration parameter */
124 
125 #if 0
126 #define PROTO_STR "https://"
127 #else
128 #define PROTO_STR "http://"
129 #endif
130 
131   strcpy(urlbuf, PROTO_STR);
132   p = urlbuf + sizeof(PROTO_STR)-1;
133   res = getenv("HTTP_HOST");
134   if (res) {
135     strcpy(p, res);
136     p+=strlen(res);
137   }
138   res = getenv("SCRIPT_NAME");
139   if (res) {
140     strcpy(p, res);
141     p+=strlen(res);
142   }
143   if (p > urlbuf + sizeof(urlbuf))
144     exit(1);
145   zxid_url_set(cf, urlbuf);
146 
147   //if (!memcmp(qs+cl-4, "?o=B", 4)) {
148   if (!memcmp(qs, "o=B", 3)) {
149     D("Metadata qs(%s)", qs);
150     //cf = zxid_new_conf_to_cf(CONF);
151 
152     res = zxid_simple_cf(cf, cl, qs, 0, 0x1fff);
153     switch (res[0]) {
154     default:
155       ERR("Unknown zxid_simple() response(%s)", res);
156     case 'd': break; /* Logged in case */
157     }
158     ERR("Not a metadata qs(%s)",qs);
159     exit(1);
160   }
161 
162   nid = zxid_wsp_validate(cf, ses, 0, buf);
163   if (!nid) {
164     DD("Request validation failed buf(%.*s)", got, buf);
165     ERR("Request validation failed len=%d", got);
166     ss = zxid_wsp_decorate(cf, ses, 0, "<Response><lu:Status code=\"INV\" comment=\"Request validation failed. Replay?\"></lu:Status></Response>");
167     DD("ss(%.*s)", ss->len, ss->s);
168     printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
169     exit(1);
170   }
171   D("Target nid(%s)", nid);
172 
173   r = zx_dec_zx_root(cf->ctx, cl, qs2, "hrxml wsp");
174 
175   DD("Decoded nid(%s)", nid);
176 
177   if (!r || !r->Envelope) {
178     ERR("No SOAP Envelope found buf(%.*s)", got, buf);
179     exit(1);
180   }
181   if (!r->Envelope->Body) {
182     ERR("No SOAP Body found buf(%.*s)", got, buf);
183     exit(1);
184   }
185 
186   if (r->Envelope->Body->idhrxml_Create) {
187     D("Create %d",0);
188     if (!r->Envelope->Body->idhrxml_Create->CreateItem) {
189       ERR("No CreateItem found buf(%.*s)", got, buf);
190       exit(1);
191     }
192 
193     if (!r->Envelope->Body->idhrxml_Create->CreateItem->NewData) {
194       ERR("No NewData found buf(%.*s)", got, buf);
195       exit(1);
196     }
197 
198     if (!r->Envelope->Body->idhrxml_Create->CreateItem->NewData->Candidate) {
199       ERR("No Candidate found buf(%.*s)", got, buf);
200 #if 0
201       env = ZXID_RESP_ENV(cf, "idhrxml:CreateResponse", "Fail", "NewData does not contain Candidate element.");
202       ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
203 #else
204       ss = zxid_wsp_decorate(cf, ses, 0, "<idhrxml:CreateResponse><lu:Status code=\"Fail\" comment=\"NewData does not contain Candidate element.\"></lu:Status></idhrxml:CreateResponse>");
205 #endif
206       printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
207       return 0;
208     }
209 
210     ss = zx_easy_enc_elem_opt(cf, &r->Envelope->Body->idhrxml_Create->CreateItem->NewData->Candidate->gg);
211 
212     fd = open_fd_from_path(O_CREAT|O_WRONLY|O_TRUNC, 0666, "create", 1, "%shrxml/cv.xml", cf->cpath);
213     write_all_fd(fd, ss->s, ss->len);
214     close_file(fd, (const char*)__FUNCTION__);
215 
216 #if 0
217     env = ZXID_RESP_ENV(cf, "idhrxml:CreateResponse", "OK", "Fine");
218     D("HERE(%p)", env);
219     ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
220     D("HERE(%p)", ss);
221 #else
222     ss = zxid_wsp_decorate(cf, ses, 0, "<idhrxml:CreateResponse><lu:Status code=\"OK\" comment=\"Fine\"></lu:Status></idhrxml:CreateResponse>");
223 #endif
224     printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
225     D("ss(%.*s)", ss->len, ss->s);
226     return 0;
227   }
228 
229   if (r->Envelope->Body->idhrxml_Query) {
230     D("Query %d",0);
231     if (!r->Envelope->Body->idhrxml_Query->QueryItem) {
232       ERR("No QueryItem found buf(%.*s)", got, buf);
233       exit(1);
234     }
235 
236     if (!r->Envelope->Body->idhrxml_Query->QueryItem->Select) {
237       ERR("No Select found buf(%.*s)", got, buf);
238       exit(1);
239     }
240 
241     /* *** This mock implementation does not actually interpret the Select string. */
242 
243     /* Parse the XML from the CV file into data structure and include it as Candidate. */
244 
245     got = read_all(sizeof(buf), buf, "query", 1, "%shrxml/cv.xml", cf->cpath);
246     if (got < 1) {
247       ERR("Reading hrxml/cv.xml resulted in error or the file was empty. ret=%d", got);
248 #if 0
249       env = ZXID_RESP_ENV(cf, "idhrxml:QueryResponse", "Fail", "Empty or no data");
250       ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
251 #else
252       ss = zxid_wsp_decorate(cf, ses, 0, "<idhrxml:QueryResponse><lu:Status code=\"Fail\" comment=\"Empty or no data\"></lu:Status></idhrxml:QueryResponse>");
253 #endif
254       printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
255       return 0;
256     }
257 
258     r = zx_dec_zx_root(cf->ctx, got, buf, "hrxml wsp cand");
259     if (!r || !r->Candidate) {
260       ERR("No hrxml:Candidate tag found in cv.xml(%s)", buf);
261 #if 0
262       env = ZXID_RESP_ENV(cf, "idhrxml:QueryResponse", "Fail", "No Candidate in data");
263       ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
264 #else
265       ss = zxid_wsp_decorate(cf, ses, 0, "<idhrxml:QueryResponse><lu:Status code=\"Fail\" comment=\"No Candidate in data.\"></lu:Status></idhrxml:QueryResponse>");
266 #endif
267       printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
268       return 0;
269     }
270 
271 #if 0
272     env = ZXID_RESP_ENV(cf, "idhrxml:QueryResponse", "OK", "Fine");
273     env->Body->idhrxml_QueryResponse->Data = zx_NEW_idhrxml_Data(cf->ctx,0);
274     env->Body->idhrxml_QueryResponse->Data->Candidate = r->Candidate;
275     ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
276 #else
277     ss = zxid_wsp_decoratef(cf, ses, 0, "<idhrxml:QueryResponse><lu:Status code=\"OK\" comment=\"Fine\"></lu:Status><idhrxml:Data><idhrxml:Candidate>%s</idhrxml:Candidate></idhrxml:Data></idhrxml:QueryResponse>", buf);
278 #endif
279     printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
280     return 0;
281   }
282 
283   // Modify
284 
285   if (r->Envelope->Body->idhrxml_Modify) {
286     D("Modify %d",0);
287     if (!r->Envelope->Body->idhrxml_Modify->ModifyItem) {
288       ERR("No ModifyItem found buf(%.*s)", got, buf);
289       exit(1);
290     }
291 
292     if (!r->Envelope->Body->idhrxml_Modify->ModifyItem->Select) {
293       ERR("No Select found buf(%.*s)", got, buf);
294       //exit(1);
295     }
296 
297     /* *** This mock implementation does not actually interpret the Select string. */
298 
299     if (!r->Envelope->Body->idhrxml_Modify->ModifyItem->NewData) {
300       ERR("No NewData found buf(%.*s)", got, buf);
301       exit(1);
302     }
303 
304     if (!r->Envelope->Body->idhrxml_Modify->ModifyItem->NewData->Candidate) {
305       ERR("No Candidate found buf(%.*s)", got, buf);
306 #if 0
307       env = ZXID_RESP_ENV(cf, "idhrxml:ModifyResponse", "Fail", "NewData does not contain Candidate element.");
308       ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
309 #else
310       ss = zxid_wsp_decorate(cf, ses, 0, "<idhrxml:ModifyResponse><lu:Status code=\"Fail\" comment=\"No Candidate in data.\"></lu:Status></idhrxml:ModifyResponse>");
311 #endif
312       printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
313       return 0;
314     }
315 
316     ss = zx_easy_enc_elem_opt(cf, &r->Envelope->Body->idhrxml_Modify->ModifyItem->NewData->Candidate->gg);
317 
318     fd = open_fd_from_path(O_CREAT|O_WRONLY|O_TRUNC, 0666, "modify", 1, "%shrxml/cv.xml", cf->cpath);
319     write_all_fd(fd, ss->s, ss->len);
320     close_file(fd, (const char*)__FUNCTION__);
321 
322 #if 0
323     env = ZXID_RESP_ENV(cf, "idhrxml:ModifyResponse", "OK", "Fine");
324     ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
325 #else
326     ss = zxid_wsp_decorate(cf, ses, 0, "<idhrxml:ModifyResponse><lu:Status code=\"OK\" comment=\"Fine\"></lu:Status></idhrxml:ModifyResponse>");
327 #endif
328     D("ss(%.*s)", ss->len, ss->s);
329     printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
330     return 0;
331   }
332 
333   // Delete
334 
335   if (r->Envelope->Body->idhrxml_Delete) {
336     D("Delete %d",0);
337     if (!r->Envelope->Body->idhrxml_Delete->DeleteItem) {
338       ERR("No DeleteItem found buf(%.*s)", got, buf);
339       exit(1);
340     }
341 
342     if (!r->Envelope->Body->idhrxml_Delete->DeleteItem->Select) {
343       ERR("No Select found buf(%.*s)", got, buf);
344       //exit(1);
345     }
346 
347     /* *** This mock implementation does not actually interpret the Select string. */
348 
349     got = name_from_path(buf, sizeof(buf), "%shrxml/cv.xml", cf->cpath);
350     unlink(buf);
351 
352 #if 0
353     env = ZXID_RESP_ENV(cf, "idhrxml:DeleteResponse", "OK", "Fine");
354     ss = zx_EASY_ENC_SO_e_Envelope(cf->ctx, env);
355 #else
356     ss = zxid_wsp_decorate(cf, ses, 0, "<idhrxml:DeleteResponse><lu:Status code=\"OK\" comment=\"Fine\"></lu:Status></idhrxml:DeleteResponse>");
357 #endif
358     D("ss(%.*s)", ss->len, ss->s);
359     printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
360     return 0;
361   }
362 
363   ss = zxid_wsp_decorate(cf, ses, 0, "<Response><lu:Status code=\"BAD\" comment=\"Unknown XML\"></lu:Status></Response>");
364   D("ss(%.*s)", ss->len, ss->s);
365   printf("CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
366   return 0;
367 }
368 
369 /* EOF  --  zxidhrxmlwsp.c */
370