Show a Useful PAM Login Message with pam_echo
By the end of this guide, a PAM stack can display a small text message such as the system name, the service being used and the account name. pam_echo is a PAM module, not a command you run at a shell prompt: a PAM-aware service loads its shared object while processing its configuration.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide uses the installed libpam-modules package, version 1.5.3-5ubuntu5.7 on this machine. Allow about 10 minutes. You need an ordinary shell for the checks and root access only if you change a system PAM file or place a message under a protected directory.
Checkpoint 1: understand what will change
pam_echo prints a file as a PAM text-information message. It provides all four PAM module types: auth, account, password and session. The module does not authenticate a user, change a password or grant access. The PAM application's conversation function decides whether and how that informational message reaches the user.
The configuration line has this shape:
type control pam_echo.so file=/path/to/message
type is the point in the PAM transaction, control is the stack rule and file= names the message file. The file contents are passed through PAM as PAM_TEXT_INFO. Keep the message short enough for the client to display clearly.
Checkpoint 2: create and inspect a message
First make a disposable message in /tmp. This step is unprivileged and does not alter PAM configuration:
message_file=$(mktemp /tmp/pam-echo.XXXXXX)
chmod 0644 "$message_file"
cat >"$message_file" <<'EOF'
Welcome to %h. Service: %s. Account: %u.
EOF
printf 'Message file: %s\n' "$message_file"
sed -n '1,3p' "$message_file"
Expected output is the temporary pathname followed by the line containing the literal placeholders. Do not expand those values in the shell. pam_echo expands them when PAM reads the message:
%His the remote host, while%his the local host.%sis the PAM service name and%tis the controlling terminal.%Uis the remote user and%uis the local user.
Any other sequence beginning with % becomes the character after the percent sign. That fallback is easy to overlook: a literal percent sign in the message must therefore be written as %%.
Checkpoint 3: add the module to a PAM stack
Choose the service deliberately. PAM configuration is service-specific, so a line in /etc/pam.d/sshd affects SSH authentication, while a line in another file affects a different client. Inspect the target file before editing:
sudo sed -n '1,160p' /etc/pam.d/sshd
This is a privileged read. If the message file is still under /tmp, replace its pathname with the exact value printed in the previous step. A representative informational line is:
auth optional pam_echo.so file=/tmp/pam-echo.REPLACE
Place it at the point where the message makes sense. For example, an auth line is processed during authentication. The optional control flag means this module's result is not, by itself, required for the stack to succeed; it does not guarantee that a client will display the text.
Do not paste this example into a production PAM file without a recovery plan. A malformed PAM edit can prevent logins, and a message file in /tmp is not a durable deployment location. Use a root-owned path with suitable permissions for a real configuration, and keep an already-open root session while testing. The exact file edit is service-specific, so copy the line into a temporary review buffer first rather than using an automatic replacement command.
Checkpoint 4: verify the common failure paths
Check that the module exists and that the configured file is readable before testing a login:
test -r /usr/lib/x86_64-linux-gnu/security/pam_echo.so && echo 'module exists'
test -r "$message_file" && echo 'message is readable'
The module's documented return values explain several confusing results. A successful message returns PAM_SUCCESS. If the file does not exist, or the PAM transaction has the PAM_SILENT flag, the module returns PAM_IGNORE and prints nothing. A memory allocation failure returns PAM_BUF_ERR. A blank terminal therefore does not prove that the line was skipped by the stack: check the path, the service file and whether the client suppresses informational messages.
Test through the actual PAM-aware service rather than invoking the shared object. For SSH, make a new connection from a separate terminal and keep the existing session open. For a graphical display manager or another service, use that service's own safe test path. Do not repeatedly test a password stack with guesses, and do not use an account lockout policy as an experiment.
Undo and cleanup
After testing, remove the exact pam_echo.so line from the service file using the same privileged editing method you used to add it. Confirm the line is gone:
sudo grep -nF 'pam_echo.so' /etc/pam.d/sshd || echo 'pam_echo line removed'
Then remove the disposable message. This deletes only the pathname held in the shell variable:
rm -- "$message_file"
If the shell was closed, locate the specific temporary file with find /tmp -maxdepth 1 -type f -name 'pam-echo.*' -user "$(id -un)" -print and remove only the file you recognise. For a permanent setup, replace the temporary path with a reviewed root-owned file and document which PAM service owns the line.
Done means
- The message file contains the text you intend to show and is readable by the PAM service.
- The module line names the correct service, module type, control flag and file path.
- Placeholders are left for PAM to expand, and literal percent signs are doubled.
- A real PAM client was tested with a separate recovery session available.
- The temporary configuration and message have been removed, or the permanent change is recorded and reviewable.