1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Sascha Schumann <sascha@schumann.cx>                         |
14    +----------------------------------------------------------------------+
15  */
16 
17 #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
18 
19 #include "php.h"
20 #ifdef strcasecmp
21 # undef strcasecmp
22 #endif
23 #ifdef strncasecmp
24 # undef strncasecmp
25 #endif
26 #include "zend_smart_str.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/head.h"
29 #include "php_ini.h"
30 #include "SAPI.h"
31 
32 #include "apr_strings.h"
33 #include "apr_time.h"
34 #include "ap_config.h"
35 #include "util_filter.h"
36 #include "httpd.h"
37 #include "http_config.h"
38 #include "http_request.h"
39 #include "http_core.h"
40 #include "http_protocol.h"
41 #include "http_log.h"
42 #include "http_main.h"
43 #include "util_script.h"
44 #include "http_core.h"
45 #include "ap_mpm.h"
46 #ifndef PHP_WIN32
47 #include "unixd.h"
48 #endif
49 
50 #include "php_apache.h"
51 #include "php_functions_arginfo.h"
52 
53 #ifdef ZTS
54 int php_apache2_info_id;
55 #else
56 php_apache2_info_struct php_apache2_info;
57 #endif
58 
59 #define SECTION(name)  PUTS("<h2>" name "</h2>\n")
60 
php_apache_lookup_uri(char * filename)61 static request_rec *php_apache_lookup_uri(char *filename)
62 {
63 	php_struct *ctx = SG(server_context);
64 
65 	if (!filename || !ctx || !ctx->r) {
66 		return NULL;
67 	}
68 
69 	return ap_sub_req_lookup_uri(filename, ctx->r, ctx->r->output_filters);
70 }
71 
72 /* {{{ Perform an apache sub-request */
PHP_FUNCTION(virtual)73 PHP_FUNCTION(virtual)
74 {
75 	char *filename;
76 	size_t filename_len;
77 	request_rec *rr;
78 
79 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
80 		RETURN_THROWS();
81 	}
82 
83 	if (!(rr = php_apache_lookup_uri(filename))) {
84 		php_error_docref(NULL, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
85 		RETURN_FALSE;
86 	}
87 
88 	if (rr->status != HTTP_OK) {
89 		php_error_docref(NULL, E_WARNING, "Unable to include '%s' - error finding URI", filename);
90 		ap_destroy_sub_req(rr);
91 		RETURN_FALSE;
92 	}
93 
94 	/* Flush everything. */
95 	php_output_end_all();
96 	php_header();
97 
98 	/* Ensure that the ap_r* layer for the main request is flushed, to
99 	 * work around http://issues.apache.org/bugzilla/show_bug.cgi?id=17629 */
100 	ap_rflush(rr->main);
101 
102 	if (ap_run_sub_req(rr)) {
103 		php_error_docref(NULL, E_WARNING, "Unable to include '%s' - request execution failed", filename);
104 		ap_destroy_sub_req(rr);
105 		RETURN_FALSE;
106 	}
107 	ap_destroy_sub_req(rr);
108 	RETURN_TRUE;
109 }
110 /* }}} */
111 
112 #define ADD_LONG(name) \
113 		add_property_long(return_value, #name, rr->name)
114 #define ADD_TIME(name) \
115 		add_property_long(return_value, #name, apr_time_sec(rr->name));
116 #define ADD_STRING(name) \
117 		if (rr->name) add_property_string(return_value, #name, (char *) rr->name)
118 
PHP_FUNCTION(apache_lookup_uri)119 PHP_FUNCTION(apache_lookup_uri)
120 {
121 	request_rec *rr;
122 	char *filename;
123 	size_t filename_len;
124 
125 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
126 		RETURN_THROWS();
127 	}
128 
129 	if (!(rr = php_apache_lookup_uri(filename))) {
130 		php_error_docref(NULL, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
131 		RETURN_FALSE;
132 	}
133 
134 	if (rr->status == HTTP_OK) {
135 		object_init(return_value);
136 
137 		ADD_LONG(status);
138 		ADD_STRING(the_request);
139 		ADD_STRING(status_line);
140 		ADD_STRING(method);
141 		ADD_TIME(mtime);
142 		ADD_LONG(clength);
143 		ADD_STRING(range);
144 		ADD_LONG(chunked);
145 		ADD_STRING(content_type);
146 		ADD_STRING(handler);
147 		ADD_LONG(no_cache);
148 		ADD_LONG(no_local_copy);
149 		ADD_STRING(unparsed_uri);
150 		ADD_STRING(uri);
151 		ADD_STRING(filename);
152 		ADD_STRING(path_info);
153 		ADD_STRING(args);
154 		ADD_LONG(allowed);
155 		ADD_LONG(sent_bodyct);
156 		ADD_LONG(bytes_sent);
157 		ADD_LONG(mtime);
158 		ADD_TIME(request_time);
159 
160 		ap_destroy_sub_req(rr);
161 		return;
162 	}
163 
164 	php_error_docref(NULL, E_WARNING, "Unable to include '%s' - error finding URI", filename);
165 	ap_destroy_sub_req(rr);
166 	RETURN_FALSE;
167 }
168 
169 /* {{{ Fetch all HTTP request headers */
PHP_FUNCTION(apache_request_headers)170 PHP_FUNCTION(apache_request_headers)
171 {
172 	php_struct *ctx;
173 	const apr_array_header_t *arr;
174 	char *key, *val;
175 
176 	if (zend_parse_parameters_none() == FAILURE) {
177 		RETURN_THROWS();
178 	}
179 
180 	array_init(return_value);
181 
182 	ctx = SG(server_context);
183 	arr = apr_table_elts(ctx->r->headers_in);
184 
185 	APR_ARRAY_FOREACH_OPEN(arr, key, val)
186 		if (!val) val = "";
187 		add_assoc_string(return_value, key, val);
188 	APR_ARRAY_FOREACH_CLOSE()
189 }
190 /* }}} */
191 
192 /* {{{ Fetch all HTTP response headers */
PHP_FUNCTION(apache_response_headers)193 PHP_FUNCTION(apache_response_headers)
194 {
195 	php_struct *ctx;
196 	const apr_array_header_t *arr;
197 	char *key, *val;
198 
199 	if (zend_parse_parameters_none() == FAILURE) {
200 		RETURN_THROWS();
201 	}
202 
203 	array_init(return_value);
204 
205 	ctx = SG(server_context);
206 	arr = apr_table_elts(ctx->r->headers_out);
207 
208 	APR_ARRAY_FOREACH_OPEN(arr, key, val)
209 		if (!val) val = "";
210 		add_assoc_string(return_value, key, val);
211 	APR_ARRAY_FOREACH_CLOSE()
212 }
213 /* }}} */
214 
215 /* {{{ Get and set Apache request notes */
PHP_FUNCTION(apache_note)216 PHP_FUNCTION(apache_note)
217 {
218 	php_struct *ctx;
219 	char *note_name, *note_val = NULL;
220 	size_t note_name_len, note_val_len;
221 	char *old_note_val=NULL;
222 
223 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!", &note_name, &note_name_len, &note_val, &note_val_len) == FAILURE) {
224 		RETURN_THROWS();
225 	}
226 
227 	ctx = SG(server_context);
228 
229 	old_note_val = (char *) apr_table_get(ctx->r->notes, note_name);
230 
231 	if (note_val) {
232 		apr_table_set(ctx->r->notes, note_name, note_val);
233 	}
234 
235 	if (old_note_val) {
236 		RETURN_STRING(old_note_val);
237 	}
238 
239 	RETURN_FALSE;
240 }
241 /* }}} */
242 
243 
244 /* {{{ Set an Apache subprocess_env variable */
245 /*
246  * XXX this doesn't look right. shouldn't it be the parent ?*/
PHP_FUNCTION(apache_setenv)247 PHP_FUNCTION(apache_setenv)
248 {
249 	php_struct *ctx;
250 	char *variable=NULL, *string_val=NULL;
251 	size_t variable_len, string_val_len;
252 	bool walk_to_top = false;
253 	request_rec *r;
254 
255 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &variable, &variable_len, &string_val, &string_val_len, &walk_to_top) == FAILURE) {
256 		RETURN_THROWS();
257 	}
258 
259 	ctx = SG(server_context);
260 
261 	r = ctx->r;
262 	if (walk_to_top) {
263 		while(r->prev) {
264 			r = r->prev;
265 		}
266 	}
267 
268 	apr_table_set(r->subprocess_env, variable, string_val);
269 
270 	RETURN_TRUE;
271 }
272 /* }}} */
273 
274 /* {{{ Get an Apache subprocess_env variable */
275 /*
276  * XXX: shouldn't this be the parent not the 'prev'
277  */
PHP_FUNCTION(apache_getenv)278 PHP_FUNCTION(apache_getenv)
279 {
280 	php_struct *ctx;
281 	char *variable;
282 	size_t variable_len;
283 	bool walk_to_top = 0;
284 	char *env_val=NULL;
285 	request_rec *r;
286 
287 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &variable, &variable_len, &walk_to_top) == FAILURE) {
288 		RETURN_THROWS();
289 	}
290 
291 	ctx = SG(server_context);
292 
293 	r = ctx->r;
294 	if (walk_to_top) {
295 		while(r->prev) {
296 			r = r->prev;
297 		}
298 	}
299 
300 	env_val = (char*) apr_table_get(r->subprocess_env, variable);
301 
302 	if (env_val != NULL) {
303 		RETURN_STRING(env_val);
304 	}
305 
306 	RETURN_FALSE;
307 }
308 /* }}} */
309 
php_apache_get_version(void)310 static const char *php_apache_get_version(void)
311 {
312 	return ap_get_server_banner();
313 }
314 
315 /* {{{ Fetch Apache version */
PHP_FUNCTION(apache_get_version)316 PHP_FUNCTION(apache_get_version)
317 {
318 	const char *apv = php_apache_get_version();
319 
320 	if (apv && *apv) {
321 		RETURN_STRING(apv);
322 	} else {
323 		RETURN_FALSE;
324 	}
325 }
326 /* }}} */
327 
328 /* {{{ Get a list of loaded Apache modules */
PHP_FUNCTION(apache_get_modules)329 PHP_FUNCTION(apache_get_modules)
330 {
331 	int n;
332 	char *p;
333 
334 	array_init(return_value);
335 
336 	for (n = 0; ap_loaded_modules[n]; ++n) {
337 		const char *s = ap_loaded_modules[n]->name;
338 		if ((p = strchr(s, '.'))) {
339 			add_next_index_stringl(return_value, s, (p - s));
340 		} else {
341 			add_next_index_string(return_value, s);
342 		}
343 	}
344 }
345 /* }}} */
346 
PHP_MINFO_FUNCTION(apache)347 PHP_MINFO_FUNCTION(apache)
348 {
349 	const char *apv = php_apache_get_version();
350 	smart_str tmp1 = {0};
351 	char tmp[1024];
352 	int n, max_requests;
353 	char *p;
354 	server_rec *serv = ((php_struct *) SG(server_context))->r->server;
355 #ifndef PHP_WIN32
356 	AP_DECLARE_DATA extern unixd_config_rec ap_unixd_config;
357 #endif
358 
359 	for (n = 0; ap_loaded_modules[n]; ++n) {
360 		const char *s = ap_loaded_modules[n]->name;
361 		if (n > 0) {
362 			smart_str_appendc(&tmp1, ' ');
363 		}
364 		if ((p = strchr(s, '.'))) {
365 			smart_str_appendl(&tmp1, s, (p - s));
366 		} else {
367 			smart_str_appends(&tmp1, s);
368 		}
369 	}
370 	if (!tmp1.s) {
371 		smart_str_appendc(&tmp1, '/');
372 	}
373 	smart_str_0(&tmp1);
374 
375 	php_info_print_table_start();
376 	if (apv && *apv) {
377 		php_info_print_table_row(2, "Apache Version", apv);
378 	}
379 	snprintf(tmp, sizeof(tmp), "%d", MODULE_MAGIC_NUMBER_MAJOR);
380 	php_info_print_table_row(2, "Apache API Version", tmp);
381 
382 	if (serv->server_admin && *(serv->server_admin)) {
383 		php_info_print_table_row(2, "Server Administrator", serv->server_admin);
384 	}
385 
386 	snprintf(tmp, sizeof(tmp), "%s:%u", serv->server_hostname, serv->port);
387 	php_info_print_table_row(2, "Hostname:Port", tmp);
388 
389 #ifndef PHP_WIN32
390 	snprintf(tmp, sizeof(tmp), "%s(%d)/%d", ap_unixd_config.user_name, ap_unixd_config.user_id, ap_unixd_config.group_id);
391 	php_info_print_table_row(2, "User/Group", tmp);
392 #endif
393 
394 	ap_mpm_query(AP_MPMQ_MAX_REQUESTS_DAEMON, &max_requests);
395 	snprintf(tmp, sizeof(tmp), "Per Child: %d - Keep Alive: %s - Max Per Connection: %d", max_requests, (serv->keep_alive ? "on":"off"), serv->keep_alive_max);
396 	php_info_print_table_row(2, "Max Requests", tmp);
397 
398 	apr_snprintf(tmp, sizeof tmp,
399 				 "Connection: %" APR_TIME_T_FMT " - Keep-Alive: %" APR_TIME_T_FMT,
400 				 apr_time_sec(serv->timeout), apr_time_sec(serv->keep_alive_timeout));
401 	php_info_print_table_row(2, "Timeouts", tmp);
402 
403 	php_info_print_table_row(2, "Virtual Server", (serv->is_virtual ? "Yes" : "No"));
404 	php_info_print_table_row(2, "Server Root", ap_server_root);
405 	php_info_print_table_row(2, "Loaded Modules", ZSTR_VAL(tmp1.s));
406 
407 	smart_str_free(&tmp1);
408 	php_info_print_table_end();
409 
410 	DISPLAY_INI_ENTRIES();
411 
412 	{
413 		const apr_array_header_t *arr = apr_table_elts(((php_struct *) SG(server_context))->r->subprocess_env);
414 		char *key, *val;
415 
416 		SECTION("Apache Environment");
417 		php_info_print_table_start();
418 		php_info_print_table_header(2, "Variable", "Value");
419 		APR_ARRAY_FOREACH_OPEN(arr, key, val)
420 			if (!val) {
421 				val = "";
422 			}
423 			php_info_print_table_row(2, key, val);
424 		APR_ARRAY_FOREACH_CLOSE()
425 
426 		php_info_print_table_end();
427 
428 		SECTION("HTTP Headers Information");
429 		php_info_print_table_start();
430 		php_info_print_table_colspan_header(2, "HTTP Request Headers");
431 		php_info_print_table_row(2, "HTTP Request", ((php_struct *) SG(server_context))->r->the_request);
432 
433 		arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_in);
434 		APR_ARRAY_FOREACH_OPEN(arr, key, val)
435 			if (!val) {
436 				val = "";
437 			}
438 		        php_info_print_table_row(2, key, val);
439 		APR_ARRAY_FOREACH_CLOSE()
440 
441 		php_info_print_table_colspan_header(2, "HTTP Response Headers");
442 		arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_out);
443 		APR_ARRAY_FOREACH_OPEN(arr, key, val)
444 			if (!val) {
445 				val = "";
446 			}
447 		        php_info_print_table_row(2, key, val);
448 		APR_ARRAY_FOREACH_CLOSE()
449 
450 		php_info_print_table_end();
451 	}
452 }
453 
454 PHP_INI_BEGIN()
455 	STD_PHP_INI_BOOLEAN("xbithack",		"0",	PHP_INI_ALL,	OnUpdateBool,	xbithack,	php_apache2_info_struct, php_apache2_info)
456 	STD_PHP_INI_BOOLEAN("engine",		"1",	PHP_INI_ALL,	OnUpdateBool,	engine, 	php_apache2_info_struct, php_apache2_info)
457 	STD_PHP_INI_BOOLEAN("last_modified",	"0",	PHP_INI_ALL,	OnUpdateBool,	last_modified,	php_apache2_info_struct, php_apache2_info)
PHP_INI_END()458 PHP_INI_END()
459 
460 static PHP_MINIT_FUNCTION(apache)
461 {
462 #ifdef ZTS
463 	ts_allocate_id(&php_apache2_info_id, sizeof(php_apache2_info_struct), (ts_allocate_ctor) NULL, NULL);
464 #endif
465 	REGISTER_INI_ENTRIES();
466 	return SUCCESS;
467 }
468 
PHP_MSHUTDOWN_FUNCTION(apache)469 static PHP_MSHUTDOWN_FUNCTION(apache)
470 {
471 	UNREGISTER_INI_ENTRIES();
472 	return SUCCESS;
473 }
474 
475 zend_module_entry php_apache_module = {
476 	STANDARD_MODULE_HEADER,
477 	"apache2handler",
478 	ext_functions,
479 	PHP_MINIT(apache),
480 	PHP_MSHUTDOWN(apache),
481 	NULL,
482 	NULL,
483 	PHP_MINFO(apache),
484 	PHP_VERSION,
485 	STANDARD_MODULE_PROPERTIES
486 };
487