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