[PATCH] auth-pam: don't leak PAM env strings after pam_putenv()

Avinash Duduskar avinash.duduskar at gmail.com
Mon May 18 17:09:57 AEST 2026


pam_putenv(3) documents that it operates on a copy of name_value
and that the application retains ownership of the buffer:

  "pam_putenv() operates on a copy of name_value, which means in
   contrast to putenv(3), the application is responsible for
   freeing the data."

import_environments() carries a comment flagging this contract
as uncertain and chooses to leak env rather than free it. The
contract is unambiguous: libpam strdups name_value on entry
(libpam/pam_env.c, _pam_strdup) and frees the internal copy in
pam_end via _pam_drop_env. The caller's buffer is safe to free
immediately after pam_putenv returns, regardless of return
code.

Free the per-iteration env allocation produced by
sshbuf_get_cstring() after the pam_putenv() call and remove the
XXX block.

Verified under Valgrind 3.25 on V_10_3_P1 (2e6f9c6a4e59) built
with --with-pam --without-sandbox. PAM stack: pam_permit auth
+ pam_env exposing 2 env strings, exercised via
keyboard-interactive. Unpatched build reports on two
independent runs:

  63 bytes in 2 blocks are definitely lost
     at malloc
     by sshbuf_get_cstring (sshbuf-getput-basic.c:290)
     by import_environments (auth-pam.c:352)
     by sshpam_query (auth-pam.c:913)
     by mm_answer_pam_query (monitor.c:1205)
     by monitor_read (monitor.c:544)
     by monitor_child_preauth (monitor.c:299)
     by privsep_preauth (sshd-session.c:326)
     by main (sshd-session.c:1241)

Patched build: zero import_environments-rooted records;
definitely-lost delta exactly -63 bytes / -2 blocks in the
matched sshd-session log; other leak signatures (setproctitle,
monitor_read sshbuf_new) unchanged.

Scope: the leak fires on the sshpam_thread-based asynchronous
PAM auth path used by keyboard-interactive authentication and
ChallengeResponseAuthentication. Publickey authentication does
not engage sshpam_thread and so does not invoke
import_environments. Password authentication via
sshpam_auth_passwd is a separate synchronous path that also
bypasses this code. Deployments using publickey-only or
password-only auth are unaffected by both the pre-existing leak
and this fix.

Real-world PAM stacks push more env strings than the test
fixture, but the per-session leak is bounded by sshd-session's
per-connection lifetime and not user-visible. Hygiene cleanup
flowing from the contract clarification, not a fix for an
observed runtime issue.

Signed-off-by: Avinash Duduskar <avinash.duduskar at gmail.com>
---
 auth-pam.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/auth-pam.c b/auth-pam.c
index 29607e041..33a4d6c17 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -356,11 +356,7 @@ import_environments(struct sshbuf *b)
 			error("PAM: pam_putenv: %s",
 			    pam_strerror(sshpam_handle, r));
 		}
-		/*
-		 * XXX this possibly leaks env because it is not documented
-		 * what pam_putenv() does with it. Does it copy it? Does it
-		 * take ownership? We don't know, so it's safest just to leak.
-		 */
+		free(env);
 	}
 #endif
 }
-- 
2.54.0



More information about the openssh-unix-dev mailing list