Home / Alt manpages / pam_echo(8)

  • pam_echo(8)
  • Admin command
  • linux

Show a Login Message from PAM with pam_echo

A janky welcome banner cobbled together in a shell wrapper is exactly what pam_echo replaces. It prints a text file as a proper PAM informational message with no custom script required. This guide sets one up, tests it safely and then removes it again. Allow about ten minutes.

pam_echo is a PAM module, not a command you run at a shell prompt: a PAM-aware service loads its shared object while working through its configuration. This guide matches the installed libpam-modules package, version 1.5.3-5ubuntu5.7. You need an ordinary shell for the checks, and root only when you edit a system PAM file or place a message under a protected directory.

1. Know what the module actually does

pam_echo prints a file as a PAM_TEXT_INFO message. It provides all four PAM module types: auth, account, password and session. It does not authenticate anyone, change a password or grant access on its own; the calling application's conversation function decides whether and how the message actually reaches the user.

The configuration line always 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. Keep the message short enough for the client to display cleanly.

2. Write and inspect a test message

Make a disposable message in /tmp first. This step is unprivileged and never touches 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"

Expect the temporary pathname followed by the line with the literal placeholders still in it. Do not expand those in the shell; pam_echo expands them itself when PAM reads the message:

  • %h is the local host, %H is the remote host.
  • %s is the PAM service name, %t is the controlling terminal.
  • %u is the local user, %U is the remote user.

Any other sequence starting with % just becomes the character after the percent sign, an easy trap: write a literal percent sign as %%.

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 only affects SSH, while a line in another file affects something else entirely. Look at the target file before touching it:

sudo sed -n '1,160p' /etc/pam.d/sshd

That is a privileged read. Keep the message file's pathname from the previous step (swap in the real one if it has moved from /tmp), then add a line like this at the point where the message makes sense, such as during authentication:

auth optional pam_echo.so file=/tmp/pam-echo.REPLACE

optional means this module's result is not, by itself, required for the stack to succeed, and it does not guarantee any client will actually display the text.

Warning

Do not paste this into a production PAM file without a recovery plan. A malformed PAM edit can prevent logins, and a message file under /tmp is not a durable deployment location. Use a root-owned path with sane permissions for a real setup, keep an already-open root session running while you test, and copy the line into a review buffer rather than trusting an automatic find-and-replace.

4. Rule out the common failure paths first

Before testing a real login, check the module and the message file both exist and are readable:

test -r /usr/lib/x86_64-linux-gnu/security/pam_echo.so && echo 'module exists'
test -r "$message_file" && echo 'message is readable'
  • Success returns PAM_SUCCESS.
  • Missing file, or the transaction carries PAM_SILENT: the module returns PAM_IGNORE and prints nothing.
  • Memory allocation failure: returns PAM_BUF_ERR.

A blank terminal therefore does not prove the line was skipped: check the path, the service file, and whether the client itself suppresses informational messages.

Test through the real service, not by invoking the shared object directly. For SSH, open a new connection from a separate terminal and keep the existing session running. For a display manager or anything else, use that service's own safe test path. Never test a password stack with repeated guesses, and never treat an account lockout policy as your test harness.

5. Undo and clean up

Remove the exact pam_echo.so line from the service file using the same privileged editing method you used to add it, then confirm it is gone:

sudo grep -nF 'pam_echo.so' /etc/pam.d/sshd || echo 'pam_echo line removed'

Then delete the disposable message, which only removes the one path 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 write down which PAM service owns the line.

Done means

  • Message file ready. It holds the text you intend and the PAM service can read it.
  • Module line correct. Right service, module type, control flag and file path.
  • Placeholders left alone for PAM to expand, with literal percent signs doubled.
  • Tested for real through an actual PAM client, with a separate recovery session open.
  • Cleaned up or documented. The temporary setup is gone, or the permanent change is recorded and reviewable.