This patch is to fix a X11 connection failure when a user's home directory is read-only. Oracle 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. --- hpn-ssh-hpn-18.4.2/session.c.orig +++ hpn-ssh-hpn-18.4.2/session.c @@ -62,6 +62,10 @@ #include #include +#ifdef PER_SESSION_XAUTHFILE +#include +#endif + #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" @@ -129,6 +133,11 @@ 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; @@ -1104,6 +1113,11 @@ 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++) { @@ -2006,6 +2020,11 @@ 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); @@ -2020,19 +2039,82 @@ 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. + */ + temporarily_use_uid(s->pw); + 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; + restore_uid(); + 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; + restore_uid(); + 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)); + } + restore_uid(); +#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; } @@ -2312,6 +2394,51 @@ PRIVSEP(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) { @@ -2459,6 +2586,9 @@ 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++) { @@ -2714,6 +2844,10 @@ 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. --- hpn-ssh-hpn-18.4.2/session.h.orig +++ hpn-ssh-hpn-18.4.2/session.h @@ -50,6 +50,9 @@ 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;