]> granicus.if.org Git - php/commitdiff
Allow numeric [UG]ID in FPM listen.{owner,group}
authorAndre Nathan <andre@digirati.com.br>
Tue, 4 Feb 2020 12:53:16 +0000 (09:53 -0300)
committerJakub Zelenka <bukka@php.net>
Sun, 29 Mar 2020 19:20:01 +0000 (20:20 +0100)
NEWS
sapi/fpm/fpm/fpm_unix.c
sapi/fpm/tests/socket-uds-numeric-ugid-nonroot.phpt [new file with mode: 0644]
sapi/fpm/tests/socket-uds-numeric-ugid.phpt [new file with mode: 0644]
sapi/fpm/tests/tester.inc
sapi/fpm/www.conf.in

diff --git a/NEWS b/NEWS
index 76fab1d4c8671ac23a64a29f598fd75097ed253a..ede62878a5e58bb04373c3c0aef1f0f0e5b4e6d7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,10 @@ PHP                                                                        NEWS
   . Fixed bug #79396 (DateTime hour incorrect during DST jump forward). (Nate
     Brunette)
 
+- FPM:
+  . Implement request #77062 (Allow numeric [UG]ID in FPM listen.{owner,group})
+    (Andre Nathan)
+
 - Iconv:
   . Fixed bug #79200 (Some iconv functions cut Windows-1258). (cmb)
 
index 6490a7b5baa31cbea02a5d74049df757d23f0e4a..1c28b1aa09bef01cc40da8cff9c8cede1332a12a 100644 (file)
@@ -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 (file)
index 0000000..a4d3521
--- /dev/null
@@ -0,0 +1,62 @@
+--TEST--
+FPM: UNIX socket owner and group settings can be numeric
+--SKIPIF--
+<?php
+include "skipif.inc";
+FPM\Tester::skipIfPosixNotLoaded();
+?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+[unconfined]
+listen = {{ADDR:UDS}}
+listen.owner = {{UID}}
+listen.group = {{GID}}
+user = {{USER}}
+ping.path = /ping
+ping.response = pong
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+EOT;
+
+$tester = new FPM\Tester($cfg);
+$tester->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--
+<?php
+require_once "tester.inc";
+FPM\Tester::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 (file)
index 0000000..d97ab2f
--- /dev/null
@@ -0,0 +1,58 @@
+--TEST--
+FPM: UNIX socket owner and group settings can be numeric
+--SKIPIF--
+<?php
+include "skipif.inc";
+FPM\Tester::skipIfPosixNotLoaded();
+FPM\Tester::skipIfNotRoot();
+?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+[unconfined]
+listen = {{ADDR:UDS}}
+listen.owner = 1234
+listen.group = 1234
+user = 1234
+ping.path = /ping
+ping.response = pong
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+EOT;
+
+$tester = new FPM\Tester($cfg);
+$tester->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--
+<?php
+require_once "tester.inc";
+FPM\Tester::clean();
+?>
index 3424898074971f0a61df99f35204db7c08aa2f4a..e553e94f154673cad3f5f01ffcba7c671d01e9fb 100644 (file)
@@ -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',
index 169d19568c892ebd88494855c91c16650bba948c..934f97b09e97bae1d4e25a29be12c1311386761f 100644 (file)
@@ -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@