x11.c 6.2 KB

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