85.1.56. pepsi-helper-dot-forward¶
run a single user’s ~/.forward as that user
- Manual section:
1
85.1.56.1.1. Name¶
pepsi-helper-dot-forward - privileged helper that processes one local
user’s ~/.forward after dropping to that user.
85.1.56.1.2. Synopsis¶
pepsi-helper-dot-forward TARGET-UID [–pipe] [–file] < message
85.1.56.1.3. Description¶
pepsi-helper-dot-forward is a minimal, security-hardened helper that runs
exactly one local user’s ~/.forward file. It exists so that a trusted but
unprivileged caller (a member of the pepsi-forward group, in practice
pepsi-stage-dot-forward(1)) can have any local user’s ~/.forward
processed as that user — and do nothing else.
The helper is installed setuid-root, owned root:pepsi-forward with mode
4750. Only members of the pepsi-forward group may execute it; when they
do, the kernel runs it with an effective uid of root. Given the numeric uid
of the target user as its sole positional argument, and the raw message on
standard input, the helper:
resolves the target user’s passwd entry, refusing uid
0/ the loginrootand any uid below/etc/login.defsUID_MIN(default1000);fully and irreversibly drops to the target user — installing the user’s supplementary groups, then the gid, then the uid (real, effective and saved) — and verifies that root cannot be regained, exactly as pepsi-helper-maildir-writer(1) and pepsi-helper-auto-pay(1) do;
refuses to act — exit
3— when the home directory, or the~/.forwarditself, is not owned by the target uid or is group- or world-writable (mode & 0o022). This is the sendmail/postfix-local rule: the file names|commandand/filedirectives the helper runs as that user, so anyone else who can write the directory could plant one. Note this is checked on the home directory before the~/.forwardis looked for, so on a host with group-writable homes the account is refused even when it has no~/.forwardat all;if the user has no
~/.forwardfile, exits1(the stage then proceeds to its next stage);otherwise reads
~/.forwardand acts on each non-empty, non-#line as the user: a bare address is collected as a forwarding address, and one written\nameis collected with its marker, which the calling stage reads as sendmail’s “deliver to this name and expand it no further”;|commandpipes the message to/bin/sh -c command(only with –pipe);/absolute/pathappends the message to that file (only with –file). A directive whose kind was not enabled, or whose execution fails, is a failure. A|commandis run with a sanitised environment —HOMEandUSER/LOGNAMEset to the target user, a fixedPATH, and the shell-hijacking variablesIFS/BASH_ENV/ENV/CDPATHremoved — so the inherited delivery-agent environment cannot influence the command.
A |command runs in its own process group and is given 300 seconds; on
expiry the entire group is SIGKILLed (the group, because a pipe directive
routinely forks) and the run is reported as a failure. A /path directive
opens the target with O_NOFOLLOW|O_NONBLOCK and refuses anything that is not
a regular file, so a FIFO or a symlink cannot block the helper indefinitely.
Without these bounds any local account could wedge a dispatcher worker for the
lifetime of the pipeline simply by writing |sleep infinity in its
~/.forward.
A directive may be written double-quoted — the form procmail’s and maildrop’s
documentation tells users to use, "|IFS=' ' && exec /usr/bin/procmail -f- ||
exit 75 #bob" — and one line may carry several comma-separated entries, as
the alias-database right-hand-side grammar that .forward inherits permits. One
layer of surrounding double quotes is removed before the directive is classified,
and a comma inside double quotes does not split.
The message is read from standard input only when a |pipe//file directive
that consumes it may run (i.e. when –pipe or –file is given).
85.1.56.1.4. Arguments¶
- TARGET-UID
The numeric user id whose
~/.forwardis processed. It must have a passwd entry; uid0/ therootaccount and any uid below/etc/login.defsUID_MINare refused.- –pipe
Permit
|commanddirectives (the stage passes this when itsALLOW_PIPEis on).- –file
Permit
/pathfile-append directives (the stage passes this when itsALLOW_FILEis on).
85.1.56.1.5. Exit Status¶
- 0
The
~/.forwardwas processed successfully. The forwarding addresses it named (possibly none) are written one per line to standard output.- 1
The user has no
~/.forward. The caller proceeds to its next stage.- 2
Processing failed — a pipe command exited non-zero (other than
75) or was killed by a signal, a file could not be written, or a disabled directive was requested. A human-readable diagnostic is written to standard output for inclusion in the bounce. This is a permanent failure of that recipient.- 3
An operational failure of the host, not of the delivery: the helper is not installed setuid-root, the target uid has no passwd entry or the passwd lookup itself failed (an NSS/LDAP outage), the account is
rootor belowUID_MIN, the home directory is not absolute, or the home directory or the~/.forwarditself is not exclusively the target user’s (not owned by them, or group-/world-writable). The diagnostic goes to both standard output and standard error. The caller must treat this as retryable — the message stays queued rather than being bounced for a condition the operator can fix.The unsafe-home case is a property of the account rather than of the message: with
chmod 0775 ~aliceevery message toalicestops here and stays queued, and none of them reach local delivery.chmod g-w,o-won the home directory (and on~/.forward) is the fix.A
|commandthat asks to be tried again also exits 3: one that exits75(EX_TEMPFAIL, what procmail and maildrop report for a locked mailbox or a full disk), one that runs past the 300-second limit, and one that cannot be started at all. The command’s own diagnostic goes to both output streams. Directives earlier in the file have already acted by then and act again on the retry, as they would under any delivery agent that defers part of the way through a.forward.
When invoked by a user who is not a member of the pepsi-forward group, the
kernel refuses to execute the binary at all; the helper itself never runs.
85.1.56.1.6. Security¶
Membership of the pepsi-forward group is the access-control gate. The helper
runs arbitrary commands and writes arbitrary files only as the unprivileged
target user, never as root: it drops privilege completely before reading the
~/.forward or touching any of the user’s files, and refuses to act for the
root account.
85.1.56.1.7. See Also¶
pepsi-stage-dot-forward(1), pepsi-helper-maildir-writer(1), pepsi-dispatch(1), pepsi.conf(5)
85.1.56.1.8. Bugs¶
Report bugs to the Pepsi issue tracker.