putty/unix/ux_x11.c

209 строки
5.5 KiB
C

/*
* ux_x11.c: fetch local auth data for X forwarding.
*/
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "putty.h"
#include "ssh.h"
#include "network.h"
void platform_get_x11_auth(struct X11Display *disp, Conf *conf)
{
char *xauthfile;
bool needs_free;
/*
* Find the .Xauthority file.
*/
needs_free = false;
xauthfile = getenv("XAUTHORITY");
if (!xauthfile) {
xauthfile = getenv("HOME");
if (xauthfile) {
xauthfile = dupcat(xauthfile, "/.Xauthority");
needs_free = true;
}
}
if (xauthfile) {
x11_get_auth_from_authfile(disp, xauthfile);
if (needs_free)
sfree(xauthfile);
}
}
const bool 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_nm();
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 = sk_namelookup(hostname, &canonicalname, addrtype);
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.
*/
close(0);
close(1);
close(2);
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);
/* a_unix doesn't need freeing, because new_unix_listener took it over */
sfree(authfilename);
strbuf_free(authfiledata);
sfree(unix_path);
return nsockets;
}