bug in OpenSSH_4.3p2: pam_open_session() called but not close for root users

Nye Liu nyet at mrv.com
Thu Apr 27 14:13:53 EST 2006


For root sessions pam_open_session is called, but not pam_close_session.

sshd behavior is broken for root logins because if pam session
is run from the child, close is never called due to exec:
on open
    since use_privsep is not set, parent calls do_exec_pty(),
    which does not open session. then, it skips calling do_setusercontext(),
    so it does not open session.

    child calls do_setusercontext(), which opens session.

on close
    child will not close sesion, because it exec'd the
    shell, and the sshd task is gone!

    parent will not close session, since session was not opened by
    parent, sshpam_session_open is not set.

as root:
parent process
    pid 205: do_exec_pty() uid 0
child process
    pid 208: do_setusercontext() uid 0
    pid 208: radius pam open session
    (exec shell)
parent process
    pid 208: radius pam close session (skipped!)
BROKEN!

but it does work for user logins because:
on open
    parent does not call do_exec_pty since use_privsep is set.
    parent then drops privs via setusercontext, which opens session
    and sets sshpam_session_open.
    
    child calls setusercontext, but that does not open session because
    uid!=0

on close
    child will not close session, because it exec'd the shell, and the
    sshd task is gone.

    parent will close session, since session was opened by parent,
    and the sshpam_session_open flag is set.

as nonroot:
parent process
    pid 223: do_setusercontext() uid 0
    pid 223: radius pam open session
child process
    pid 224: do_setusercontext() uid 104
    (exec shell)
parent process
    pid 223: radius pam close session
WORKS!


One solution is to open session from do_exec_pty() and to NOT reopen session
in if sshpam_session_open is set, so that in the case of the user, both
the parent AND the child do not open session. Has drawback that open
session is being called by parent, not child.

root:
parent process
    pid 205: do_exec_pty() uid 0
    pid 205: radius pam open session
child process
    pid 208: do_setusercontext() uid 0
    pid 208: radius pam open session (skipped!)
    (exec shell)
parent process
    pid 205: radius pam close session
OK!

another solution: "fake" set sshpam_session_open. has drawback
that if child failed to open session, you'll get a close when the
parent exists, but no open.

root:
parent process
    pid 205: do_exec_pty() uid 0
    pid 205: set sshpam_session_open=1
child process
    pid 208: do_setusercontext() uid 0
    pid 208: radius pam open session
    (exec shell)
parent process
    pid 205: radius pam close session 
OK!

see also 
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=132681
and
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=150968

possible patch for first solution:

Index: session.c
===================================================================
--- session.c	(revision 15)
+++ session.c	(working copy)
@@ -394,8 +394,10 @@
 	session_proctitle(s);
 
 #if defined(USE_PAM)
-	if (options.use_pam && !use_privsep)
+	if (options.use_pam && !use_privsep) {
+		do_pam_session();
 		do_pam_setcred(1);
+	}
 #endif /* USE_PAM */
 
 	/* Fork the child. */
@@ -531,8 +533,10 @@
 #if defined(USE_PAM)
 	if (options.use_pam) {
 		do_pam_set_tty(s->tty);
-		if (!use_privsep)
+		if (!use_privsep) {
+			do_pam_session();
 			do_pam_setcred(1);
+		}
 	}
 #endif
 
Index: auth-pam.c
===================================================================
--- auth-pam.c	(revision 15)
+++ auth-pam.c	(working copy)
@@ -996,6 +996,8 @@
 void
 do_pam_session(void)
 {
+	if(sshpam_session_open) return;
+
 	debug3("PAM: opening session");
 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
 	    (const void *)&store_conv);
-- 
Nye Liu
nliu at mrv.com
(818) 772-6235x248
(818) 772-0576 fax

"Who would be stupid enough to quote a fictitious character?"
	-- Don Quixote




More information about the openssh-unix-dev mailing list