php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Return to Bug #68526
Patch posix-acl.patch revision 2014-11-30 17:34 UTC by remi@php.net
revision 2014-11-30 16:50 UTC by remi@php.net
revision 2014-11-30 10:31 UTC by remi@php.net
revision 2014-11-30 09:56 UTC by remi@php.net
revision 2014-11-30 09:44 UTC by remi@php.net
revision 2014-11-30 09:21 UTC by remi@php.net
revision 2014-11-30 08:35 UTC by remi@php.net

Patch posix-acl.patch for FPM related Bug #68526

Patch version 2014-11-30 17:34 UTC

Return to Bug #68526 | Download this patch
This patch renders other patches obsolete

Obsolete patches:

Patch Revisions:

Developer: remi@php.net

diff -ru /home/php/php-src/sapi/fpm/config.m4 sapi/fpm/config.m4
--- /home/php/php-src/sapi/fpm/config.m4	2014-11-24 09:31:58.000000000 +0100
+++ sapi/fpm/config.m4	2014-11-30 18:31:27.193354685 +0100
@@ -583,6 +583,9 @@
   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 @@
   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 -ru /home/php/php-src/sapi/fpm/fpm/fpm_conf.c sapi/fpm/fpm/fpm_conf.c
--- /home/php/php-src/sapi/fpm/fpm/fpm_conf.c	2014-11-29 17:28:25.000000000 +0100
+++ sapi/fpm/fpm/fpm_conf.c	2014-11-30 10:54:37.973527763 +0100
@@ -123,6 +123,10 @@
 	{ "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 @@
 		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 -ru /home/php/php-src/sapi/fpm/fpm/fpm_conf.h sapi/fpm/fpm/fpm_conf.h
--- /home/php/php-src/sapi/fpm/fpm/fpm_conf.h	2014-11-24 09:31:58.000000000 +0100
+++ sapi/fpm/fpm/fpm_conf.h	2014-11-30 10:52:52.919133385 +0100
@@ -58,6 +58,7 @@
 	char *group;
 	char *listen_address;
 	int listen_backlog;
+	/* Using chown */
 	char *listen_owner;
 	char *listen_group;
 	char *listen_mode;
@@ -91,6 +92,11 @@
 #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 -ru /home/php/php-src/sapi/fpm/fpm/fpm_unix.c sapi/fpm/fpm/fpm_unix.c
--- /home/php/php-src/sapi/fpm/fpm/fpm_unix.c	2014-11-29 16:52:25.000000000 +0100
+++ sapi/fpm/fpm/fpm_unix.c	2014-11-30 18:33:09.211756546 +0100
@@ -21,6 +21,10 @@
 #include <sys/apparmor.h>
 #endif
 
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+
 #include "fpm.h"
 #include "fpm_conf.h"
 #include "fpm_cleanup.h"
@@ -35,8 +39,12 @@
 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 @@
 		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,24 +188,71 @@
 		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;
 		}
 	}
 	return 0;
 }
 /* }}} */
+
+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) /* {{{ */
 {
diff -ru /home/php/php-src/sapi/fpm/fpm/fpm_unix.h sapi/fpm/fpm/fpm_unix.h
--- /home/php/php-src/sapi/fpm/fpm/fpm_unix.h	2014-11-29 16:52:25.000000000 +0100
+++ sapi/fpm/fpm/fpm_unix.h	2014-11-30 08:26:06.904956526 +0100
@@ -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();
 
Les fichiers binaires /home/php/php-src/sapi/fpm/fpm/fpm_unix.o et sapi/fpm/fpm/fpm_unix.o sont différents
diff -ru /home/php/php-src/sapi/fpm/fpm/fpm_worker_pool.c sapi/fpm/fpm/fpm_worker_pool.c
--- /home/php/php-src/sapi/fpm/fpm/fpm_worker_pool.c	2014-03-06 11:00:20.000000000 +0100
+++ sapi/fpm/fpm/fpm_worker_pool.c	2014-11-30 11:30:15.460576401 +0100
@@ -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 @@
 	if (wp->home) {
 		free(wp->home);
 	}
+	fpm_unix_free_socket_premissions(wp);
 	free(wp);
 }
 /* }}} */
diff -ru /home/php/php-src/sapi/fpm/fpm/fpm_worker_pool.h sapi/fpm/fpm/fpm_worker_pool.h
--- /home/php/php-src/sapi/fpm/fpm/fpm_worker_pool.h	2014-03-06 11:00:20.000000000 +0100
+++ sapi/fpm/fpm/fpm_worker_pool.h	2014-11-30 08:39:44.687175401 +0100
@@ -42,6 +42,10 @@
 	/* 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 -ru /home/php/php-src/sapi/fpm/php-fpm.conf.in sapi/fpm/php-fpm.conf.in
--- /home/php/php-src/sapi/fpm/php-fpm.conf.in	2014-11-29 17:28:25.000000000 +0100
+++ sapi/fpm/php-fpm.conf.in	2014-11-30 10:51:33.536836172 +0100
@@ -175,6 +175,11 @@
 ;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
--- /dev/null	2014-11-30 17:14:22.119979747 +0100
+++ sapi/fpm/tests/021-uds-acl.phpt	2014-11-30 17:47:51.667947148 +0100
@@ -0,0 +1,89 @@
+--TEST--
+FPM: Test Unix Domain Socket with Posix ACL
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!(file_exists('/usr/bin/getfacl') && file_exists('/etc/passwd') && file_exists('/etc/group'))) die ("skip missing getfacl command");
+?>
+--XFAIL--
+Mark as XFAIL because --with-fpm-acl is not enabled in default build
+--FILE--
+<?php
+
+include "include.inc";
+
+$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+$socket  = dirname(__FILE__).'/php-fpm.sock';
+
+// Select 3 users and 2 groups known by system (avoid root)
+$users = $groups = [];
+$tmp = file('/etc/passwd');
+for ($i=1 ; $i<=3 ; $i++) {
+	$tab = explode(':', $tmp[$i]);
+	$users[] = $tab[0];
+}
+$users = implode(',', $users);
+$tmp = file('/etc/group');
+for ($i=1 ; $i<=2 ; $i++) {
+	$tab = explode(':', $tmp[$i]);
+	$groups[] = $tab[0];
+}
+$groups = implode(',', $groups);
+
+$cfg = <<<EOT
+[global]
+error_log = $logfile
+[unconfined]
+listen = $socket
+listen.acl_users = $users
+listen.acl_groups = $groups
+listen.mode = 0600
+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;
+
+$fpm = run_fpm($cfg, $tail);
+if (is_resource($fpm)) {
+    fpm_display_log($tail, 2);
+    try {
+		var_dump(strpos(run_request('unix://'.$socket, -1), 'pong'));
+		echo "UDS ok\n";
+	} catch (Exception $e) {
+		echo "UDS error\n";
+	}
+	passthru("/usr/bin/getfacl -cp $socket");
+
+	proc_terminate($fpm);
+    echo stream_get_contents($tail);
+    fclose($tail);
+    proc_close($fpm);
+}
+
+?>
+--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--
+<?php
+    $logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+    @unlink($logfile);
+?>
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 23:01:28 2024 UTC