php / php/php-src

php-fpm listen.acl_users and listen.acl_groups not checking for duplicate ACL entries causing `Invalid argument (22)`

Abierto
#18,357 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Bug SAPI: fpm
Lenguaje dominante
C
Estrellas
40.4k
Forks
8.2k
Merge medio
2 d 13 h
PR fusionados (30 d)
96

Descripción

Description

if listen.acl_users or listen.acl_groups directives in an FPM pool config define entries that are already present in the socket ACL via having the default ACL on the parent directory, the whole FPM fails to start with something akin

ERROR: [pool %s] failed to write the ACL of the socket '%s': Invalid argument (22)
ERROR: FPM initialization failed

quick search led me to int fpm_unix_set_socket_permissions(struct fpm_worker_pool_s *wp, const char *path) that does not check the resulting ACL for entries with duplicate uids or gids and thus aborting FPM initialization

a simple testcase would be

mkdir acl-test
setfacl -m d:g:www-data:rx acl-test

and trying to start FPM with a pool like

[acl-test]
listen = acl-test/php.socket
listen.acl_groups = www-data

it seems the whole if (wp->socket_acl) case should look like this:

	if (wp->socket_acl) {
		acl_t aclfile, aclconf;
		acl_entry_t entryfile, entryconf;
		int ifile, iconf;
		acl_tag_t tagfile, tagconf;
		uid_t *uidfile, *uidconf;
		gid_t *gidfile, *gidconf;

		/* 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 (iconf=ACL_FIRST_ENTRY ; acl_get_entry(aclconf, iconf, &entryconf) ; iconf=ACL_NEXT_ENTRY) {
			if (0 > acl_get_tag_type(entryconf, &tagconf)) {
				zlog(ZLOG_SYSERROR, "[pool %s] failed to get tag of the ACL entry of the pool", wp->config->name);
				acl_free(aclfile);
				return -1;
			}
			if (tagconf == ACL_USER) {
				uidconf = acl_get_qualifier(entryfile);
				if (!uidconf) {
					zlog(ZLOG_SYSERROR, "[pool %s] failed to get user qualifier of the ACL entry of the pool", wp->config->name);
					acl_free(aclfile);
					return -1;
				}
			} else {
				gidconf = acl_get_qualifier(entryfile);
				if (!gidconf) {
					zlog(ZLOG_SYSERROR, "[pool %s] failed to get group qualifier of the ACL entry of the pool", wp->config->name);
					acl_free(aclfile);
					return -1;
				}
			}
			for (ifile=ACL_FIRST_ENTRY ; acl_get_entry(aclfile, ifile, &entryfile) ; ifile=ACL_NEXT_ENTRY) {
				if (0 > acl_get_tag_type(entryfile, &tagfile)) {
					zlog(ZLOG_SYSERROR, "[pool %s] failed to get tag of the ACL entry of the socket '%s'", wp->config->name, path);
					acl_free(tagconf == ACL_USER ? uidconf : gidconf);
					acl_free(aclfile);
					return -1;
				}
				if (tagfile != ACL_USER && tagfile != ACL_GROUP)
					continue;
				if (tagfile != tagconf)
					continue;
				if (tagfile == ACL_USER) {
					uidfile = acl_get_qualifier(entryfile);
					if (!uidfile) {
						zlog(ZLOG_SYSERROR, "[pool %s] failed to get user qualifier of the ACL entry of the socket '%s'", wp->config->name, path);
						acl_free(uidconf);
						acl_free(aclfile);
						return -1;
					}
					if (*uidfile != *uidconf) {
						acl_free(uidfile);
						continue;
					}
				} else {
					gidfile = acl_get_qualifier(entryfile);
					if (!gidfile) {
						zlog(ZLOG_SYSERROR, "[pool %s] failed to get group qualifier of the ACL entry of the socket '%s'", wp->config->name, path);
						acl_free(gidconf);
						acl_free(aclfile);
						return -1;
					}
					if (*gidfile != *gidconf) {
						acl_free(gidfile);
						continue;
					}
				}
				acl_free(tagfile == ACL_USER ? uidfile : gidfile);
				acl_delete_entry(aclfile, entryfile);
			}
			acl_free(tagconf == ACL_USER ? uidconf : gidconf);
			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;
	}

the code above is untested, as I currently have no desire to delve deep into php build process

PHP Version

PHP 8.4.6 (fpm-fcgi) (built: Apr 11 2025 02:09:29) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.6, Copyright (c) Zend Technologies
with Zend OPcache v8.4.6, Copyright (c), by Zend Technologies

Operating System

Debian 12

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza en sapi/fpm/fpm/fpm_unix.c, en fpm_unix_set_socket_permissions(), y después reproduce el fallo con el comando setfacl y la configuración de pool de FPM mostrada. Traza cómo se combinan las entradas de ACL existentes del socket con listen.acl_groups o listen.acl_users. El trabajo estará terminado cuando las entradas de usuario o grupo duplicadas ya no provoquen que falle la escritura de la ACL o la inicialización de FPM.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
c, php
Área
backend, operating-systems
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
42/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.