xref: /PHP-7.0/sapi/fpm/fpm/fpm_unix.c (revision 276d19fe)
1 
2 	/* $Id: fpm_unix.c,v 1.25.2.1 2008/12/13 03:18:23 anight Exp $ */
3 	/* (c) 2007,2008 Andrei Nigmatulin */
4 
5 #include "fpm_config.h"
6 
7 #include <string.h>
8 #include <sys/time.h>
9 #include <sys/resource.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <pwd.h>
14 #include <grp.h>
15 
16 #ifdef HAVE_PRCTL
17 #include <sys/prctl.h>
18 #endif
19 
20 #ifdef HAVE_APPARMOR
21 #include <sys/apparmor.h>
22 #endif
23 
24 #ifdef HAVE_SYS_ACL_H
25 #include <sys/acl.h>
26 #endif
27 
28 #include "fpm.h"
29 #include "fpm_conf.h"
30 #include "fpm_cleanup.h"
31 #include "fpm_clock.h"
32 #include "fpm_stdio.h"
33 #include "fpm_unix.h"
34 #include "fpm_signals.h"
35 #include "zlog.h"
36 
37 size_t fpm_pagesize;
38 
fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s * wp)39 int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
40 {
41 	struct fpm_worker_pool_config_s *c = wp->config;
42 #ifdef HAVE_FPM_ACL
43 	int n;
44 
45 	/* uninitialized */
46 	wp->socket_acl  = NULL;
47 #endif
48 	wp->socket_uid = -1;
49 	wp->socket_gid = -1;
50 	wp->socket_mode = 0660;
51 
52 	if (!c) {
53 		return 0;
54 	}
55 
56 	if (c->listen_mode && *c->listen_mode) {
57 		wp->socket_mode = strtoul(c->listen_mode, 0, 8);
58 	}
59 
60 #ifdef HAVE_FPM_ACL
61 	/* count the users and groups configured */
62 	n = 0;
63 	if (c->listen_acl_users && *c->listen_acl_users) {
64 		char *p;
65 		n++;
66 		for (p=strchr(c->listen_acl_users, ',') ; p ; p=strchr(p+1, ',')) {
67 			n++;
68 		}
69 	}
70 	if (c->listen_acl_groups && *c->listen_acl_groups) {
71 		char *p;
72 		n++;
73 		for (p=strchr(c->listen_acl_groups, ',') ; p ; p=strchr(p+1, ',')) {
74 			n++;
75 		}
76 	}
77 	/* if ACL configured */
78 	if (n) {
79 		acl_t acl;
80 		acl_entry_t entry;
81 		acl_permset_t perm;
82 		char *tmp, *p, *end;
83 
84 		acl = acl_init(n);
85 		if (!acl) {
86 			zlog(ZLOG_SYSERROR, "[pool %s] cannot allocate ACL", wp->config->name);
87 			return -1;
88 		}
89 		/* Create USER ACL */
90 		if (c->listen_acl_users && *c->listen_acl_users) {
91 			struct passwd *pwd;
92 
93 			tmp = estrdup(c->listen_acl_users);
94 			for (p=tmp ; p ; p=end) {
95 				if ((end = strchr(p, ','))) {
96 					*end++ = 0;
97 				}
98 				pwd = getpwnam(p);
99 				if (pwd) {
100 					zlog(ZLOG_DEBUG, "[pool %s] user '%s' have uid=%d", wp->config->name, p, pwd->pw_uid);
101 				} else {
102 					zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, p);
103 					acl_free(acl);
104 					efree(tmp);
105 					return -1;
106 				}
107 				if (0 > acl_create_entry(&acl, &entry) ||
108 					0 > acl_set_tag_type(entry, ACL_USER) ||
109 					0 > acl_set_qualifier(entry, &pwd->pw_uid) ||
110 					0 > acl_get_permset(entry, &perm) ||
111 					0 > acl_clear_perms (perm) ||
112 					0 > acl_add_perm (perm, ACL_READ) ||
113 					0 > acl_add_perm (perm, ACL_WRITE)) {
114 					zlog(ZLOG_SYSERROR, "[pool %s] cannot create ACL for user '%s'", wp->config->name, p);
115 					acl_free(acl);
116 					efree(tmp);
117 					return -1;
118 				}
119 			}
120 			efree(tmp);
121 		}
122 		/* Create GROUP ACL */
123 		if (c->listen_acl_groups && *c->listen_acl_groups) {
124 			struct group *grp;
125 
126 			tmp = estrdup(c->listen_acl_groups);
127 			for (p=tmp ; p ; p=end) {
128 				if ((end = strchr(p, ','))) {
129 					*end++ = 0;
130 				}
131 				grp = getgrnam(p);
132 				if (grp) {
133 					zlog(ZLOG_DEBUG, "[pool %s] group '%s' have gid=%d", wp->config->name, p, grp->gr_gid);
134 				} else {
135 					zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, p);
136 					acl_free(acl);
137 					efree(tmp);
138 					return -1;
139 				}
140 				if (0 > acl_create_entry(&acl, &entry) ||
141 					0 > acl_set_tag_type(entry, ACL_GROUP) ||
142 					0 > acl_set_qualifier(entry, &grp->gr_gid) ||
143 					0 > acl_get_permset(entry, &perm) ||
144 					0 > acl_clear_perms (perm) ||
145 					0 > acl_add_perm (perm, ACL_READ) ||
146 					0 > acl_add_perm (perm, ACL_WRITE)) {
147 					zlog(ZLOG_SYSERROR, "[pool %s] cannot create ACL for group '%s'", wp->config->name, p);
148 					acl_free(acl);
149 					efree(tmp);
150 					return -1;
151 				}
152 			}
153 			efree(tmp);
154 		}
155 		if (c->listen_owner && *c->listen_owner) {
156 			zlog(ZLOG_WARNING, "[pool %s] ACL set, listen.owner = '%s' is ignored", wp->config->name, c->listen_owner);
157 		}
158 		if (c->listen_group && *c->listen_group) {
159 			zlog(ZLOG_WARNING, "[pool %s] ACL set, listen.group = '%s' is ignored", wp->config->name, c->listen_group);
160 		}
161 		wp->socket_acl  = acl;
162 		return 0;
163 	}
164 	/* When listen.users and listen.groups not configured, continue with standard right */
165 #endif
166 
167 	if (c->listen_owner && *c->listen_owner) {
168 		struct passwd *pwd;
169 
170 		pwd = getpwnam(c->listen_owner);
171 		if (!pwd) {
172 			zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner);
173 			return -1;
174 		}
175 
176 		wp->socket_uid = pwd->pw_uid;
177 		wp->socket_gid = pwd->pw_gid;
178 	}
179 
180 	if (c->listen_group && *c->listen_group) {
181 		struct group *grp;
182 
183 		grp = getgrnam(c->listen_group);
184 		if (!grp) {
185 			zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group);
186 			return -1;
187 		}
188 		wp->socket_gid = grp->gr_gid;
189 	}
190 
191 	return 0;
192 }
193 /* }}} */
194 
fpm_unix_set_socket_premissions(struct fpm_worker_pool_s * wp,const char * path)195 int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *path) /* {{{ */
196 {
197 #ifdef HAVE_FPM_ACL
198 	if (wp->socket_acl) {
199 		acl_t aclfile, aclconf;
200 		acl_entry_t entryfile, entryconf;
201 		int i;
202 
203 		/* Read the socket ACL */
204 		aclconf = wp->socket_acl;
205 		aclfile = acl_get_file (path, ACL_TYPE_ACCESS);
206 		if (!aclfile) {
207 			zlog(ZLOG_SYSERROR, "[pool %s] failed to read the ACL of the socket '%s'", wp->config->name, path);
208 			return -1;
209 		}
210 		/* Copy the new ACL entry from config */
211 		for (i=ACL_FIRST_ENTRY ; acl_get_entry(aclconf, i, &entryconf) ; i=ACL_NEXT_ENTRY) {
212 			if (0 > acl_create_entry (&aclfile, &entryfile) ||
213 			    0 > acl_copy_entry(entryfile, entryconf)) {
214 				zlog(ZLOG_SYSERROR, "[pool %s] failed to add entry to the ACL of the socket '%s'", wp->config->name, path);
215 				acl_free(aclfile);
216 				return -1;
217 			}
218 		}
219 		/* Write the socket ACL */
220 		if (0 > acl_calc_mask (&aclfile) ||
221 			0 > acl_valid (aclfile) ||
222 			0 > acl_set_file (path, ACL_TYPE_ACCESS, aclfile)) {
223 			zlog(ZLOG_SYSERROR, "[pool %s] failed to write the ACL of the socket '%s'", wp->config->name, path);
224 			acl_free(aclfile);
225 			return -1;
226 		} else {
227 			zlog(ZLOG_DEBUG, "[pool %s] ACL of the socket '%s' is set", wp->config->name, path);
228 		}
229 
230 		acl_free(aclfile);
231 		return 0;
232 	}
233 	/* When listen.users and listen.groups not configured, continue with standard right */
234 #endif
235 
236 	if (wp->socket_uid != -1 || wp->socket_gid != -1) {
237 		if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
238 			zlog(ZLOG_SYSERROR, "[pool %s] failed to chown() the socket '%s'", wp->config->name, wp->config->listen_address);
239 			return -1;
240 		}
241 	}
242 	return 0;
243 }
244 /* }}} */
245 
fpm_unix_free_socket_premissions(struct fpm_worker_pool_s * wp)246 int fpm_unix_free_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
247 {
248 #ifdef HAVE_FPM_ACL
249 	if (wp->socket_acl) {
250 		return acl_free(wp->socket_acl);
251 	}
252 #endif
253 	return 0;
254 }
255 /* }}} */
256 
fpm_unix_conf_wp(struct fpm_worker_pool_s * wp)257 static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
258 {
259 	struct passwd *pwd;
260 	int is_root = !geteuid();
261 
262 	if (is_root) {
263 		if (wp->config->user && *wp->config->user) {
264 			if (strlen(wp->config->user) == strspn(wp->config->user, "0123456789")) {
265 				wp->set_uid = strtoul(wp->config->user, 0, 10);
266 			} else {
267 				struct passwd *pwd;
268 
269 				pwd = getpwnam(wp->config->user);
270 				if (!pwd) {
271 					zlog(ZLOG_ERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, wp->config->user);
272 					return -1;
273 				}
274 
275 				wp->set_uid = pwd->pw_uid;
276 				wp->set_gid = pwd->pw_gid;
277 
278 				wp->user = strdup(pwd->pw_name);
279 				wp->home = strdup(pwd->pw_dir);
280 			}
281 		}
282 
283 		if (wp->config->group && *wp->config->group) {
284 			if (strlen(wp->config->group) == strspn(wp->config->group, "0123456789")) {
285 				wp->set_gid = strtoul(wp->config->group, 0, 10);
286 			} else {
287 				struct group *grp;
288 
289 				grp = getgrnam(wp->config->group);
290 				if (!grp) {
291 					zlog(ZLOG_ERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, wp->config->group);
292 					return -1;
293 				}
294 				wp->set_gid = grp->gr_gid;
295 			}
296 		}
297 
298 		if (!fpm_globals.run_as_root) {
299 			if (wp->set_uid == 0 || wp->set_gid == 0) {
300 				zlog(ZLOG_ERROR, "[pool %s] please specify user and group other than root", wp->config->name);
301 				return -1;
302 			}
303 		}
304 	} else { /* not root */
305 		if (wp->config->user && *wp->config->user) {
306 			zlog(ZLOG_NOTICE, "[pool %s] 'user' directive is ignored when FPM is not running as root", wp->config->name);
307 		}
308 		if (wp->config->group && *wp->config->group) {
309 			zlog(ZLOG_NOTICE, "[pool %s] 'group' directive is ignored when FPM is not running as root", wp->config->name);
310 		}
311 		if (wp->config->chroot && *wp->config->chroot) {
312 			zlog(ZLOG_NOTICE, "[pool %s] 'chroot' directive is ignored when FPM is not running as root", wp->config->name);
313 		}
314 		if (wp->config->process_priority != 64) {
315 			zlog(ZLOG_NOTICE, "[pool %s] 'process.priority' directive is ignored when FPM is not running as root", wp->config->name);
316 		}
317 
318 		/* set up HOME and USER anyway */
319 		pwd = getpwuid(getuid());
320 		if (pwd) {
321 			wp->user = strdup(pwd->pw_name);
322 			wp->home = strdup(pwd->pw_dir);
323 		}
324 	}
325 	return 0;
326 }
327 /* }}} */
328 
fpm_unix_init_child(struct fpm_worker_pool_s * wp)329 int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
330 {
331 	int is_root = !geteuid();
332 	int made_chroot = 0;
333 
334 	if (wp->config->rlimit_files) {
335 		struct rlimit r;
336 
337 		r.rlim_max = r.rlim_cur = (rlim_t) wp->config->rlimit_files;
338 
339 		if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
340 			zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_files for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", wp->config->name, wp->config->rlimit_files);
341 		}
342 	}
343 
344 	if (wp->config->rlimit_core) {
345 		struct rlimit r;
346 
347 		r.rlim_max = r.rlim_cur = wp->config->rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) wp->config->rlimit_core;
348 
349 		if (0 > setrlimit(RLIMIT_CORE, &r)) {
350 			zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", wp->config->name, wp->config->rlimit_core);
351 		}
352 	}
353 
354 	if (is_root && wp->config->chroot && *wp->config->chroot) {
355 		if (0 > chroot(wp->config->chroot)) {
356 			zlog(ZLOG_SYSERROR, "[pool %s] failed to chroot(%s)",  wp->config->name, wp->config->chroot);
357 			return -1;
358 		}
359 		made_chroot = 1;
360 	}
361 
362 	if (wp->config->chdir && *wp->config->chdir) {
363 		if (0 > chdir(wp->config->chdir)) {
364 			zlog(ZLOG_SYSERROR, "[pool %s] failed to chdir(%s)", wp->config->name, wp->config->chdir);
365 			return -1;
366 		}
367 	} else if (made_chroot) {
368 		if (0 > chdir("/")) {
369 			zlog(ZLOG_WARNING, "[pool %s] failed to chdir(/)", wp->config->name);
370 		}
371 	}
372 
373 	if (is_root) {
374 
375 		if (wp->config->process_priority != 64) {
376 			if (setpriority(PRIO_PROCESS, 0, wp->config->process_priority) < 0) {
377 				zlog(ZLOG_SYSERROR, "[pool %s] Unable to set priority for this new process", wp->config->name);
378 				return -1;
379 			}
380 		}
381 
382 		if (wp->set_gid) {
383 			if (0 > setgid(wp->set_gid)) {
384 				zlog(ZLOG_SYSERROR, "[pool %s] failed to setgid(%d)", wp->config->name, wp->set_gid);
385 				return -1;
386 			}
387 		}
388 		if (wp->set_uid) {
389 			if (0 > initgroups(wp->config->user, wp->set_gid)) {
390 				zlog(ZLOG_SYSERROR, "[pool %s] failed to initgroups(%s, %d)", wp->config->name, wp->config->user, wp->set_gid);
391 				return -1;
392 			}
393 			if (0 > setuid(wp->set_uid)) {
394 				zlog(ZLOG_SYSERROR, "[pool %s] failed to setuid(%d)", wp->config->name, wp->set_uid);
395 				return -1;
396 			}
397 		}
398 	}
399 
400 #ifdef HAVE_PRCTL
401 	if (wp->config->process_dumpable && 0 > prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) {
402 		zlog(ZLOG_SYSERROR, "[pool %s] failed to prctl(PR_SET_DUMPABLE)", wp->config->name);
403 	}
404 #endif
405 
406 	if (0 > fpm_clock_init()) {
407 		return -1;
408 	}
409 
410 #ifdef HAVE_APPARMOR
411 	if (wp->config->apparmor_hat) {
412 		char *con, *new_con;
413 
414 		if (aa_getcon(&con, NULL) == -1) {
415 			zlog(ZLOG_SYSERROR, "[pool %s] failed to query apparmor confinement. Please check if \"/proc/*/attr/current\" is read and writeable.", wp->config->name);
416 			return -1;
417 		}
418 
419 		new_con = malloc(strlen(con) + strlen(wp->config->apparmor_hat) + 3); // // + 0 Byte
420 		if (!new_con) {
421 			zlog(ZLOG_SYSERROR, "[pool %s] failed to allocate memory for apparmor hat change.", wp->config->name);
422 			return -1;
423 		}
424 
425 		if (0 > sprintf(new_con, "%s//%s", con, wp->config->apparmor_hat)) {
426 			zlog(ZLOG_SYSERROR, "[pool %s] failed to construct apparmor confinement.", wp->config->name);
427 			return -1;
428 		}
429 
430 		if (0 > aa_change_profile(new_con)) {
431 			zlog(ZLOG_SYSERROR, "[pool %s] failed to change to new confinement (%s). Please check if \"/proc/*/attr/current\" is read and writeable and \"change_profile -> %s//*\" is allowed.", wp->config->name, new_con, con);
432 			return -1;
433 		}
434 
435 		free(con);
436 		free(new_con);
437 	}
438 #endif
439 
440 	return 0;
441 }
442 /* }}} */
443 
fpm_unix_init_main()444 int fpm_unix_init_main() /* {{{ */
445 {
446 	struct fpm_worker_pool_s *wp;
447 	int is_root = !geteuid();
448 
449 	if (fpm_global_config.rlimit_files) {
450 		struct rlimit r;
451 
452 		r.rlim_max = r.rlim_cur = (rlim_t) fpm_global_config.rlimit_files;
453 
454 		if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
455 			zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", fpm_global_config.rlimit_files);
456 			return -1;
457 		}
458 	}
459 
460 	if (fpm_global_config.rlimit_core) {
461 		struct rlimit r;
462 
463 		r.rlim_max = r.rlim_cur = fpm_global_config.rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) fpm_global_config.rlimit_core;
464 
465 		if (0 > setrlimit(RLIMIT_CORE, &r)) {
466 			zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", fpm_global_config.rlimit_core);
467 			return -1;
468 		}
469 	}
470 
471 	fpm_pagesize = getpagesize();
472 	if (fpm_global_config.daemonize) {
473 		/*
474 		 * If daemonize, the calling process will die soon
475 		 * and the master process continues to initialize itself.
476 		 *
477 		 * The parent process has then to wait for the master
478 		 * process to initialize to return a consistent exit
479 		 * value. For this pupose, the master process will
480 		 * send \"1\" into the pipe if everything went well
481 		 * and \"0\" otherwise.
482 		 */
483 
484 
485 		struct timeval tv;
486 		fd_set rfds;
487 		int ret;
488 
489 		if (pipe(fpm_globals.send_config_pipe) == -1) {
490 			zlog(ZLOG_SYSERROR, "failed to create pipe");
491 			return -1;
492 		}
493 
494 		/* then fork */
495 		pid_t pid = fork();
496 		switch (pid) {
497 
498 			case -1 : /* error */
499 				zlog(ZLOG_SYSERROR, "failed to daemonize");
500 				return -1;
501 
502 			case 0 : /* children */
503 				close(fpm_globals.send_config_pipe[0]); /* close the read side of the pipe */
504 				break;
505 
506 			default : /* parent */
507 				close(fpm_globals.send_config_pipe[1]); /* close the write side of the pipe */
508 
509 				/*
510 				 * wait for 10s before exiting with error
511 				 * the child is supposed to send 1 or 0 into the pipe to tell the parent
512 				 * how it goes for it
513 				 */
514 				FD_ZERO(&rfds);
515 				FD_SET(fpm_globals.send_config_pipe[0], &rfds);
516 
517 				tv.tv_sec = 10;
518 				tv.tv_usec = 0;
519 
520 				zlog(ZLOG_DEBUG, "The calling process is waiting for the master process to ping via fd=%d", fpm_globals.send_config_pipe[0]);
521 				ret = select(fpm_globals.send_config_pipe[0] + 1, &rfds, NULL, NULL, &tv);
522 				if (ret == -1) {
523 					zlog(ZLOG_SYSERROR, "failed to select");
524 					exit(FPM_EXIT_SOFTWARE);
525 				}
526 				if (ret) { /* data available */
527 					int readval;
528 					ret = read(fpm_globals.send_config_pipe[0], &readval, sizeof(readval));
529 					if (ret == -1) {
530 						zlog(ZLOG_SYSERROR, "failed to read from pipe");
531 						exit(FPM_EXIT_SOFTWARE);
532 					}
533 
534 					if (ret == 0) {
535 						zlog(ZLOG_ERROR, "no data have been read from pipe");
536 						exit(FPM_EXIT_SOFTWARE);
537 					} else {
538 						if (readval == 1) {
539 							zlog(ZLOG_DEBUG, "I received a valid acknoledge from the master process, I can exit without error");
540 							fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT);
541 							exit(FPM_EXIT_OK);
542 						} else {
543 							zlog(ZLOG_DEBUG, "The master process returned an error !");
544 							exit(FPM_EXIT_SOFTWARE);
545 						}
546 					}
547 				} else { /* no date sent ! */
548 					zlog(ZLOG_ERROR, "the master process didn't send back its status (via the pipe to the calling process)");
549 				  exit(FPM_EXIT_SOFTWARE);
550 				}
551 				exit(FPM_EXIT_SOFTWARE);
552 		}
553 	}
554 
555 	/* continue as a child */
556 	setsid();
557 	if (0 > fpm_clock_init()) {
558 		return -1;
559 	}
560 
561 	if (fpm_global_config.process_priority != 64) {
562 		if (is_root) {
563 			if (setpriority(PRIO_PROCESS, 0, fpm_global_config.process_priority) < 0) {
564 				zlog(ZLOG_SYSERROR, "Unable to set priority for the master process");
565 				return -1;
566 			}
567 		} else {
568 			zlog(ZLOG_NOTICE, "'process.priority' directive is ignored when FPM is not running as root");
569 		}
570 	}
571 
572 	fpm_globals.parent_pid = getpid();
573 	for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
574 		if (0 > fpm_unix_conf_wp(wp)) {
575 			return -1;
576 		}
577 	}
578 
579 	return 0;
580 }
581 /* }}} */
582 
583