85.1.7. pepsi-stage-decrypt

decrypt mail addressed to our users and verify what signed it

Section du manuel:

1

85.1.7.1.1. Nom

pepsi-stage-decrypt - l’étape entrante de déchiffrement de bout en bout et de vérification de signature du pipeline Pepsi.

85.1.7.1.2. Synopsis

pepsi-stage-decrypt [GLOBAL-OPTIONS] worker

85.1.7.1.3. Description

pepsi-stage-decrypt est un programme d’étape exécuté par pepsi-dispatch(1) comme un worker persistant lisant les identifiants de message sur l’entrée standard. Pour un message adressé à un destinataire que cet hôte sert, il ouvre tout texte chiffré que le message porte, vérifie toute signature qu’il porte contre une véritable chaîne de confiance, note le verdict, et retire tout indicateur de sécurité que l’expéditeur aurait falsifié. Il lit les certificats que le message transporte afin de vérifier cette signature, mais n’en stocke aucun : apprendre les clés des correspondants est le travail de pepsi-stage-autocrypt-learn(1), et c’est une étape distincte parce qu’elle doit s’exécuter après le verdict de spam, qui n’existe pas encore ici.

Faire cela sur le serveur est ce qui supprime le besoin d’un greffon côté client : l’utilisateur lit du courrier ordinaire dans son client habituel. Le marché ne tient que parce que le message est sur le point de cesser d’être un objet SMTP pour devenir un fichier dans une boîte aux lettres — voir Localité. Ce fichier est en clair, à moins que le destinataire n’ait enregistré la propre clé de son client de messagerie et que pepsi-stage-reencrypt(1) ne se trouve devant la remise locale, scellant pour cette clé ce que cette étape a ouvert, une fois que les étapes ultérieures l’ont lu.

La moitié vérification est délibérément stricte sur une distinction. Une signature cryptographiquement saine vous dit que le message a été signé par une clé ; savoir si cette clé est celle de l’expéditeur est une question distincte. Les deux ne sont jamais rapportées comme la même chose (Verdicts).

85.1.7.1.4. Localité

L’étape ne s’exécute que pour les destinataires que cet hôte sert, et pour un message qu’il ne sert pas elle n’essaie même pas — une tentative de déchiffrement ratée ne devrait pas colorer le verdict, et de toute façon il n’y a aucune clé pour un tel message.

La raison est que le déchiffrement réécrit le corps. La signature DKIM de l’expéditeur ne vérifie alors plus, et un sceau ARC posé avant le déchiffrement (voir Placement) couvre la forme chiffrée. Pour un message sur le point d’être remis localement, c’est sans conséquence. Pour un message réacheminé plus loin, ce ne l’est pas : il arriverait au saut suivant avec une signature cassée, ce qui est un signal négatif plus fort que pas de signature du tout.

La localité est le même test qu’emploient les étapes de remise — LOCAL_DOMAINS, TARGETS et RECIPIENT_DELIMITER — à une différence près. Par défaut, seul le domaine doit correspondre, car cette étape n’a pas besoin de savoir dans quelle boîte écrire ; un domaine hébergé dont les utilisateurs détiennent des identités cryptographiques mais aucun compte shell est un déploiement ordinaire. Réglez REQUIRE_ACCOUNT = yes pour exiger en outre une entrée passwd dans TARGETS.

Un message ayant à la fois des destinataires locaux et étrangers est scindé : les destinataires étrangers sont détachés sur une ligne sœur à NEXT_STAGE portant le message exactement tel qu’il est arrivé, et cette ligne garde les locaux. L’état DSN par destinataire est découpé en phase par le même code qu’emploie toute autre scission de destinataires.

85.1.7.1.5. Placement

La chaîne entrante recommandée est

init = arc -> decrypt -> aliases -> (anti-spam / language / …) -> local delivery

ARC s’exécute d’abord. Un ensemble ARC scelle le message tel qu’il est arrivé ; déchiffrer d’abord ferait décrire à l”AMS scellé des octets que personne d’autre n’a jamais vus, si bien que le sceau serait une affirmation portant sur un message qui n’a jamais existé. pepsi-setup(1) avertit lorsqu’il trouve une étape ARC atteignable depuis une étape de déchiffrement.

L’inspection du contenu s’exécute après. La détection de langue, la barrière pay-to-send et la vérification de liste blanche sur l’en-tête From: lisent toutes le corps, et toutes veulent du texte clair.

Le placement est aussi une question de performance. L’étape déclare Load::Full, car le fait qu’un message soit protégé ne se voit pas dans les colonnes d’enveloppe et Load est fixé par étape. Chaque message qui la traverse charge donc tout son corps depuis la base, même un message qui s’avère être du courrier ordinaire non chiffré. C’est abordable sur un chemin entrant se terminant par une remise locale ; ce n’est pas une étape à placer devant du trafic de relais en masse.

85.1.7.1.6. Verdicts

state.crypto.in.signature.status vaut l’un de :

none

Le message ne porte aucune signature.

valid

Cryptographiquement saine et la clé était ancrée : une chaîne X.509 vers une ancre ca_trust configurée, ou une clé OpenPGP issue d’une source de découverte classée (DANE, WKD, un serveur de clés, ou une clé que l’opérateur a épinglée).

valid-untrusted

Cryptographiquement saine, mais la clé n’a pu être rattachée à rien : une clé vue pour la première fois, apprise du message lui-même ou d’un en-tête Autocrypt, ou un certificat d’une AC pour laquelle ce déploiement ne détient aucune ancre. La majeure partie du courrier signé du monde est dans cet état.

Cela n’est jamais rapporté comme ``valid``. C’est la distinction que l’étape existe pour faire, et un indicateur de sécurité qui confond les deux est pire que pas d’indicateur du tout.

invalid

La signature ne vérifie pas sur les octets qu’elle couvre, ou le certificat du signataire est expiré, révoqué, ou lié à une autre adresse que le From: du message.

unverifiable

Aucune clé ni aucun certificat n’était disponible pour le signataire prétendu : rien n’a donc pu être établi dans un sens ou dans l’autre. C’est le verdict qui pousse l’étape à attendre une clé (Découverte de clés).

Un message portant plusieurs couches de signature prend le pire des verdicts des couches qui parlent pour lui. Une signature cassée rend le message invalid, si bonnes que soient les autres. Une couche qui ne couvre que du texte chiffré n’en fait partie que lorsque rien ne couvre le texte en clair (voir ci-dessous) : une telle couche ne peut jamais rendre le message valid, et dès qu’une couche couvre effectivement le texte en clair, elle est entièrement écartée, si mauvaise soit-elle.

Seul valid positionne state.signature_verified, ce à quoi pepsi-stage-check-whitelist(1) conditionne une entrée de liste blanche.

85.1.7.1.6.1. Une signature qui couvre du texte chiffré

Dans signed(encrypted(body)), la signature est calculée sur le texte chiffré, non sur quoi que ce soit qu’un lecteur verra jamais. Cela prouve que le signataire a transmis le bloc ; cela ne prouve pas qu’il l’a écrit, ni qu’il peut le lire — quiconque obtient un message chiffré peut l’envelopper dans sa propre signature et le réexpédier. Une telle couche est notée avec "covers_ciphertext": true dans state.crypto.in.layers et, quel que soit son propre verdict, elle n’est jamais autorisée à rendre le message ``valid`` : une bonne est rapportée valid-untrusted, elle ne gagne donc aucune marque [verified] et ne positionne pas state.signature_verified. Une mauvaise est rapportée invalid et acheminée sous ON_BAD_SIGNATURE seulement quand c’est tout ce qu’il y a — c’est-à-dire quand aucune couche ne couvre le texte clair.

Dès qu’une couche couvre bel et bien le texte clair, celles qui couvrent le texte chiffré sont entièrement écartées, quel que soit leur propre verdict. Une couche qui ne peut pas accorder un verdict ne doit pas pouvoir en détruire un : quiconque obtient le bloc chiffré peut le ré-envelopper, donc si un emballage corrompu imposait invalid, corrompre l’emballage serait strictement plus utile à un attaquant que de le retirer (ce qui laisse un encrypted(signed(body)) ordinaire qui se vérifie encore). La signature extérieure d’un triple emballage est de toute façon un artefact de saut à saut — la §3.7 la fait poser par un agent de liste de diffusion — de sorte que son échec ne dit rien de l’auteur. La falsification n’est pas perdue : elle reste visible dans state.crypto.in.layers et dans X-Pepsi-Crypto, comme une observation plutôt que comme une entrée d’acheminement. Le même raisonnement couvre le cas plus courant d’une signature extérieure simplement unverifiable (pas de clé pour le redirecteur), de sorte que le triple emballage ci-dessous n’est pas puni pour un signataire qu’il se trouve que nous ne connaissons pas.

Ainsi, dans signed(encrypted(signed(body))) — le triple emballage que recommande la RFC 8551 §3.7 — la signature intérieure est celle qui parle pour le contenu, et c’est celle que cette étape rapporte. Cette forme est donc rapportée valid là où une simple signature extérieure ne l’est pas.

Chaque fois qu’il y a un verdict de signature, state.crypto.in.signature note aussi covers_plaintext : true lorsque le verdict a été accordé par une signature sur le texte en clair, false lorsqu’il provient d’une couche enveloppant du texte chiffré. pepsi-stage-autocrypt-learn(1) n’accepte que le premier cas comme preuve qu’un correspondant détient l’une de nos clés.

85.1.7.1.7. Traitement des échecs

Les deux chemins d’échec remettent par défaut, conformément à la posture fail-open délibérée de Pepsi sur le SPF, le DKIM et le DMARC entrants :

ON_DECRYPT_FAILURE = passthrough

Le message, toujours chiffré, est remis. L’utilisateur détient peut-être la clé dans son propre client, et renvoyer du courrier que nous n’avons simplement pas pu ouvrir n’aide personne. quarantine et bounce sont disponibles.

ON_BAD_SIGNATURE = record

Une mauvaise signature est un verdict noté, jamais un échec de remise. Les listes de diffusion qui réécrivent les corps, les redirecteurs et les clients à la canonicalisation cassée produisent tous de mauvaises signatures sur du courrier parfaitement légitime ; les mettre en quarantaine coûterait bien plus que cela n’attrape. quarantine et bounce sont disponibles.

Notez que seule invalid est une « mauvaise signature » à cet effet. valid-untrusted et unverifiable sont l’état normal du courrier d’un correspondant dont personne ici n’a jamais entendu parler, et les acheminer quelque part reviendrait à acheminer la majeure partie de l’internet.

La politique de déchiffrement est consultée d’abord : un message qui n’a pas pu être ouvert n’a aucun verdict de signature sur lequel il vaille la peine d’agir.

Une clé de destinataire que cet hôte détient mais ne peut pas ouvrir — une clé de chiffrement de clés qui ne correspond pas au blob stocké, un fragment secrets.d disparu, un droit de base de données révoqué — aboutit aussi à decrypt-failure=no-decryption-key et prend la route ON_DECRYPT_FAILURE, exactement comme un message adressé à une clé que nous n’avons jamais eue. C’est délibéré (une identité cassée ne doit pas empêcher d’essayer les autres), mais c’est un déploiement cassé et non le fait de l’expéditeur, si bien que chacune de ces identités est journalisée au niveau error avec l’adresse et la raison. Il en va de même pour les destinataires qui détiennent des identités de déchiffrement sur un hôte où aucune [pepsi-crypto] KEY_WRAP_SECRET n’est configurée.

85.1.7.1.8. Erreurs de cet hôte

Tout le reste de ce qui tourne mal sur cet hôte est réessayé plutôt que mis en échec (voir pepsi-dispatch(1)) : une base de données qui ne peut pas répondre — y compris pour la recherche des propres clés MUA des destinataires, qui n’est jamais interprétée comme « pas de telle clé », puisque cela ouvrirait du courrier destiné au client de l’utilisateur lui-même — est mise en pause et réessayée jusqu’au MAX_LIFETIME de l’étape. La section de l’étape, la politique crypto [pepsi], [pepsi-keydiscovery] et, lorsqu’une clé de chiffrement de clés est configurée, le trousseau construit à partir d’elle sont vérifiés au démarrage du worker : un élément qui ne fonctionne pas fait refuser au worker de démarrer (sortie 78), de sorte que le dispatcher retient la file au lieu que chaque message échoue sur la même défaillance. Un message qui fait planter la cryptographie (une panique pendant son analyse) est mis en échec immédiatement : c’est le fait du message, et il ne ferait que planter de nouveau.

85.1.7.1.9. Courrier pour la propre clé de l’utilisateur

Un utilisateur peut enregistrer sa propre clé, dont la moitié privée ne réside que dans son client de messagerie (une clé MUA, crypto_identity.custody = client ; voir pepsi-stage-encrypt(1) et pepsi-keys(1)). Le courrier chiffré pour une telle clé est destiné au client de messagerie, et l’étape le reconnaît avant de tenter d’ouvrir quoi que ce soit : elle lit les identifiants de destinataires du texte chiffré (identifiants de clé PKESK OpenPGP, RecipientIdentifiers S/MIME) sans déchiffrer et les compare aux clés client non révoquées des destinataires. Un identifiant de clé joker (« destinataire caché ») ne correspond à rien et suit le chemin ordinaire.

En cas de correspondance, le message est transmis sans être ouvert, même lorsque l’une de nos propres clés MTA figure elle aussi parmi ses destinataires : l’utilisateur a enregistré sa propre clé pour obtenir une protection de bout en bout, et ouvrir une copie sur le serveur puis stocker le texte en clair dans la boîte aux lettres l’annulerait silencieusement. L’étape note state.crypto.in.decryption = for-client, for_client = true et client_fingerprint, positionne state.encrypted et ajoute for-client=yes à X-Pepsi-Crypto. Ce n’est pas un échec de déchiffrement : ON_DECRYPT_FAILURE n’est donc pas consulté, et le message avance vers NEXT_STAGE comme n’importe quel autre : les vérifications anti-spam et le reste du pipeline décident toujours de son sort. Un message ayant plusieurs destinataires locaux est d’abord scindé à raison d’un par ligne, puisque la clé client d’un destinataire ne dit rien de la copie d’un autre.

La conséquence est que les fonctionnalités côté serveur deviennent aveugles pour un tel courrier. Les étapes qui lisent le contenu (la détection de langue, par exemple) voient du texte chiffré et se replient comme pour tout message indéchiffrable, et rien n’a été vérifié : state.signature_verified n’est donc jamais positionné et une entrée de liste blanche avec signature_required ne lui correspond pas.

85.1.7.1.10. Anti-falsification

Chaque champ d’en-tête X-Pepsi-* est retiré de chaque message que cette étape voit — y compris d’un message qu’elle ne va pas toucher, et d’un message pour lequel l’étape est désactivée. C’est seulement ensuite que le nôtre est ajouté.

X-Pepsi-Crypto est un en-tête privé, donc falsifiable par définition ; le retrait inconditionnel est toute la base sur laquelle un client peut y croire. L’en-tête de preuve d’origine Pepsi-Origin n’est délibérément pas dans cet espace de noms et survit.

Le même argument vaut pour le Subject. Avec STRIP_SUBJECT_TAGS = yes (la valeur par défaut), le vocabulaire de marques propre à cette étape — TAG_DECRYPTED, TAG_VERIFIED, TAG_BAD_SIGNATURE, les [decrypted] et [verified] intégrés quelle que soit la configuration des marques, et tout ce qui figure dans STRIP_SUBJECT_KEYWORDS — est retiré du début d’un sujet entrant avant que le nôtre ne soit ajouté en tête. Sinon, un expéditeur extérieur écrit tout simplement [verified] lui-même. Les marques ne sont retirées qu’en tête, et derrière un préfixe Re:/Fwd:, de sorte qu’un sujet qui en mentionne une en milieu de phrase la conserve.

85.1.7.1.11. Marques de sujet

Avec SUBJECT_TAGS = yes (la valeur par défaut), les marques méritées sont ajoutées en tête du Subject dans l”ordre d’imbrication

encrypted(signed(body))   ->  [decrypted][verified] quarterly figures
signed(encrypted(body))   ->  [verified][decrypted] quarterly figures

Les deux formes s’ouvrent. La seconde ligne énonce la règle d’ordre ; notez que le second exemple ne gagne son [verified] que d’une signature intérieure, car une signature extérieure sur du texte chiffré n’atteint jamais le verdict valid pour lequel la marque est écrite (Une signature qui couvre du texte chiffré). Un message signed(encrypted(body)) sans signature intérieure est donc marqué du seul [decrypted].

Pour la plupart des utilisateurs, c’est le seul signal qu’ils verront jamais — personne lisant son courrier dans un client webmail n’inspecte les en-têtes — de sorte que faire le travail invisiblement le gaspillerait. Le prix est qu’un champ visible par l’utilisateur est modifié : cela affecte la recherche, l’enfilage et le sujet cité des réponses. Réglez SUBJECT_TAGS = no pour vous fier au seul en-tête.

[verified] n’est écrit que pour le verdict valid. TAG_BAD_SIGNATURE est vide par défaut : une mauvaise signature est une propriété courante du courrier légitime, de sorte qu’une marque activée par défaut décorerait une grande part de la boîte d’un utilisateur d’une accusation.

85.1.7.1.12. L’en-tête de résultat

Avec ADD_RESULT_HEADER = yes (la valeur par défaut), un message qui portait une protection quelconque gagne un champ d’en-tête, ajouté tout en haut afin de ne perturber aucune signature existante

X-Pepsi-Crypto: encrypted=yes; decrypted=yes; protocol=openpgp;
  signature=valid; signer=alice@example.net;
  fingerprint=0123456789ABCDEF; key-source=wkd; trust=wkd-advanced;
  layers="encrypted,signed"

La grammaire est faite de paires key=value séparées par des points-virgules, dans un ordre fixe. Une valeur qui n’est pas un jeton nu (A–Z, a–z, 0–9 et . _ @ : + / -) est mise entre guillemets doubles ; un " incorporé devient ', et les caractères de contrôle sont remplacés par des espaces, de sorte qu’aucune valeur issue du message ne puisse terminer le champ ni en commencer un autre. Les valeurs longues sont repliées aux espaces.

Les clés sont :

encrypted

yes/no — le message est arrivé porteur de texte chiffré.

decrypted

yes/no — présent uniquement lorsque encrypted=yes.

for-client

yes lorsque le message est chiffré pour la propre clé MUA du destinataire et a été transmis sans être ouvert (Courrier pour la propre clé de l’utilisateur).

protocol

openpgp ou smime.

signature

L’un des verdicts ci-dessus.

signer, fingerprint, key-source, trust, algorithm

La clé de signature, d’où elle vient et ce qu’elle valait. Présents uniquement lorsqu’ils sont connus.

failure

La raison lisible par une machine pour laquelle un verdict de signature n’est pas bon.

decrypt-failure

La raison lisible par une machine pour laquelle le déchiffrement a échoué.

layers

Les couches de protection, de l’extérieur vers l’intérieur, séparées par des virgules.

La RFC 8601 n’enregistre aucune méthode smime ni pgp : une extension d”Authentication-Results signifierait donc inventer un nom de méthode dans un registre qui ne nous appartient pas — que d’autres implémentations sont en droit d’ignorer ou de suspecter. Un en-tête privé à la grammaire documentée est le choix honnête, et son prix est le retrait inconditionnel décrit plus haut.

85.1.7.1.13. Découverte de clés

L’étape ne fait aucune E/S réseau d’aucune sorte. Lorsque la clé du signataire n’est ni dans le message ni dans le cache peer_key, elle met en file une demande de découverte et gare le message ; un service pepsi-keydisc(1) le libère lorsque la demande se conclut, clé trouvée ou non, et l’étape s’exécute à nouveau. Une demande conclue sans clé libère le message tout autant, et il se vérifie alors en unverifiable. Une entrée fraîche de cache négatif court-circuite entièrement l’attente, ce qui empêche un spammeur signant avec une clé inconnue de faire attendre chaque message : le premier se gare, les autres non.

[pepsi-keydiscovery] INBOUND_TIMEOUT borne l’attente et se règle séparément du TIMEOUT sortant ; DISCOVERY_TIMEOUT dans la section propre à cette étape le redéfinit. DISCOVERY = no supprime entièrement la demande et laisse l’étape sur le chemin cache-seulement.

Le fait qu’un message garé soit stocké déchiffré dépend de la forme du message, et la différence compte :

  • Une signature S/MIME imbriquée dans une enveloppe survit au déchiffrement comme une couche MIME ordinaire : le message est donc garé avec le texte clair validé et déchiffré exactement une fois.

  • Une signature OpenPGP ne vit qu’à l’intérieur du flux de paquets : elle disparaît dès que le texte clair en sort. Valider le texte clair détruirait la preuve pour la vérification de laquelle la clé est récupérée : un tel message est donc garé inchangé et déchiffré à nouveau à la reprise.

Un message garé avec son texte clair validé demeure déchiffré dans pepsi.workqueue jusqu’à l’échéance de découverte. C’est la même exposition que la remise créerait un instant plus tard de toute façon ; c’est une fenêtre plus longue, non une nouvelle.

85.1.7.1.14. Les certificats que le message transporte

La plupart du courrier signé porte le certificat qui l’a signé : avant de rien décider, l’étape lit donc dans le message les certificats de signataire S/MIME et les parties application/pgp-keys et les propose au vérificateur. Elle refait de même sur le texte clair une fois le message ouvert, car un certificat joint à l’intérieur du chiffré est invisible jusque-là, et réévalue une fois si cela a donné quelque chose que la première passe n’avait pas.

Ces certificats sont utilisés puis abandonnés. Cette étape ne stocke rien : apprendre du courrier entrant — l’en-tête Autocrypt:, les clés jointes, et le gossip — relève de pepsi-stage-autocrypt-learn(1), qui s’exécute plus loin dans le pipeline, après les étapes qui décident si un message est du spam. C’est cette scission qui rend possible de respecter la règle anti-spam d’Autocrypt Level 1 §5.3 : cette étape s’exécute en premier sur le chemin entrant, par conception, avant qu’un tel verdict n’existe. Cela signifie aussi que rien n’est écrit dans le magasin de clés par le processus qui détient les clés privées.

Employer un certificat que le message transportait ne peut qu”abaisser la valeur d’un verdict, jamais l’élever : un tel matériel est de provenance non fiable, de sorte qu’une signature vérifiée contre lui revient valid-untrusted et ne positionne pas state.signature_verified — la clé dont pepsi-stage-check-whitelist(1) fait sa condition. C’est le même échelon qu’aurait une clé moissonnée et stockée, de sorte que la faire passer par la base n’apporterait rien.

85.1.7.1.15. Deux faits consignés pour l’étape d’apprentissage

Le déchiffrement valide le texte clair par-dessus les octets arrivés : deux choses que seule cette étape peut voir sont donc écrites dans state.crypto.in pour que pepsi-stage-autocrypt-learn(1) les lise — encrypted, que le message portait bien du texte chiffré, et outer_gossip, que le bloc d’en-têtes à l’arrivée portait déjà un champ Autocrypt-Gossip:. Les deux sont des préconditions pour croire le gossip d’un message, et ni l’un ni l’autre n’est plus déterminable une fois le texte clair dans la ligne.

85.1.7.1.16. Les clés de nos propres utilisateurs ne sont jamais outrepassées

Le moissonnage des clés se fait sur l’en-tête From:, que l’expéditeur écrit, et l’authentification entrante est délibérément fail-open. Un message prétendant venir de l’un des utilisateurs propres à ce déploiement sème donc pour cet utilisateur une ligne peer_key contenant la clé qu’il transportait — et une signature vérifiée contre elle se vérifierait sinon, positionnant state.signature_verified, dont pepsi-stage-check-whitelist(1) fait sa condition.

Avant que le magasin ne soit consulté, l’étape demande donc si l’expéditeur prétendu est quelqu’un pour qui elle détient une identité. Si c’est le cas, la clé publique de cette identité est le seul candidat : aucune ligne peer_key pour l’adresse n’est proposée au vérificateur, quel que soit son rang et quelle qu’ait été son arrivée. Tout sauf une identité revoked compte, les identités expirées comprises — une signature faite l’an dernier l’a été avec la clé de l’an dernier, et refuser de la regarder rapporterait le courrier de nos propres utilisateurs comme invérifiable à chaque renouvellement de clé.

La règle tient au fait de détenir une clé, non au fait de servir l’adresse : un utilisateur qui exploite son propre client OpenPGP et n’a jamais téléversé de clé ici n’a pas d’identité, de sorte que sa clé moissonnée ou issue du gossip est utilisée exactement comme celle de n’importe quel correspondant. Le prix en est l’image miroir — dès qu’une identité est détenue pour une adresse, une signature faite avec la clé personnelle distincte de cet utilisateur ne se vérifie plus. Voir Les clés de nos propres utilisateurs ne sont pas découvertes dans le manuel.

85.1.7.1.17. Ancres de confiance

Les ancres X.509 proviennent de la table pepsi.ca_trust (pepsi-keys(1) ca) et sont mises en cache par processus worker pendant TRUST_ANCHOR_TTL (cinq minutes par défaut). Une ancre ajoutée ou désactivée devient donc effective dans cette fenêtre ; redémarrez les workers pour l’appliquer aussitôt. Elles sont chargées paresseusement et seulement pour un message portant réellement une couche S/MIME, de sorte que le trafic ordinaire ne coûte aucune requête.

Les données de révocation ne sont pas récupérées : une récupération de CRL serait de l’E/S réseau dans une étape, ce que la conception de la découverte existe pour éviter. Une chaîne est par conséquent rapportée avec une révocation not-checked, ce qui est énoncé plutôt que laissé à suggérer « non révoqué ».

85.1.7.1.18. Configuration

L’étape lit sa propre section [stage-<name>] (PROGRAM = pepsi-stage-decrypt, NEXT_STAGE (obligatoire), BOUNCE_STAGE, plus ENABLED, LOCAL_DOMAINS, TARGETS, RECIPIENT_DELIMITER, REQUIRE_ACCOUNT, ON_DECRYPT_FAILURE, ON_BAD_SIGNATURE, QUARANTINE_STAGE, SUBJECT_TAGS, TAG_DECRYPTED, TAG_VERIFIED, TAG_BAD_SIGNATURE, STRIP_SUBJECT_TAGS, STRIP_SUBJECT_KEYWORDS, ADD_RESULT_HEADER, MAX_LAYERS, TRUST_ANCHOR_TTL, DISCOVERY, DISCOVERY_TIMEOUT), toutes documentées dans pepsi.conf(5). pepsi-setup(1) exige un BOUNCE_STAGE dès que l’une des politiques d’échec vaut bounce, et un QUARANTINE_STAGE dès que l’une vaut quarantine. Ce sont des options de comportement, redéfinissables par adresse via la table pepsi.settings.

La politique cryptographique — choix des algorithmes, acceptation des condensats faibles, tailles de clé minimales — réside dans les options CRYPTO_* de [pepsi] et dans [pepsi-crypto]. Elles relèvent du seul administrateur système et sont délibérément inatteignables depuis pepsi.settings ou pepsi-stage-edit-settings(1).

L’une d’elles ne s’applique pas ici. CRYPTO_ALLOW_DOWNGRADE régit ce qui peut être émis : SEIPDv1+MDC OpenPGP et EnvelopedData CMS sont toujours lisibles quoi qu’elle dise, car les refuser à l’entrée rendrait cet hôte incapable de recevoir du courrier de la majeure partie du monde sans protéger personne. Seuls le 3DES et les condensats de signature faibles sont conditionnés à l’entrée, et chaque refus produit son propre verdict plutôt qu’un échec générique. La barrière 3DES n’est pas la même des deux côtés : un message S/MIME 3DES entrant n’est accepté que sous yes, un message OpenPGP entrant l’est aussi sous negotiate, car un certificat OpenPGP énonce ses propres capacités de chiffrement et un certificat X.509 n’en énonce aucune. Le conteneur réellement employé est noté par couche, de sorte qu’un déploiement puisse voir quelle part de son courrier entrant est encore pré-AEAD.

85.1.7.1.19. Privilège

Le binaire est installé setuid pepsi-crypto, mode 4750 pepsi-crypto:pepsi, et n’est donc pas plié dans le binaire multi-appel pepsi unifié.

Les deux moitiés de la limite de garde des clés reposent sur cette identité. Le rôle de base de données pepsi-crypto est le seul à qui crypto_identity.private_wrapped est accordé, et l’authentification par pair de PostgreSQL se fonde sur l”uid effectif ; la clé de chiffrement de clés réside dans secrets.d/pepsi-crypto.secret, mode 0640, appartenant au même compte. Un binaire setgid gagnerait le groupe — de quoi lire le fragment — et s’authentifierait tout de même auprès de la base de données en tant que pepsi, précisément le rôle à qui la colonne est refusée.

Le groupe dans le mode restreint l’exécution au compte du dispatcher et à root ; le programme refuse en outre de s’exécuter si son uid réel n’est ni root ni l’un des comptes pepsi, pepsi-crypto et pepsi-owner. Cette vérification à l’exécution ne s’applique que lorsque le binaire porte réellement le bit setuid — un build installé sans lui (un arbre de développement, cargo test) laisse n’importe qui l’exécuter, au motif qu’un tel appelant atteint la base de données en son propre nom et que les droits de PostgreSQL font alors autorité. -c est accepté de ces appelants (la forme de lancement documentée du dispatcher est PROGRAM -c cfg worker) et l’environnement est assaini avant le chargement de la configuration, car la couche de configuration est pilotable par XDG_CONFIG_HOME, HOME, PATH et la famille PG*.

Les clés de déchiffrement sont sélectionnées par capacité : chaque identité non révoquée de chaque destinataire local dont le purpose est encrypt ou both, y compris les expirées, car l’ancien courrier doit rester lisible.

85.1.7.1.20. Limites de ressources

MAX_LAYERS (4 par défaut) plafonne le nombre de couches chiffrées imbriquées déballées ; un message qui en a davantage est remis avec le reste non ouvert et decrypt-failure=unsupported consigné.

Un message OpenPGP est couramment compressé et le taux d’expansion relève du choix de l”expéditeur : pepsi-crypto refuse donc d’emblée un texte clair de plus de 64 Mio plutôt que de le tronquer — un texte clair partiel ne doit jamais être rapporté comme vérifié. Ce cas est rapporté avec sa propre raison d’échec, decrypt-failure=too-large, et non comme un échec de déchiffrement générique : « ce message est une bombe » et « nous n’avons pas pu ouvrir ceci » sont des faits différents pour un opérateur.

85.1.7.1.21. État

Entrées : state.dsn (honoré et découpé en phase avec la scission de localité), state.crypto.in (sur une ligne reprise, ce qu’a noté la passe qui l’a déchiffrée).

Sorties : state.crypto.in — si le message était chiffré, ce qu’est devenu le texte chiffré (decryption : not-encrypted, decrypted, failed ou for-client), quelle identité l’a ouvert, le verdict de signature et son détail (y compris covers_plaintext), la liste ordonnée des couches et, pour le courrier destiné à la propre clé d’un utilisateur, for_client et client_fingerprint. Également state.encrypted = true lorsque le message est arrivé chiffré, et state.signature_verified = true pour le seul verdict valid ; pepsi-stage-check-whitelist(1) et pepsi-stage-auto-whitelist(1) les lisent. Sur un rebond, state.bounce. La disposition de l’état est décrite dans pepsi.state(7).

Transitions : NEXT_STAGE pour un message remis ; QUARANTINE_STAGE ou BOUNCE_STAGE selon les politiques d’échec ; paused pendant l’attente de la découverte de clés ; pending à cette même étape après une scission de localité.

85.1.7.1.22. Commandes

worker

Exécuté comme worker persistant du dispatcher, lisant les identifiants de message sur l’entrée standard et écrivant une ligne de statut par message. C’est le seul mode ; il n’y a pas de forme ponctuelle. Pour traiter un seul message à la main, envoyez son identifiant dans un tube

echo 1234 | pepsi-stage-decrypt -c /etc/pepsi/pepsi.conf worker

85.1.7.1.23. Code de sortie

0

Le worker s’est terminé proprement (entrée standard fermée).

1

Une erreur s’est produite (mauvaise configuration, magasin de clés illisible, ou erreur de base de données). La raison est écrite dans le journal.

78

Le worker a refusé de démarrer : sa configuration ou son trousseau ne fonctionne pas (voir Erreurs de cet hôte). Le dispatcher remet en file ce qu’il lui avait confié et réessaie l’étape plus tard.

85.1.7.1.24. Voir aussi

pepsi-stage-encrypt(1), pepsi-stage-reencrypt(1), pepsi-stage-arc(1), pepsi-keys(1), pepsi-keydisc(1), pepsi-stage-check-whitelist(1), pepsi-stage-relay-to-maildir(1), pepsi-stage-relay-to-lmtp(1), pepsi-stage-bounce(1), pepsi-dispatch(1), pepsi-setup(1), pepsi.conf(5), pepsi.state(7)

85.1.7.1.25. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.