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