Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'unix/x11.c')
-rw-r--r--unix/x11.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/unix/x11.c b/unix/x11.c
new file mode 100644
index 00000000..710ff849
--- /dev/null
+++ b/unix/x11.c
@@ -0,0 +1,221 @@
+/*
+ * 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 we weren't able to bind to this port because it's in use
+ * by another program, go round this loop and try again. But
+ * for any other reason, give up completely and return failure
+ * to our caller.
+ *
+ * sk_socket_error currently has no machine-readable component
+ * (it would need a cross-platform abstraction of the socket
+ * error types we care about, plus translation from each OS
+ * error enumeration into that). So we use the disgusting
+ * approach of a string compare between the error string and
+ * the one EADDRINUSE would have given :-(
+ */
+ if (strcmp(err, strerror(EADDRINUSE)))
+ 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;
+}