85.1.55. pepsi-helper-maildir-writer

drop a message into a local user’s Maildir/new/ as root

Section du manuel:

1

85.1.55.1.1. Nom

pepsi-helper-maildir-writer - helper de remise locale privilégié qui écrit un message unique depuis l’entrée standard dans le Maildir/new/ d’un utilisateur cible.

85.1.55.1.2. Synopsis

pepsi-helper-maildir-writer [OPTIONS] TARGET-UID < message

pepsi-helper-maildir-writer measure [OPTIONS] TARGET-UID

85.1.55.1.3. Description

pepsi-helper-maildir-writer est un helper minimal et durci en sécurité qui remet exactement un message dans le Maildir d’un utilisateur local. Il existe afin qu’un appelant de confiance mais non privilégié (un membre du groupe pepsi-maildir) puisse déposer du courrier dans la boîte aux lettres de n’importe quel utilisateur local — et ne rien faire d’autre.

Le helper est installé setuid-root, possédé root:pepsi-maildir avec le mode 4750 (rwxr-x--- plus le bit setuid). Par conséquent, seuls les membres du groupe pepsi-maildir peuvent l’exécuter ; lorsqu’ils le font, le noyau l’exécute avec un uid effectif de root.

Recevant l’uid numérique de l’utilisateur cible, et le message brut sur l’entrée standard, le helper :

  1. résout l’entrée passwd de l’utilisateur cible (son répertoire personnel et son groupe primaire), refusant l’uid 0 et tout uid inférieur à /etc/login.defs UID_MIN (par défaut 1000 s’il est illisible) — les comptes système ne sont jamais des cibles de remise valides ;

  2. abandonne complètement et irréversiblement ses privilèges de root vers l’utilisateur cible — uid et gid réels, effectifs et sauvegardés — en abandonnant d’abord tous les groupes supplémentaires, puis le gid, puis l’uid, et vérifie que root ne peut être regagné. Tout ce qui suit agit avec l’identité de l’utilisateur cible, de sorte que tout fichier qu’il crée est possédé par cet utilisateur ;

  3. crée la disposition Maildir standard (tmp, new et cur, mode 0700) sous ~/Maildir si elle n’existe pas déjà ;

  4. crée un fichier au nom unique dans Maildir/tmp/ avec O_CREAT|O_EXCL (de sorte qu’il a un contrôle exclusif sur le fichier et ne perturbe jamais le fichier d’une autre remise), mode 0600, suivant la convention de nommage Maildir <seconds>.M<microseconds>P<pid>Q<sequence>.<hostname> ;

  5. copie le message de l’entrée standard dans ce fichier, en utilisant splice(2) lorsque les descripteurs le permettent (le cas courant, lorsque stdin est un tube) et une simple boucle lecture/écriture sinon, en comptant les octets au passage ;

  6. fait un fsync(2) sur le fichier et — lorsqu’un quota est appliqué et que --measure a été donné — décide si la boîte aux lettres a de la place pour lui, abandonnant la remise avec le code de sortie 3 si ce n’est pas le cas ; et

  7. le renomme dans Maildir/new/ sous un nom portant l’étiquette Maildir++ ,S=taille, fait un fsync(2) sur ce répertoire, et rapporte sur la sortie standard ce que la boîte aux lettres contient désormais.

Le helper ne lit aucune configuration, n’ouvre aucune connexion réseau et ne contacte aucune base de données. La politique de quota est décidée par l’appelant et passée en arguments, parce qu’au moment où le helper pourrait lire quoi que ce soit il est l’utilisateur cible, qui n’a pas à choisir son propre quota.

85.1.55.1.4. Quota de boîte aux lettres

Le helper est la seule partie de Pepsi capable de voir à quel point une boîte aux lettres est pleine : un Maildir est en mode 0700, et c’est l’unique processus qui devient l’utilisateur. Tout le reste — les décisions de l’étape de remise, le refus de pepsi-ingress(1) au RCPT, le rapport de pepsi-status(1) — travaille à partir de chiffres que ce helper a rapportés.

Il ne mesure que lorsqu’on le lui demande (--measure). L’appelant tient une estimation courante dans pepsi.mailbox_quota et réclame une mesure lorsque cette estimation suggère que le compte pourrait être proche de sa limite ; comme l’estimation ne peut que surcompter (rien ne lui indique qu’un utilisateur supprime du courrier par IMAP), « pas besoin de mesure » est une preuve qu’il reste de la place. Une remise ordinaire dans une boîte aux lettres loin d’être pleine coûte donc un petit ajout et aucun parcours de répertoire.

Trois couches de comptabilité sont employées ensemble, la moins chère d’abord :

maildirsize

Le fichier de taille Maildir++ à la racine de la boîte aux lettres, au format de Courier (une ligne de définition de quota, puis des deltas <bytes> <messages> ajoutés). Le backend quota = maildir: de Dovecot, Courier et le maildir_use_size_file d’Exim lisent et écrivent tous le même fichier, de sorte qu’un site faisant tourner l’un d’eux pour IMAP et Pepsi pour la remise tient un seul jeu de chiffres. Pour cette raison, un maildirsize existant est tenu à jour par chaque remise, même lorsque Pepsi lui-même n’applique aucun quota — sinon le compteur de l’autre agent sous-estimerait silencieusement tout ce que Pepsi remet. Sans quota en vigueur, il n’en est jamais créé là où il n’en existe pas. Il est utilisé lorsqu’il est à jour, et réécrit à partir d’un parcours complet lorsqu’il manque, ne s’analyse pas, a été calculé contre d’autres limites, ou a accumulé un long journal de deltas.

La réécriture, contrairement au delta ajouté, n’a lieu que tant qu’une limite est en vigueur — [pepsi] MAILBOX_QUOTA, MAILBOX_QUOTA_COUNT ou une ligne pepsi.mailbox_quota pour le compte — car elle remplace la ligne de définition de quota en tête du fichier, qu’il revient à l’autre agent d’écrire lorsque Pepsi n’applique rien. Il réside dans le répertoire propre de l’utilisateur : il n’est donc qu’indicatif, un utilisateur pouvant réécrire son propre maildirsize.

Un parcours complet de l’arborescence

new/ et cur/ de l’INBOX et de chaque dossier Maildir++ (.Sent, .Trash, …) — toute l’arborescence, ce que l’utilisateur appelle « ma boîte aux lettres ». tmp/ n’est pas compté (un fichier là est une remise en vol). Le parcours est un readdir(3) par dossier et aucun appel stat(2), parce que Maildir++ encode la taille de chaque message dans son nom de fichier (,S=n) — ce qui est exactement pourquoi ce helper étiquette les fichiers qu’il écrit. Un message laissé sans étiquette par un autre agent est staté individuellement.

quotactl(2)

Avec --fs-quota, la comptabilité propre du noyau est également consultée. C’est la seule couche inviolable, et sa limite resserre la limite effective : promettre de l’espace qu”EDQUOT refuserait n’aide personne. Cela fonctionne sans privilège parce que Q_GETQUOTA est permis à un appelant qui interroge son propre uid, et à ce stade le helper est irréversiblement devenu cet utilisateur. Notez que le noyau compte chaque bloc que l’utilisateur possède sur ce système de fichiers, pas seulement son courrier : ses chiffres sont donc rapportés séparément plutôt que de remplacer la mesure de la boîte aux lettres. Un système de fichiers qui ne répond pas (quotas de projet XFS, ZFS, absence de support de quota) ne donne simplement rien, et les autres couches s’appliquent toujours.

Comme la taille du message est inconnue tant que l’entrée standard n’a pas été drainée, le message est mis en tampon dans tmp/ (qui ne compte pas dans le quota) avant que la décision ne soit prise, et supprimé s’il ne tient pas. L’entrée standard est drainée dans tous les cas : un helper qui sortirait trop tôt laisserait son appelant écrire dans un tube cassé, ce qui serait rapporté comme une panne de transport plutôt que comme le refus propre que c’est.

Deux remises simultanées peuvent toutes deux voir de la place et toutes deux écrire, dépassant la limite d’au plus un message chacune. Toute implémentation du quota Maildir++ l’accepte ; la couche de quota du noyau, là où un site en dispose, referme exactement cet écart.

85.1.55.1.5. Arguments

TARGET-UID

L’identifiant numérique d’utilisateur de l’utilisateur local dans le Maildir/new/ duquel le message est remis. Il doit être un entier positif avec une entrée passwd correspondante ; l’uid 0 (root) et tout uid inférieur à /etc/login.defs UID_MIN sont refusés.

measure

Mesure la boîte aux lettres et rapporte, sans rien remettre. L’entrée standard n’est pas lue. Employé par measure et reconcile de pepsi-quota(1).

--measure

Établit la taille réelle de la boîte aux lettres avant d’écrire, et applique contre elle le quota donné par --quota-bytes/--quota-count. Sans cet indicateur, le helper remet sans condition et ne rapporte que ce qu’il a ajouté.

--fs-quota

Consulte aussi quotactl(2), rapporte les chiffres du noyau, et laisse la limite du noyau resserrer la limite effective. Positionné par l’étape de remise depuis [pepsi] MAILBOX_FS_QUOTA, que pepsi-setup(1) sonde.

--quota-bytes N

La limite en octets à appliquer. 0 ou absent signifie aucune limite.

--quota-count N

La limite en nombre de messages à appliquer. 0 ou absent signifie aucune limite.

Le message à remettre est lu dans son intégralité depuis l’entrée standard.

85.1.55.1.6. Sortie

Sur la sortie standard, le helper écrit un enregistrement étiqueté et séparé par des tabulations par ligne. Une étiquette inconnue est ignorée par l’appelant plutôt que d’être une erreur, de sorte qu’un helper plus récent puisse rapporter davantage à un appelant plus ancien.

uTABbytesTABmessages

Occupation mesurée de toute l’arborescence Maildir++, y compris le message qui vient d’être remis. L’appelant la stocke comme occupation du compte et remet à zéro ses compteurs depuis-la-mesure.

fTABusedTABlimitTABinodesTABinode-limit

Les chiffres de quotactl(2), avec 0 là où le noyau n’applique aucune limite.

dTABbytes

Octets ajoutés par cette exécution, rapportés à la place de u lorsqu’aucune mesure n’a été faite, de sorte que l’appelant ajoute à son estimation plutôt que de la remplacer.

sTAB(fsquota|maildirsize|scan)

Comment le chiffre u a été obtenu.

wTABtext

Un problème non fatal (un dossier illisible, un maildirsize qui n’a pas pu être mis à jour). Jamais fatal pour la remise.

Un rapport est écrit même lorsque la remise a été refusée faute de place — surtout alors, puisque c’est une mesure récente qui autorise pepsi-ingress(1) à refuser le message suivant dans la session SMTP plutôt que de l’accepter pour le faire rebondir ensuite.

85.1.55.1.7. Installation

Le binaire doit être installé setuid-root et restreint au groupe

chown root:pepsi-maildir pepsi-helper-maildir-writer
chmod 4750 pepsi-helper-maildir-writer

make install fait cela automatiquement (son étape install-helper), à condition d’être exécuté en tant que root et que le groupe pepsi-maildir existe ; sinon il imprime les commandes exactes à exécuter à la main. Si le helper est placé sur un système de fichiers monté nosuid, le bit setuid est ignoré à l’exécution et le helper refusera de s’exécuter (il ne peut obtenir root), installez-le donc sur un système de fichiers normal.

85.1.55.1.8. Code de sortie

Le code de sortie indique à l’étape appelante si réessayer peut servir à quelque chose, de sorte qu’un message ne fait ni l’objet d’un rebond alors qu’il pourrait encore être remis, ni d’une attente de plusieurs jours en file d’attente contre une boîte aux lettres qui ne l’acceptera jamais. Un diagnostic est toujours écrit sur la sortie d’erreur standard, et pepsi-stage-relay-to-maildir(1) à la fois le journalise et le place dans le rebond qu’il génère.

0

Le message a été écrit dans le Maildir/new/ de l’utilisateur cible et synchronisé sur disque.

1

Échec transitoire : une tentative ultérieure peut réussir, l’appelant garde donc le message en file et le réessaie à intervalles croissants. Cela couvre un disque plein (ENOSPC), une erreur d’E/S, un système de fichiers en lecture seule, et un helper qui n’est pas (encore) installé setuid-root — autant de choses qu’un administrateur peut réparer sous un message en file. Cela couvre aussi tout échec survenant avant que la boîte aux lettres ne soit touchée, quel que soit l”errno qu’il porte : une consultation passwd qui échoue ou ne trouve aucune entrée pour l’uid (un backend d’annuaire qui répond par intermittence, puisque l’étape a résolu l’uid un instant plus tôt), et un passage à l’utilisateur (setgroups, setgid, setuid) que le noyau refuse. Ceux-là signalent aussi EPERM ou ENOENT, et faire rebondir sur eux ferait rebondir chaque destinataire local pour une défaillance de l’hôte.

2

Échec permanent : la boîte aux lettres est inutilisable et aucun réessai n’y changera rien tant qu’un administrateur n’aura pas réparé le compte ; l’appelant fait donc rebondir le message immédiatement. Cela couvre un Maildir qui n’est pas un répertoire (un ancien fichier mbox), une appartenance ou un mode qui verrouille l’utilisateur hors de son propre Maildir (EACCES/EPERM), un répertoire personnel absent (ENOENT), un répertoire personnel non absolu, et un uid inférieur à UID_MIN. Les valeurs d”errno ne comptent que pour les opérations sur la boîte aux lettres elle-même, après le passage à l’utilisateur.

3

La boîte aux lettres est au-delà de son quota, le message n’a donc pas été écrit. Délibérément ni transitoire ni permanent : savoir si une boîte aux lettres pleine mérite qu’on l’attende est une politique de site ([pepsi] MAILBOX_OVER_QUOTA), et le helper n’est pas en position de savoir si cet utilisateur vide sa boîte aux lettres tous les jours ou l’a abandonnée il y a un an. Il rapporte le fait ; pepsi-stage-relay-to-maildir(1) applique la politique. Un EDQUOT du noyau est rapporté de la même façon — un disque plein est le problème de l’administrateur et se règle sous un message en file d’attente, tandis qu’une boîte aux lettres pleine est celui du destinataire et peut ne jamais se régler.

Lorsqu’il est invoqué par un utilisateur qui n’est pas membre du groupe pepsi-maildir, le noyau refuse purement et simplement d’exécuter le binaire (Permission denied) ; le helper lui-même ne s’exécute jamais.

85.1.55.1.9. Sécurité

Le helper fait confiance à son appelant pour être autorisé (l’appartenance au groupe pepsi-maildir est la barrière de contrôle d’accès) et remettra à n’importe quel utilisateur local ordinaire (uid ≥ UID_MIN, jamais un compte système). Il abandonne les groupes supplémentaires avant de changer d’uid puis se dépouille complètement de root (ids réel, effectif et sauvegardé), en vérifiant que root ne peut être regagné avant toute E/S de fichier, et crée le fichier de remise de façon exclusive afin de ne jamais pouvoir être trompé pour écrire par-dessus un fichier qu’il ne possède pas.

85.1.55.1.10. Voir aussi

pepsi-stage-relay-to-maildir(1), pepsi-ingress(1), pepsi-dispatch(1), pepsi.conf(5), maildir(5)

85.1.55.1.11. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.