Je suis "gavé"... Un peu lassé par l'immobilisme et la force (d'inertie) dont fait preuve l'équipe de dev Gentoo depuis plusieurs mois. Le bug en question est pourtant con comme la lune, et la correction à appliquer n'est que d'une poignée d'octets dans un script. Et pourtant... 6 mois que ça traîne, malgré mes mails et contacts divers par tous les moyens auxquels j'ai pensé...

Mais avant de continuer à pester, quelques explications...

Par convention, depuis plusieurs dizaines d'années dans le monde UNIX, les comptes utilisateurs et les groupes sont rangés d'une façon simple et efficace : les UID et GID inférieurs à 1000 sont réservés pour le système et les démons. Ainsi, lorsqu'on crée un compte utilisateur "normal" sur un système tout neuf, on se retrouve la plupart du temps avec l'UID 1000. En revanche, lorsqu'on installe le serveur OpenSSH, il est presque toujours installé sous l'UID 22 (par convention, et analogie avec le port TCP sur lequel il tourne). Ca facilite le rangement, et, malgré les années qui passent, une réserve de 1000 UID "système" est toujours suffisante. Cette convention est tellement ancrée dans les mentalités que presque tout est conçu sur la base de ces conventions. Ces valeurs par défaut sont fixées dans le fichier /etc/login.defs, que consultent les outils de gestion de comptes :

UID_MIN 1000
SYS_UID_MIN 100
GID_MIN 1000
SYS_GID_MIN 100

Les noms des variables sont assez clairs, mais il faut préciser un détail : certains UID sont quasiment "réservés". C'est le cas de l'UID du serveur SSH (22) par exemple, ou encore de celui d'Apache (81). Ceux-là sont en général compris entre 0 et 100. Au-delà, et jusqu'à 1000, c'est plus aléatoire. GDM par exemple, prendra par défaut le premier UID libre entre 101 et 999. Les ebuilds de HAL et DBus fonctionnent également sur ce modèle. La différence se fait au niveau des ebuilds : celui d'apache demande précisemment l'UID 81, alors que ceux de hal ou dbus ne précisent rien. Mais les fonctions utilisées sont les mêmes, seuls les arguments changent. Les prototypes de ces fonctions, utilisées dans les ebuilds, se trouvent dans le fichier /usr/portage/eclass/eutils.eclass :

# @FUNCTION: enewgroup
# @USAGE: <group> gid
# @DESCRIPTION:
# This function does not require you to understand how to properly add a
# group to the system.  Just give it a group name to add and enewgroup will
# do the rest.  You may specify the gid for the group or allow the group to
# allocate the next available one.
[ ... ]
# @FUNCTION: enewuser
# @USAGE: <user> uid shell homedir groups params
# @DESCRIPTION:
# Same as enewgroup, you are not required to understand how to properly add
# a user to the system.  The only required parameter is the username.
# Default uid is (pass -1 for this) next available, default shell is
# /bin/false, default homedir is /dev/null, there are no default groups,
# and default params sets the comment as 'added by portage for ${PN}'.

Le problème est en fait dans le corps de ces fonctions ; en effet, elles appelent les commandes classiques useradd et groupadd, mais de façon incorrecte.

Les manpage de useradd ou groupadd précisent que, pour créer un compte ou un groupe "système", il faut appeler ces commandes avec l'option -r :

-r, --system
    Create a system account.
    System users will be created with no aging information in /etc/shadow, and their numeric identifiers are choosen in the
    SYS_UID_MIN-SYS_UID_MAX range, defined in login.defs, instead of UID_MIN-UID_MAX (and their GID counterparts for the creation of
    groups).

Si l'option n'est pas précisée, alors le compte créé (respectivement le groupe) se retrouve avec un UID (respectivement un GID) "classique", choisi parmi ceux normalement affectés aux utilisateurs. Et dans le cas de Gentoo, depuis plus de 6 mois, c'est ce qui se passe. Et donc, sur une Gentoo toute neuve, installer GDM et ses dépendances conduit à créer successivement, les utilisateurs hald, messagebus et gdm avec des UID > 1000. Et de même pour pas mal d'autres logiciels plus ou moins "sensibles". Pas dramatique à priori... Sauf que...

Certaines machines (particulièrement des serveurs), utilisées en réseau, utilisent des backends d'authentification externes. NIS et OpenLDAP sont les plus utilisés dans le monde UNIX. Et par défaut, ces outils reposent sur les conventions citées plus haut : les comptes utilisateurs stockés dedans ont donc des UID et des GID à partir de 1000, Imaginons maintenant qu'un utilisateur dont l'UID dans la base NIS est 1002 vienne se connecter sur une machine Gentoo fraîchement installée. Sur celle-ci, si GDM tourne avec l'UID 1002... Ca ouvre pas mal de possibilités...

Selon les divers contacts que j'ai eu avec plusieurs dev gentoo (via #gentoo-security, mails direct, mails indirect, bugzilla, etc...), il s'agit d'un bug mineur... On m'a même parlé de modification "cosmétique" ! J'ai pourtant expliqué le problème à une demi-douzaine de personnes différentes, mais rien ne se passe. Les 2 seuls devs qui ont compris le problème ne font pas partie de l'équipe habilitée à régler le souci...

Pour ma part, je vais indiquer ci-après comment régler le problème. Ce sera probablement mon dernier billet concernant Gentoo : tant que les problèmes d'organisation internes n'interféraient qu'avec la fraîcheur des ebuilds, c'était pénible mais pas grave (encore que depuis le temps que ça dure, ça gave méchamment de publier des scripts parfaitement fonctionnels que les usagers doivent aller récupérer eux-même sur le bugzilla). Mais là on a passé les bornes de ce que je suis en mesure d'accepter ; je vais sans aucun doute me tourner vers une autre solution dans les mois à venir.

De votre côté, comment régler le problème ? Si vous êtes déjà touché, ce qui est plus que probable, la procédure est pénible mais simple : il s'agit de changer l'UID (et/ou le GID) des démons concernés (via les commandes usermod et groupmod), puis de faire une recherche des fichiers appartenant à ce paquet pour les ré-affecter au nouvel UID. Cela peut se faire avec un simple find :

find /usr -uid $ancien_uid_foireux -exec chown nouvel_uid:nouveau_group {}\;

Attention à ne pas inclure /home dans le champ d'action de find : vous risqueriez d'affecter des données utilisateur à des démons.

Et dans tous les cas, il faut vous protéger pour la suite du cycle de vie de votre système. La procédure la plus accessible est de vous faire un overlay de l'eclass problématique en attendant qu'un dev veuille bien "committer" le diff... Peut-être un jour ;-). Et n'oubliez pas d'ajouter "transfer-metadata" à la variable FEATURES dans votre /etc/make.conf, afin de rendre cette modif persistante.