1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 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 #ifdef ZEND_WIN32
28 # define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE)
29 #else
30 # define REGEX_MODE (REG_EXTENDED|REG_NOSUB)
31 #endif
32 
33 #ifdef HAVE_GLOB
34 #ifdef PHP_WIN32
35 #include "win32/glob.h"
36 #else
37 #include <glob.h>
38 #endif
39 #endif
40 
41 #include "ext/pcre/php_pcre.h"
42 
43 #define ZEND_BLACKLIST_BLOCK_SIZE	32
44 
45 struct _zend_regexp_list {
46 	pcre             *re;
47 	zend_regexp_list *next;
48 };
49 
50 zend_blacklist accel_blacklist;
51 
zend_accel_blacklist_init(zend_blacklist * blacklist)52 void zend_accel_blacklist_init(zend_blacklist *blacklist)
53 {
54 	blacklist->pos = 0;
55 	blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE;
56 
57 	if (blacklist->entries != NULL) {
58 		zend_accel_blacklist_shutdown(blacklist);
59 	}
60 
61 	blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size);
62 	if (!blacklist->entries) {
63 		zend_accel_error(ACCEL_LOG_FATAL, "Blacklist initialization: no memory\n");
64 		return;
65 	}
66 	blacklist->regexp_list = NULL;
67 }
68 
blacklist_report_regexp_error(const char * pcre_error,int pcre_error_offset)69 static void blacklist_report_regexp_error(const char *pcre_error, int pcre_error_offset)
70 {
71 	zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation failed (offset: %d), %s\n", pcre_error_offset, pcre_error);
72 }
73 
zend_accel_blacklist_update_regexp(zend_blacklist * blacklist)74 static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
75 {
76 	const char *pcre_error;
77 	int i, pcre_error_offset;
78 	zend_regexp_list **regexp_list_it, *it;
79 	char regexp[12*1024], *p, *end, *c, *backtrack = NULL;
80 
81 	if (blacklist->pos == 0) {
82 		/* we have no blacklist to talk about */
83 		return;
84 	}
85 
86 	regexp_list_it = &(blacklist->regexp_list);
87 
88 	regexp[0] = '^';
89 	regexp[1] = '(';
90 	p = regexp + 2;
91 	end = regexp + sizeof(regexp) - sizeof("[^\\\\]*)\0");
92 
93 	for (i = 0; i < blacklist->pos; ) {
94 		c = blacklist->entries[i].path;
95 		if (p + blacklist->entries[i].path_length < end) {
96 			while (*c && p < end) {
97 				switch (*c) {
98 					case '?':
99 						c++;
100 #ifdef ZEND_WIN32
101 				 		p[0] = '[';			/* * => [^\\] on Win32 */
102 					 	p[1] = '^';
103 					 	p[2] = '\\';
104 					 	p[3] = '\\';
105 					 	p[4] = ']';
106 						p += 5;
107 #else
108 					 	p[0] = '[';			/* * => [^/] on *nix */
109 					 	p[1] = '^';
110 					 	p[2] = '/';
111 					 	p[3] = ']';
112 						p += 4;
113 #endif
114 						break;
115 					case '*':
116 						c++;
117 						if (*c == '*') {
118 							c++;
119 						 	p[0] = '.';			/* ** => .* */
120 							p[1] = '*';
121 							p += 2;
122 						} else {
123 #ifdef ZEND_WIN32
124 						 	p[0] = '[';			/* * => [^\\]* on Win32 */
125 						 	p[1] = '^';
126 						 	p[2] = '\\';
127 						 	p[3] = '\\';
128 						 	p[4] = ']';
129 						 	p[5] = '*';
130 							p += 6;
131 #else
132 						 	p[0] = '[';			/* * => [^/]* on *nix */
133 						 	p[1] = '^';
134 						 	p[2] = '/';
135 						 	p[3] = ']';
136 						 	p[4] = '*';
137 							p += 5;
138 #endif
139 						}
140 						break;
141 					case '^':
142 					case '.':
143 					case '[':
144 					case ']':
145 					case '$':
146 					case '(':
147 					case ')':
148 					case '|':
149 					case '+':
150 					case '{':
151 					case '}':
152 					case '\\':
153 						*p++ = '\\';
154 						/* break missing intentionally */
155 					default:
156 						*p++ = *c++;
157 				}
158 			}
159 		}
160 
161 		if (*c || i == blacklist->pos - 1) {
162 			if (*c) {
163 				if (!backtrack) {
164 					zend_accel_error(ACCEL_LOG_ERROR, "Too long blacklist entry\n");
165 				}
166 				p = backtrack;
167 			} else {
168 				i++;
169 			}
170 			*p++ = ')';
171 			*p++ = '\0';
172 
173 			it = (zend_regexp_list*)malloc(sizeof(zend_regexp_list));
174 			if (!it) {
175 				zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
176 				return;
177 			}
178 			it->next = NULL;
179 
180 			if ((it->re = pcre_compile(regexp, PCRE_NO_AUTO_CAPTURE, &pcre_error, &pcre_error_offset, 0)) == NULL) {
181 				free(it);
182 				blacklist_report_regexp_error(pcre_error, pcre_error_offset);
183 				return;
184 			}
185 			/* prepare for the next iteration */
186 			p = regexp + 2;
187 			*regexp_list_it = it;
188 			regexp_list_it = &it->next;
189 		} else {
190 			backtrack = p;
191 			*p++ = '|';
192 			i++;
193 		}
194 	}
195 }
196 
zend_accel_blacklist_shutdown(zend_blacklist * blacklist)197 void zend_accel_blacklist_shutdown(zend_blacklist *blacklist)
198 {
199 	zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos;
200 
201 	while (p<end) {
202 		free(p->path);
203 		p++;
204 	}
205 	free(blacklist->entries);
206 	blacklist->entries = NULL;
207 	if (blacklist->regexp_list) {
208 		zend_regexp_list *temp, *it = blacklist->regexp_list;
209 		while (it) {
210 			pcre_free(it->re);
211 			temp = it;
212 			it = it->next;
213 			free(temp);
214 		}
215 	}
216 }
217 
zend_accel_blacklist_allocate(zend_blacklist * blacklist)218 static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist)
219 {
220 	if (blacklist->pos == blacklist->size) {
221 		blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE;
222 		blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size);
223 	}
224 }
225 
226 #ifdef HAVE_GLOB
zend_accel_blacklist_loadone(zend_blacklist * blacklist,char * filename)227 static void zend_accel_blacklist_loadone(zend_blacklist *blacklist, char *filename)
228 #else
229 void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
230 #endif
231 {
232 	char buf[MAXPATHLEN + 1], real_path[MAXPATHLEN + 1], *blacklist_path = NULL;
233 	FILE *fp;
234 	int path_length, blacklist_path_length;
235 
236 	if ((fp = fopen(filename, "r")) == NULL) {
237 		zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename);
238 		return;
239 	}
240 
241 	zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file:  '%s'", filename);
242 
243 	if (VCWD_REALPATH(filename, buf)) {
244 		blacklist_path_length = zend_dirname(buf, strlen(buf));
245 		blacklist_path = zend_strndup(buf, blacklist_path_length);
246 	}
247 
248 	memset(buf, 0, sizeof(buf));
249 	memset(real_path, 0, sizeof(real_path));
250 
251 	while (fgets(buf, MAXPATHLEN, fp) != NULL) {
252 		char *path_dup, *pbuf;
253 		path_length = strlen(buf);
254 		if (path_length > 0 && buf[path_length - 1] == '\n') {
255 			buf[--path_length] = 0;
256 			if (path_length > 0 && buf[path_length - 1] == '\r') {
257 				buf[--path_length] = 0;
258 			}
259 		}
260 
261 		/* Strip ctrl-m prefix */
262 		pbuf = &buf[0];
263 		while (*pbuf == '\r') {
264 			*pbuf++ = 0;
265 			path_length--;
266 		}
267 
268 		/* strip \" */
269 		if (pbuf[0] == '\"' && pbuf[path_length - 1]== '\"') {
270 			*pbuf++ = 0;
271 			path_length -= 2;
272 		}
273 
274 		if (path_length == 0) {
275 			continue;
276 		}
277 
278 		/* skip comments */
279 		if (pbuf[0]==';') {
280 			continue;
281 		}
282 
283 		path_dup = zend_strndup(pbuf, path_length);
284 		if (blacklist_path) {
285 			expand_filepath_ex(path_dup, real_path, blacklist_path, blacklist_path_length);
286 		} else {
287 			expand_filepath(path_dup, real_path);
288 		}
289 		path_length = strlen(real_path);
290 
291 		free(path_dup);
292 
293 		zend_accel_blacklist_allocate(blacklist);
294 		blacklist->entries[blacklist->pos].path_length = path_length;
295 		blacklist->entries[blacklist->pos].path = (char *)malloc(path_length + 1);
296 		if (!blacklist->entries[blacklist->pos].path) {
297 			zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
298 			fclose(fp);
299 			return;
300 		}
301 		blacklist->entries[blacklist->pos].id = blacklist->pos;
302 		memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length + 1);
303 		blacklist->pos++;
304 	}
305 	fclose(fp);
306 	if (blacklist_path) {
307 		free(blacklist_path);
308 	}
309 	zend_accel_blacklist_update_regexp(blacklist);
310 }
311 
312 #ifdef HAVE_GLOB
zend_accel_blacklist_load(zend_blacklist * blacklist,char * filename)313 void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
314 {
315 	glob_t globbuf;
316 	int    ret;
317 	unsigned int i;
318 
319 	memset(&globbuf, 0, sizeof(glob_t));
320 
321 	ret = glob(filename, 0, NULL, &globbuf);
322 #ifdef GLOB_NOMATCH
323 	if (ret == GLOB_NOMATCH || !globbuf.gl_pathc) {
324 #else
325 	if (!globbuf.gl_pathc) {
326 #endif
327 		zend_accel_error(ACCEL_LOG_WARNING, "No blacklist file found matching: %s\n", filename);
328 	} else {
329 		for(i=0 ; i<globbuf.gl_pathc; i++) {
330 			zend_accel_blacklist_loadone(blacklist, globbuf.gl_pathv[i]);
331 		}
332 		globfree(&globbuf);
333 	}
334 }
335 #endif
336 
337 zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path)
338 {
339 	int ret = 0;
340 	zend_regexp_list *regexp_list_it = blacklist->regexp_list;
341 
342 	if (regexp_list_it == NULL) {
343 		return 0;
344 	}
345 	while (regexp_list_it != NULL) {
346 		if (pcre_exec(regexp_list_it->re, NULL, verify_path, strlen(verify_path), 0, 0, NULL, 0) >= 0) {
347 			ret = 1;
348 			break;
349 		}
350 		regexp_list_it = regexp_list_it->next;
351 	}
352 	return ret;
353 }
354 
355 void zend_accel_blacklist_apply(zend_blacklist *blacklist, blacklist_apply_func_arg_t func, void *argument)
356 {
357 	int i;
358 
359 	for (i = 0; i < blacklist->pos; i++) {
360 		func(&blacklist->entries[i], argument);
361 	}
362 }
363