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