Provided by: manpages-fr_4.13-4_all bug

NOM

       user_namespaces – Présentation des espaces de noms utilisateur sous Linux

DESCRIPTION

       Pour une présentation générale des espaces de noms, consultez namespaces(7).

       Les  espaces de noms utilisateur isolent les identifiants et attributs liés à la sécurité, en particulier
       les identifiants d'utilisateurs et de groupes (consultez credentials(7)), le répertoire racine, les clefs
       (consultez keyctl(2)) et les capacités (consultez capabilities(7)). Les identifiants d'utilisateur et  de
       groupe  d'un  processus  peuvent  être différents selon que l'on se trouve à l'intérieur ou à l'extérieur
       d'un espace de noms utilisateur.  Un  processus  peut  notamment  avoir  un  identifiant  sans  privilège
       particulier  en  dehors  d'un  espace de noms et avoir l'identifiant 0 à l'intérieur d'un espace de noms.
       Autrement dit, le processus dispose de tous les privilèges pour des opérations effectuées  dans  l'espace
       de  noms,  tandis  qu'il  n'en  a  aucun  pour  les  opérations  réalisées  en dehors de l'espace de noms
       utilisateur.

   Espaces de noms imbriqués, appartenance aux espaces de noms
       Les espaces de noms utilisateur  peuvent  être  imbriqués.  Cela  signifie  que  chaque  espace  de  noms
       utilisateur  — à  l'exception de l'espace de noms initial (« root ») — a un espace de noms parent et peut
       avoir éventuellement un ou plusieurs espaces de noms utilisateur enfant.  L'espace  de  noms  utilisateur
       parent  est  l'espace de noms du processus qui a créé l'espace de noms utilisateur au moyen de unshare(2)
       ou de clone(2) invoqué avec l'attribut CLONE_NEWUSER.

       Le noyau impose (à partir de Linux 3.11) une limite de 32 niveaux d'imbrication pour les espaces de  noms
       utilisateur.  Si un appel à unshare(2) ou à clone(2) provoque le dépassement de cette limite, la commande
       échoue en renvoyant l'erreur EUSERS.

       Chaque processus est membre d'exactement un espace de noms utilisateur. Un processus créé par fork(2)  ou
       par clone(2) sans l'attribut CLONE_NEWUSER est membre du même espace de noms que son processus parent. Un
       processus  mono-threadé  peut  rejoindre un autre espace de noms en utilisant setns(2) s'il dispose de la
       capacité CAP_SYS_ADMIN dans cet espace de noms ; cette action lui octroie un ensemble de  capacités  dans
       cet espace de noms.

       Un appel à clone(2) ou à unshare(2) avec l'attribut CLONE_NEWUSER place le nouveau processus enfant (pour
       clone(2)) ou l'appelant (pour unshare(2)) dans le nouvel espace de noms utilisateur créé par l'appel.

       L’opération  ioctl(2)  NS_GET_PARENT peut être utilisée pour découvrir les relations de parenté entre les
       espaces de noms utilisateur. Consultez ioctl_ns(2).

   Capacités
       Le processus enfant créé par clone(2) avec l'attribut CLONE_NEWUSER s’initialise avec un nouvel  ensemble
       de  capacités  dans le nouvel espace de noms utilisateur. De même, un processus qui crée un nouvel espace
       de noms au moyen de unshare(2) ou qui rejoint un espace de noms existant à l’aide de setns(2)  reçoit  un
       ensemble  de capacités dans cet espace de noms. D’un autre côté, le processus n’a aucune capacité dans le
       parent (dans le cas de clone(2)) ou dans le  précédent  espace  de  noms  utilisateur  (dans  le  cas  de
       unshare(2)  et  setns(2)),  même  si  le  nouvel  espace  de  noms  utilisateur  est  créé ou rejoint par
       l’utilisateur racine (c’est-à-dire un processus avec l’ID utilisateur 0 dans l’espace de noms racine).

       Remarquez qu'un appel à execve(2) déclenche la réévaluation des capacités  selon  la  méthode  habituelle
       (consultez  capabilities(7)),  de  sorte  que  le processus perdra ses capacités, sauf si son identifiant
       utilisateur vaut 0 dans l'espace de noms ou si le fichier exécutable a un masque de  capacités  héritable
       non  vide.  Pour  en savoir plus, consultez les commentaires sur le mappage entre utilisateurs et groupes
       ci-dessous.

       Un appel à clone(2) ou unshare(2) en utilisant l'attribut  CLONE_NEWUSER  ou  un  appel  à  setns(2)  qui
       déplace  l’appelant  dans  d’autres  jeux  d’espaces  de  noms  utilisateur  positionne  les  indicateurs
       « securebits » (consultez capabilities(7)) à leurs valeurs par défaut (tous les  indicateurs  désactivés)
       dans  l’enfant  (pour  clone(2))  ou  l’appelant  (pour  unshare(2) ou setns(2)). Remarquez que parce que
       l’appelant n’a plus de capacités dans son espace de noms utilisateur après un appel à setns(2), il  n’est
       pas  possible  à  un  processus  de  réinitialiser  ses indicateurs « securebits » tout en conservant son
       appartenance à un espace de noms utilisateur en utilisant une paire d’appels setns(2)  pour  se  déplacer
       vers  un  autre  espace  de  noms  utilisateur  et  ensuite retourner vers son espace de noms utilisateur
       original.

       Les règles pour déterminer si un processus a ou n’a pas de capacités dans un espace de  noms  utilisateur
       particulier sont comme suit :

       1. Un  processus  dispose d'une capacité dans un espace de noms utilisateur s'il est membre de cet espace
          de noms et si cette capacité est activée dans son jeu de capacités.  Un  processus  peut  obtenir  une
          nouvelle  capacité  dans  son  jeu de capacités de plusieurs façons. Il peut, par exemple, exécuter un
          programme set-user-ID ou un exécutable avec des capacités de  fichier  associées.  Il  peut  également
          obtenir  des  capacités  à  l’aide  de  l'action  de  clone(2),  unshare(2)  ou setns(2) comme indiqué
          précédemment.

       2. Si un processus dispose d'une capacité dans un espace de noms  utilisateur,  alors  il  a  cette  même
          capacité dans tous les espaces de noms enfant (et les espaces descendants supprimés).

       3. Lorsqu'un espace de noms est créé, le noyau enregistre l'identifiant utilisateur effectif du processus
          de  création  comme  étant le « propriétaire » de l'espace de noms. Un processus qui se trouve dans le
          parent d'un espace de noms utilisateur et qui a un identifiant utilisateur effectif qui correspond  au
          propriétaire  de l'espace de noms dispose de toutes les capacités dans cet espace de noms. En vertu de
          la règle précédente, cela signifie que ce processus a également toutes les  capacités  dans  tous  les
          descendants  supprimés  de  cet  espace  de  noms.  L’opération  NS_GET_OWNER_UID d’ioctl(2) peut être
          utilisée pour découvrir l’ID d’utilisateur du propriétaire de l’espace de noms. Consultez ioctl_ns(2).

   Effet des capacités à l’intérieur d’un espace de noms utilisateur
       Un processus qui possède des capacités dans un espace de noms utilisateur a  la  possibilité  d'effectuer
       des  opérations  (nécessitant des privilèges) seulement sur les ressources gérées par cet espace de noms.
       En d’autres mots, avoir une capacité dans un espace de  noms  permet  à  un  processus  de  réaliser  des
       opérations  privilégiées sur des ressources gérées par des espaces de noms (non utilisateur) possédés par
       (associés avec) l’espace de noms utilisateur (consultez la sous-section suivante).

       D’un autre coté, il existe beaucoup d’opérations  privilégiées  affectant  les  ressources  qui  ne  sont
       associées  à  aucun  type  d’espace  de  noms,  par exemple, modifier l’heure du système (c’est-à-dire le
       calendrier) (régi par CAP_SYS_TIME), charger un module du noyau (régi par  CAP_SYS_MODULE)  et  créer  un
       périphérique  (régi  par  CAP_MKNOD).  Seuls  les processus avec privilèges dans l’espace de noms initial
       peuvent réaliser de telles opérations.

       Avoir CAP_SYS_ADMIN dans un espace de noms utilisateur qui possède  un  espace  de  noms  de  montage  de
       processus  permet  à ce processus de créer des remontages (bind mount) et de monter les types suivants de
       système de fichiers :

           – /proc/ (depuis Linux 3.8)
           – /sys (depuis Linux 3.8)
           – devpts (depuis Linux 3.9)
           – tmpfs(5) (depuis Linux 3.9)
           – ramfs (depuis Linux 3.9)
           – mqueue (depuis Linux 3.9)
           – bpf (depuis Linux 4.4)

       Avoir CAP_SYS_ADMIN dans l’espace de noms utilisateur qui possède un espace de noms cgroup  de  processus
       permet  (depuis  Linux 4.6)  à  ce  processus de monter un système de fichiers cgroup version 2 ou cgroup
       version 1  appelés  hiérarchies  (c’est-à-dire  des   systèmes   de   fichiers   cgroup   avec   l’option
       « none,name= »).

       Avoir  CAP_SYS_ADMIN  dans  un  espace de noms utilisateur qui possède un espace de noms PID de processus
       permet (depuis Linux 3.8) à ce processus de monter des systèmes de fichiers /proc.

       Remarquez cependant que le montage de systèmes  de  fichiers  basés  sur  les  blocs  peut  être  réalisé
       seulement par un processus ayant CAP_SYS_ADMIN dans l’espace de noms utilisateur initial.

   Liens entre les espaces de noms utilisateur et les autres espaces de noms
       À partir de Linux 3.8, les processus sans privilèges peuvent créer des espaces de noms utilisateur et les
       autres espaces de noms peuvent être créés avec simplement la capacité CAP_SYS_ADMIN dans l'espace de noms
       utilisateur de l'appelant.

       Lorsqu'un  espace  de  noms  autre  qu'utilisateur est créé, il appartient à l'espace de noms utilisateur
       auquel appartenait à ce moment là le processus à l'origine de la création de  cet  espace  de  noms.  Les
       opérations  privilégiées  sur des ressources régies par un espace de noms non utilisateur nécessitent que
       le processus aient les capacités requises dans l’espace de noms utilisateur qui possède l’espace de  noms
       non utilisateur.

       Si  CLONE_NEWUSER est indiqué en complément de l'attribut CLONE_NEW* lors d'un appel simple à clone(2) ou
       à unshare(2), l'espace de noms utilisateur est garanti d'être créé en premier. Cela donne des  privilèges
       à l’enfant (dans le cas de clone(2)) ou à l'appelant (dans le cas de unshare(2)) dans les espaces de noms
       subsistants  créés  par  l'appel.  Il  est ainsi possible à un appelant sans privilèges d'indiquer ce jeu
       d'attributs.

       Lorsqu'un nouvel espace de noms (autre qu’un espace de noms utilisateur) est créé à l’aide de clone(2) ou
       unshare(2), le noyau enregistre l'espace de noms utilisateur du processus créateur comme le  propriétaire
       du  nouvel  espace  de  noms. (Cette association ne peut pas être changée). Lorsqu'un processus du nouvel
       espace de noms effectue ensuite une opération privilégiée sur une ressource globale isolée  par  l'espace
       de  noms,  les  vérifications  de  permissions sont réalisées en fonction des capacités du processus dans
       l'espace de noms utilisateur que le noyau a associé au nouvel espace  de  noms.  Par  exemple,  supposons
       qu’un  processus  essaie  de modifier le nom d’hôte (sethostname(2)), une ressource régie par l’espace de
       noms UTS. Dans ce cas le noyau déterminera quel espace de noms utilisateur possède l’espace de  noms  UTS
       du  processus  et vérifiera si le processus à la capacité requise (CAP_SYS_ADMIN) dans cet espace de noms
       utilisateur.

       L’opération NS_GET_USERNS d’ioctl(2) peut être utilisée  pour  découvrir  l’espace  de  noms  utilisateur
       possédant l’espace de noms non utilisateur. Consultez ioctl_ns(2).

   Correspondance des identifiants d'utilisateur et de groupe : uid_map et gid_map
       Lorsqu'un  espace  de  noms  utilisateur  est  créé,  il  s'initialise  sans établir de mappage entre ses
       identifiants utilisateurs (identifiants de groupes) et ceux de l'espace  de  noms  parent.  Les  fichiers
       /proc/[pid]/uid_map   et  /proc/[pid]/gid_map  présentent  (à  partir  de  Linux 3.5)  le  mappage  entre
       identifiants utilisateur et groupe à l'intérieur de l'espace de noms utilisateur pour le  processus  pid.
       Ces  fichiers  peuvent  être  consultés  pour  prendre  connaissance  des mappages dans un espace de noms
       utilisateur et peuvent être modifiés (une seule fois) pour définir les mappages.

       Les paragraphes suivants décrivent uid_map en détails. gid_map est parfaitement analogue, chaque instance
       de « identifiant utilisateur » étant remplacée par « identifiant groupe ».

       Le fichier uid_map présente le mappage entre les identifiants utilisateur de l'espace de noms utilisateur
       du processus pid et ceux de l'espace de  noms  utilisateur  du  processus  qui  a  ouvert  uid_map  (mais
       consultez  la  réserve  concernant ce point exposée ci-dessous). En d'autres termes, des processus qui se
       trouvent dans différents espaces de noms verront des valeurs différentes lors de la lecture d'un  fichier
       uid_map  selon  les  mappages des identifiants utilisateur pour l'espace de noms utilisateur du processus
       qui effectue la lecture.

       Chaque ligne du fichier uid_map affiche un mappage un-pour-un d'un intervalle d'identifiants  utilisateur
       contigus  de deux espaces de noms utilisateur. Lorsqu'un espace de noms utilisateur vient d'être créé, ce
       fichier est vide. Chaque ligne contient trois nombres  délimités  par  des  espaces.  Les  deux  premiers
       nombres  indiquent les premiers identifiants utilisateur de chacun des deux espaces de noms. Le troisième
       nombre indique la longueur de l'intervalle de mappage. Plus précisément, les champs sont  interprétés  de
       la façon suivante :

       (1) Le  début  de  l'intervalle d'identifiants utilisateur dans l'espace de noms utilisateur du processus
           pid.

       (2) Le début de l'intervalle d'identifiants utilisateur auquel mappe  l'identifiant  utilisateur  indiqué
           dans  le  premier  champ.  Selon que le processus qui a ouvert le fichier uid_map et le processus pid
           sont ou non dans le même espace de noms, le  deuxième  champ  est  interprété  de  l'une  des  façons
           suivantes :

           a) Si  les deux processus sont dans différents espaces de noms utilisateur : le deuxième champ est le
              début de l'intervalle d'identifiants utilisateur dans l'espace de noms  utilisateur  du  processus
              qui a ouvert uid_map.

           b) Si les deux processus sont dans le même espace de noms utilisateur : le second champ correspond au
              début  de  la  séquence  d'identifiants  utilisateur  dans  l'espace de noms utilisateur parent du
              processus pid. Cela permet au processus qui a ouvert uid_map  (généralement,  le  processus  ouvre
              /proc/self/uid_map)  de  voir  le  mappage  des  identifiants  utilisateur  dans  l'espace de noms
              utilisateur du processus qui a créé cet espace de noms utilisateur.

       (3) La longueur de l'intervalle des identifiants utilisateur qui est mappé entre les deux espaces de noms
           utilisateur.

       Les appels système qui renvoient des identifiants utilisateur (des identifiant de  groupes)  — comme  par
       exemple, getuid(2), getgid(2), et les champs relatifs aux droits dans la structure renvoyée par stat(2) —
       affichent  la  valeur  de l'identifiant utilisateur (l'identifiant de groupe) mappé dans l'espace de noms
       utilisateur de l'appelant.

       Lorsqu'un processus accède à un fichier, ses identifiant utilisateur et groupe sont mappés dans  l’espace
       de noms utilisateur initial pour pouvoir vérifier les droits ou pour assigner des identifiants lors de la
       création  d'un  fichier.  Lorsqu'un processus obtient les identifiants utilisateur et groupe d'un fichier
       par la commande stat(2), les identifiants sont évalués dans le sens inverse, afin de renvoyer les valeurs
       relatives aux mappages des ID utilisateur et de groupe du processus.

       L'espace de noms utilisateur initial n'a pas d'espace de noms parent, mais pour conserver  la  cohérence,
       le  noyau  lui  attribue  des  fichiers de mappage d'identifiants utilisateur et groupe factices pour cet
       espace de noms. Si l'on consulte le fichier uid_map (ou gid_map de la même façon) depuis  une  invite  de
       commande dans l'espace de noms initial, on peut voir :

           $ cat /proc/$$/uid_map
                    0          0 4294967295

       Ce  mappage  nous indique que l'intervalle commençant avec l'identifiant utilisateur 0 dans cet espace de
       noms mappe avec un intervalle commençant à 0 dans l'espace de noms parent (qui n'existe pas), et  que  la
       longueur  de  cet intervalle est la valeur du plus grand entier 32 bits non signé. Cela laisse 4294967295
       (la valeur 32 bits signé moins 1) non mappé. Cela est  voulu :  (uid_t) -1  est  utilisé  dans  plusieurs
       interfaces (par exemple, setreuid(2)) comme façon d’indiquer « pas d’ID utilisateur ». Laisser (uid_t) -1
       non  mappé  et  inutilisable  garantit  qu’il  n’y  aura  aucune  confusion  lors de l’utilisation de ces
       interfaces.

   Création des mappages d'ID utilisateur et groupe : écriture dans uid_map et gid_map
       Après la création d'un nouvel espace de noms utilisateur, le fichier uid_map de  l'un  des  processus  de
       l'espace de noms peut être ouvert en écriture une seule fois pour y consigner le mappage des identifiants
       utilisateur  dans  le nouvel espace de noms utilisateur. Toute tentative d'écrire plus d'une fois dans un
       fichier uid_map se solde par un échec qui renvoie l'erreur EPERM. Des règles analogues  s'appliquent  aux
       fichiers gid_map.

       Les lignes inscrites dans uid_map (gid_map) doivent suivre les règles suivantes :

       –  Les trois champs doivent être des nombres valables et le dernier champ doit être strictement positif.

       –  Les lignes doivent se terminer par un saut de ligne.

       –  Il  y  a  une limite (arbitraire) du nombre de lignes que peut contenir le fichier. Dans Linux 4.14 et
          précédents, la  limite  est  (arbitrairement)  de  5 lignes.  Depuis  Linux 4.15,  la  limite  est  de
          340 lignes.  En  outre,  le  nombre  d'octets inscrits dans le fichier doit être inférieur à la taille
          d'une page du système, et l'écriture doit être réalisée au début du fichier (c’est-à-dire lseek(2)  et
          pwrite(2) ne peuvent être utilisées pour écrire dans le fichier avec un décalage non nul).

       –  L'intervalle d'identifiants utilisateur (ou de groupe) indiqué dans chaque ligne ne peut recouvrir les
          intervalles  des  autres lignes. Dans l'implémentation initiale (Linux 3.8), cette règle était assurée
          par une implémentation plus sommaire qui comprenait une contrainte supplémentaire : les deux  premiers
          champs de chaque ligne devaient apparaître en ordre croissant. Cela empêchait cependant la création de
          mappages  valables.  Ce  problème  a  été réglé dans Linux 3.9 et suivants, et toutes les combinaisons
          valables de mappages non recouvrantes sont désormais acceptées.

       –  Au moins une ligne doit être inscrite dans le fichier.

       Les opérations d'écritures qui ne respectent pas les règles énoncées précédemment échouent  en  renvoyant
       l'erreur EINVAL.

       Un  processus  ne peut écrire dans le fichier /proc/[pid]/uid_map (/proc/[pid]/gid_map) qu'à la condition
       de respecter les contraintes suivantes :

       1. Le processus réalisant l'écriture doit disposer de la capacité CAP_SETUID (CAP_SETGID)  dans  l'espace
          de noms utilisateur du processus pid.

       2. Le  processus réalisant l'écriture doit se trouver soit dans l'espace de noms utilisateur du processus
          pid, soit dans l'espace de noms utilisateur parent du processus pid.

       3. Les identifiants utilisateur (ou groupe) mappés doivent, en retour, avoir un mappage dans l'espace  de
          noms utilisateur parent.

       4. L'un des deux points suivants est vérifié :

          –  soit  le  processus réalisant l'écriture doit disposer de la capacité CAP_SETUID ( CAP_SETGID) dans
             l'espace de noms utilisateur parent.

             *  Aucune autre restriction, le processus  peut  établir  des  mappages  vers  les  ID  utilisateur
                (groupe) dans l’espace de noms parent.

          –  Ou sinon toutes les restrictions suivantes s’appliquent :

             *  Les  données  inscrites  dans  uid_map  (gid_map) doivent consister en une seule ligne qui mappe
                l'identifiant utilisateur  effectif  (groupe)  du  processus  écrivant  dans  l’espace  de  noms
                utilisateur parent à un ID utilisateur (groupe) dans l’espace de noms utilisateur.

             *  Le  processus  réalisant  l'écriture doit avoir le même ID utilisateur effectif que le processus
                ayant créé l’espace de noms utilisateur.

             *  Dans le cas de gid_map, l’utilisation de l’appel système setgroups(2) doit être d’abord interdit
                en écrivant « deny » dans le fichier /proc/[pid]/setgroups (voir ci-dessous) avant d’écrire dans
                gid_map.

       Les écritures violant ces règles échouent avec l’erreur EPERM.

   Interaction avec les appels système qui modifient les UID ou les GID
       Dans un espace de noms utilisateur où aucun fichier  uid_map  n’a  été  écrit,  les  appels  système  qui
       modifient  l’ID  utilisateur échoueront. De la même manière, si le fichier gid_map n’a pas été écrit, les
       appels système modifiant les ID de groupe échoueront. Après que les fichiers uid_map et gid_map aient été
       écrits, seules les valeurs mappées peuvent être utilisées  dans  les  appels  système  modifiant  les  ID
       utilisateur et groupe.

       Pour  les  ID  utilisateur,  les appels système concernés incluent setuid(2), setfsuid(2), setreuid(2) et
       setresuid(2). Pour les ID de groupe,  les  appels  système  concernés  incluent  setgid(2),  setfsgid(2),
       setregid(2), setresgid(2) et setgroups(2).

       Écrire « deny » dans le fichier /proc/[pid]/setgroups avant d’écrire dans /proc/[pid]/gid_map désactivera
       de  manière  permanente  setgroups(2)  dans  un  espace  de  noms  utilisateur et permettra d’écrire dans
       /proc/[pid]/gid_map sans avoir la capacité CAP_SETGID dans l’espace de noms utilisateur parent.

   Le fichier /proc/[pid]/setgroups
       Le fichier /proc/[pid]/setgroups affichera la chaîne « allow » si les processus  dans  l’espace  de  noms
       utilisateur  qui  contient  le  processus  pid sont autorisés à employer l’appel système setgroups(2). Il
       affichera « deny » si setgroups(2) n’est pas autorisé dans cet espace de noms utilisateur. Remarquez  que
       quelque  soit  la  valeur  dans  le  fichier  /proc/[pid]/setgroups  (et  quelque soient les capacités du
       processus), les appels à setgroups(2) ne sont pas aussi permis si /proc/[pid]/gid_map n’a pas encore  été
       défini.

       Un  processus  privilégié  (un  avec la capacité CAP_SYS_ADMIN dans l’espace de noms) peut écrire une des
       chaînes « allow » ou « deny » dans ce fichier avant d’écrire un mappage d’ID de groupe pour cet espace de
       noms utilisateur dans le fichier /proc/[pid]/gid_map. Écrire la chaîne « deny »  empêche  tout  processus
       dans l’espace de noms utilisateur d’employer setgroups(2).

       L’idée  de  ces  restrictions  décrites  dans  le paragraphe précédent est qu’il est permis d’écrire dans
       /proc/[pid]/setgroups  seulement  à  condition  que  l’appel  à  setgroups(2)  est  désactivé  parce  que
       /proc/[pid]/gid_map  n’a  pas  été défini. Cela garantit qu’un processus ne peut transiter d’un état dans
       lequel setgroups(2) est autorisé vers un état dans lequel setgroups(2) est interdit.  Un  processus  peut
       transiter seulement de setgroups(2) interdit vers setgroups(2) autorisé.

       La valeur par défaut dans ce fichier dans l’espace de noms utilisateur initial est « allow ».

       Une  fois que /proc/[pid]/gid_map ait été écrit (ce qui a pour effet d’activer setgroups(2) dans l’espace
       de noms utilisateur), il n’est plus  possible  de  désactiver  setgroups(2)  en  écrivant  « deny »  dans
       /proc/[pid]/setgroups (l’écriture échoue avec l’erreur EPERM).

       Un espace de noms utilisateur enfant hérite du réglage /proc/[pid]/setgroups de son parent.

       Si le fichier setgroups a la valeur « deny », alors l’appel système setgroups(2) ne peut pas par la suite
       être  réactivé  (en  écrivant  « allow »  dans  le  fichier)  dans  cet espace de noms utilisateur (toute
       tentative échouera avec  l’erreur  EPERM).  Cette  restriction  se  propage  vers  les  espaces  de  noms
       utilisateur enfant de cet espace de noms utilisateur.

       Le  fichier  /proc/[pid]/setgroups  a  été  ajouté  dans Linux 3.19, mais a été rétroporté vers plusieurs
       séries stables du noyau car il corrige un problème de sécurité. Cela concernait  les  fichiers  avec  les
       permissions  telles  que  « rwx---rwx ».  De  tels  fichiers  accordent moins de permissions au « group »
       qu’elles ne donnent à « other ». Cela signifie qu’abandonner  les  groupes  utilisant  setgroups(2)  peut
       permettre  un  accès  au  fichier du processus que celui-ci n’avait pas auparavant. Avant l’existence des
       espaces de noms utilisateur cela n’était pas un problème, puisque seul un processus privilégié  (un  avec
       la  capacité CAP_SETGID) pouvait appeler setgroups(2). Cependant, avec l’introduction des espaces de noms
       utilisateur, il est devenu possible pour un processus non privilégié de créer un nouvel  espace  de  noms
       dans  lequel  l’utilisateur  a tous les privilèges. Cela permet alors à des utilisateurs anciennement non
       privilégiés d’abandonner les groupes et donc obtenir l’accès à des fichiers auxquels ils ne pouvaient pas
       accéder. Le fichier /proc/[pid]/setgroups a été ajouté pour régler le problème de sécurité en refusant  à
       tout chemin pour un processus non privilégié d’abandonner les groupes avec setgroups(2).

   ID utilisateur et groupe non mappés
       Il existe différentes situations dans lesquelles un identifiant utilisateur (ou de groupe) non mappé peut
       être  exposé  dans un espace de noms utilisateur. Par exemple, le premier processus d'un nouvel espace de
       noms utilisateur peut appeler getuid() avant que le mappage des identifiants utilisateur ait  été  défini
       pour l'espace de noms. Dans la plupart de ces cas, l'identifiant utilisateur non mappé est converti en un
       identifiant  utilisateur  (groupe)  au-delà de la limite de débordement ; la valeur par défaut au delà de
       cette limite pour un identifiant utilisateur  (ou  groupe)  est  65534.  Consultez  les  descriptions  de
       /proc/sys/kernel/overflowuid et de /proc/sys/kernel/overflowgid dans proc(5).

       Les  situations  dans  lesquelles des identifiants non mappés sont transformés de cette façon comprennent
       les cas des appels système qui renvoient des identifiants utilisateur (getuid(2), getgid(2) et les appels
       similaires), les accréditations passées  à  l’aide  d’un  socket  de  domaine  UNIX,  les  accréditations
       renvoyées  par  stat(2),  waitid(2)  et  les  autres  opérations  IPC  « ctl »  IPC_STAT de System V, les
       accréditations présentées par /proc/[pid]/status et  les  fichiers  /proc/sysvipc/*,  les  accréditations
       renvoyées  par  le  champ  si_uid  de  siginfo_t  reçues  avec  un  signal  (consultez sigaction(2)), les
       accréditations écrites dans le fichier du processus de tenue  des  comptes  (consultez  acct(5))  et  les
       accréditations renvoyées avec des notifications de files de messages POSIX (consultez mq_notify(3)).

       Il  est un cas notable où des identifiants d'utilisateur et de groupe non mappés ne sont pas convertis en
       des valeurs d’ID correspondantes au-delà de la limite. Lors de la consultation d'un  fichier  uid_map  ou
       gid_map  dans lequel il n'y a pas de mappage pour le second champ, ce champ apparaît comme 4294967295 (-1
       représenté comme un entier non signé).

   Accession aux fichiers
       Dans le but de déterminer les permissions quand un processus non privilégié  accède  à  un  fichier,  les
       accréditations  du  processus (UID, GID) et les accréditations du fichier sont en réalité mappées vers ce
       qu’elles seraient dans l’espace de noms utilisateur  initial  et  alors  comparées  pour  déterminer  les
       permissions que le processus possède sur le fichier. La même chose est valable pour les autres objets qui
       emploient  les  accréditations  plus le modèle d’accessibilité avec le masque de permission, tels que les
       objets IPC de System V.

   Opérations sur les capacités relatives aux fichiers
       Certaines capacités permettent à un processus de contourner diverses restrictions imposées par  le  noyau
       lors  d’opérations  sur  des  fichiers  possédés par d’autres utilisateurs ou groupes. Ce sont CAP_CHOWN,
       CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER et CAP_FSETID.

       Dans un espace de noms utilisateur, ces capacités permettent à un processus de contourner les  règles  si
       le processus possède la capacité adéquate sur le fichier, signifiant que :

       –  le processus a la capacité effective adéquate dans son espace de noms utilisateur;

       –  les  ID utilisateur et groupe du fichier ont tous les deux des mappages valables dans l’espace de noms
          utilisateur.

       La capacité CAP_FOWNER est traitée de manière quelque peu exceptionnelle. Elle permet à un  processus  de
       contourner  les  règles  correspondantes  à  condition qu’au moins l’ID utilisateur du fichier possède un
       mappage dans l’espace de noms utilisateur (c’est-à-dire que l’ID de groupe  du  fichier  n’a  nul  besoin
       d’avoir un mappage valable).

   Programmes set-user-ID et set-group-ID
       Lorsqu'un  processus  appartenant  à  un  espace de noms exécute un programme set-user-ID (set-group-ID),
       l'identifiant utilisateur (groupe) effectif du processus dans l'espace de noms  est  changé  à  n’importe
       quelle  valeur  mappée  pour  l’identifiant  utilisateur (groupe) du fichier. Cependant, si l'identifiant
       utilisateur ou groupe n'a pas de mappage dans l'espace de noms, le  bit  set-user-ID  (set-group-ID)  est
       ignoré  silencieusement :  le  nouveau  programme  est  exécuté,  mais l'identifiant utilisateur (groupe)
       effectif n’est pas modifié. Cela reproduit  la  sémantique  d'exécution  d'un  programme  set-user-ID  ou
       set-group-ID  qui  se trouve dans un système de fichiers monté avec l'indicateur MS_NOSUID, comme indiqué
       dans mount(2).

   Divers
       Lorsque les identifiants utilisateur et groupe d'un processus sont  transmis  à  l’aide  d’un  socket  de
       domaine  UNIX  à un processus d'un autre espace de noms (consultez la description de SCM_CREDENTIALS dans
       unix(7)), ils sont transformés en leur  valeur  correspondante  suivant  les  mappages  des  identifiants
       utilisateur et groupe du processus réceptionnaire.

CONFORMITÉ

       Les espaces de noms sont propres à Linux.

NOTES

       Au  fil  des  ans,  de  nombreuses  fonctionnalités  ont  été  ajoutées au noyau Linux mais réservées aux
       utilisateurs disposant de  privilèges  du  fait  de  la  confusion  qu'elles  peuvent  induire  dans  les
       applications  set-user-ID-root.  En  général, il n'est pas dangereux d'autoriser un superutilisateur d'un
       espace de noms à utiliser ces fonctionnalités  parce  qu'il  est  impossible,  dans  un  espace  de  noms
       utilisateur,  d'obtenir  plus  de  droits que ce que peut obtenir le superutilisateur d’un espace de noms
       utilisateur.

   Disponibilité
       Le noyau doit avoir été configuré avec l'option CONFIG_USER_NS pour permettre l'utilisation  des  espaces
       de  noms  utilisateur. Ces espaces doivent également être pris en charge par un ensemble de sous-systèmes
       du noyau. Si un sous-système non pris en charge est activé dans  le  noyau,  il  n'est  pas  possible  de
       configurer la prise en charge des espaces de noms.

       Depuis  Linux 3.8,  la  plupart  des  principaux  sous-systèmes  prennent  en  charge les espaces de noms
       utilisateur, mais certains systèmes de fichiers n'ont pas l'infrastructure  nécessaire  pour  mapper  les
       identifiants   utilisateur  et  groupe  entre  les  espaces  de  noms  utilisateur.  Linux 3.9  a  fourni
       l'infrastructure nécessaire à la prise en charge de nombreux systèmes de fichiers restants (Plan 9  (9P),
       Andrew  File  System  (AFS),  Ceph, CIFS, CODA, NFS et OCFS2). Linux 3.12 a apporté la prise en charge du
       dernier des principaux systèmes de fichiers non encore géré, XFS.

EXEMPLES

       Le programme suivant est conçu pour permettre de s'exercer avec les espaces de  noms  utilisateur,  comme
       avec  d'autres espaces de noms. Il crée des espaces de noms tels que définis dans les options de la ligne
       de commande et exécute une commande dans ces espaces de noms. Les commentaires  et  la  fonction  usage()
       dans  le programme fournissent une explication détaillée du programme. La session shell suivante illustre
       son utilisation.

       Tout d'abord, regardons l'environnement d'exécution :

           $ uname -rs     # à partir de Linux 3.8
           Linux 3.8.0
           $ id -u         # exécuté comme utilisateur sans privilèges
           1000
           $ id -g
           1000

       Démarrons maintenant un nouveau shell dans les nouveaux espaces de noms utilisateur (-U), de montage (-m)
       et de PID (-p), avec l'identifiant utilisateur (-M) et groupe (-G) 1000 mappés à 0 dans l'espace de  noms
       utilisateur :

           $ ./userns_child_exec -p -m -U -M '0 1000 1' -G '0 1000 1' bash

       Le shell a le PID 1 puisqu'il est le premier processus de l'espace de noms :

           bash$ echo $$
           1

       Lorsque  l'on  monte un nouveau système de fichiers /proc et que l'on affiche tous les processus visibles
       dans le nouvel espace de noms PID, on constate que le shell peut voir tous les processus qui se  trouvent
       à l'extérieur de l'espace de noms PID :

           bash$ mount -t proc proc /proc
           bash$ ps ax
             PID TTY      STAT   TIME COMMAND
               1 pts/3    S      0:00 bash
              22 pts/3    R+     0:00 ps ax

       Dans  l'espace  de  noms  utilisateur,  le  shell a les identifiants utilisateur et groupe 0, ainsi qu'un
       ensemble complet de capacités autorisées et effectives :

           bash$ cat /proc/$$/status | egrep '^[UG]id'
           Uid: 0    0    0    0
           Gid: 0    0    0    0
           bash$ cat /proc/$$/status | egrep '^Cap(Prm|Inh|Eff)'
           CapInh:   0000000000000000
           CapPrm:   0000001fffffffff
           CapEff:   0000001fffffffff

   Source du programme

       /* userns_child_exec.c

          Sous licence publique générale GNU, versions 2 ou postérieures

       Créer un processus enfant qui exécute une commande de shell dans
       un nouvel espace de noms. Il permet de préciser les mappages
       d'identifiants utilisateur et groupe lors de la création d’un
       nouvel espace de noms utilisateur.
       #define _GNU_SOURCE
       #include <sched.h>
       #include <unistd.h>
       #include <stdint.h>
       #include <stdlib.h>
       #include <sys/wait.h>
       #include <signal.h>
       #include <fcntl.h>
       #include <stdio.h>
       #include <string.h>
       #include <limits.h>
       #include <errno.h>

       /* Une fonction de gestion des erreurs simple : afficher
          un message d'erreur dépendant de la valeur de 'errno' et
          terminer le processus appelant. */

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       struct child_args {
           char **argv;        /* Commande à exécuter par l’enfant, avec arguments */
           int    pipe_fd[2];  /* Tube utilisé pour synchroniser le parent et l’enfant */
       };

       static int verbose;

       static void
       usage(char *pname)
       {
           fprintf(stderr, "Utilisation: %s [options] cmd [arg...]\n\n", pname);
           fprintf(stderr, "Créer un processus enfant qui exécute une invite "
                   "de commandes dans un nouvel espace de noms utilisateur et\n"
                   "éventuellement au moins un nouvel espace de noms.\n\n");
           fprintf(stderr, "Les options sont :\n\n");
       #define fpe(str) fprintf(stderr, "    %s", str);
           fpe("-i          Nouvel espace de noms IPC\n");
           fpe("-m          Nouvel espace de noms de montage\n");
           fpe("-n          Nouvel espace de noms réseau \n");
           fpe("-p          Nouvel espace de noms PID\n");
           fpe("-u          Nouvel espace de noms UTS\n");
           fpe("-U          Nouvel espace de noms utilisateur\n");
           fpe("-M uid_map  Mappage UID pour l'espace de noms utilisateur\n");
           fpe("-G gid_map  Mappage GID pour l'espace de noms utilisateur\n");
           fpe("-z          Mappage des UID et GID à 0 dans l'espace de noms
                            utilisateur\n");
           fpe("            (équivalent à: -M '0 <uid> 1' -G '0 <gid> 1')\n");
           fpe("-v          Affichage détaillé\n");
           fpe("\n");
           fpe("Si -z, -M, or -G est invoqué, -U doit être précisé.\n");
           fpe("Il n'est pas possible d'utiliser -z et soit -M, soit -G.\n");
           fpe("\n");
           fpe("Les chaînes de mappages pour -M et -G se composent"
               "d'enregistrements de la forme :\n");
           fpe("\n");
           fpe("    ID-inside-ns   ID-outside-ns   len\n");
           fpe("\n");
           fpe("Une chaîne de mappage peut contenir plusieurs"
               "enregistrements séparés par des virgules;\n");
           fpe("les virgules sont remplacées par des retours à la ligne"
               "avant l'écriture des fichiers de mappage.\n");

           exit(EXIT_FAILURE);
       }

       /* Mise à jour du fichier de mappage 'map_file', avec la valeur fournie
          dans 'mapping', une chaîne qui définit un mappage d'identifiant
          utilisateur ou groupe. Un mappage d'identifiant d'utilisateur ou groupe
          se compose d'un ou plusieurs enregistrements séparés par des retours
          à la ligne de la forme suivante :

              ID_dans-Espace    ID-hors-Espace   longueur

         La nécessité de fournir une chaîne qui contienne des retours
         à la ligne ne convient pas bien à une utilisation en ligne de commande.
         C'est pour cette raison que l'utilisation des virgules pour délimiter les
         champs de la chaîne est autorisée. Celles-ci sont remplacées par des
         retours à la ligne avant l'écriture de la chaîne dans le fichier. */

       static void
       update_map(char *mapping, char *map_file)
       {
           int fd;
           size_t map_len;     /* Longueur de 'mapping' */

           /* Remplacer les virgules de la chaîne de mappage
              avec des retours à la ligne */

           map_len = strlen(mapping);
           for (int j = 0; j < map_len; j++)
               if (mapping[j] == ',')
                   mapping[j] = '\n';

           fd = open(map_file, O_RDWR);
           if (fd == -1) {
               fprintf(stderr, "ERROR: open %s: %s\n", map_file,
                       strerror(errno));
               exit(EXIT_FAILURE);
           }

           if (write(fd, mapping, map_len) != map_len) {
               fprintf(stderr, "ERROR: write %s: %s\n", map_file,
                       strerror(errno));
               exit(EXIT_FAILURE);
           }

           close(fd);
       }

       /* Linux 3.19 a modifié la gestion de setgroups(2) et le fichier
          'gid_map' pour corriger le problème de sécurité. Celui-ci
          permet aux utilisateurs *non privilégiés* d’employer des espaces de
          noms utilisateur pour aboutir. Le résultat des modifications de 3.19
          est que dans le but de mettre à jour le fichier 'gid_maps',
          l’utilisation de l’appel système setgroups() dans cet espace de noms
          utilisateur doit d’abord être désactivée en écrivant « deny » dans
          un des fichiers /proc/PID/setgroups pour cet espace de noms. C’est
          le but de la fonction suivante. */

       static void
       proc_setgroups_write(pid_t child_pid, char *str)
       {
           char setgroups_path[PATH_MAX];
           int fd;

           snprintf(setgroups_path, PATH_MAX, "/proc/%jd/setgroups",
                   (intmax_t) child_pid);

           fd = open(setgroups_path, O_RDWR);
           if (fd == -1) {

               /* Nous sommes peut être sur un système qui ne gère pas
                  /proc/PID/setgroups. Dans ce cas, le fichier n’existe pas
                  et le système n’impose pas les restrictions que Linux 3.19
                  a ajoutées. Bien, nous n’avons pas besoin de faire quelque
                  chose pour permettre la mise à jour de 'gid_map'.

                  Cependant, si l’erreur d’open() était quelque chose autre que
                  l’erreur ENOENT attendue dans ce cas, faisons que l’utilisateur
                  le sache. */

               if (errno != ENOENT)
                   fprintf(stderr, "ERROR: open %s: %s\n", setgroups_path,
                       strerror(errno));
               return;
           }

           if (write(fd, str, strlen(str)) == -1)
               fprintf(stderr, "ERROR: write %s: %s\n", setgroups_path,
                   strerror(errno));

           close(fd);
       }

       static int              /* Lancer la fonction pour l’enfant cloné */
       childFunc(void *arg)
       {
           struct child_args *args = arg;
           char ch;

           /* Attendre que le parent ait mis à jour les mappages d'identifiants
              d'utilisateur et de groupe. Consultez le commentaire de main(). On
              attend le signal de fin de fichier dans le tube qui sera fermé par le
              processus parent lorsque les mappages seront mis à jour. */

          close(args->pipe_fd[1]);    /* Fermer notre descripteur à la fin
                                          d’écriture du tube afin de présenter EOF
                                          lorsque le parent ferme son descripteur */
           if (read(args->pipe_fd[0], &ch, 1) != 0) {
               fprintf(stderr,
                       "Échec du fils : donnée renvoyée par le tube != 0\n");
               exit(EXIT_FAILURE);
           }

           close(args->pipe_fd[0]);

           /* Lancer une commande de shell */

           printf("About to exec %s\n", args->argv[0]);
           execvp(args->argv[0], args->argv);
           errExit("execvp");
       }

       #define STACK_SIZE (1024 * 1024)

       static char child_stack[STACK_SIZE];    /* Espace pour la pile de l’enfant */

       int
       main(int argc, char *argv[])
       {
           int flags, opt, map_zero;
           pid_t child_pid;
           struct child_args args;
           char *uid_map, *gid_map;
           const int MAP_BUF_SIZE = 100;
           char map_buf[MAP_BUF_SIZE];
           char map_path[PATH_MAX];

           /* Analyser les options de la ligne de commande. Le caractère
              '+' initial de l'argument final de getopt() empêche la
              permutation des options de la ligne de commande de style
              GNU. Cela peut être utile dans les cas où la 'commande'
              exécutée par le programme lui-même a des options de ligne de
              commande. Cela évite que getopt() ne traite ces options comme
              étant celles du programme */

           flags = 0;
           verbose = 0;
           gid_map = NULL;
           uid_map = NULL;
           map_zero = 0;
           while ((opt = getopt(argc, argv, "+imnpuUM:G:zv")) != -1) {
               switch (opt) {
               case 'i': flags |= CLONE_NEWIPC;        break;
               case 'm': flags |= CLONE_NEWNS;         break;
               case 'n': flags |= CLONE_NEWNET;        break;
               case 'p': flags |= CLONE_NEWPID;        break;
               case 'u': flags |= CLONE_NEWUTS;        break;
               case 'v': verbose = 1;                  break;
               case 'z': map_zero = 1;                 break;
               case 'M': uid_map = optarg;             break;
               case 'G': gid_map = optarg;             break;
               case 'U': flags |= CLONE_NEWUSER;       break;
               default:  usage(argv[0]);
               }
           }

           /* -M ou -G sans -U est incohérent */

           if (((uid_map != NULL || gid_map != NULL || map_zero) &&
                       !(flags & CLONE_NEWUSER)) ||
                   (map_zero && (uid_map != NULL || gid_map != NULL)))
               usage(argv[0]);

           args.argv = &argv[optind];

           /* L'utilisation d'un tube pour réaliser la synchronisation du parent et
              de l’enfant a pour but d'obliger le parent à définir les mappages
              d'identifiants utilisateur et groupe avant que l’enfant n'appelle
              execve(). Cela permet d'assurer que l’enfant conserve ses capacités
              pendant l'exécution de execve() dans le cas classique où l'on souhaite
              mapper l’identifiant utilisateur effectif de l’enfant avec 0 dans le
              nouvel espace de noms utilisateur. Sans cette synchronisation, l’enfant
              perdrait ses capacités s'il effectuait execve() avec un identifiant
              utilisateur autre que 0 (consultez la page du manuel consacrée
              à capabilities(7) pour plus de détails sur la modification des capacités
              d'un processus lors de l'exécution de execve()). */

           if (pipe(args.pipe_fd) == -1)
               errExit("pipe");

           /* Création de l’enfant dans le ou les nouveaux espaces de noms */

           child_pid = clone(childFunc, child_stack + STACK_SIZE,
                             flags | SIGCHLD, &args);
           if (child_pid == -1)
               errExit("clone");

           /* Le parent se retrouve ici */

           if (verbose)
               printf("%s: le PID de l’enfant créé par clone() est %jd\n",
                       argv[0], (intmax_t) child_pid);

           /* Mise à jour des mappages d’UID et de PID pour l’enfant */

           if (uid_map != NULL || map_zero) {
               snprintf(map_path, PATH_MAX, "/proc/%jd/uid_map",
                       (intmax_t) child_pid);
               if (map_zero) {
                   snprintf(map_buf, MAP_BUF_SIZE, "0 %jd 1",
                           (intmax_t) getuid());
                   uid_map = map_buf;
               }
               update_map(uid_map, map_path);
           }

           if (gid_map != NULL || map_zero) {
               proc_setgroups_write(child_pid, "deny");

               snprintf(map_path, PATH_MAX, "/proc/%jd/gid_map",
                       (intmax_t) child_pid);
               if (map_zero) {
                   snprintf(map_buf, MAP_BUF_SIZE, "0 %ld 1",
                           (intmax_t) getgid());
                   gid_map = map_buf;
               }
               update_map(gid_map, map_path);
           }

           /* Fermer le côté écriture du tube afin d'indiquer à l’enfant que
              les mappages d'UID et de GID ont été mis à jour */

           close(args.pipe_fd[1]);

           if (waitpid(child_pid, NULL, 0) == -1)      /* Attente de l’enfant */
               errExit("waitpid");

           if (verbose)
               printf("%s: fin d'exécution\n", argv[0]);

           exit(EXIT_SUCCESS);
       }

VOIR AUSSI

       newgidmap(1), newuidmap(1), clone(2), ptrace(2), setns(2),  unshare(2),  proc(5),  subgid(5),  subuid(5),
       capabilities(7), cgroup_namespaces(7), credentials(7), namespaces(7), pid_namespaces(7)

       Le fichier Documentation/namespaces/resource-control.txt des sources du noyau.

COLOPHON

       Cette page fait partie de la publication 5.10 du projet man-pages Linux. Une description du projet et des
       instructions  pour  signaler  des  anomalies et la dernière version de cette page peuvent être trouvées à
       l'adresse https://www.kernel.org/doc/man-pages/.

TRADUCTION

       La  traduction  française   de   cette   page   de   manuel   a   été   créée   par   Christophe   Blaess
       <https://www.blaess.fr/christophe/>,   Stéphan   Rafin   <stephan.rafin@laposte.net>,   Thierry   Vignaud
       <tvignaud@mandriva.com>, François Micaux, Alain Portal  <aportal@univ-montp2.fr>,  Jean-Philippe  Guérard
       <fevrier@tigreraye.org>,   Jean-Luc   Coulon   (f5ibh)   <jean-luc.coulon@wanadoo.fr>,   Julien   Cristau
       <jcristau@debian.org>,     Thomas     Huriaux      <thomas.huriaux@gmail.com>,      Nicolas      François
       <nicolas.francois@centraliens.net>,     Florentin     Duneau    <fduneau@gmail.com>,    Simon    Paillard
       <simon.paillard@resel.enst-bretagne.fr>,    Denis    Barbier    <barbier@debian.org>,    David     Prévot
       <david@tilapin.org>,     Cédric     Boutillier     <cedric.boutillier@gmail.com>,    Frédéric    Hantrais
       <fhantrais@gmail.com> et Jean-Paul Guillonneau <guillonneau.jeanpaul@free.fr>

       Cette traduction est une documentation libre ; veuillez vous  reporter  à  la  GNU General Public License
       version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.

       Si  vous  découvrez  un  bogue  dans la traduction de cette page de manuel, veuillez envoyer un message à
       debian-l10n-french@lists.debian.org.

Linux                                            1 novembre 2020                              USER_NAMESPACES(7)