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