From cb6c8f4391f75983553dc576014ab58695eee3eb Mon Sep 17 00:00:00 2001
From: oracle <solaris@oracle.com>
Date: Tue, 22 Dec 2015 17:12:50 -0800
Subject: [PATCH 18/34] Per-session xauthfile

This patch is to fix a X11 connection failure when a user's home directory
is read-only.

We have contributed back this fix to the OpenSSH upstream community. For
more information, see https://bugzilla.mindrot.org/show_bug.cgi?id=2440
In the future, if this fix is accepted by the upsteam in a later release, we
will remove this patch when we upgrade to that release.

diff -wpruN --no-dereference '--exclude=*.orig' a~/session.c a/session.c
--- a~/session.c	1970-01-01 00:00:00
+++ a/session.c	1970-01-01 00:00:00
@@ -62,6 +62,10 @@
 #include <unistd.h>
 #include <limits.h>
 
+#ifdef PER_SESSION_XAUTHFILE
+#include <libgen.h>
+#endif
+
 #include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
@@ -139,6 +143,11 @@ static void do_authenticated2(struct ssh
 
 static int session_pty_req(struct ssh *, Session *);
 
+#ifdef PER_SESSION_XAUTHFILE
+void   session_xauthfile_cleanup(Session *);
+void   cleanup_all_session_xauthfile();
+#endif
+
 /* import */
 extern ServerOptions options;
 extern char *__progname;
@@ -1087,6 +1096,11 @@ do_setup_env(struct ssh *ssh, Session *s
 		    auth_sock_name);
 
 
+#ifdef PER_SESSION_XAUTHFILE
+        if (s->auth_file != NULL)
+                child_set_env(&env, &envsize, "XAUTHORITY", s->auth_file);
+#endif
+
 	/* Set custom environment options from pubkey authentication. */
 	if (options.permit_user_env) {
 		for (n = 0 ; n < auth_opts->nenv; n++) {
@@ -1985,6 +1999,11 @@ session_x11_req(struct ssh *ssh, Session
 	int r, success;
 	u_char single_connection = 0;
 
+#ifdef PER_SESSION_XAUTHFILE
+	int fd;
+	char xauthdir[] = "/tmp/ssh-xauth-XXXXXX";
+#endif
+
 	if (s->auth_proto != NULL || s->auth_data != NULL) {
 		error("session_x11_req: session %d: "
 		    "x11 forwarding already active", s->self);
@@ -1999,19 +2018,78 @@ session_x11_req(struct ssh *ssh, Session
 
 	s->single_connection = single_connection;
 
-	if (xauth_valid_string(s->auth_proto) &&
-	    xauth_valid_string(s->auth_data))
-		success = session_setup_x11fwd(ssh, s);
-	else {
+	if (!xauth_valid_string(s->auth_proto) ||
+	    !xauth_valid_string(s->auth_data)) {
 		success = 0;
 		error("Invalid X11 forwarding data");
+		goto out;
+	}
+
+#ifdef PER_SESSION_XAUTHFILE
+	/*
+	 * Create per session X authority file in the /tmp directory.
+	 *
+	 * If mkdtemp() or open() fails then s->auth_file remains NULL which
+	 * means that we won't set XAUTHORITY variable in child's environment
+	 * and xauth(1) will use the default location for the authority file.
+	 */
+	if (mkdtemp(xauthdir) != NULL) {
+		s->auth_file = xmalloc(MAXPATHLEN);
+		if (snprintf(s->auth_file, MAXPATHLEN, "%s/xauthfile",
+		    xauthdir) >= MAXPATHLEN) {
+			error("temporary X authority file name was too long "
+			    "for the buffer allocated");
+			success = 0;
+			goto out;
 	}
+		/*
+		 * we don't want that "creating new authority file" message to
+		 * be printed by xauth(1) so we must create that file
+		 * beforehand.
+		 */
+		if ((fd = open(s->auth_file, O_CREAT | O_EXCL | O_RDONLY,
+		    S_IRUSR | S_IWUSR)) == -1) {
+			error("failed to create the temporary X authority "
+			    "file %s: %.100s; will use the default one",
+			    s->auth_file, strerror(errno));
+			free(s->auth_file);
+			s->auth_file = NULL;
+			if (rmdir(xauthdir) == -1) {
+				error("cannot remove xauth directory "
+				    "%s: %.100s", xauthdir, strerror(errno));
+			}
+		} else {
+			if (close(fd) != 0) {
+				error("close() failed on temporary X authority "
+				    "file: %s", strerror(errno));
+				success = 0;
+				goto out;
+			}
+			debug("temporary X authority file %s created",
+			    s->auth_file);
+			debug("session number = %d", s->self);
+		}
+	} else {
+		error("failed to create a directory for the temporary X "
+		    "authority file: %.100s; will use the default xauth file",
+		    strerror(errno));
+	}
+#endif
+
+	success = session_setup_x11fwd(ssh, s);
+
+out:
 	if (!success) {
 		free(s->auth_proto);
 		free(s->auth_data);
 		s->auth_proto = NULL;
 		s->auth_data = NULL;
+#ifdef PER_SESSION_XAUTHFILE
+		free(s->auth_file);
+		s->auth_file = NULL;
+#endif
 	}
+
 	return success;
 }
 
@@ -2291,6 +2369,51 @@ session_pty_cleanup(Session *s)
 	mm_session_pty_cleanup2(s);
 }
 
+#ifdef PER_SESSION_XAUTHFILE
+/*
+ * We use a different temporary X authority file per session so we should
+ * remove those files when cleanup_exit() is called.
+ */
+void
+session_xauthfile_cleanup(Session *s)
+{
+	if (s == NULL || s->auth_file == NULL) {
+		return;
+	}
+
+	debug("session_xauthfile_cleanup: session %d removing %s", s->self,
+	    s->auth_file);
+
+	if (unlink(s->auth_file) == -1) {
+		error("session_xauthfile_cleanup: cannot remove xauth file "
+		    "%s: %.100s", s->auth_file, strerror(errno));
+		return;
+	}
+
+	/* dirname() will modify s->auth_file but that's ok */
+	if (rmdir(dirname(s->auth_file)) == -1) {
+		error("session_xauthfile_cleanup: "
+		    "cannot remove xauth directory %s: %.100s",
+		    s->auth_file, strerror(errno));
+		return;
+	}
+	free(s->auth_file);
+	s->auth_file = NULL;
+}
+
+/*
+ * This is called by do_cleanup() when cleanup_exit() is called. 
+ */
+void
+cleanup_all_session_xauthfile()
+{
+	int i;
+	for (i = 0; i < sessions_nalloc; i++) {
+                session_xauthfile_cleanup(&sessions[i]);
+	}
+}
+#endif
+
 static char *
 sig2name(int sig)
 {
@@ -2438,6 +2561,9 @@ session_close(struct ssh *ssh, Session *
 	free(s->auth_display);
 	free(s->auth_data);
 	free(s->auth_proto);
+#ifdef PER_SESSION_XAUTHFILE
+	session_xauthfile_cleanup(s);
+#endif
 	free(s->subsys);
 	if (s->env != NULL) {
 		for (i = 0; i < s->num_env; i++) {
@@ -2693,6 +2819,10 @@ do_cleanup(struct ssh *ssh, Authctxt *au
 		auth_info_file = NULL;
 	}
 
+#ifdef PER_SESSION_XAUTHFILE
+	cleanup_all_session_xauthfile();
+#endif
+
 	/*
 	 * Cleanup ptys/utmp only if privsep is disabled,
 	 * or if running in monitor.
diff -wpruN --no-dereference '--exclude=*.orig' a~/session.h a/session.h
--- a~/session.h	1970-01-01 00:00:00
+++ a/session.h	1970-01-01 00:00:00
@@ -50,6 +50,9 @@ struct Session {
 	char	*auth_display;
 	char	*auth_proto;
 	char	*auth_data;
+#ifdef PER_SESSION_XAUTHFILE
+	char    *auth_file;	/* xauth(1) authority file */
+#endif
 	int	single_connection;
 
 	int	chanid;
