Skip to content

Commit 0b4e80b

Browse files
andrenthbukka
authored andcommitted
Allow numeric [UG]ID in FPM listen.{owner,group}
1 parent d66a063 commit 0b4e80b

File tree

6 files changed

+204
-15
lines changed

6 files changed

+204
-15
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ PHP NEWS
1414
. Fixed bug #79396 (DateTime hour incorrect during DST jump forward). (Nate
1515
Brunette)
1616

17+
- FPM:
18+
. Implement request #77062 (Allow numeric [UG]ID in FPM listen.{owner,group})
19+
(Andre Nathan)
20+
1721
- Iconv:
1822
. Fixed bug #79200 (Some iconv functions cut Windows-1258). (cmb)
1923

sapi/fpm/fpm/fpm_unix.c

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -163,27 +163,35 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
163163
#endif
164164

165165
if (c->listen_owner && *c->listen_owner) {
166-
struct passwd *pwd;
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;
167170

168-
pwd = getpwnam(c->listen_owner);
169-
if (!pwd) {
170-
zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner);
171-
return -1;
172-
}
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+
}
173176

174-
wp->socket_uid = pwd->pw_uid;
175-
wp->socket_gid = pwd->pw_gid;
177+
wp->socket_uid = pwd->pw_uid;
178+
wp->socket_gid = pwd->pw_gid;
179+
}
176180
}
177181

178182
if (c->listen_group && *c->listen_group) {
179-
struct group *grp;
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;
180187

181-
grp = getgrnam(c->listen_group);
182-
if (!grp) {
183-
zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group);
184-
return -1;
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;
185194
}
186-
wp->socket_gid = grp->gr_gid;
187195
}
188196

189197
return 0;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
FPM: UNIX socket owner and group settings can be numeric
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
FPM\Tester::skipIfPosixNotLoaded();
7+
?>
8+
--FILE--
9+
<?php
10+
11+
require_once "tester.inc";
12+
13+
$cfg = <<<EOT
14+
[global]
15+
error_log = {{FILE:LOG}}
16+
[unconfined]
17+
listen = {{ADDR:UDS}}
18+
listen.owner = {{UID}}
19+
listen.group = {{GID}}
20+
user = {{USER}}
21+
ping.path = /ping
22+
ping.response = pong
23+
pm = dynamic
24+
pm.max_children = 5
25+
pm.start_servers = 2
26+
pm.min_spare_servers = 1
27+
pm.max_spare_servers = 3
28+
EOT;
29+
30+
$tester = new FPM\Tester($cfg);
31+
$tester->testConfig();
32+
$tester->start();
33+
$tester->expectLogNotice(
34+
"'user' directive is ignored when FPM is not running as root",
35+
'unconfined'
36+
);
37+
$tester->expectLogStartNotices();
38+
$tester->ping('{{ADDR:UDS}}');
39+
$st = stat($tester->getListen('{{ADDR:UDS}}'));
40+
if ($st) {
41+
$pw = posix_getpwuid($st['uid']);
42+
$gr = posix_getgrgid($st['gid']);
43+
$user = $pw ? $pw['name'] : 'UNKNOWN';
44+
$group = $gr ? $gr['name'] : 'UNKNOWN';
45+
echo "{$st['uid']}/{$user},{$st['gid']}/{$group}\n";
46+
} else {
47+
echo "stat failed for " . $tester->getListen('{{ADDR:UDS}}');
48+
}
49+
$tester->terminate();
50+
$tester->expectLogTerminatingNotices();
51+
$tester->close();
52+
53+
?>
54+
Done
55+
--EXPECTF--
56+
%d/%s,%d/%s
57+
Done
58+
--CLEAN--
59+
<?php
60+
require_once "tester.inc";
61+
FPM\Tester::clean();
62+
?>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
--TEST--
2+
FPM: UNIX socket owner and group settings can be numeric
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
FPM\Tester::skipIfPosixNotLoaded();
7+
FPM\Tester::skipIfNotRoot();
8+
?>
9+
--FILE--
10+
<?php
11+
12+
require_once "tester.inc";
13+
14+
$cfg = <<<EOT
15+
[global]
16+
error_log = {{FILE:LOG}}
17+
[unconfined]
18+
listen = {{ADDR:UDS}}
19+
listen.owner = 1234
20+
listen.group = 1234
21+
user = 1234
22+
ping.path = /ping
23+
ping.response = pong
24+
pm = dynamic
25+
pm.max_children = 5
26+
pm.start_servers = 2
27+
pm.min_spare_servers = 1
28+
pm.max_spare_servers = 3
29+
EOT;
30+
31+
$tester = new FPM\Tester($cfg);
32+
$tester->start();
33+
$tester->expectLogStartNotices();
34+
$tester->ping('{{ADDR:UDS}}');
35+
$st = stat($tester->getListen('{{ADDR:UDS}}'));
36+
if ($st) {
37+
$pw = posix_getpwuid($st['uid']);
38+
$gr = posix_getgrgid($st['gid']);
39+
$user = $pw ? $pw['name'] : 'UNKNOWN';
40+
$group = $gr ? $gr['name'] : 'UNKNOWN';
41+
echo "{$st['uid']}/{$user},{$st['gid']}/{$group}\n";
42+
} else {
43+
echo "stat failed for " . $tester->getListen('{{ADDR:UDS}}');
44+
}
45+
$tester->terminate();
46+
$tester->expectLogTerminatingNotices();
47+
$tester->close();
48+
49+
?>
50+
Done
51+
--EXPECT--
52+
1234/UNKNOWN,1234/UNKNOWN
53+
Done
54+
--CLEAN--
55+
<?php
56+
require_once "tester.inc";
57+
FPM\Tester::clean();
58+
?>

sapi/fpm/tests/tester.inc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,26 @@ class Tester
268268
}
269269
}
270270

271+
/**
272+
* Skip if not running as root.
273+
*/
274+
static public function skipIfNotRoot()
275+
{
276+
if (getmyuid() != 0) {
277+
die('skip not running as root');
278+
}
279+
}
280+
281+
/**
282+
* Skip if posix extension not loaded.
283+
*/
284+
static public function skipIfPosixNotLoaded()
285+
{
286+
if (!extension_loaded('posix')) {
287+
die('skip posix extension not loaded');
288+
}
289+
}
290+
271291
/**
272292
* Tester constructor.
273293
*
@@ -665,6 +685,38 @@ class Tester
665685
return $lines[0] ?? '';
666686
}
667687

688+
/**
689+
* @return string
690+
*/
691+
public function getUser()
692+
{
693+
return get_current_user();
694+
}
695+
696+
/**
697+
* @return string
698+
*/
699+
public function getGroup()
700+
{
701+
return get_current_group();
702+
}
703+
704+
/**
705+
* @return int
706+
*/
707+
public function getUid()
708+
{
709+
return getmyuid();
710+
}
711+
712+
/**
713+
* @return int
714+
*/
715+
public function getGid()
716+
{
717+
return getmygid();
718+
}
719+
668720
/**
669721
* Send signal to the supplied PID or the server PID.
670722
*
@@ -761,6 +813,10 @@ class Tester
761813
'ADDR:UDS' => ['getAddr', 'uds'],
762814
'PORT' => ['getPort', 'ip'],
763815
'INCLUDE:CONF' => self::CONF_DIR . '/*.conf',
816+
'USER' => ['getUser'],
817+
'GROUP' => ['getGroup'],
818+
'UID' => ['getUid'],
819+
'GID' => ['getGid'],
764820
];
765821
$aliases = [
766822
'ADDR' => 'ADDR:IPv4',

sapi/fpm/www.conf.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ listen = 127.0.0.1:9000
4141

4242
; Set permissions for unix socket, if one is used. In Linux, read/write
4343
; permissions must be set in order to allow connections from a web server. Many
44-
; BSD-derived systems allow connections regardless of permissions.
44+
; BSD-derived systems allow connections regardless of permissions. The owner
45+
; and group can be specified either by name or by their numeric IDs.
4546
; Default Values: user and group are set as the running user
4647
; mode is set to 0660
4748
;listen.owner = @php_fpm_user@

0 commit comments

Comments
 (0)