1 /* zxidwspcgi.c  -  WSP CGI shell script connector
2  * Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3  * This is confidential unpublished proprietary source code of the author.
4  * NO WARRANTY, not even implied warranties. Contains trade secrets.
5  * Distribution prohibited unless authorized in writing.
6  * Licensed under Apache License 2.0, see file COPYING.
7  * $Id: zxidhrxmlwsp.c,v 1.14 2009-11-29 12:23:06 sampo Exp $
8  *
9  * 9.2.2010, created --Sampo
10  *
11  * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
12  *           mini_httpd_filter.c
13  */
14 
15 #include <zx/platform.h>
16 
17 #include <string.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <errno.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/c/zxidvers.h>
31 
32 #define CONF "PATH=/var/zxid/"
33 
34 char* help =
35 "zxidwspcgi  -  ID-WSF 2.0 WSP CGI - R" ZXID_REL "\n\
36 SAML 2.0 and ID-WSF 2.0 are standards for federated identity and web services.\n\
37 Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.\n\
38 NO WARRANTY, not even implied warranties. Licensed under Apache License v2.0\n\
39 See http://www.apache.org/licenses/LICENSE-2.0\n\
40 Send well-researched bug reports to the author. Home: zxid.org\n\
41 \n\
42 Usage: zxidwspcgi [options]   (when used as CGI, no options can be supplied)\n\
43   -h               This help message\n\
44   --               End of options\n\
45 \n\
46 Built-in configuration: " CONF "\n\
47 \n\
48 This C program implements a generic Web Services Provider. It is meant\n\
49 to be run as a cgi script from a web server. It will validate\n\
50 an incoming web service request and then pass it to sysadmin-supplied\n\
51 external program on stdin. The external program could be a shell script or\n\
52 a perl program - whatever you want. The external program reads the payload\n\
53 request from stdin and prints the payload response to stdout. zxidwspcgi\n\
54 handles the pipe-in - pipe-out deadlock dilemma by forking a process to\n\
55 perform the feeding in, while the original process will receive the\n\
56 stdout of the subprocess. Once entire payload response has been received,\n\
57 the response will be decorated with ID-WSF headers and sent as response\n\
58 to the original caller.\n";
59 
60 char buf[256*1024];  /* *** should figure the size dynamically */
61 int child_in_fds[2];   /* Parent out */
62 int child_out_fds[2];  /* Parent in */
63 
64 /*() Send data to script using child process */
65 
66 /* Called by:  zxidwspcgi_main */
zxidwspcgi_child(zxid_conf * cf,int len,char * buf,char * sid,char * nid)67 static int zxidwspcgi_child(zxid_conf* cf, int len, char* buf, char* sid, char* nid)
68 {
69   int status;
70   pid_t pid;
71   close(0);
72   close(1);
73   if (pid = fork()) {  /* Parent pumps child's input */
74     close(child_out_fds[0]);
75     close(child_out_fds[1]);
76     close(child_in_fds[0]);
77     D("Writing %d bytes to child pid=%d", len, pid);
78     write_all_fd(child_in_fds[1], buf, len);
79     D("Waiting for child pid=%d", pid);
80     if (waitpid(pid, &status, 0) == -1) {
81       perror("waitpid");
82     }
83     return status;
84   } else {
85     close(child_out_fds[0]);
86     close(child_in_fds[1]);
87     if (dup(child_in_fds[0]) != 0) {
88       perror("dup stdin");
89       return 1;
90     }
91     if (dup(child_out_fds[1]) != 1) {
92       perror("dup stdout");
93       return 1;
94     }
95     setenv("idpnid", nid, 1);
96     setenv("sid", sid, 1);
97     D("exec(%s)", cf->wspcgicmd);
98     execl(cf->wspcgicmd, cf->wspcgicmd);     /* At least gcc-3.4.6 gives bogus "warning: not enough variable arguments to fit a sentinel [-Wformat]" on this line. AFAIK you can safely ignore the warning. --Sampo */
99     perror("exec");
100     ERR("Exec(%s) failed: errno=%d", cf->wspcgicmd, errno);
101     return 1;
102   }
103   return 0;
104 }
105 
106 /*() Read from script using parent, and send resp. */
107 
108 /* Called by:  zxidwspcgi_main */
zxidwspcgi_parent(zxid_conf * cf,zxid_ses * ses,int pid)109 static int zxidwspcgi_parent(zxid_conf* cf, zxid_ses* ses, int pid)
110 {
111   struct zx_str* ss;
112   int got_all;
113   if (pid == -1) {
114     perror("first fork");
115     return 1;
116   }
117   close(child_out_fds[1]);
118   close(child_in_fds[0]);
119   close(child_in_fds[1]);
120   D("Reading from child writer_pid=%d", pid);
121   read_all_fd(child_out_fds[0], buf, sizeof(buf)-1, &got_all);
122   buf[got_all] = 0;
123   D("Got from child %d bytes", got_all);
124   ss = zxid_wsp_decorate(cf, ses, 0, buf);
125   fprintf(stdout, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\n\r\n%.*s", ss->len, ss->len, ss->s);
126   fflush(stdout);
127   if (waitpid(pid, &got_all, 0) == -1) {
128     perror("waitpid");
129   }
130   return 0;
131 }
132 
133 /* ============== M A I N ============== */
134 
135 #ifndef zxidwspcgi_main
136 #define zxidwspcgi_main main
137 #endif
138 
139 /* Called by: */
zxidwspcgi_main(int argc,char ** argv)140 int zxidwspcgi_main(int argc, char** argv)
141 {
142   zxid_conf* cf;
143   zxid_ses sess;
144   zxid_ses* ses = &sess;
145   char* nid;
146   char* p;
147   char* res;
148   char urlbuf[256];
149   int got, cl=0;
150   char* qs;
151   char* qs2;
152   pid_t pid;
153   ZERO(ses, sizeof(zxid_ses));
154 
155 #if 1
156   /* Helps debugging CGI scripts if you see stderr. */
157   /* Reopen stderr only in mini_httpd case */
158   p = getenv("SERVER_SOFTWARE");
159   if (p && !memcmp(p, "mini_httpd", sizeof("mini_httpd")-1)) {
160     close(2);
161     if (open("/var/tmp/zxidwspcgi.stderr", O_WRONLY | O_CREAT | O_APPEND, 0666) != 2)
162       exit(2);
163   }
164   fprintf(stderr, "=================== Running zxidwspcgi %s ===================\n", ZXID_REL);
165   errmac_debug = 1;
166 #endif
167 
168   qs = getenv("CONTENT_LENGTH");
169   if (qs)
170     sscanf(qs, "%d", &cl);
171 
172   if (cl) {
173     read_all_fd(fdstdin, buf, MIN(cl, sizeof(buf)-1), &got);
174     buf[got] = 0;
175     qs2 = buf;
176   } else {
177     qs2 = getenv("QUERY_STRING");
178     if (!qs2)
179       qs2 = "";
180     cl = strlen(qs2);
181   }
182   qs = strdup(qs2);
183   D("qs(%s)", qs);
184 
185   if (argc > 1) {
186     fprintf(stderr, "This is a CGI script (written in C). No arguments are accepted.\n%s", help);
187     return 1;
188   }
189 
190   cf = zxid_new_conf_to_cf(CONF);
191 
192   /* Dynamic construction of URL configuration parameter */
193 
194 #if 0
195 #define PROTO_STR "https://"
196 #else
197 #define PROTO_STR "http://"
198 #endif
199 
200 #if 1
201   /* Is this virtual hosting section still needed given that VHOST and VURL are
202    * supported directly by the configuration syntax? *** */
203   strcpy(urlbuf, PROTO_STR);
204   p = urlbuf + sizeof(PROTO_STR)-1;
205   res = getenv("HTTP_HOST");
206   if (res) {
207     strcpy(p, res);
208     p+=strlen(res);
209   }
210   res = getenv("SCRIPT_NAME");
211   if (res) {
212     strcpy(p, res);
213     p+=strlen(res);
214   }
215   if (p > urlbuf + sizeof(urlbuf))
216     exit(1);
217   zxid_url_set(cf, urlbuf);
218 #endif
219 
220   //if (!memcmp(qs+cl-4, "?o=B", 4)) {
221   if (qs[0] == 'o' && qs[1] == '=' && ONE_OF_2(qs[2], 'B', 'd')) {
222     D("Metadata qs(%s)", qs);
223     //cf = zxid_new_conf_to_cf(CONF);
224 
225     res = zxid_simple_cf(cf, cl, qs, 0, 0x1fff);
226     switch (res[0]) {
227     default:
228       ERR("Unknown zxid_simple() response(%s)", res);
229     case 'd': break; /* Logged in case */
230     }
231     ERR("Not a metadata qs(%s)",qs);
232     return 1;
233   }
234 
235   nid = zxid_wsp_validate(cf, ses, 0, buf);
236   if (!nid) {
237     ERR("Request validation failed buf(%.*s)", got, buf);
238     return 1;
239   }
240   D("target nid(%s)", nid);
241 
242   if (pipe(child_in_fds) == -1) {
243     perror("pipe");
244     return 1;
245   }
246   if (pipe(child_out_fds) == -1) {
247     perror("pipe");
248     return 1;
249   }
250   if (pid = fork())
251     return zxidwspcgi_parent(cf, ses, pid); /* Read from script using parent, and send resp. */
252   else
253     return zxidwspcgi_child(cf, got, buf, ses->sid, nid);  /* Send data to script using child process */
254 }
255 
256 /* EOF  --  zxidwspcgi.c */
257