From: Remi Collet Date: Wed, 10 Dec 2014 07:15:21 +0000 (+0100) Subject: Fix bug #68526 Implement POSIX Access Control List for UDS X-Git-Tag: PRE_NATIVE_TLS_MERGE~17^2~8 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=744ada7d9ddb7c0f37c494bf0e7636cb1ed6cb34;p=php Fix bug #68526 Implement POSIX Access Control List for UDS - add --with-fpm-acl build option which allow to manage ACL on Unix Domain Socket - add listen.acl_users pool option - add listen.acl_groups pool option Keep old behavior (chmod) if option not used or not supported. --- diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index 9c10aa6be2..f87776aa24 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -583,6 +583,9 @@ if test "$PHP_FPM" != "no"; then PHP_ARG_WITH(fpm-systemd,, [ --with-fpm-systemd Activate systemd integration], no, no) + PHP_ARG_WITH(fpm-acl,, + [ --with-fpm-acl Use POSIX Access Control Lists], no, no) + if test "$PHP_FPM_SYSTEMD" != "no" ; then if test -z "$PKG_CONFIG"; then AC_PATH_PROG(PKG_CONFIG, pkg-config, no) @@ -624,6 +627,17 @@ if test "$PHP_FPM" != "no"; then else php_fpm_systemd=simple fi + + if test "$PHP_FPM_ACL" != "no" ; then + AC_CHECK_HEADERS([sys/acl.h]) + AC_CHECK_LIB(acl, acl_free, [ + PHP_ADD_LIBRARY(acl) + AC_DEFINE(HAVE_FPM_ACL, 1, [ POSIX Access Control List ]) + ],[ + AC_MSG_ERROR(libacl required not found) + ]) + fi + PHP_SUBST_OLD(php_fpm_systemd) AC_DEFINE_UNQUOTED(PHP_FPM_SYSTEMD, "$php_fpm_systemd", [fpm systemd service type]) diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index 1f73bc915c..cca4e1dbb8 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -123,6 +123,10 @@ static struct ini_value_parser_s ini_fpm_pool_options[] = { { "group", &fpm_conf_set_string, WPO(group) }, { "listen", &fpm_conf_set_string, WPO(listen_address) }, { "listen.backlog", &fpm_conf_set_integer, WPO(listen_backlog) }, +#ifdef HAVE_FPM_ACL + { "listen.acl_users", &fpm_conf_set_string, WPO(listen_acl_users) }, + { "listen.acl_groups", &fpm_conf_set_string, WPO(listen_acl_groups) }, +#endif { "listen.owner", &fpm_conf_set_string, WPO(listen_owner) }, { "listen.group", &fpm_conf_set_string, WPO(listen_group) }, { "listen.mode", &fpm_conf_set_string, WPO(listen_mode) }, @@ -1583,6 +1587,10 @@ static void fpm_conf_dump() /* {{{ */ zlog(ZLOG_NOTICE, "\tgroup = %s", STR2STR(wp->config->group)); zlog(ZLOG_NOTICE, "\tlisten = %s", STR2STR(wp->config->listen_address)); zlog(ZLOG_NOTICE, "\tlisten.backlog = %d", wp->config->listen_backlog); +#ifdef HAVE_FPM_ACL + zlog(ZLOG_NOTICE, "\tlisten.acl_users = %s", STR2STR(wp->config->listen_acl_users)); + zlog(ZLOG_NOTICE, "\tlisten.acl_groups = %s", STR2STR(wp->config->listen_acl_groups)); +#endif zlog(ZLOG_NOTICE, "\tlisten.owner = %s", STR2STR(wp->config->listen_owner)); zlog(ZLOG_NOTICE, "\tlisten.group = %s", STR2STR(wp->config->listen_group)); zlog(ZLOG_NOTICE, "\tlisten.mode = %s", STR2STR(wp->config->listen_mode)); diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h index 12fabe2805..540b22795d 100644 --- a/sapi/fpm/fpm/fpm_conf.h +++ b/sapi/fpm/fpm/fpm_conf.h @@ -58,6 +58,7 @@ struct fpm_worker_pool_config_s { char *group; char *listen_address; int listen_backlog; + /* Using chown */ char *listen_owner; char *listen_group; char *listen_mode; @@ -91,6 +92,11 @@ struct fpm_worker_pool_config_s { #ifdef HAVE_APPARMOR char *apparmor_hat; #endif +#ifdef HAVE_FPM_ACL + /* Using Posix ACL */ + char *listen_acl_users; + char *listen_acl_groups; +#endif }; struct ini_value_parser_s { diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 57707d8f8a..f0d4573483 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -21,6 +21,10 @@ #include #endif +#ifdef HAVE_SYS_ACL_H +#include +#endif + #include "fpm.h" #include "fpm_conf.h" #include "fpm_cleanup.h" @@ -35,8 +39,12 @@ size_t fpm_pagesize; int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */ { struct fpm_worker_pool_config_s *c = wp->config; +#ifdef HAVE_FPM_ACL + int n; /* uninitialized */ + wp->socket_acl = NULL; +#endif wp->socket_uid = -1; wp->socket_gid = -1; wp->socket_mode = 0660; @@ -45,6 +53,117 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */ return 0; } + if (c->listen_mode && *c->listen_mode) { + wp->socket_mode = strtoul(c->listen_mode, 0, 8); + } + +#ifdef HAVE_FPM_ACL + /* count the users and groups configured */ + n = 0; + if (c->listen_acl_users && *c->listen_acl_users) { + char *p; + n++; + for (p=strchr(c->listen_acl_users, ',') ; p ; p=strchr(p+1, ',')) { + n++; + } + } + if (c->listen_acl_groups && *c->listen_acl_groups) { + char *p; + n++; + for (p=strchr(c->listen_acl_groups, ',') ; p ; p=strchr(p+1, ',')) { + n++; + } + } + /* if ACL configured */ + if (n) { + acl_t acl; + acl_entry_t entry; + acl_permset_t perm; + char *tmp, *p, *end; + + acl = acl_init(n); + if (!acl) { + zlog(ZLOG_SYSERROR, "[pool %s] cannot allocate ACL", wp->config->name); + return -1; + } + /* Create USER ACL */ + if (c->listen_acl_users && *c->listen_acl_users) { + struct passwd *pwd; + + tmp = estrdup(c->listen_acl_users); + for (p=tmp ; p ; p=end) { + if ((end = strchr(p, ','))) { + *end++ = 0; + } + pwd = getpwnam(p); + if (pwd) { + zlog(ZLOG_DEBUG, "[pool %s] user '%s' have uid=%d", wp->config->name, p, pwd->pw_uid); + } else { + zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, p); + acl_free(acl); + efree(tmp); + return -1; + } + if (0 > acl_create_entry(&acl, &entry) || + 0 > acl_set_tag_type(entry, ACL_USER) || + 0 > acl_set_qualifier(entry, &pwd->pw_uid) || + 0 > acl_get_permset(entry, &perm) || + 0 > acl_clear_perms (perm) || + 0 > acl_add_perm (perm, ACL_READ) || + 0 > acl_add_perm (perm, ACL_WRITE)) { + zlog(ZLOG_SYSERROR, "[pool %s] cannot create ACL for user '%s'", wp->config->name, p); + acl_free(acl); + efree(tmp); + return -1; + } + } + efree(tmp); + } + /* Create GROUP ACL */ + if (c->listen_acl_groups && *c->listen_acl_groups) { + struct group *grp; + + tmp = estrdup(c->listen_acl_groups); + for (p=tmp ; p ; p=end) { + if ((end = strchr(p, ','))) { + *end++ = 0; + } + grp = getgrnam(p); + if (grp) { + zlog(ZLOG_DEBUG, "[pool %s] group '%s' have gid=%d", wp->config->name, p, grp->gr_gid); + } else { + zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, p); + acl_free(acl); + efree(tmp); + return -1; + } + if (0 > acl_create_entry(&acl, &entry) || + 0 > acl_set_tag_type(entry, ACL_GROUP) || + 0 > acl_set_qualifier(entry, &grp->gr_gid) || + 0 > acl_get_permset(entry, &perm) || + 0 > acl_clear_perms (perm) || + 0 > acl_add_perm (perm, ACL_READ) || + 0 > acl_add_perm (perm, ACL_WRITE)) { + zlog(ZLOG_SYSERROR, "[pool %s] cannot create ACL for group '%s'", wp->config->name, p); + acl_free(acl); + efree(tmp); + return -1; + } + } + efree(tmp); + } + if (c->listen_owner && *c->listen_owner) { + zlog(ZLOG_WARNING, "[pool %s] ACL set, listen.owner = '%s' is ignored", wp->config->name, c->listen_owner); + } + if (c->listen_group && *c->listen_group) { + zlog(ZLOG_WARNING, "[pool %s] ACL set, listen.group = '%s' is ignored", wp->config->name, c->listen_group); + } + wp->socket_acl = acl; + return 0; + } + /* When listen.users and listen.groups not configured, continue with standard right */ +#endif + if (c->listen_owner && *c->listen_owner) { struct passwd *pwd; @@ -69,18 +188,54 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */ wp->socket_gid = grp->gr_gid; } - if (c->listen_mode && *c->listen_mode) { - wp->socket_mode = strtoul(c->listen_mode, 0, 8); - } return 0; } /* }}} */ int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *path) /* {{{ */ { +#ifdef HAVE_FPM_ACL + if (wp->socket_acl) { + acl_t aclfile, aclconf; + acl_entry_t entryfile, entryconf; + int i; + + /* Read the socket ACL */ + aclconf = wp->socket_acl; + aclfile = acl_get_file (path, ACL_TYPE_ACCESS); + if (!aclfile) { + zlog(ZLOG_SYSERROR, "[pool %s] failed to read the ACL of the socket '%s'", wp->config->name, path); + return -1; + } + /* Copy the new ACL entry from config */ + for (i=ACL_FIRST_ENTRY ; acl_get_entry(aclconf, i, &entryconf) ; i=ACL_NEXT_ENTRY) { + if (0 > acl_create_entry (&aclfile, &entryfile) || + 0 > acl_copy_entry(entryfile, entryconf)) { + zlog(ZLOG_SYSERROR, "[pool %s] failed to add entry to the ACL of the socket '%s'", wp->config->name, path); + acl_free(aclfile); + return -1; + } + } + /* Write the socket ACL */ + if (0 > acl_calc_mask (&aclfile) || + 0 > acl_valid (aclfile) || + 0 > acl_set_file (path, ACL_TYPE_ACCESS, aclfile)) { + zlog(ZLOG_SYSERROR, "[pool %s] failed to write the ACL of the socket '%s'", wp->config->name, path); + acl_free(aclfile); + return -1; + } else { + zlog(ZLOG_DEBUG, "[pool %s] ACL of the socket '%s' is set", wp->config->name, path); + } + + acl_free(aclfile); + return 0; + } + /* When listen.users and listen.groups not configured, continue with standard right */ +#endif + if (wp->socket_uid != -1 || wp->socket_gid != -1) { if (0 > chown(path, wp->socket_uid, wp->socket_gid)) { - zlog(ZLOG_SYSERROR, "failed to chown() the socket '%s'", wp->config->listen_address); + zlog(ZLOG_SYSERROR, "[pool %s] failed to chown() the socket '%s'", wp->config->name, wp->config->listen_address); return -1; } } @@ -88,6 +243,17 @@ int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *pa } /* }}} */ +int fpm_unix_free_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */ +{ +#ifdef HAVE_FPM_ACL + if (wp->socket_acl) { + return acl_free(wp->socket_acl); + } +#endif + return 0; +} +/* }}} */ + static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */ { struct passwd *pwd; diff --git a/sapi/fpm/fpm/fpm_unix.h b/sapi/fpm/fpm/fpm_unix.h index b2995ff3e0..a79559f9e6 100644 --- a/sapi/fpm/fpm/fpm_unix.h +++ b/sapi/fpm/fpm/fpm_unix.h @@ -9,6 +9,8 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp); int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *path); +int fpm_unix_free_socket_premissions(struct fpm_worker_pool_s *wp); + int fpm_unix_init_child(struct fpm_worker_pool_s *wp); int fpm_unix_init_main(); diff --git a/sapi/fpm/fpm/fpm_worker_pool.c b/sapi/fpm/fpm/fpm_worker_pool.c index ebe1866c8a..a0022915cd 100644 --- a/sapi/fpm/fpm/fpm_worker_pool.c +++ b/sapi/fpm/fpm/fpm_worker_pool.c @@ -15,6 +15,7 @@ #include "fpm_shm.h" #include "fpm_scoreboard.h" #include "fpm_conf.h" +#include "fpm_unix.h" struct fpm_worker_pool_s *fpm_worker_all_pools; @@ -29,6 +30,7 @@ void fpm_worker_pool_free(struct fpm_worker_pool_s *wp) /* {{{ */ if (wp->home) { free(wp->home); } + fpm_unix_free_socket_premissions(wp); free(wp); } /* }}} */ diff --git a/sapi/fpm/fpm/fpm_worker_pool.h b/sapi/fpm/fpm/fpm_worker_pool.h index 05c993de4e..6b2bc908dc 100644 --- a/sapi/fpm/fpm/fpm_worker_pool.h +++ b/sapi/fpm/fpm/fpm_worker_pool.h @@ -42,6 +42,10 @@ struct fpm_worker_pool_s { /* for ondemand PM */ struct fpm_event_s *ondemand_event; int socket_event_set; + +#ifdef HAVE_FPM_ACL + void *socket_acl; +#endif }; struct fpm_worker_pool_s *fpm_worker_pool_alloc(); diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index 833a4f4f8c..0daf1070fb 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -175,6 +175,11 @@ listen = 127.0.0.1:9000 ;listen.owner = @php_fpm_user@ ;listen.group = @php_fpm_group@ ;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a coma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = ; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. ; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original diff --git a/sapi/fpm/tests/021-uds-acl.phpt b/sapi/fpm/tests/021-uds-acl.phpt new file mode 100644 index 0000000000..f39c526418 --- /dev/null +++ b/sapi/fpm/tests/021-uds-acl.phpt @@ -0,0 +1,89 @@ +--TEST-- +FPM: Test Unix Domain Socket with Posix ACL +--SKIPIF-- + +--XFAIL-- +Mark as XFAIL because --with-fpm-acl is not enabled in default build +--FILE-- + +--EXPECTF-- +[%s] NOTICE: fpm is running, pid %d +[%s] NOTICE: ready to handle connections +int(%d) +UDS ok +user::rw- +user:%s:rw- +user:%s:rw- +user:%s:rw- +group::--- +group:%s:rw- +group:%s:rw- +mask::rw- +other::--- + +[%s] NOTICE: Terminating ... +[%s] NOTICE: exiting, bye-bye! +--CLEAN-- +