From ac56ce17f6255da918dab4af7aeea50a437b18fe Mon Sep 17 00:00:00 2001 From: Andre Nathan Date: Tue, 4 Feb 2020 09:53:16 -0300 Subject: [PATCH] Allow numeric [UG]ID in FPM listen.{owner,group} --- sapi/fpm/fpm/fpm_unix.c | 36 ++++++----- .../socket-uds-numeric-ugid-nonroot.phpt | 62 +++++++++++++++++++ sapi/fpm/tests/socket-uds-numeric-ugid.phpt | 58 +++++++++++++++++ sapi/fpm/tests/tester.inc | 56 +++++++++++++++++ sapi/fpm/www.conf.in | 3 +- 5 files changed, 200 insertions(+), 15 deletions(-) create mode 100644 sapi/fpm/tests/socket-uds-numeric-ugid-nonroot.phpt create mode 100644 sapi/fpm/tests/socket-uds-numeric-ugid.phpt diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 6490a7b5baa31..1c28b1aa09bef 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -163,27 +163,35 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */ #endif if (c->listen_owner && *c->listen_owner) { - struct passwd *pwd; + if (strlen(c->listen_owner) == strspn(c->listen_owner, "0123456789")) { + wp->socket_uid = strtoul(c->listen_owner, 0, 10); + } else { + struct passwd *pwd; - pwd = getpwnam(c->listen_owner); - if (!pwd) { - zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner); - return -1; - } + pwd = getpwnam(c->listen_owner); + if (!pwd) { + zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner); + return -1; + } - wp->socket_uid = pwd->pw_uid; - wp->socket_gid = pwd->pw_gid; + wp->socket_uid = pwd->pw_uid; + wp->socket_gid = pwd->pw_gid; + } } if (c->listen_group && *c->listen_group) { - struct group *grp; + if (strlen(c->listen_group) == strspn(c->listen_group, "0123456789")) { + wp->socket_gid = strtoul(c->listen_group, 0, 10); + } else { + struct group *grp; - grp = getgrnam(c->listen_group); - if (!grp) { - zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group); - return -1; + grp = getgrnam(c->listen_group); + if (!grp) { + zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group); + return -1; + } + wp->socket_gid = grp->gr_gid; } - wp->socket_gid = grp->gr_gid; } return 0; diff --git a/sapi/fpm/tests/socket-uds-numeric-ugid-nonroot.phpt b/sapi/fpm/tests/socket-uds-numeric-ugid-nonroot.phpt new file mode 100644 index 0000000000000..a4d352123cc0b --- /dev/null +++ b/sapi/fpm/tests/socket-uds-numeric-ugid-nonroot.phpt @@ -0,0 +1,62 @@ +--TEST-- +FPM: UNIX socket owner and group settings can be numeric +--SKIPIF-- + +--FILE-- +testConfig(); +$tester->start(); +$tester->expectLogNotice( + "'user' directive is ignored when FPM is not running as root", + 'unconfined' +); +$tester->expectLogStartNotices(); +$tester->ping('{{ADDR:UDS}}'); +$st = stat($tester->getListen('{{ADDR:UDS}}')); +if ($st) { + $pw = posix_getpwuid($st['uid']); + $gr = posix_getgrgid($st['gid']); + $user = $pw ? $pw['name'] : 'UNKNOWN'; + $group = $gr ? $gr['name'] : 'UNKNOWN'; + echo "{$st['uid']}/{$user},{$st['gid']}/{$group}\n"; +} else { + echo "stat failed for " . $tester->getListen('{{ADDR:UDS}}'); +} +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECTF-- +%d/%s,%d/%s +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/socket-uds-numeric-ugid.phpt b/sapi/fpm/tests/socket-uds-numeric-ugid.phpt new file mode 100644 index 0000000000000..d97ab2fc3fa82 --- /dev/null +++ b/sapi/fpm/tests/socket-uds-numeric-ugid.phpt @@ -0,0 +1,58 @@ +--TEST-- +FPM: UNIX socket owner and group settings can be numeric +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->ping('{{ADDR:UDS}}'); +$st = stat($tester->getListen('{{ADDR:UDS}}')); +if ($st) { + $pw = posix_getpwuid($st['uid']); + $gr = posix_getgrgid($st['gid']); + $user = $pw ? $pw['name'] : 'UNKNOWN'; + $group = $gr ? $gr['name'] : 'UNKNOWN'; + echo "{$st['uid']}/{$user},{$st['gid']}/{$group}\n"; +} else { + echo "stat failed for " . $tester->getListen('{{ADDR:UDS}}'); +} +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +1234/UNKNOWN,1234/UNKNOWN +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index 3424898074971..e553e94f15467 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -268,6 +268,26 @@ class Tester } } + /** + * Skip if not running as root. + */ + static public function skipIfNotRoot() + { + if (getmyuid() != 0) { + die('skip not running as root'); + } + } + + /** + * Skip if posix extension not loaded. + */ + static public function skipIfPosixNotLoaded() + { + if (!extension_loaded('posix')) { + die('skip posix extension not loaded'); + } + } + /** * Tester constructor. * @@ -665,6 +685,38 @@ class Tester return $lines[0] ?? ''; } + /** + * @return string + */ + public function getUser() + { + return get_current_user(); + } + + /** + * @return string + */ + public function getGroup() + { + return get_current_group(); + } + + /** + * @return int + */ + public function getUid() + { + return getmyuid(); + } + + /** + * @return int + */ + public function getGid() + { + return getmygid(); + } + /** * Send signal to the supplied PID or the server PID. * @@ -761,6 +813,10 @@ class Tester 'ADDR:UDS' => ['getAddr', 'uds'], 'PORT' => ['getPort', 'ip'], 'INCLUDE:CONF' => self::CONF_DIR . '/*.conf', + 'USER' => ['getUser'], + 'GROUP' => ['getGroup'], + 'UID' => ['getUid'], + 'GID' => ['getGid'], ]; $aliases = [ 'ADDR' => 'ADDR:IPv4', diff --git a/sapi/fpm/www.conf.in b/sapi/fpm/www.conf.in index 815618c65d056..0bb4008ae1633 100644 --- a/sapi/fpm/www.conf.in +++ b/sapi/fpm/www.conf.in @@ -41,7 +41,8 @@ listen = 127.0.0.1:9000 ; Set permissions for unix socket, if one is used. In Linux, read/write ; permissions must be set in order to allow connections from a web server. Many -; BSD-derived systems allow connections regardless of permissions. +; BSD-derived systems allow connections regardless of permissions. The owner +; and group can be specified either by name or by their numeric IDs. ; Default Values: user and group are set as the running user ; mode is set to 0660 ;listen.owner = @php_fpm_user@