From e77a67a4ddf0fd81c3e33a6c2c99dc4b6a5c1993 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 12 Oct 2012 14:49:45 -0400
Subject: [PATCH] pam: don't close sessions we didn't open

The CreateSession call may implicitly return an already opened
session, that the caller is already a part of.  In that case
we need to be careful not to release the session, since it
doesn't really belong to us.
---
 src/login/pam-module.c | 36 +++++++++++++++++++++++++++++-------
 1 file changed, 29 insertions(+), 7 deletions(-)

diff --git a/src/login/pam-module.c b/src/login/pam-module.c
index af108c4..f6c8a93 100644
--- a/src/login/pam-module.c
+++ b/src/login/pam-module.c
@@ -33,6 +33,7 @@
 #include <security/pam_misc.h>
 
 #include <systemd/sd-daemon.h>
+#include <systemd/sd-login.h>
 
 #include "util.h"
 #include "audit.h"
@@ -323,7 +324,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         struct passwd *pw;
         bool kill_processes = false, debug = false;
         const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type, *class, *cvtnr = NULL;
-        char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
+        char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL, *existing_id = NULL;
         DBusError error;
         uint32_t uid, pid;
         DBusMessageIter iter;
@@ -524,6 +525,13 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 goto finish;
         }
 
+        r = sd_pid_get_session(pid, &existing_id);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        pam_syslog(handle, LOG_ERR, "Could not query existing session for pid %ld.", (long) pid);
+                existing_id = NULL;
+        }
+
         if (debug)
                 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
                            "uid=%u pid=%u service=%s type=%s class=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
@@ -587,10 +595,21 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         }
 
         if (session_fd >= 0) {
-                r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
-                if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
-                        return r;
+                /* Check if CreateSession returned the session we're already in.  If so,
+                 * we don't own this session and shouldn't try to close it at pam_close_session
+                 * time.
+                 */
+                if (id != NULL && existing_id != NULL && strcmp(id, existing_id) == 0) {
+                        pam_syslog(handle, LOG_DEBUG, "logind session already created, using it instead: "
+                                   "uid=%u pid=%u service=%s type=%s class=%s seat=%s session=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
+                                   uid, pid, service, type, class, seat, id, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
+                        close_nointr_nofail(session_fd);
+                } else {
+                        r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
+                        if (r != PAM_SUCCESS) {
+                                pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
+                                return r;
+                        }
                 }
         }
 
@@ -620,6 +639,8 @@ finish:
         if (session_fd >= 0)
                 close_nointr_nofail(session_fd);
 
+        free(existing_id);
+
         return r;
 }
 
@@ -639,8 +660,10 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
         dbus_error_init(&error);
 
+        pam_get_data(handle, "systemd.session-fd", &p);
+
         id = pam_getenv(handle, "XDG_SESSION_ID");
-        if (id) {
+        if (p && id) {
 
                 /* Before we go and close the FIFO we need to tell
                  * logind that this is a clean session shutdown, so
@@ -684,7 +707,6 @@ _public_ PAM_EXTERN int pam_sm_close_session(
         r = PAM_SUCCESS;
 
 finish:
-        pam_get_data(handle, "systemd.session-fd", &p);
         if (p)
                 close_nointr(PTR_TO_INT(p) - 1);
 
-- 
1.7.12

