1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2015 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@zend.com>                                |
16    |          Zeev Suraski <zeev@zend.com>                                |
17    |          Stanislav Malyshev <stas@zend.com>                          |
18    |          Dmitry Stogov <dmitry@zend.com>                             |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "main/php.h"
23 #include "main/fopen_wrappers.h"
24 #include "ZendAccelerator.h"
25 #include "zend_accelerator_blacklist.h"
26 
27 #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
28 # include "ext/ereg/php_regex.h"
29 #else
30 # include "main/php_regex.h"
31 #endif
32 
33 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
34 # include "ext/standard/php_string.h"
35 #endif
36 
37 #ifdef ZEND_WIN32
38 # define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE)
39 #else
40 # define REGEX_MODE (REG_EXTENDED|REG_NOSUB)
41 #endif
42 
43 #ifdef HAVE_GLOB
44 #ifdef PHP_WIN32
45 #include "win32/glob.h"
46 #else
47 #include <glob.h>
48 #endif
49 #endif
50 
51 #define ZEND_BLACKLIST_BLOCK_SIZE	32
52 
53 struct _zend_regexp_list {
54 	regex_t           comp_regex;
55 	zend_regexp_list *next;
56 };
57 
58 zend_blacklist accel_blacklist;
59 
zend_accel_blacklist_init(zend_blacklist * blacklist)60 void zend_accel_blacklist_init(zend_blacklist *blacklist)
61 {
62 	blacklist->pos = 0;
63 	blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE;
64 
65 	if (blacklist->entries != NULL) {
66 		zend_accel_blacklist_shutdown(blacklist);
67 	}
68 
69 	blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size);
70 	if (!blacklist->entries) {
71 		zend_accel_error(ACCEL_LOG_FATAL, "Blacklist initialization: no memory\n");
72 		return;
73 	}
74 	blacklist->regexp_list = NULL;
75 }
76 
blacklist_report_regexp_error(regex_t * comp_regex,int reg_err)77 static void blacklist_report_regexp_error(regex_t *comp_regex, int reg_err)
78 {
79 	char *errbuf;
80 	int errsize = regerror(reg_err, comp_regex, NULL, 0);
81 	errbuf = malloc(errsize);
82 	if (!errbuf) {
83 		zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation: no memory\n");
84 		return;
85 	}
86 	regerror(reg_err, comp_regex, errbuf, errsize);
87 	zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation: %s\n", errbuf);
88 	free(errbuf);
89 }
90 
zend_accel_blacklist_update_regexp(zend_blacklist * blacklist)91 static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
92 {
93 	int i, reg_err;
94 	zend_regexp_list **regexp_list_it, *it;
95 	char regexp[12*1024], *p, *end, *c, *backtrack = NULL;
96 
97 	if (blacklist->pos == 0) {
98 		/* we have no blacklist to talk about */
99 		return;
100 	}
101 
102 	regexp_list_it = &(blacklist->regexp_list);
103 
104 	regexp[0] = '^';
105 	regexp[1] = '(';
106 	p = regexp + 2;
107 	end = regexp + sizeof(regexp) - sizeof("[^\\\\]*)\0");
108 
109 	for (i = 0; i < blacklist->pos; ) {
110 		c = blacklist->entries[i].path;
111 		if (p + blacklist->entries[i].path_length < end) {
112 			while (*c && p < end) {
113 				switch (*c) {
114 					case '?':
115 						c++;
116 #ifdef ZEND_WIN32
117 				 		p[0] = '[';			/* * => [^\\] on Win32 */
118 					 	p[1] = '^';
119 					 	p[2] = '\\';
120 					 	p[3] = '\\';
121 					 	p[4] = ']';
122 						p += 5;
123 #else
124 					 	p[0] = '[';			/* * => [^/] on *nix */
125 					 	p[1] = '^';
126 					 	p[2] = '/';
127 					 	p[3] = ']';
128 						p += 4;
129 #endif
130 						break;
131 					case '*':
132 						c++;
133 						if (*c == '*') {
134 							c++;
135 						 	p[0] = '.';			/* ** => .* */
136 							p[1] = '*';
137 							p += 2;
138 						} else {
139 #ifdef ZEND_WIN32
140 						 	p[0] = '[';			/* * => [^\\]* on Win32 */
141 						 	p[1] = '^';
142 						 	p[2] = '\\';
143 						 	p[3] = '\\';
144 						 	p[4] = ']';
145 						 	p[5] = '*';
146 							p += 6;
147 #else
148 						 	p[0] = '[';			/* * => [^/]* on *nix */
149 						 	p[1] = '^';
150 						 	p[2] = '/';
151 						 	p[3] = ']';
152 						 	p[4] = '*';
153 							p += 5;
154 #endif
155 						}
156 						break;
157 					case '^':
158 					case '.':
159 					case '[':
160 					case ']':
161 					case '$':
162 					case '(':
163 					case ')':
164 					case '|':
165 					case '+':
166 					case '{':
167 					case '}':
168 					case '\\':
169 						*p++ = '\\';
170 						/* break missing intentionally */
171 					default:
172 						*p++ = *c++;
173 				}
174 			}
175 		}
176 
177 		if (*c || i == blacklist->pos - 1) {
178 			if (*c) {
179 				if (!backtrack) {
180 					zend_accel_error(ACCEL_LOG_ERROR, "Too long blacklist entry\n");
181 				}
182 				p = backtrack;
183 			} else {
184 				i++;
185 			}
186 			*p++ = ')';
187 			*p++ = '\0';
188 
189 			it = (zend_regexp_list*)malloc(sizeof(zend_regexp_list));
190 			if (!it) {
191 				zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
192 				return;
193 			}
194 			it->next = NULL;
195 
196 			if ((reg_err = regcomp(&it->comp_regex, regexp, REGEX_MODE)) != 0) {
197 				blacklist_report_regexp_error(&it->comp_regex, reg_err);
198 			}
199 			/* prepare for the next iteration */
200 			p = regexp + 2;
201 			*regexp_list_it = it;
202 			regexp_list_it = &it->next;
203 		} else {
204 			backtrack = p;
205 			*p++ = '|';
206 			i++;
207 		}
208 	}
209 }
210 
zend_accel_blacklist_shutdown(zend_blacklist * blacklist)211 void zend_accel_blacklist_shutdown(zend_blacklist *blacklist)
212 {
213 	zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos;
214 
215 	while (p<end) {
216 		free(p->path);
217 		p++;
218 	}
219 	free(blacklist->entries);
220 	blacklist->entries = NULL;
221 	if (blacklist->regexp_list) {
222 		zend_regexp_list *temp, *it = blacklist->regexp_list;
223 		while (it) {
224 			regfree(&it->comp_regex);
225 			temp = it;
226 			it = it->next;
227 			free(temp);
228 		}
229 	}
230 }
231 
zend_accel_blacklist_allocate(zend_blacklist * blacklist)232 static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist)
233 {
234 	if (blacklist->pos == blacklist->size) {
235 		blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE;
236 		blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size);
237 	}
238 }
239 
240 #ifdef HAVE_GLOB
zend_accel_blacklist_loadone(zend_blacklist * blacklist,char * filename)241 static void zend_accel_blacklist_loadone(zend_blacklist *blacklist, char *filename)
242 #else
243 void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
244 #endif
245 {
246 	char buf[MAXPATHLEN + 1], real_path[MAXPATHLEN + 1], *blacklist_path = NULL;
247 	FILE *fp;
248 	int path_length, blacklist_path_length;
249 	TSRMLS_FETCH();
250 
251 	if ((fp = fopen(filename, "r")) == NULL) {
252 		zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename);
253 		return;
254 	}
255 
256 	zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file:  '%s'", filename);
257 
258 	if (VCWD_REALPATH(filename, buf)) {
259 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
260 		blacklist_path_length = php_dirname(buf, strlen(buf));
261 #else
262 		blacklist_path_length = zend_dirname(buf, strlen(buf));
263 #endif
264 		blacklist_path = zend_strndup(buf, blacklist_path_length);
265 	}
266 
267 	memset(buf, 0, sizeof(buf));
268 	memset(real_path, 0, sizeof(real_path));
269 
270 	while (fgets(buf, MAXPATHLEN, fp) != NULL) {
271 		char *path_dup, *pbuf;
272 		path_length = strlen(buf);
273 		if (path_length > 0 && buf[path_length - 1] == '\n') {
274 			buf[--path_length] = 0;
275 			if (path_length > 0 && buf[path_length - 1] == '\r') {
276 				buf[--path_length] = 0;
277 			}
278 		}
279 
280 		/* Strip ctrl-m prefix */
281 		pbuf = &buf[0];
282 		while (*pbuf == '\r') {
283 			*pbuf++ = 0;
284 			path_length--;
285 		}
286 
287 		/* strip \" */
288 		if (pbuf[0] == '\"' && pbuf[path_length - 1]== '\"') {
289 			*pbuf++ = 0;
290 			path_length -= 2;
291 		}
292 
293 		if (path_length == 0) {
294 			continue;
295 		}
296 
297 		/* skip comments */
298 		if (pbuf[0]==';') {
299 			continue;
300 		}
301 
302 		path_dup = zend_strndup(pbuf, path_length);
303 		if (blacklist_path) {
304 			expand_filepath_ex(path_dup, real_path, blacklist_path, blacklist_path_length TSRMLS_CC);
305 		} else {
306 			expand_filepath(path_dup, real_path TSRMLS_CC);
307 		}
308 		path_length = strlen(real_path);
309 
310 		free(path_dup);
311 
312 		zend_accel_blacklist_allocate(blacklist);
313 		blacklist->entries[blacklist->pos].path_length = path_length;
314 		blacklist->entries[blacklist->pos].path = (char *)malloc(path_length + 1);
315 		if (!blacklist->entries[blacklist->pos].path) {
316 			zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
317 			fclose(fp);
318 			return;
319 		}
320 		blacklist->entries[blacklist->pos].id = blacklist->pos;
321 		memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length + 1);
322 		blacklist->pos++;
323 	}
324 	fclose(fp);
325 	if (blacklist_path) {
326 		free(blacklist_path);
327 	}
328 	zend_accel_blacklist_update_regexp(blacklist);
329 }
330 
331 #ifdef HAVE_GLOB
zend_accel_blacklist_load(zend_blacklist * blacklist,char * filename)332 void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
333 {
334 	glob_t globbuf;
335 	int    ret;
336 	unsigned int i;
337 
338 	memset(&globbuf, 0, sizeof(glob_t));
339 
340 	ret = glob(filename, 0, NULL, &globbuf);
341 #ifdef GLOB_NOMATCH
342 	if (ret == GLOB_NOMATCH || !globbuf.gl_pathc) {
343 #else
344 	if (!globbuf.gl_pathc) {
345 #endif
346 		zend_accel_error(ACCEL_LOG_WARNING, "No blacklist file found matching: %s\n", filename);
347 	} else {
348 		for(i=0 ; i<globbuf.gl_pathc; i++) {
349 			zend_accel_blacklist_loadone(blacklist, globbuf.gl_pathv[i]);
350 		}
351 		globfree(&globbuf);
352 	}
353 }
354 #endif
355 
356 zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path)
357 {
358 	int ret = 0;
359 	zend_regexp_list *regexp_list_it = blacklist->regexp_list;
360 
361 	if (regexp_list_it == NULL) {
362 		return 0;
363 	}
364 	while (regexp_list_it != NULL) {
365 		if (regexec(&(regexp_list_it->comp_regex), verify_path, 0, NULL, 0) == 0) {
366 			ret = 1;
367 			break;
368 		}
369 		regexp_list_it = regexp_list_it->next;
370 	}
371 	return ret;
372 }
373 
374 void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC)
375 {
376 	int i;
377 
378 	for (i = 0; i < blacklist->pos; i++) {
379 		func(&blacklist->entries[i], argument TSRMLS_CC);
380 	}
381 }
382