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