1 #include <uwsgi.h>
2 
3 extern struct uwsgi_server uwsgi;
4 
5 #define kill_on_error if (!uc.do_not_kill_on_error) { if (kill(cgi_pid, SIGKILL)) uwsgi_error("kill()");}
6 
7 struct uwsgi_cgi {
8 	struct uwsgi_dyn_dict *mountpoint;
9 	struct uwsgi_dyn_dict *helpers;
10 	size_t buffer_size;
11 	int timeout;
12 	struct uwsgi_string_list *index;
13 	struct uwsgi_string_list *allowed_ext;
14 	struct uwsgi_string_list *unset;
15 	struct uwsgi_string_list *loadlib;
16 	struct uwsgi_string_list *cgi_safe;
17 	int optimize;
18 	int from_docroot;
19 	int has_mountpoints;
20 	struct uwsgi_dyn_dict *default_cgi;
21 	int path_info;
22 	int do_not_kill_on_error;
23 	int async_max_attempts;
24 	int close_stdin_on_eof;
25 } uc ;
26 
uwsgi_opt_add_cgi(char * opt,char * value,void * foobar)27 static void uwsgi_opt_add_cgi(char *opt, char *value, void *foobar) {
28 
29 	char *val = strchr(value, '=');
30         if (!val) {
31         	uwsgi_dyn_dict_new(&uc.mountpoint, value, strlen(value), NULL, 0);
32         }
33         else {
34         	uwsgi_dyn_dict_new(&uc.mountpoint, value, val-value, val+1, strlen(val+1));
35         }
36 
37 }
38 
uwsgi_opt_add_cgi_maphelper(char * opt,char * value,void * foobar)39 static void uwsgi_opt_add_cgi_maphelper(char *opt, char *value, void *foobar) {
40 	char *val = strchr(value, '=');
41         if (!val) {
42         	uwsgi_log("invalid CGI helper syntax, must be ext=command\n");
43                 exit(1);
44         }
45         uwsgi_dyn_dict_new(&uc.helpers, value, val-value, val+1, strlen(val+1));
46 }
47 
48 struct uwsgi_option uwsgi_cgi_options[] = {
49 
50         {"cgi", required_argument, 0, "add a cgi mountpoint/directory/script", uwsgi_opt_add_cgi, NULL, 0},
51 
52         {"cgi-map-helper", required_argument, 0, "add a cgi map-helper", uwsgi_opt_add_cgi_maphelper, NULL, 0},
53         {"cgi-helper", required_argument, 0, "add a cgi map-helper", uwsgi_opt_add_cgi_maphelper, NULL, 0},
54 
55         {"cgi-from-docroot", no_argument, 0, "blindly enable cgi in DOCUMENT_ROOT", uwsgi_opt_true, &uc.from_docroot, 0},
56 
57         {"cgi-buffer-size", required_argument, 0, "set cgi buffer size", uwsgi_opt_set_64bit, &uc.buffer_size, 0},
58         {"cgi-timeout", required_argument, 0, "set cgi script timeout", uwsgi_opt_set_int, &uc.timeout, 0},
59 
60         {"cgi-index", required_argument, 0, "add a cgi index file", uwsgi_opt_add_string_list, &uc.index, 0},
61         {"cgi-allowed-ext", required_argument, 0, "cgi allowed extension", uwsgi_opt_add_string_list, &uc.allowed_ext, 0},
62 
63         {"cgi-unset", required_argument, 0, "unset specified environment variables", uwsgi_opt_add_string_list, &uc.unset, 0},
64 
65         {"cgi-loadlib", required_argument, 0, "load a cgi shared library/optimizer", uwsgi_opt_add_string_list, &uc.loadlib, 0},
66         {"cgi-optimize", no_argument, 0, "enable cgi realpath() optimizer", uwsgi_opt_true, &uc.optimize, 0},
67         {"cgi-optimized", no_argument, 0, "enable cgi realpath() optimizer", uwsgi_opt_true, &uc.optimize, 0},
68 
69         {"cgi-path-info", no_argument, 0, "disable PATH_INFO management in cgi scripts", uwsgi_opt_true, &uc.path_info, 0},
70 
71         {"cgi-do-not-kill-on-error", no_argument, 0, "do not send SIGKILL to cgi script on errors", uwsgi_opt_true, &uc.do_not_kill_on_error, 0},
72         {"cgi-async-max-attempts", no_argument, 0, "max waitpid() attempts in cgi async mode (default 10)", uwsgi_opt_set_int, &uc.async_max_attempts, 0},
73 
74         {"cgi-close-stdin-on-eof", no_argument, 0, "close STDIN on input EOF", uwsgi_opt_true, &uc.close_stdin_on_eof, 0},
75 
76         {"cgi-safe", required_argument, 0, "skip security checks if the cgi file is under the specified path", uwsgi_opt_add_string_list, &uc.cgi_safe, 0},
77 
78         {0, 0, 0, 0, 0, 0, 0},
79 
80 };
81 
uwsgi_cgi_apps()82 static void uwsgi_cgi_apps() {
83 
84 	struct uwsgi_dyn_dict *udd = uc.mountpoint;
85 	struct stat st;
86 
87 	while(udd) {
88 		if (udd->vallen) {
89 			if (uc.optimize) {
90 				udd->value = realpath(udd->value, NULL);
91 				if (!udd->value) {
92 					uwsgi_log("unable to find CGI path %.*s\n", udd->vallen, udd->value);
93 					exit(1);
94 				}
95 				udd->vallen = strlen(udd->value);
96 				udd->status = 1;
97 				if (stat(udd->value, &st)) {
98 					uwsgi_error("stat()");
99 					uwsgi_log("something horrible happened during CGI initialization\n");
100 					exit(1);
101 				}
102 
103 				if (!S_ISDIR(st.st_mode)) {
104 					udd->status = 2;
105 				}
106 			}
107 			uc.has_mountpoints = 1;
108 			uwsgi_log("initialized CGI mountpoint: %.*s = %.*s\n", udd->keylen, udd->key, udd->vallen, udd->value);
109 		}
110 		else {
111 			if (uc.optimize) {
112 				udd->key = realpath(udd->key, NULL);
113 				if (!udd->key) {
114                                         uwsgi_log("unable to find CGI path %.*s\n", udd->keylen, udd->key);
115                                         exit(1);
116                                 }
117                                 udd->keylen = strlen(udd->key);
118 				udd->status = 1;
119 
120 				if (stat(udd->key, &st)) {
121                                         uwsgi_error("stat()");
122                                         uwsgi_log("something horrible happened during CGI initialization\n");
123                                         exit(1);
124                                 }
125 
126                                 if (!S_ISDIR(st.st_mode)) {
127                                         udd->status = 2;
128                                 }
129 
130 			}
131 			uwsgi_log("initialized CGI path: %.*s\n", udd->keylen, udd->key);
132 			uc.default_cgi = udd;
133 		}
134 		udd = udd->next;
135 	}
136 
137 }
138 
uwsgi_cgi_init()139 static int uwsgi_cgi_init(){
140 
141 	void (*cgi_sym)(void);
142 
143 	if (!uc.buffer_size) uc.buffer_size = 65536;
144 	if (!uc.timeout) uc.timeout = 60;
145 
146 	struct uwsgi_string_list *ll = uc.loadlib;
147 	while(ll) {
148 		char *colon = strchr(ll->value, ':');
149 		if (!colon) {
150 			uwsgi_log("invalid cgi-loadlib syntax, must be in the form lib:func\n");
151 			exit(1);
152 		}
153 		*colon = 0;
154 		void *cgi_lib = dlopen(ll->value, RTLD_NOW | RTLD_GLOBAL);
155 		if (!cgi_lib) {
156 			uwsgi_log( "cgi-loadlib: %s\n", dlerror());
157 			exit(1);
158 		}
159 
160 		cgi_sym = dlsym(cgi_lib, colon+1);
161 		if (!cgi_sym) {
162 			uwsgi_log("unknown symbol %s in lib %s\n", colon+1, ll->value);
163 			exit(1);
164 		}
165 
166 		cgi_sym();
167 		uwsgi_log("[cgi-loadlib] loaded symbol %s from %s\n", colon+1, ll->value);
168 
169 		*colon = ':';
170 		ll = ll->next;
171 	}
172 
173 	return 0;
174 
175 }
176 
uwsgi_cgi_get_helper(char * filename)177 static char *uwsgi_cgi_get_helper(char *filename) {
178 
179 	struct uwsgi_dyn_dict *helpers = uc.helpers;
180 	size_t len = strlen(filename);
181 
182 	while(helpers) {
183 		if (len >= (size_t) helpers->keylen) {
184 			if (!uwsgi_strncmp((filename+len)-helpers->keylen, helpers->keylen, helpers->key, helpers->keylen)) {
185 				return helpers->value;
186 			}
187 		}
188 		helpers = helpers->next;
189 	}
190 
191 	return NULL;
192 
193 }
194 
195 /*
196 start reading each line until Status or Location are found
197 -1 error
198 0 not found
199 1 found
200 */
uwsgi_cgi_check_status(struct wsgi_request * wsgi_req,char * buf,size_t len)201 static int uwsgi_cgi_check_status(struct wsgi_request *wsgi_req, char *buf, size_t len) {
202 	char *key = buf, *value = NULL;
203 	size_t header_size = 0;
204 	size_t i;
205 
206 	for(i=0;i<len;i++) {
207        		// end of a line
208                 if (buf[i] == '\n') {
209                 	// end of headers
210                         if (key == NULL) {
211                         	// Default status
212 #ifdef UWSGI_DEBUG
213 				uwsgi_log("setting default Status header\n");
214 #endif
215 				if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1;
216 				return 1;
217 			}
218                         // invalid header
219                         else if (value == NULL) return -1;
220                         header_size = (buf+i) - key;
221                         // security check
222                         if (buf+i > buf) {
223 				// remove \r
224                         	if ((buf[i-1]) == '\r') {
225                                 	header_size--;
226                                 }
227                         }
228 
229 			// enough space for Status ?
230                         if (header_size >= 11) {
231                         	// "Status: NNN"
232                                 if (!strncasecmp("Status: ", key, 8)) {
233 #ifdef UWSGI_DEBUG
234                                 	uwsgi_log("found Status header: %.*s\n", header_size, key);
235 #endif
236                                         if (uwsgi_response_prepare_headers(wsgi_req, key+8, header_size - 8)) return -1;
237 					return 1;
238                                 }
239                                 // Location: X
240                                 if (!strncasecmp("Location: ", key, 10)) {
241 #ifdef UWSGI_DEBUG
242                                 	uwsgi_log("found Location header: %.*s\n", header_size, key);
243 #endif
244                                         if (uwsgi_response_prepare_headers(wsgi_req, "302 Found", 9)) return -1;
245 					return 1;
246                                 }
247 			}
248 
249                         key = NULL;
250                         value = NULL;
251 		}
252                 else if (buf[i] == ':') {
253 			value = buf+i;
254                 }
255                 else if (buf[i] != '\r') {
256                 	if (key == NULL) key = buf + i;
257                 }
258 
259 	}
260 
261 	// no Status/Location found
262 	return 0;
263 
264 }
265 
uwsgi_cgi_parse(struct wsgi_request * wsgi_req,int fd,char * buf,size_t blen)266 static int uwsgi_cgi_parse(struct wsgi_request *wsgi_req, int fd, char *buf, size_t blen) {
267 
268 	size_t i;
269 	size_t header_size = 0;
270 	int status_sent = 0;
271 	size_t remains = blen;
272 	char *ptr = buf;
273 	size_t len = 0;
274 
275 	while(remains > 0) {
276 		ssize_t rlen = uwsgi_read_true_nb(fd, ptr, remains, uc.timeout);
277 		if (rlen < 0) {
278 			if (!errno) return 1;
279 			return -1;
280 		}
281 		// timed out
282 		if (rlen == 0) return -1;
283 		remains -= rlen;
284 		len += rlen;
285 		ptr += rlen;
286 
287 		// Search for Status/Location headers
288 		if (!status_sent) {
289 			status_sent = uwsgi_cgi_check_status(wsgi_req, buf, len);
290 			if (status_sent < 0) return -1;
291 			// need more data ?
292 			if (status_sent == 0) continue;
293 		}
294 
295 		// send headers
296 		char *key = buf;
297 		char *value = NULL;
298 
299 		for(i=0;i<len;i++) {
300 			// end of a line
301 			if (buf[i] == '\n') {
302 				// end of headers
303 				if (key == NULL) {
304 					i++;
305 					goto send_body;
306 				}
307 				// invalid header
308 				else if (value == NULL) {
309 					return -1;
310 				}
311 				header_size = (buf+i) - key;
312 				// security check
313 				if (buf+i > buf) {
314 					if ((buf[i-1]) == '\r') {
315 						header_size--;
316 					}
317 				}
318 
319 #ifdef UWSGI_DEBUG
320 				uwsgi_log("found CGI header: %.*s\n", header_size, key);
321 #endif
322 
323 				// Ignore "Status: NNN" header
324 				if (header_size >= 11) {
325 					if (!strncasecmp("Status: ", key, 8)) {
326 						key = NULL;
327 						value = NULL;
328 						continue;
329 					}
330 				}
331 
332 				uwsgi_response_add_header(wsgi_req, NULL, 0, key, header_size);
333 				key = NULL;
334 				value = NULL;
335 			}
336 			else if (buf[i] == ':') {
337 				value = buf+i;
338 			}
339 			else if (buf[i] != '\r') {
340 				if (key == NULL) {
341 					key = buf + i;
342 				}
343 			}
344 		}
345 	}
346 
347 	return -1;
348 
349 send_body:
350 
351 	if (len-i > 0) {
352 		uwsgi_response_write_body_do(wsgi_req, buf+i, len-i);
353 	}
354 
355 	return 0;
356 }
357 
uwsgi_cgi_get_docroot(char * path_info,uint16_t path_info_len,int * need_free,int * is_a_file,int * discard_base,char ** script_name)358 static char *uwsgi_cgi_get_docroot(char *path_info, uint16_t path_info_len, int *need_free, int *is_a_file, int *discard_base, char **script_name) {
359 
360 	struct uwsgi_dyn_dict *udd = uc.mountpoint, *choosen_udd = NULL;
361 	int best_found = 0;
362 	struct stat st;
363 	char *path = NULL;
364 
365 	if (uc.has_mountpoints) {
366 		while(udd) {
367 			if (udd->vallen) {
368 				if (!uwsgi_starts_with(path_info, path_info_len, udd->key, udd->keylen) && udd->keylen > best_found) {
369 					best_found = udd->keylen ;
370 					choosen_udd = udd;
371 					path = udd->value;
372 					*script_name = udd->key;
373 					*discard_base = udd->keylen;
374 					if (udd->key[udd->keylen-1] == '/') {
375 						*discard_base = *discard_base-1;
376 					}
377 				}
378 			}
379 			udd = udd->next;
380 		}
381 	}
382 
383 	if (choosen_udd == NULL) {
384 		choosen_udd = uc.default_cgi;
385 		if (!choosen_udd) return NULL;
386 		path = choosen_udd->key;
387 	}
388 
389 	if (choosen_udd->status == 0) {
390 		char *tmp_udd = uwsgi_malloc(PATH_MAX+1);
391 		if (!realpath(path, tmp_udd)) {
392 			free(tmp_udd);
393 			return NULL;
394 		}
395 
396 		if (stat(tmp_udd, &st)) {
397 			uwsgi_error("stat()");
398 			free(tmp_udd);
399 			return NULL;
400 		}
401 
402 		if (!S_ISDIR(st.st_mode)) {
403 			*is_a_file = 1;
404 		}
405 
406 		*need_free = 1;
407 		return tmp_udd;
408 	}
409 
410 	if (choosen_udd->status == 2)
411 		*is_a_file = 1;
412 	return path;
413 }
414 
uwsgi_cgi_walk(struct wsgi_request * wsgi_req,char * full_path,char * docroot,size_t docroot_len,int discard_base,char ** path_info)415 static int uwsgi_cgi_walk(struct wsgi_request *wsgi_req, char *full_path, char *docroot, size_t docroot_len, int discard_base, char **path_info) {
416 
417 	// and now start walking...
418         uint16_t i;
419         char *ptr = wsgi_req->path_info+discard_base;
420         char *dst = full_path+docroot_len;
421         char *part = ptr;
422         int part_size = 0;
423 	struct stat st;
424 
425 	if (wsgi_req->path_info_len == 0) return 0;
426 
427         if (ptr[0] == '/') part_size++;
428 
429         for(i=0;i<wsgi_req->path_info_len-discard_base;i++) {
430         	if (ptr[i] == '/') {
431                 	memcpy(dst, part, part_size-1);
432                         *(dst+part_size-1) = 0;
433 
434                         if (stat(full_path, &st)) {
435                         	uwsgi_404(wsgi_req);
436                                 return -1;
437                         }
438 
439 
440 			// not a directory, stop walking
441                         if (!S_ISDIR(st.st_mode)) {
442 				if (i < (wsgi_req->path_info_len-discard_base)-1) {
443                         		*path_info = ptr + i;
444 				}
445 
446 				return 0;
447                         }
448 
449 
450 			// check for buffer overflow !!!
451                         *(dst+part_size-1) = '/';
452                         *(dst+part_size) = 0;
453 
454                         dst += part_size ;
455                         part_size = 0;
456                         part = ptr + i + 1;
457          	}
458 
459                 part_size++;
460 	}
461 
462 	if (part < wsgi_req->path_info+wsgi_req->path_info_len) {
463 		memcpy(dst, part, part_size-1);
464 		*(dst+part_size-1) = 0;
465 	}
466 
467 	return 0;
468 
469 
470 }
471 
472 static int uwsgi_cgi_run(struct wsgi_request *, char *, size_t, char *, char *, char *, char *, int, int);
473 
uwsgi_cgi_request(struct wsgi_request * wsgi_req)474 static int uwsgi_cgi_request(struct wsgi_request *wsgi_req) {
475 
476 	char full_path[PATH_MAX];
477 	char tmp_path[PATH_MAX];
478 	struct stat cgi_stat;
479 	int need_free = 0;
480 	int is_a_file = 0;
481 	int discard_base = 0;
482 	size_t docroot_len = 0;
483 	size_t full_path_len = 0;
484 	char *helper = NULL;
485 	char *path_info = NULL;
486 	char *script_name = NULL;
487 
488 	/* Standard CGI request */
489 	if (!wsgi_req->uh->pktsize) {
490 		uwsgi_log("Empty CGI request. skip.\n");
491 		return -1;
492 	}
493 
494 
495 	if (uwsgi_parse_vars(wsgi_req)) {
496 		return -1;
497 	}
498 
499 	char *docroot = NULL;
500 
501 	// check for file availability (and 'runnability')
502 	if (uc.from_docroot) {
503 		docroot = wsgi_req->document_root;
504 		docroot_len = wsgi_req->document_root_len;
505 	}
506 	else {
507 		docroot = uwsgi_cgi_get_docroot(wsgi_req->path_info, wsgi_req->path_info_len, &need_free, &is_a_file, &discard_base, &script_name);
508 		if (docroot)
509 			docroot_len = strlen(docroot);
510 	}
511 
512 	if (docroot == NULL || docroot_len == 0) {
513 		uwsgi_404(wsgi_req);
514 		return UWSGI_OK;
515 	}
516 
517 	memcpy(full_path, docroot, docroot_len);
518 
519 	if (!is_a_file) {
520 
521 		*(full_path+docroot_len) = '/';
522 		*(full_path+docroot_len+1) = 0;
523 
524 		if (uwsgi_cgi_walk(wsgi_req, full_path, docroot, docroot_len, discard_base, &path_info)) {
525 			if (need_free)
526 				free(docroot);
527 			return UWSGI_OK;
528 		}
529 
530 		if (realpath(full_path, tmp_path) == NULL) {
531 			if (need_free)
532 				free(docroot);
533 			uwsgi_404(wsgi_req);
534 			return UWSGI_OK;
535 		}
536 
537 		full_path_len = strlen(tmp_path);
538 		// add +1 to copy the null byte
539 		memcpy(full_path, tmp_path, full_path_len+1);
540 
541 		struct uwsgi_string_list *safe = uc.cgi_safe;
542 		while(safe) {
543 			if (!uwsgi_starts_with(full_path, full_path_len, safe->value, safe->len))
544 				break;
545 			safe = safe->next;
546 		}
547 
548 		if (!safe) {
549 			if (uwsgi_starts_with(full_path, full_path_len, docroot, docroot_len)) {
550 				uwsgi_log("CGI security error: %s is not under %s\n", full_path, docroot);
551 				if (need_free)
552 					free(docroot);
553 				return -1;
554 			}
555 		}
556 
557 	}
558 	else {
559 		*(full_path+docroot_len) = 0;
560 		path_info = wsgi_req->path_info+discard_base;
561 	}
562 
563 	if (stat(full_path, &cgi_stat)) {
564 		uwsgi_404(wsgi_req);
565 		if (need_free)
566 			free(docroot);
567 		return UWSGI_OK;
568 	}
569 
570 	if (S_ISDIR(cgi_stat.st_mode)) {
571 
572 		// add / to directories
573 		if (wsgi_req->path_info_len == 0 || (wsgi_req->path_info_len > 0 && wsgi_req->path_info[wsgi_req->path_info_len-1] != '/')) {
574 			uwsgi_redirect_to_slash(wsgi_req);
575 			if (need_free)
576                         	free(docroot);
577                 	return UWSGI_OK;
578 		}
579 		struct uwsgi_string_list *ci = uc.index;
580 		full_path[full_path_len] = '/';
581 		full_path_len++;
582 		int found = 0;
583 		while(ci) {
584 			if (full_path_len + ci->len + 1 < PATH_MAX) {
585 				// add + 1 to ensure null byte
586 				memcpy(full_path+full_path_len, ci->value, ci->len + 1);
587 				if (!access(full_path, R_OK)) {
588 
589 					found = 1;
590 					break;
591 				}
592 			}
593 			ci = ci->next;
594 		}
595 
596 		if (!found) {
597 			uwsgi_404(wsgi_req);
598 			if (need_free)
599 				free(docroot);
600 			return UWSGI_OK;
601 		}
602 
603 	}
604 
605 	full_path_len = strlen(full_path);
606 
607 	int cgi_allowed = 1;
608 	struct uwsgi_string_list *allowed = uc.allowed_ext;
609 	while(allowed) {
610 		cgi_allowed = 0;
611 		if (full_path_len >= allowed->len) {
612 			if (!uwsgi_strncmp(full_path+(full_path_len-allowed->len), allowed->len, allowed->value, allowed->len)) {
613 				cgi_allowed = 1;
614 				break;
615 			}
616 		}
617 		allowed = allowed->next;
618 	}
619 
620 	if (!cgi_allowed) {
621 		uwsgi_403(wsgi_req);
622 		if (need_free)
623 			free(docroot);
624 		return UWSGI_OK;
625 	}
626 
627 	// get the helper
628 	if (!is_a_file) {
629 		helper = uwsgi_cgi_get_helper(full_path);
630 
631 		if (helper == NULL) {
632 			if (access(full_path, X_OK)) {
633 				uwsgi_error("access()");
634 				uwsgi_403(wsgi_req);
635                 		if (need_free)
636                         		free(docroot);
637 				return UWSGI_OK;
638 			}
639 		}
640 	}
641 
642 	int ret = uwsgi_cgi_run(wsgi_req, docroot, docroot_len, full_path, helper, path_info, script_name, is_a_file, discard_base);
643 	if (need_free) free(docroot);
644 	return ret;
645 }
646 
uwsgi_cgi_run(struct wsgi_request * wsgi_req,char * docroot,size_t docroot_len,char * full_path,char * helper,char * path_info,char * script_name,int is_a_file,int discard_base)647 static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t docroot_len, char *full_path, char *helper, char *path_info, char *script_name, int is_a_file, int discard_base) {
648 
649 	int cgi_pipe[2];
650 	int post_pipe[2];
651 	int nargs = 0;
652 	int waitpid_status;
653 	int i;
654 	char **argv;
655 
656 	char *command = full_path;
657 	int stdin_closed = 0;
658 
659 	if (is_a_file) {
660                 command = docroot;
661         }
662 
663 	if (pipe(cgi_pipe)) {
664 		uwsgi_error("uwsgi_cgi_run()/pipe()");
665 		return UWSGI_OK;
666 	}
667 
668 	if (pipe(post_pipe)) {
669 		close(cgi_pipe[0]);
670 		close(cgi_pipe[1]);
671 		uwsgi_error("uwsgi_cgi_run()/pipe()");
672 		return UWSGI_OK;
673 	}
674 
675 	pid_t cgi_pid = fork();
676 
677 	if (cgi_pid < 0) {
678 		uwsgi_error("uwsgi_cgi_run()/fork()");
679 		close(cgi_pipe[0]);
680 		close(cgi_pipe[1]);
681 		close(post_pipe[0]);
682 		close(post_pipe[1]);
683 		return UWSGI_OK;
684 	}
685 
686 	if (cgi_pid > 0) {
687 
688 		close(cgi_pipe[1]);
689 		close(post_pipe[0]);
690 
691 		uwsgi_socket_nb(cgi_pipe[0]);
692 		uwsgi_socket_nb(post_pipe[1]);
693 
694 		// ok start sending post data...
695 		size_t remains = wsgi_req->post_cl;
696 		while(remains > 0) {
697                 	ssize_t rlen = 0;
698                 	char *buf = uwsgi_request_body_read(wsgi_req, 8192, &rlen);
699                 	if (!buf) {
700 				goto clear2;
701                 	}
702                 	if (buf == uwsgi.empty) break;
703                 	// write data to the node
704                 	if (uwsgi_write_true_nb(post_pipe[1], buf, rlen, uc.timeout)) {
705 				goto clear2;
706                 	}
707                 	remains -= rlen;
708         	}
709 
710 		if (uc.close_stdin_on_eof) {
711 			close(post_pipe[1]);
712 			stdin_closed = 1;
713 		}
714 
715 		// wait for data
716 		char *buf = uwsgi_malloc(uc.buffer_size);
717 
718 		int completed = uwsgi_cgi_parse(wsgi_req, cgi_pipe[0], buf, uc.buffer_size);
719 		if (completed < 0) {
720 			uwsgi_log("invalid CGI response !!!\n");
721 			kill_on_error
722 			goto clear;
723 		}
724 
725 		while (!completed) {
726 			ssize_t rlen = uwsgi_read_true_nb(cgi_pipe[0], buf, uc.buffer_size, uc.timeout);
727 			if (rlen > 0) {
728 				if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) {
729 					kill_on_error
730 					goto clear;
731 				}
732 			}
733 			else if (rlen == 0) {
734 				uwsgi_log("CGI timeout !!!\n");
735 				kill_on_error
736 				goto clear;
737 			}
738 			else {
739 				if (errno) {
740 					uwsgi_req_error("error reading CGI response\n");
741 					kill_on_error
742 				}
743 				goto clear;
744 			}
745 		}
746 
747 clear:
748 		free(buf);
749 clear2:
750 		close(cgi_pipe[0]);
751 		if (!stdin_closed)
752 			close(post_pipe[1]);
753 
754 		// now wait for process exit/death
755 		// in async mode we need a trick...
756 		if (uwsgi.async > 1) {
757 			pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG);
758 			if (diedpid < 0) {
759                                	uwsgi_error("waitpid()");
760                         }
761 			else if (diedpid == 0) {
762 				// pass the pid of the cgi to async_plagued (the after request hook will clear the process)
763 				wsgi_req->async_plagued = (int) cgi_pid;
764 			}
765 		}
766 		else {
767 			if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
768 				uwsgi_error("waitpid()");
769 			}
770 		}
771 
772 		return UWSGI_OK;
773 	}
774 
775 	// now map wsgi_req->poll.fd (or async_post) to 0 & cgi_pipe[1] to 1
776 	dup2(post_pipe[0], 0);
777 	close(post_pipe[0]);
778 
779 	dup2(cgi_pipe[1],1);
780 	close(cgi_pipe[1]);
781 
782 	// close all the fd > 2
783 	DIR *dirp = opendir("/proc/self/fd");
784 	if (dirp == NULL)
785 		dirp = opendir("/dev/fd");
786 	if (dirp != NULL) {
787 		struct dirent *dent;
788 		while ((dent = readdir(dirp)) != NULL) {
789 			int fd = atoi(dent->d_name);
790 			if ((fd > 2) && fd != dirfd(dirp))
791 				close(fd);
792 		}
793 		closedir(dirp);
794 	} else {
795 		for(i=3;i<(int)uwsgi.max_fd;i++) {
796 			close(i);
797 		}
798 	}
799 
800 	// fill cgi env
801 	for(i=0;i<wsgi_req->var_cnt;i+=2) {
802 		// no need to free the putenv() memory
803 		if (putenv(uwsgi_concat3n(wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, "=", 1, wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len))) {
804 			uwsgi_error("putenv()");
805 		}
806 	}
807 
808 
809 	if (setenv("GATEWAY_INTERFACE", "CGI/1.1", 0)) {
810 		uwsgi_error("setenv()");
811 	}
812 
813 	if (setenv("SERVER_SOFTWARE", uwsgi_concat2("uWSGI/", UWSGI_VERSION), 0)) {
814 		uwsgi_error("setenv()");
815 	}
816 
817 	// for newer php
818 	if (setenv("REDIRECT_STATUS", "200", 0)) {
819 		uwsgi_error("setenv()");
820 	}
821 
822 
823 
824 	if (path_info) {
825 
826 		size_t pi_len = wsgi_req->path_info_len - (path_info - wsgi_req->path_info);
827 
828 		if (setenv("PATH_INFO", uwsgi_concat2n(path_info, pi_len, "", 0), 1)) {
829 			uwsgi_error("setenv()");
830 		}
831 
832 		if (wsgi_req->document_root_len > 0) {
833 			if (setenv("PATH_TRANSLATED", uwsgi_concat3n(wsgi_req->document_root, wsgi_req->document_root_len, path_info, pi_len, "", 0) , 1)) {
834 				uwsgi_error("setenv()");
835 			}
836 		}
837 		else {
838 			if (setenv("PATH_TRANSLATED", uwsgi_concat3n(docroot, docroot_len, path_info, pi_len, "", 0) , 1)) {
839 				uwsgi_error("setenv()");
840 			}
841 		}
842 
843 	}
844 	else {
845 		unsetenv("PATH_INFO");
846 		unsetenv("PATH_TRANSLATED");
847 	}
848 
849 	if (is_a_file) {
850 		if (setenv("DOCUMENT_ROOT", uwsgi.cwd, 0)) {
851 			uwsgi_error("setenv()");
852 		}
853 
854 		if (setenv("SCRIPT_FILENAME", docroot, 0)) {
855 			uwsgi_error("setenv()");
856 		}
857 
858 		if (script_name && discard_base > 1) {
859 			if (setenv("SCRIPT_NAME", uwsgi_concat2n(script_name, discard_base, "", 0), 1)) {
860 				uwsgi_error("setenv()");
861 			}
862 		}
863 	}
864 	else {
865 		if (setenv("DOCUMENT_ROOT", docroot, 0)) {
866 			uwsgi_error("setenv()");
867 		}
868 
869 		if (setenv("SCRIPT_FILENAME", full_path, 0)) {
870 			uwsgi_error("setenv()");
871 		}
872 
873 		if (setenv("SCRIPT_NAME", uwsgi_concat2n(wsgi_req->path_info, discard_base, full_path+docroot_len, strlen(full_path+docroot_len)), 1)) {
874 			uwsgi_error("setenv()");
875 		}
876 
877 		char *base = uwsgi_get_last_char(full_path, '/');
878 		if (base) {
879 			// a little trick :P
880 			*base = 0;
881 			if (chdir(full_path)) {
882                                 uwsgi_error("chdir()");
883                         }
884 			*base = '/';
885 		}
886 		else if (docroot_len > 0) {
887 			if (chdir(docroot)) {
888 				uwsgi_error("chdir()");
889 			}
890 		}
891 	}
892 
893 	struct uwsgi_string_list *drop_env = uc.unset;
894 	while(drop_env) {
895 		unsetenv(drop_env->value);
896 		drop_env = drop_env->next;
897 	}
898 
899 	argv = uwsgi_malloc(sizeof(char *) * 3);
900 
901 	// check if we need to parse indexed QUERY_STRING
902 	if (wsgi_req->query_string_len > 0) {
903 		if (!memchr(wsgi_req->query_string, '=', wsgi_req->query_string_len)) {
904 			nargs = 1;
905 			for(i=0;i<wsgi_req->query_string_len;i++) {
906 				if (wsgi_req->query_string[i] == '+')
907 					nargs++;
908 			}
909 
910 
911 			// reallocate argv and qs
912 			argv = uwsgi_malloc(sizeof(char *) * (3+nargs));
913 			char *qs = uwsgi_concat2n(wsgi_req->query_string, wsgi_req->query_string_len, "", 0);
914 			// set the start position of args in argv
915 			i = 1;
916 			if (helper) i = 2;
917 			char *p, *ctx = NULL;
918 			uwsgi_foreach_token(qs, "+", p, ctx) {
919 				// create a copy for the url_decoded string
920 				char *arg_copy = uwsgi_str(p);
921 				uint16_t arg_copy_len = strlen(p);
922 				http_url_decode(p, &arg_copy_len, arg_copy);
923 				// and a final copy for shell escaped arg
924 				argv[i] = uwsgi_malloc( (arg_copy_len * 2) +1);
925 				escape_shell_arg(arg_copy, arg_copy_len, argv[i]);
926 				i++;
927 			}
928 			free(qs);
929 		}
930 	}
931 
932 	if (helper) {
933 		if (!uwsgi_starts_with(helper, strlen(helper), "sym://", 6)) {
934 			void (*cgi_func)(char *) = dlsym(RTLD_DEFAULT, helper+6);
935 			if (cgi_func) {
936 				cgi_func(command);
937 			}
938 			else {
939 				uwsgi_log("unable to find symbol %s\n", helper+6);
940 			}
941 			exit(0);
942 		}
943 		argv[0] = helper;
944 		argv[1] = command;
945 		argv[nargs+2] = NULL;
946 	}
947 	else {
948 		argv[0] = command;
949 		argv[nargs+1] = NULL;
950 	}
951 
952 	if (execvp(argv[0], argv)) {
953 		uwsgi_error("uwsgi_cgi_run()/execvp()");
954 	}
955 
956 	// never here
957 	exit(1);
958 }
959 
960 
uwsgi_cgi_after_request(struct wsgi_request * wsgi_req)961 static void uwsgi_cgi_after_request(struct wsgi_request *wsgi_req) {
962 	if (wsgi_req->async_plagued > 0) {
963 		int waitpid_status;
964 		pid_t cgi_pid = (pid_t) wsgi_req->async_plagued;
965 		int max_attempts = uc.async_max_attempts;
966                         if (!max_attempts) max_attempts = 10;
967                         while(max_attempts) {
968                                 pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG);
969                                 if (diedpid < 0) {
970                                         uwsgi_error("waitpid()");
971                                         break;
972                                 }
973                                 else if (diedpid == 0) {
974                                         int ret = uwsgi.wait_milliseconds_hook(1000);
975                                         if (ret < 0) {
976                                                 kill_on_error
977 						if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
978                                 			uwsgi_error("waitpid()");
979                         			}
980                                         }
981                                 }
982                                 else {
983                                         break;
984                                 }
985                                 max_attempts--;
986                         }
987                         if (max_attempts == 0) {
988                                 kill_on_error
989 				if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
990                                 	uwsgi_error("waitpid()");
991                         	}
992                         }
993 	}
994 
995 	log_request(wsgi_req);
996 }
997 
998 #ifdef UWSGI_ROUTING
uwsgi_routing_func_cgi(struct wsgi_request * wsgi_req,struct uwsgi_route * ur)999 static int uwsgi_routing_func_cgi(struct wsgi_request *wsgi_req, struct uwsgi_route *ur){
1000 
1001         char **subject = (char **) (((char *)(wsgi_req))+ur->subject);
1002         uint16_t *subject_len = (uint16_t *) (((char *)(wsgi_req))+ur->subject_len);
1003 
1004         struct uwsgi_buffer *ub_command = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, ur->data, ur->data_len);
1005         if (!ub_command) return UWSGI_ROUTE_BREAK;
1006 	struct uwsgi_buffer *ub_helper = NULL;
1007 	if (ur->data2_len) {
1008 		ub_helper = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, ur->data2, ur->data2_len);
1009 		if (!ub_helper) {
1010 			uwsgi_buffer_destroy(ub_command);
1011         		return UWSGI_ROUTE_BREAK;
1012 		}
1013 	}
1014 	else {
1015 		if (!uwsgi_is_file(ub_command->buf)) {
1016 			uwsgi_404(wsgi_req);
1017 			uwsgi_buffer_destroy(ub_command);
1018 			return UWSGI_ROUTE_BREAK;
1019 		}
1020 
1021 		if (access(ub_command->buf, X_OK)) {
1022 			uwsgi_403(wsgi_req);
1023 			uwsgi_buffer_destroy(ub_command);
1024 			return UWSGI_ROUTE_BREAK;
1025 		}
1026 	}
1027 	// we need a NULL suffix-ed copy of the docroot
1028 	char *docroot = uwsgi_concat2n(wsgi_req->document_root, wsgi_req->document_root_len, "", 0);
1029         uwsgi_cgi_run(wsgi_req, wsgi_req->document_root, wsgi_req->document_root_len, ub_command->buf, ub_helper ? ub_helper->buf : NULL, NULL, NULL, 0, 0 );
1030 	free(docroot);
1031         uwsgi_buffer_destroy(ub_command);
1032 	if (ub_helper) uwsgi_buffer_destroy(ub_helper);
1033         return UWSGI_ROUTE_BREAK;
1034 }
1035 
uwsgi_router_cgi_helper(struct uwsgi_route * ur,char * args)1036 static int uwsgi_router_cgi_helper(struct uwsgi_route *ur, char *args) {
1037         ur->func = uwsgi_routing_func_cgi;
1038 	char *space = strchr(args, ' ');
1039 	if (!space) {
1040 		uwsgi_log("invalid cgihelper syntax, must be \"cgihelper:helper command\"\n");
1041 		return -1;
1042 	}
1043 	*space = 0;
1044         ur->data = space+1;
1045         ur->data_len = strlen(space+1);
1046 	ur->data2 = args;
1047 	ur->data2_len = strlen(args);
1048         return 0;
1049 }
uwsgi_router_cgi(struct uwsgi_route * ur,char * args)1050 static int uwsgi_router_cgi(struct uwsgi_route *ur, char *args) {
1051         ur->func = uwsgi_routing_func_cgi;
1052 	ur->data = args;
1053 	ur->data_len = strlen(args);
1054         return 0;
1055 }
uwsgi_cgi_register_router()1056 static void uwsgi_cgi_register_router() {
1057         uwsgi_register_router("cgi", uwsgi_router_cgi);
1058         uwsgi_register_router("cgihelper", uwsgi_router_cgi_helper);
1059 }
1060 #endif
1061 
1062 struct uwsgi_plugin cgi_plugin = {
1063 
1064 	.name = "cgi",
1065 	.modifier1 = 9,
1066 	.init = uwsgi_cgi_init,
1067 	.init_apps = uwsgi_cgi_apps,
1068 	.options = uwsgi_cgi_options,
1069 	.request = uwsgi_cgi_request,
1070 	.after_request = uwsgi_cgi_after_request,
1071 #ifdef UWSGI_ROUTING
1072         .on_load = uwsgi_cgi_register_router,
1073 #endif
1074 
1075 };
1076