Cuando montaba un servidor en el que trabajo, encontré un problemilla de a priori no evidente solución al hacer trabajar apache y proftpd, a saber: El servidor en cuestión serviría para hostear webs de clientes. Cada cliente tiene un usuario en el sistema. Además, cada usuario tiene un grupo del mismo nombre; la intención de esto era que ningún usuario pudiese leer nada de las homes de los demás, seteando los permisos adecuados.

El problema de este enfoque es, claramente, que no consigue lo requerido: No es posible con este esquema hacer que un usuario no pueda leer las webs de las homes de los demás, dado que también apache (usuario www-data, gid www-data cuando corre) necesita leer dichas webs, así que cualquiera de las 2 siguientes soluciones echa por tierra la idea de mantener estancas las homes:

- Hacer las webs leíbles por el resto.
- Hacer las webs propiedad del usuario en cuestion y del grupo www-data.

La segunda solución, no falla al mantener estancas las homes, sino que el problema es otro: Ficheros creados por algún script php en apache dinámicamente pertenecerán (en mi sistema) a www-data:www-data, así que para poder borrarlos he de conseguir que no sólo los .php sino también los directorios que los contienen pertenezcan al grupo www-data. Esto es así porque de otra manera mis usuarios, que no tienen acceso ssh ni nada similar, sino sólo ftp para subir y borrar sus webs, no podrían borrar estos ficheros.

A priori esto requiere siempre la intervención del administrador, es decir, ningún servidor ftp que haya probado (que alguien me notifique por favor si alguno lo hace) puede hacer setgid(2) en el proceso atendedor a un grupo al que no pertenezca el uid de proceso (en este caso www-data). Podría hacerse haciendo que el atendedor (llamo atendedor al proceso que ha forkeado el escuchador, en un esquema simple, creo que se entiende) herede la capacidad CAP_CHMOD. También, como otra vía puede llamarse a initgroups(3) antes del setuid(2) al usuario no privilegiado que ejecutará el atendedor; antes de setuid el proceso aún es privilegiado y podrá especificar a initgroups un grupo adicional como segundo parámetro (ver manual, después de llamar a setuid la lista se hereda). No sé cual de las dos maneras es más 'limpia', si alguien con mejores fundamentos de programación que yo tiene argumentos en favor de alguna, por favor que me ponga un comentario.

Recapitulando: Necesito poder borrar ficheros que pertenecen a www-data:www-data, pero como usuario no puedo crear los directorios que los contendrán perteneciendo al grupo www-data, ya que como usuario no pertenezco a ese grupo, y ningún servidor ftp me permite hacer 'trampa'.

La solución? Pues resulta que el comportamiento cuando a un directorio le doy el big setgid el comportamiento es dependiente de la implementación (a priori el bit setgid tiene sentido en ejecutables): En Linux, poner el bit setgid sobre un directorio tiene la consecuencia de hacer que los ficheros y directorios creados dentro de este, pertenecen por defecto al grupo al que pertenece el directorio padre con el bit setgid puesto, pertenezca o no el usuario que crea dichos ficheros a este.

Esto nos da la solución de la siguiente manera: Hemos de hacer que php cree estos ficheros dinámicos (que pueden ser fotos subidas, ficheros temporales, etc) con una umask permisiva (que permita escritura al grupo www-data en este caso, para que sean borrables por ftp). A continuación, si nuestro usuario es fulano y pertenece solo al grupo fulano, y el DocumentRoot de su web (supongamos hosting virtual) esta en /home/fulano/www.fulano.com, lo único que hemos de hacer como administradores al dar de alta la cuenta es crear el usuario, la configuración del VirtualHost de apache y a continuación como superusuario:

$ mkdir /home/fulano/www.fulano.com
$ chown fulano.www-data /home/fulano/www.fulano.com
$ chmod g+s /home/fulano/www.fulano.com

Ya reiniciar apache para que coja la configuración nueva y listo!