1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Server prep: routine to create a local X display.

This will be used for the server side of X forwarding. It wraps up the
mechanics of listening on the right TCP port and (if possible) the
associated AF_UNIX socket, and also creates an appropriate X authority
file containing authorisation data provided by its caller.

Like the new platform_create_agent_socket, this function spawns a
watchdog subprocess to clean up the mess afterwards, in the hope of at
least _most_ of the time not leaving old sockets and authority files
lying around /tmp,
This commit is contained in:
Simon Tatham 2018-10-20 22:42:17 +01:00
parent f4db9196da
commit 61976b417e
3 changed files with 210 additions and 0 deletions

3
ssh.h
View File

@ -1002,6 +1002,9 @@ char *platform_get_x_display(void);
*/
void x11_get_auth_from_authfile(struct X11Display *display,
const char *authfilename);
void x11_format_auth_for_authfile(
BinarySink *bs, SockAddr *addr, int display_no,
ptrlen authproto, ptrlen authdata);
int x11_identify_auth_proto(ptrlen protoname);
void *x11_dehexify(ptrlen hex, int *outlen);

View File

@ -7,6 +7,8 @@
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "putty.h"
#include "ssh.h"
@ -38,3 +40,166 @@ void platform_get_x11_auth(struct X11Display *disp, Conf *conf)
}
const int platform_uses_x11_unix_by_default = TRUE;
int platform_make_x11_server(Plug *plug, const char *progname, int mindisp,
const char *screen_number_suffix,
ptrlen authproto, ptrlen authdata,
Socket **sockets, Conf *conf)
{
char *tmpdir;
char *authfilename = NULL;
strbuf *authfiledata = NULL;
char *unix_path = NULL;
SockAddr *a_tcp = NULL, *a_unix = NULL;
int authfd;
FILE *authfp;
int displayno;
authfiledata = strbuf_new();
int nsockets = 0;
/*
* Look for a free TCP port to run our server on.
*/
for (displayno = mindisp;; displayno++) {
const char *err;
int tcp_port = displayno + 6000;
int addrtype = ADDRTYPE_IPV4;
sockets[nsockets] = new_listener(
NULL, tcp_port, plug, FALSE, conf, addrtype);
err = sk_socket_error(sockets[nsockets]);
if (!err) {
char *hostname = get_hostname();
if (hostname) {
char *canonicalname = NULL;
a_tcp = name_lookup(hostname, tcp_port, &canonicalname,
conf, addrtype, NULL, "");
sfree(canonicalname);
}
sfree(hostname);
nsockets++;
break; /* success! */
} else {
sk_close(sockets[nsockets]);
}
if (!strcmp(err, strerror(EADDRINUSE))) /* yuck! */
goto out;
}
if (a_tcp) {
x11_format_auth_for_authfile(
BinarySink_UPCAST(authfiledata),
a_tcp, displayno, authproto, authdata);
}
/*
* Try to establish the Unix-domain analogue. That may or may not
* work - file permissions in /tmp may prevent it, for example -
* but it's worth a try, and we don't consider it a fatal error if
* it doesn't work.
*/
unix_path = dupprintf("/tmp/.X11-unix/X%d", displayno);
a_unix = unix_sock_addr(unix_path);
sockets[nsockets] = new_unix_listener(a_unix, plug);
if (!sk_socket_error(sockets[nsockets])) {
x11_format_auth_for_authfile(
BinarySink_UPCAST(authfiledata),
a_unix, displayno, authproto, authdata);
nsockets++;
} else {
sk_close(sockets[nsockets]);
sfree(unix_path);
unix_path = NULL;
}
/*
* Decide where the authority data will be written.
*/
tmpdir = getenv("TMPDIR");
if (!tmpdir || !*tmpdir)
tmpdir = "/tmp";
authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX");
{
int oldumask = umask(077);
authfd = mkstemp(authfilename);
umask(oldumask);
}
if (authfd < 0) {
while (nsockets-- > 0)
sk_close(sockets[nsockets]);
goto out;
}
/*
* Spawn a subprocess which will try to reliably delete our
* auth file when we terminate, in case we die unexpectedly.
*/
{
int cleanup_pipe[2];
pid_t pid;
/* Don't worry if pipe or fork fails; it's not _that_ critical. */
if (!pipe(cleanup_pipe)) {
if ((pid = fork()) == 0) {
int buf[1024];
/*
* Our parent process holds the writing end of
* this pipe, and writes nothing to it. Hence,
* we expect read() to return EOF as soon as
* that process terminates.
*/
setpgid(0, 0);
close(cleanup_pipe[1]);
close(authfd);
while (read(cleanup_pipe[0], buf, sizeof(buf)) > 0);
unlink(authfilename);
if (unix_path)
unlink(unix_path);
_exit(0);
} else if (pid < 0) {
close(cleanup_pipe[0]);
close(cleanup_pipe[1]);
} else {
close(cleanup_pipe[0]);
cloexec(cleanup_pipe[1]);
}
}
}
authfp = fdopen(authfd, "wb");
fwrite(authfiledata->u, 1, authfiledata->len, authfp);
fclose(authfp);
{
char *display = dupprintf(":%d%s", displayno, screen_number_suffix);
conf_set_str_str(conf, CONF_environmt, "DISPLAY", display);
sfree(display);
}
conf_set_str_str(conf, CONF_environmt, "XAUTHORITY", authfilename);
/*
* FIXME: return at least the DISPLAY and XAUTHORITY env settings,
* and perhaps also the display number
*/
out:
if (a_tcp)
sk_addr_free(a_tcp);
if (a_unix)
sk_addr_free(a_unix);
sfree(authfilename);
strbuf_free(authfiledata);
sfree(unix_path);
return nsockets;
}

View File

@ -449,6 +449,15 @@ ptrlen BinarySource_get_string_xauth(BinarySource *src)
#define get_string_xauth(src) \
BinarySource_get_string_xauth(BinarySource_UPCAST(src))
void BinarySink_put_stringpl_xauth(BinarySink *bs, ptrlen pl)
{
assert((pl.len >> 16) == 0);
put_uint16(bs, pl.len);
put_data(bs, pl.ptr, pl.len);
}
#define put_stringpl_xauth(bs, ptrlen) \
BinarySink_put_stringpl_xauth(BinarySink_UPCAST(bs),ptrlen)
void x11_get_auth_from_authfile(struct X11Display *disp,
const char *authfilename)
{
@ -631,6 +640,39 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
sfree(ourhostname);
}
void x11_format_auth_for_authfile(
BinarySink *bs, SockAddr *addr, int display_no,
ptrlen authproto, ptrlen authdata)
{
if (sk_address_is_special_local(addr)) {
char *ourhostname = get_hostname();
put_uint16(bs, 256); /* indicates Unix-domain socket */
put_stringpl_xauth(bs, ptrlen_from_asciz(ourhostname));
sfree(ourhostname);
} else if (sk_addrtype(addr) == ADDRTYPE_IPV4) {
char ipv4buf[4];
sk_addrcopy(addr, ipv4buf);
put_uint16(bs, 0); /* indicates IPv4 */
put_stringpl_xauth(bs, make_ptrlen(ipv4buf, 4));
} else if (sk_addrtype(addr) == ADDRTYPE_IPV6) {
char ipv6buf[16];
sk_addrcopy(addr, ipv6buf);
put_uint16(bs, 6); /* indicates IPv6 */
put_stringpl_xauth(bs, make_ptrlen(ipv6buf, 16));
} else {
assert(FALSE && "Bad address type in x11_format_auth_for_authfile");
}
{
char *numberbuf = dupprintf("%d", display_no);
put_stringpl_xauth(bs, ptrlen_from_asciz(numberbuf));
sfree(numberbuf);
}
put_stringpl_xauth(bs, authproto);
put_stringpl_xauth(bs, authdata);
}
static void x11_log(Plug *p, int type, SockAddr *addr, int port,
const char *error_msg, int error_code)
{