x11.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * x11.c: fetch local auth data for X forwarding.
  3. */
  4. #include <ctype.h>
  5. #include <unistd.h>
  6. #include <assert.h>
  7. #include <stdlib.h>
  8. #include <errno.h>
  9. #include <sys/types.h>
  10. #include <sys/stat.h>
  11. #include "putty.h"
  12. #include "ssh.h"
  13. #include "network.h"
  14. void platform_get_x11_auth(struct X11Display *disp, Conf *conf)
  15. {
  16. char *xauthfile;
  17. bool needs_free;
  18. /*
  19. * Find the .Xauthority file.
  20. */
  21. needs_free = false;
  22. xauthfile = getenv("XAUTHORITY");
  23. if (!xauthfile) {
  24. xauthfile = getenv("HOME");
  25. if (xauthfile) {
  26. xauthfile = dupcat(xauthfile, "/.Xauthority");
  27. needs_free = true;
  28. }
  29. }
  30. if (xauthfile) {
  31. x11_get_auth_from_authfile(disp, xauthfile);
  32. if (needs_free)
  33. sfree(xauthfile);
  34. }
  35. }
  36. const bool platform_uses_x11_unix_by_default = true;
  37. int platform_make_x11_server(Plug *plug, const char *progname, int mindisp,
  38. const char *screen_number_suffix,
  39. ptrlen authproto, ptrlen authdata,
  40. Socket **sockets, Conf *conf)
  41. {
  42. char *tmpdir;
  43. char *authfilename = NULL;
  44. strbuf *authfiledata = NULL;
  45. char *unix_path = NULL;
  46. SockAddr *a_tcp = NULL, *a_unix = NULL;
  47. int authfd;
  48. FILE *authfp;
  49. int displayno;
  50. authfiledata = strbuf_new_nm();
  51. int nsockets = 0;
  52. /*
  53. * Look for a free TCP port to run our server on.
  54. */
  55. for (displayno = mindisp;; displayno++) {
  56. const char *err;
  57. int tcp_port = displayno + 6000;
  58. int addrtype = ADDRTYPE_IPV4;
  59. sockets[nsockets] = new_listener(
  60. NULL, tcp_port, plug, false, conf, addrtype);
  61. err = sk_socket_error(sockets[nsockets]);
  62. if (!err) {
  63. char *hostname = get_hostname();
  64. if (hostname) {
  65. char *canonicalname = NULL;
  66. a_tcp = sk_namelookup(hostname, &canonicalname, addrtype);
  67. sfree(canonicalname);
  68. }
  69. sfree(hostname);
  70. nsockets++;
  71. break; /* success! */
  72. } else {
  73. sk_close(sockets[nsockets]);
  74. }
  75. /*
  76. * If we weren't able to bind to this port because it's in use
  77. * by another program, go round this loop and try again. But
  78. * for any other reason, give up completely and return failure
  79. * to our caller.
  80. *
  81. * sk_socket_error currently has no machine-readable component
  82. * (it would need a cross-platform abstraction of the socket
  83. * error types we care about, plus translation from each OS
  84. * error enumeration into that). So we use the disgusting
  85. * approach of a string compare between the error string and
  86. * the one EADDRINUSE would have given :-(
  87. */
  88. if (strcmp(err, strerror(EADDRINUSE)))
  89. goto out;
  90. }
  91. if (a_tcp) {
  92. x11_format_auth_for_authfile(
  93. BinarySink_UPCAST(authfiledata),
  94. a_tcp, displayno, authproto, authdata);
  95. }
  96. /*
  97. * Try to establish the Unix-domain analogue. That may or may not
  98. * work - file permissions in /tmp may prevent it, for example -
  99. * but it's worth a try, and we don't consider it a fatal error if
  100. * it doesn't work.
  101. */
  102. unix_path = dupprintf("/tmp/.X11-unix/X%d", displayno);
  103. a_unix = unix_sock_addr(unix_path);
  104. sockets[nsockets] = new_unix_listener(a_unix, plug);
  105. if (!sk_socket_error(sockets[nsockets])) {
  106. x11_format_auth_for_authfile(
  107. BinarySink_UPCAST(authfiledata),
  108. a_unix, displayno, authproto, authdata);
  109. nsockets++;
  110. } else {
  111. sk_close(sockets[nsockets]);
  112. sfree(unix_path);
  113. unix_path = NULL;
  114. }
  115. /*
  116. * Decide where the authority data will be written.
  117. */
  118. tmpdir = getenv("TMPDIR");
  119. if (!tmpdir || !*tmpdir)
  120. tmpdir = "/tmp";
  121. authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX");
  122. {
  123. int oldumask = umask(077);
  124. authfd = mkstemp(authfilename);
  125. umask(oldumask);
  126. }
  127. if (authfd < 0) {
  128. while (nsockets-- > 0)
  129. sk_close(sockets[nsockets]);
  130. goto out;
  131. }
  132. /*
  133. * Spawn a subprocess which will try to reliably delete our
  134. * auth file when we terminate, in case we die unexpectedly.
  135. */
  136. {
  137. int cleanup_pipe[2];
  138. pid_t pid;
  139. /* Don't worry if pipe or fork fails; it's not _that_ critical. */
  140. if (!pipe(cleanup_pipe)) {
  141. if ((pid = fork()) == 0) {
  142. int buf[1024];
  143. /*
  144. * Our parent process holds the writing end of
  145. * this pipe, and writes nothing to it. Hence,
  146. * we expect read() to return EOF as soon as
  147. * that process terminates.
  148. */
  149. close(0);
  150. close(1);
  151. close(2);
  152. setpgid(0, 0);
  153. close(cleanup_pipe[1]);
  154. close(authfd);
  155. while (read(cleanup_pipe[0], buf, sizeof(buf)) > 0);
  156. unlink(authfilename);
  157. if (unix_path)
  158. unlink(unix_path);
  159. _exit(0);
  160. } else if (pid < 0) {
  161. close(cleanup_pipe[0]);
  162. close(cleanup_pipe[1]);
  163. } else {
  164. close(cleanup_pipe[0]);
  165. cloexec(cleanup_pipe[1]);
  166. }
  167. }
  168. }
  169. authfp = fdopen(authfd, "wb");
  170. fwrite(authfiledata->u, 1, authfiledata->len, authfp);
  171. fclose(authfp);
  172. {
  173. char *display = dupprintf(":%d%s", displayno, screen_number_suffix);
  174. conf_set_str_str(conf, CONF_environmt, "DISPLAY", display);
  175. sfree(display);
  176. }
  177. conf_set_str_str(conf, CONF_environmt, "XAUTHORITY", authfilename);
  178. /*
  179. * FIXME: return at least the DISPLAY and XAUTHORITY env settings,
  180. * and perhaps also the display number
  181. */
  182. out:
  183. if (a_tcp)
  184. sk_addr_free(a_tcp);
  185. /* a_unix doesn't need freeing, because new_unix_listener took it over */
  186. sfree(authfilename);
  187. strbuf_free(authfiledata);
  188. sfree(unix_path);
  189. return nsockets;
  190. }