Fixed issue #3848: "Git Sync..." > "Compare tags" does not properly clear state from...
[TortoiseGit.git] / src / TortoisePlink / X11FWD.C
blob579a06494adb0b064ed0f10c7ede0bc08c4956c4
1 /*\r
2  * Platform-independent bits of X11 forwarding.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <assert.h>\r
8 #include <time.h>\r
9 \r
10 #include "putty.h"\r
11 #include "ssh.h"\r
12 #include "sshchan.h"\r
13 #include "tree234.h"\r
15 static inline uint16_t GET_16BIT_X11(char endian, const void *p)\r
16 {\r
17     return endian == 'B' ? GET_16BIT_MSB_FIRST(p) : GET_16BIT_LSB_FIRST(p);\r
18 }\r
20 static inline void PUT_16BIT_X11(char endian, void *p, uint16_t value)\r
21 {\r
22     if (endian == 'B')\r
23         PUT_16BIT_MSB_FIRST(p, value);\r
24     else\r
25         PUT_16BIT_LSB_FIRST(p, value);\r
26 }\r
28 const char *const x11_authnames[] = {\r
29     "", "MIT-MAGIC-COOKIE-1", "XDM-AUTHORIZATION-1"\r
30 };\r
32 struct XDMSeen {\r
33     unsigned int time;\r
34     unsigned char clientid[6];\r
35 };\r
37 typedef struct X11Connection {\r
38     unsigned char firstpkt[12];        /* first X data packet */\r
39     tree234 *authtree;\r
40     struct X11Display *disp;\r
41     char *auth_protocol;\r
42     unsigned char *auth_data;\r
43     int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;\r
44     bool verified;\r
45     bool input_wanted;\r
46     bool no_data_sent_to_x_client;\r
47     char *peer_addr;\r
48     int peer_port;\r
49     SshChannel *c;               /* channel structure held by SSH backend */\r
50     Socket *s;\r
52     Plug plug;\r
53     Channel chan;\r
54 } X11Connection;\r
56 static int xdmseen_cmp(void *a, void *b)\r
57 {\r
58     struct XDMSeen *sa = a, *sb = b;\r
59     return sa->time > sb->time ? 1 :\r
60            sa->time < sb->time ? -1 :\r
61            memcmp(sa->clientid, sb->clientid, sizeof(sa->clientid));\r
62 }\r
64 struct X11FakeAuth *x11_invent_fake_auth(tree234 *authtree, int authtype)\r
65 {\r
66     struct X11FakeAuth *auth = snew(struct X11FakeAuth);\r
67     int i;\r
69     /*\r
70      * This function has the job of inventing a set of X11 fake auth\r
71      * data, and adding it to 'authtree'. We must preserve the\r
72      * property that for any given actual authorisation attempt, _at\r
73      * most one_ thing in the tree can possibly match it.\r
74      *\r
75      * For MIT-MAGIC-COOKIE-1, that's not too difficult: the match\r
76      * criterion is simply that the entire cookie is correct, so we\r
77      * just have to make sure we don't make up two cookies the same.\r
78      * (Vanishingly unlikely, but we check anyway to be sure, and go\r
79      * round again inventing a new cookie if add234 tells us the one\r
80      * we thought of is already in use.)\r
81      *\r
82      * For XDM-AUTHORIZATION-1, it's a little more fiddly. The setup\r
83      * with XA1 is that half the cookie is used as a DES key with\r
84      * which to CBC-encrypt an assortment of stuff. Happily, the stuff\r
85      * encrypted _begins_ with the other half of the cookie, and the\r
86      * IV is always zero, which means that any valid XA1 authorisation\r
87      * attempt for a given cookie must begin with the same cipher\r
88      * block, consisting of the DES ECB encryption of the first half\r
89      * of the cookie using the second half as a key. So we compute\r
90      * that cipher block here and now, and use it as the sorting key\r
91      * for distinguishing XA1 entries in the tree.\r
92      */\r
94     if (authtype == X11_MIT) {\r
95         auth->proto = X11_MIT;\r
97         /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */\r
98         auth->datalen = 16;\r
99         auth->data = snewn(auth->datalen, unsigned char);\r
100         auth->xa1_firstblock = NULL;\r
102         while (1) {\r
103             random_read(auth->data, auth->datalen);\r
104             if (add234(authtree, auth) == auth)\r
105                 break;\r
106         }\r
108         auth->xdmseen = NULL;\r
109     } else {\r
110         assert(authtype == X11_XDM);\r
111         auth->proto = X11_XDM;\r
113         /* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */\r
114         auth->datalen = 16;\r
115         auth->data = snewn(auth->datalen, unsigned char);\r
116         auth->xa1_firstblock = snewn(8, unsigned char);\r
117         memset(auth->xa1_firstblock, 0, 8);\r
119         while (1) {\r
120             random_read(auth->data, 15);\r
121             auth->data[15] = auth->data[8];\r
122             auth->data[8] = 0;\r
124             memcpy(auth->xa1_firstblock, auth->data, 8);\r
125             des_encrypt_xdmauth(auth->data + 9, auth->xa1_firstblock, 8);\r
126             if (add234(authtree, auth) == auth)\r
127                 break;\r
128         }\r
130         auth->xdmseen = newtree234(xdmseen_cmp);\r
131     }\r
132     auth->protoname = dupstr(x11_authnames[auth->proto]);\r
133     auth->datastring = snewn(auth->datalen * 2 + 1, char);\r
134     for (i = 0; i < auth->datalen; i++)\r
135         sprintf(auth->datastring + i*2, "%02x",\r
136                 auth->data[i]);\r
138     auth->disp = NULL;\r
139     auth->share_cs = NULL;\r
140     auth->share_chan = NULL;\r
142     return auth;\r
145 void x11_free_fake_auth(struct X11FakeAuth *auth)\r
147     if (auth->data)\r
148         smemclr(auth->data, auth->datalen);\r
149     sfree(auth->data);\r
150     sfree(auth->protoname);\r
151     sfree(auth->datastring);\r
152     sfree(auth->xa1_firstblock);\r
153     if (auth->xdmseen != NULL) {\r
154         struct XDMSeen *seen;\r
155         while ((seen = delpos234(auth->xdmseen, 0)) != NULL)\r
156             sfree(seen);\r
157         freetree234(auth->xdmseen);\r
158     }\r
159     sfree(auth);\r
162 int x11_authcmp(void *av, void *bv)\r
164     struct X11FakeAuth *a = (struct X11FakeAuth *)av;\r
165     struct X11FakeAuth *b = (struct X11FakeAuth *)bv;\r
167     if (a->proto < b->proto)\r
168         return -1;\r
169     else if (a->proto > b->proto)\r
170         return +1;\r
172     if (a->proto == X11_MIT) {\r
173         if (a->datalen < b->datalen)\r
174             return -1;\r
175         else if (a->datalen > b->datalen)\r
176             return +1;\r
178         return memcmp(a->data, b->data, a->datalen);\r
179     } else {\r
180         assert(a->proto == X11_XDM);\r
182         return memcmp(a->xa1_firstblock, b->xa1_firstblock, 8);\r
183     }\r
186 struct X11Display *x11_setup_display(const char *display, Conf *conf,\r
187                                      char **error_msg)\r
189     struct X11Display *disp = snew(struct X11Display);\r
190     char *localcopy;\r
192     *error_msg = NULL;\r
194     if (!display || !*display) {\r
195         localcopy = platform_get_x_display();\r
196         if (!localcopy || !*localcopy) {\r
197             sfree(localcopy);\r
198             localcopy = dupstr(":0");  /* plausible default for any platform */\r
199         }\r
200     } else\r
201         localcopy = dupstr(display);\r
203     /*\r
204      * Parse the display name.\r
205      *\r
206      * We expect this to have one of the following forms:\r
207      *\r
208      *  - the standard X format which looks like\r
209      *    [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ]\r
210      *    (X11 also permits a double colon to indicate DECnet, but\r
211      *    that's not our problem, thankfully!)\r
212      *\r
213      *  - only seen in the wild on MacOS (so far): a pathname to a\r
214      *    Unix-domain socket, which will typically and confusingly\r
215      *    end in ":0", and which I'm currently distinguishing from\r
216      *    the standard scheme by noting that it starts with '/'.\r
217      */\r
218     if (localcopy[0] == '/') {\r
219         disp->unixsocketpath = localcopy;\r
220         disp->unixdomain = true;\r
221         disp->hostname = NULL;\r
222         disp->displaynum = -1;\r
223         disp->screennum = 0;\r
224         disp->addr = NULL;\r
225     } else {\r
226         char *colon, *dot, *slash;\r
227         char *protocol, *hostname;\r
229         colon = host_strrchr(localcopy, ':');\r
230         if (!colon) {\r
231             *error_msg = dupprintf("display name '%s' has no ':number'"\r
232                                    " suffix", localcopy);\r
234             sfree(disp);\r
235             sfree(localcopy);\r
236             return NULL;\r
237         }\r
239         *colon++ = '\0';\r
240         dot = strchr(colon, '.');\r
241         if (dot)\r
242             *dot++ = '\0';\r
244         disp->displaynum = atoi(colon);\r
245         if (dot)\r
246             disp->screennum = atoi(dot);\r
247         else\r
248             disp->screennum = 0;\r
250         protocol = NULL;\r
251         hostname = localcopy;\r
252         if (colon > localcopy) {\r
253             slash = strchr(localcopy, '/');\r
254             if (slash) {\r
255                 *slash++ = '\0';\r
256                 protocol = localcopy;\r
257                 hostname = slash;\r
258             }\r
259         }\r
261         disp->hostname = *hostname ? dupstr(hostname) : NULL;\r
263         if (protocol)\r
264             disp->unixdomain = (!strcmp(protocol, "local") ||\r
265                                 !strcmp(protocol, "unix"));\r
266         else if (!*hostname || !strcmp(hostname, "unix"))\r
267             disp->unixdomain = platform_uses_x11_unix_by_default;\r
268         else\r
269             disp->unixdomain = false;\r
271         if (!disp->hostname && !disp->unixdomain)\r
272             disp->hostname = dupstr("localhost");\r
274         disp->unixsocketpath = NULL;\r
275         disp->addr = NULL;\r
277         sfree(localcopy);\r
278     }\r
280     /*\r
281      * Look up the display hostname, if we need to.\r
282      */\r
283     if (!disp->unixdomain) {\r
284         const char *err;\r
286         disp->port = 6000 + disp->displaynum;\r
287         disp->addr = name_lookup(disp->hostname, disp->port,\r
288                                  &disp->realhost, conf, ADDRTYPE_UNSPEC,\r
289                                  NULL, NULL);\r
291         if ((err = sk_addr_error(disp->addr)) != NULL) {\r
292             *error_msg = dupprintf("unable to resolve host name '%s' in "\r
293                                    "display name", disp->hostname);\r
295             sk_addr_free(disp->addr);\r
296             sfree(disp->hostname);\r
297             sfree(disp->unixsocketpath);\r
298             sfree(disp);\r
299             return NULL;\r
300         }\r
301     }\r
303     /*\r
304      * Try upgrading an IP-style localhost display to a Unix-socket\r
305      * display (as the standard X connection libraries do).\r
306      */\r
307     if (!disp->unixdomain && sk_address_is_local(disp->addr)) {\r
308         SockAddr *ux = platform_get_x11_unix_address(NULL, disp->displaynum);\r
309         const char *err = sk_addr_error(ux);\r
310         if (!err) {\r
311             /* Create trial connection to see if there is a useful Unix-domain\r
312              * socket */\r
313             Socket *s = sk_new(sk_addr_dup(ux), 0, false, false,\r
314                                false, false, nullplug);\r
315             err = sk_socket_error(s);\r
316             sk_close(s);\r
317         }\r
318         if (err) {\r
319             sk_addr_free(ux);\r
320         } else {\r
321             sk_addr_free(disp->addr);\r
322             disp->unixdomain = true;\r
323             disp->addr = ux;\r
324             /* Fill in the rest in a moment */\r
325         }\r
326     }\r
328     if (disp->unixdomain) {\r
329         if (!disp->addr)\r
330             disp->addr = platform_get_x11_unix_address(disp->unixsocketpath,\r
331                                                        disp->displaynum);\r
332         if (disp->unixsocketpath)\r
333             disp->realhost = dupstr(disp->unixsocketpath);\r
334         else\r
335             disp->realhost = dupprintf("unix:%d", disp->displaynum);\r
336         disp->port = 0;\r
337     }\r
339     /*\r
340      * Fetch the local authorisation details.\r
341      */\r
342     disp->localauthproto = X11_NO_AUTH;\r
343     disp->localauthdata = NULL;\r
344     disp->localauthdatalen = 0;\r
345     platform_get_x11_auth(disp, conf);\r
347     return disp;\r
350 void x11_free_display(struct X11Display *disp)\r
352     sfree(disp->hostname);\r
353     sfree(disp->unixsocketpath);\r
354     if (disp->localauthdata)\r
355         smemclr(disp->localauthdata, disp->localauthdatalen);\r
356     sfree(disp->localauthdata);\r
357     sk_addr_free(disp->addr);\r
358     sfree(disp);\r
361 #define XDM_MAXSKEW 20*60      /* 20 minute clock skew should be OK */\r
363 static const char *x11_verify(unsigned long peer_ip, int peer_port,\r
364                               tree234 *authtree, char *proto,\r
365                               unsigned char *data, int dlen,\r
366                               struct X11FakeAuth **auth_ret)\r
368     struct X11FakeAuth match_dummy;    /* for passing to find234 */\r
369     struct X11FakeAuth *auth;\r
371     /*\r
372      * First, do a lookup in our tree to find the only authorisation\r
373      * record that _might_ match.\r
374      */\r
375     if (!strcmp(proto, x11_authnames[X11_MIT])) {\r
376         /*\r
377          * Just look up the whole cookie that was presented to us,\r
378          * which x11_authcmp will compare against the cookies we\r
379          * currently believe in.\r
380          */\r
381         match_dummy.proto = X11_MIT;\r
382         match_dummy.datalen = dlen;\r
383         match_dummy.data = data;\r
384     } else if (!strcmp(proto, x11_authnames[X11_XDM])) {\r
385         /*\r
386          * Look up the first cipher block, against the stored first\r
387          * cipher blocks for the XDM-AUTHORIZATION-1 cookies we\r
388          * currently know. (See comment in x11_invent_fake_auth.)\r
389          */\r
390         match_dummy.proto = X11_XDM;\r
391         match_dummy.xa1_firstblock = data;\r
392     } else {\r
393         return "Unsupported authorisation protocol";\r
394     }\r
396     if ((auth = find234(authtree, &match_dummy, 0)) == NULL)\r
397         return "Authorisation not recognised";\r
399     /*\r
400      * If we're using MIT-MAGIC-COOKIE-1, that was all we needed. If\r
401      * we're doing XDM-AUTHORIZATION-1, though, we have to check the\r
402      * rest of the auth data.\r
403      */\r
404     if (auth->proto == X11_XDM) {\r
405         unsigned long t;\r
406         time_t tim;\r
407         int i;\r
408         struct XDMSeen *seen, *ret;\r
410         if (dlen != 24)\r
411             return "XDM-AUTHORIZATION-1 data was wrong length";\r
412         if (peer_port == -1)\r
413             return "cannot do XDM-AUTHORIZATION-1 without remote address data";\r
414         des_decrypt_xdmauth(auth->data+9, data, 24);\r
415         if (memcmp(auth->data, data, 8) != 0)\r
416             return "XDM-AUTHORIZATION-1 data failed check"; /* cookie wrong */\r
417         if (GET_32BIT_MSB_FIRST(data+8) != peer_ip)\r
418             return "XDM-AUTHORIZATION-1 data failed check";   /* IP wrong */\r
419         if ((int)GET_16BIT_MSB_FIRST(data+12) != peer_port)\r
420             return "XDM-AUTHORIZATION-1 data failed check";   /* port wrong */\r
421         t = GET_32BIT_MSB_FIRST(data+14);\r
422         for (i = 18; i < 24; i++)\r
423             if (data[i] != 0)          /* zero padding wrong */\r
424                 return "XDM-AUTHORIZATION-1 data failed check";\r
425         tim = time(NULL);\r
426         if (((unsigned long)t - (unsigned long)tim\r
427              + XDM_MAXSKEW) > 2*XDM_MAXSKEW)\r
428             return "XDM-AUTHORIZATION-1 time stamp was too far out";\r
429         seen = snew(struct XDMSeen);\r
430         seen->time = t;\r
431         memcpy(seen->clientid, data+8, 6);\r
432         assert(auth->xdmseen != NULL);\r
433         ret = add234(auth->xdmseen, seen);\r
434         if (ret != seen) {\r
435             sfree(seen);\r
436             return "XDM-AUTHORIZATION-1 data replayed";\r
437         }\r
438         /* While we're here, purge entries too old to be replayed. */\r
439         for (;;) {\r
440             seen = index234(auth->xdmseen, 0);\r
441             assert(seen != NULL);\r
442             if (t - seen->time <= XDM_MAXSKEW)\r
443                 break;\r
444             sfree(delpos234(auth->xdmseen, 0));\r
445         }\r
446     }\r
447     /* implement other protocols here if ever required */\r
449     *auth_ret = auth;\r
450     return NULL;\r
453 ptrlen BinarySource_get_string_xauth(BinarySource *src)\r
455     size_t len = get_uint16(src);\r
456     return get_data(src, len);\r
458 #define get_string_xauth(src) \\r
459     BinarySource_get_string_xauth(BinarySource_UPCAST(src))\r
461 void BinarySink_put_stringpl_xauth(BinarySink *bs, ptrlen pl)\r
463     assert((pl.len >> 16) == 0);\r
464     put_uint16(bs, pl.len);\r
465     put_datapl(bs, pl);\r
467 #define put_stringpl_xauth(bs, ptrlen) \\r
468     BinarySink_put_stringpl_xauth(BinarySink_UPCAST(bs),ptrlen)\r
470 void x11_get_auth_from_authfile(struct X11Display *disp,\r
471                                 const char *authfilename)\r
473     FILE *authfp;\r
474     char *buf;\r
475     int size;\r
476     BinarySource src[1];\r
477     int family, protocol;\r
478     ptrlen addr, protoname, data;\r
479     char *displaynum_string;\r
480     int displaynum;\r
481     bool ideal_match = false;\r
482     char *ourhostname;\r
484     /* A maximally sized (wildly implausible) .Xauthority record\r
485      * consists of a 16-bit integer to start with, then four strings,\r
486      * each of which has a 16-bit length field followed by that many\r
487      * bytes of data (i.e. up to 0xFFFF bytes). */\r
488     const size_t MAX_RECORD_SIZE = 2 + 4 * (2+0xFFFF);\r
490     /* We'll want a buffer of twice that size (see below). */\r
491     const size_t BUF_SIZE = 2 * MAX_RECORD_SIZE;\r
493     /*\r
494      * Normally we should look for precisely the details specified in\r
495      * `disp'. However, there's an oddity when the display is local:\r
496      * displays like "localhost:0" usually have their details stored\r
497      * in a Unix-domain-socket record (even if there isn't actually a\r
498      * real Unix-domain socket available, as with OpenSSH's proxy X11\r
499      * server).\r
500      *\r
501      * This is apparently a fudge to get round the meaninglessness of\r
502      * "localhost" in a shared-home-directory context -- xauth entries\r
503      * for Unix-domain sockets already disambiguate this by storing\r
504      * the *local* hostname in the conveniently-blank hostname field,\r
505      * but IP "localhost" records couldn't do this. So, typically, an\r
506      * IP "localhost" entry in the auth database isn't present and if\r
507      * it were it would be ignored.\r
508      *\r
509      * However, we don't entirely trust that (say) Windows X servers\r
510      * won't rely on a straight "localhost" entry, bad idea though\r
511      * that is; so if we can't find a Unix-domain-socket entry we'll\r
512      * fall back to an IP-based entry if we can find one.\r
513      */\r
514     bool localhost = !disp->unixdomain && sk_address_is_local(disp->addr);\r
516     authfp = fopen(authfilename, "rb");\r
517     if (!authfp)\r
518         return;\r
520     ourhostname = get_hostname();\r
522     /*\r
523      * Allocate enough space to hold two maximally sized records, so\r
524      * that a full record can start anywhere in the first half. That\r
525      * way we avoid the accidentally-quadratic algorithm that would\r
526      * arise if we moved everything to the front of the buffer after\r
527      * consuming each record; instead, we only move everything to the\r
528      * front after our current position gets past the half-way mark.\r
529      * Before then, there's no need to move anyway; so this guarantees\r
530      * linear time, in that every byte written into this buffer moves\r
531      * at most once (because every move is from the second half of the\r
532      * buffer to the first half).\r
533      */\r
534     buf = snewn(BUF_SIZE, char);\r
535     size = fread(buf, 1, BUF_SIZE, authfp);\r
536     BinarySource_BARE_INIT(src, buf, size);\r
538     while (!ideal_match) {\r
539         bool match = false;\r
541         if (src->pos >= MAX_RECORD_SIZE) {\r
542             size -= src->pos;\r
543             memcpy(buf, buf + src->pos, size);\r
544             size += fread(buf + size, 1, BUF_SIZE - size, authfp);\r
545             BinarySource_BARE_INIT(src, buf, size);\r
546         }\r
548         family = get_uint16(src);\r
549         addr = get_string_xauth(src);\r
550         displaynum_string = mkstr(get_string_xauth(src));\r
551         displaynum = displaynum_string[0] ? atoi(displaynum_string) : -1;\r
552         sfree(displaynum_string);\r
553         protoname = get_string_xauth(src);\r
554         data = get_string_xauth(src);\r
555         if (get_err(src))\r
556             break;\r
558         /*\r
559          * Now we have a full X authority record in memory. See\r
560          * whether it matches the display we're trying to\r
561          * authenticate to.\r
562          *\r
563          * The details we've just read should be interpreted as\r
564          * follows:\r
565          *\r
566          *  - 'family' is the network address family used to\r
567          *    connect to the display. 0 means IPv4; 6 means IPv6;\r
568          *    256 means Unix-domain sockets.\r
569          *\r
570          *  - 'addr' is the network address itself. For IPv4 and\r
571          *    IPv6, this is a string of binary data of the\r
572          *    appropriate length (respectively 4 and 16 bytes)\r
573          *    representing the address in big-endian format, e.g.\r
574          *    7F 00 00 01 means IPv4 localhost. For Unix-domain\r
575          *    sockets, this is the host name of the machine on\r
576          *    which the Unix-domain display resides (so that an\r
577          *    .Xauthority file on a shared file system can contain\r
578          *    authority entries for Unix-domain displays on\r
579          *    several machines without them clashing).\r
580          *\r
581          *  - 'displaynum' is the display number. An empty display\r
582          *    number is a wildcard for any display number.\r
583          *\r
584          *  - 'protoname' is the authorisation protocol, encoded as\r
585          *    its canonical string name (i.e. "MIT-MAGIC-COOKIE-1",\r
586          *    "XDM-AUTHORIZATION-1" or something we don't recognise).\r
587          *\r
588          *  - 'data' is the actual authorisation data, stored in\r
589          *    binary form.\r
590          */\r
592         if (disp->displaynum < 0 ||\r
593             (displaynum >= 0 && disp->displaynum != displaynum))\r
594             continue;                  /* not the one */\r
596         for (protocol = 1; protocol < lenof(x11_authnames); protocol++)\r
597             if (ptrlen_eq_string(protoname, x11_authnames[protocol]))\r
598                 break;\r
599         if (protocol == lenof(x11_authnames))\r
600             continue;  /* don't recognise this protocol, look for another */\r
602         switch (family) {\r
603           case 0:   /* IPv4 */\r
604             if (!disp->unixdomain &&\r
605                 sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {\r
606                 char buf[4];\r
607                 sk_addrcopy(disp->addr, buf);\r
608                 if (addr.len == 4 && !memcmp(addr.ptr, buf, 4)) {\r
609                     match = true;\r
610                     /* If this is a "localhost" entry, note it down\r
611                      * but carry on looking for a Unix-domain entry. */\r
612                     ideal_match = !localhost;\r
613                 }\r
614             }\r
615             break;\r
616           case 6:   /* IPv6 */\r
617             if (!disp->unixdomain &&\r
618                 sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {\r
619                 char buf[16];\r
620                 sk_addrcopy(disp->addr, buf);\r
621                 if (addr.len == 16 && !memcmp(addr.ptr, buf, 16)) {\r
622                     match = true;\r
623                     ideal_match = !localhost;\r
624                 }\r
625             }\r
626             break;\r
627           case 256: /* Unix-domain / localhost */\r
628             if ((disp->unixdomain || localhost)\r
629                 && ourhostname && ptrlen_eq_string(addr, ourhostname)) {\r
630                 /* A matching Unix-domain socket is always the best\r
631                  * match. */\r
632                 match = true;\r
633                 ideal_match = true;\r
634             }\r
635             break;\r
636         }\r
638         if (match) {\r
639             /* Current best guess -- may be overridden if !ideal_match */\r
640             disp->localauthproto = protocol;\r
641             sfree(disp->localauthdata); /* free previous guess, if any */\r
642             disp->localauthdata = snewn(data.len, unsigned char);\r
643             memcpy(disp->localauthdata, data.ptr, data.len);\r
644             disp->localauthdatalen = data.len;\r
645         }\r
646     }\r
648     fclose(authfp);\r
649     smemclr(buf, 2 * MAX_RECORD_SIZE);\r
650     sfree(buf);\r
651     sfree(ourhostname);\r
654 void x11_format_auth_for_authfile(\r
655     BinarySink *bs, SockAddr *addr, int display_no,\r
656     ptrlen authproto, ptrlen authdata)\r
658     if (sk_address_is_special_local(addr)) {\r
659         char *ourhostname = get_hostname();\r
660         put_uint16(bs, 256); /* indicates Unix-domain socket */\r
661         put_stringpl_xauth(bs, ptrlen_from_asciz(ourhostname));\r
662         sfree(ourhostname);\r
663     } else if (sk_addrtype(addr) == ADDRTYPE_IPV4) {\r
664         char ipv4buf[4];\r
665         sk_addrcopy(addr, ipv4buf);\r
666         put_uint16(bs, 0); /* indicates IPv4 */\r
667         put_stringpl_xauth(bs, make_ptrlen(ipv4buf, 4));\r
668     } else if (sk_addrtype(addr) == ADDRTYPE_IPV6) {\r
669         char ipv6buf[16];\r
670         sk_addrcopy(addr, ipv6buf);\r
671         put_uint16(bs, 6); /* indicates IPv6 */\r
672         put_stringpl_xauth(bs, make_ptrlen(ipv6buf, 16));\r
673     } else {\r
674         unreachable("Bad address type in x11_format_auth_for_authfile");\r
675     }\r
677     {\r
678         char *numberbuf = dupprintf("%d", display_no);\r
679         put_stringpl_xauth(bs, ptrlen_from_asciz(numberbuf));\r
680         sfree(numberbuf);\r
681     }\r
683     put_stringpl_xauth(bs, authproto);\r
684     put_stringpl_xauth(bs, authdata);\r
687 static void x11_log(Plug *p, PlugLogType type, SockAddr *addr, int port,\r
688                     const char *error_msg, int error_code)\r
690     /* We have no interface to the logging module here, so we drop these. */\r
693 static void x11_send_init_error(struct X11Connection *conn,\r
694                                 const char *err_message);\r
696 static void x11_closing(Plug *plug, const char *error_msg, int error_code,\r
697                         bool calling_back)\r
699     struct X11Connection *xconn = container_of(\r
700         plug, struct X11Connection, plug);\r
702     if (error_msg) {\r
703         /*\r
704          * Socket error. If we're still at the connection setup stage,\r
705          * construct an X11 error packet passing on the problem.\r
706          */\r
707         if (xconn->no_data_sent_to_x_client) {\r
708             char *err_message = dupprintf("unable to connect to forwarded "\r
709                                           "X server: %s", error_msg);\r
710             x11_send_init_error(xconn, err_message);\r
711             sfree(err_message);\r
712         }\r
714         /*\r
715          * Whether we did that or not, now we slam the connection\r
716          * shut.\r
717          */\r
718         sshfwd_initiate_close(xconn->c, error_msg);\r
719     } else {\r
720         /*\r
721          * Ordinary EOF received on socket. Send an EOF on the SSH\r
722          * channel.\r
723          */\r
724         if (xconn->c)\r
725             sshfwd_write_eof(xconn->c);\r
726     }\r
729 static void x11_receive(Plug *plug, int urgent, const char *data, size_t len)\r
731     struct X11Connection *xconn = container_of(\r
732         plug, struct X11Connection, plug);\r
734     xconn->no_data_sent_to_x_client = false;\r
735     sshfwd_write(xconn->c, data, len);\r
738 static void x11_sent(Plug *plug, size_t bufsize)\r
740     struct X11Connection *xconn = container_of(\r
741         plug, struct X11Connection, plug);\r
743     sshfwd_unthrottle(xconn->c, bufsize);\r
746 /*\r
747  * When setting up X forwarding, we should send the screen number\r
748  * from the specified local display. This function extracts it from\r
749  * the display string.\r
750  */\r
751 int x11_get_screen_number(char *display)\r
753     int n;\r
755     n = host_strcspn(display, ":");\r
756     if (!display[n])\r
757         return 0;\r
758     n = strcspn(display, ".");\r
759     if (!display[n])\r
760         return 0;\r
761     return atoi(display + n + 1);\r
764 static const PlugVtable X11Connection_plugvt = {\r
765     .log = x11_log,\r
766     .closing = x11_closing,\r
767     .receive = x11_receive,\r
768     .sent = x11_sent,\r
769 };\r
771 static void x11_chan_free(Channel *chan);\r
772 static size_t x11_send(\r
773     Channel *chan, bool is_stderr, const void *vdata, size_t len);\r
774 static void x11_send_eof(Channel *chan);\r
775 static void x11_set_input_wanted(Channel *chan, bool wanted);\r
776 static char *x11_log_close_msg(Channel *chan);\r
778 static const ChannelVtable X11Connection_channelvt = {\r
779     .free = x11_chan_free,\r
780     .open_confirmation = chan_remotely_opened_confirmation,\r
781     .open_failed = chan_remotely_opened_failure,\r
782     .send = x11_send,\r
783     .send_eof = x11_send_eof,\r
784     .set_input_wanted = x11_set_input_wanted,\r
785     .log_close_msg = x11_log_close_msg,\r
786     .want_close = chan_default_want_close,\r
787     .rcvd_exit_status = chan_no_exit_status,\r
788     .rcvd_exit_signal = chan_no_exit_signal,\r
789     .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,\r
790     .run_shell = chan_no_run_shell,\r
791     .run_command = chan_no_run_command,\r
792     .run_subsystem = chan_no_run_subsystem,\r
793     .enable_x11_forwarding = chan_no_enable_x11_forwarding,\r
794     .enable_agent_forwarding = chan_no_enable_agent_forwarding,\r
795     .allocate_pty = chan_no_allocate_pty,\r
796     .set_env = chan_no_set_env,\r
797     .send_break = chan_no_send_break,\r
798     .send_signal = chan_no_send_signal,\r
799     .change_window_size = chan_no_change_window_size,\r
800     .request_response = chan_no_request_response,\r
801 };\r
803 /*\r
804  * Called to set up the X11Connection structure, though this does not\r
805  * yet connect to an actual server.\r
806  */\r
807 Channel *x11_new_channel(tree234 *authtree, SshChannel *c,\r
808                          const char *peeraddr, int peerport,\r
809                          bool connection_sharing_possible)\r
811     struct X11Connection *xconn;\r
813     /*\r
814      * Open socket.\r
815      */\r
816     xconn = snew(struct X11Connection);\r
817     xconn->plug.vt = &X11Connection_plugvt;\r
818     xconn->chan.vt = &X11Connection_channelvt;\r
819     xconn->chan.initial_fixed_window_size =\r
820         (connection_sharing_possible ? 128 : 0);\r
821     xconn->auth_protocol = NULL;\r
822     xconn->authtree = authtree;\r
823     xconn->verified = false;\r
824     xconn->data_read = 0;\r
825     xconn->input_wanted = true;\r
826     xconn->no_data_sent_to_x_client = true;\r
827     xconn->c = c;\r
829     /*\r
830      * We don't actually open a local socket to the X server just yet,\r
831      * because we don't know which one it is. Instead, we'll wait\r
832      * until we see the incoming authentication data, which may tell\r
833      * us what display to connect to, or whether we have to divert\r
834      * this X forwarding channel to a connection-sharing downstream\r
835      * rather than handling it ourself.\r
836      */\r
837     xconn->disp = NULL;\r
838     xconn->s = NULL;\r
840     /*\r
841      * Stash the peer address we were given in its original text form.\r
842      */\r
843     xconn->peer_addr = peeraddr ? dupstr(peeraddr) : NULL;\r
844     xconn->peer_port = peerport;\r
846     return &xconn->chan;\r
849 static void x11_chan_free(Channel *chan)\r
851     assert(chan->vt == &X11Connection_channelvt);\r
852     X11Connection *xconn = container_of(chan, X11Connection, chan);\r
854     if (xconn->auth_protocol) {\r
855         sfree(xconn->auth_protocol);\r
856         sfree(xconn->auth_data);\r
857     }\r
859     if (xconn->s)\r
860         sk_close(xconn->s);\r
862     sfree(xconn->peer_addr);\r
863     sfree(xconn);\r
866 static void x11_set_input_wanted(Channel *chan, bool wanted)\r
868     assert(chan->vt == &X11Connection_channelvt);\r
869     X11Connection *xconn = container_of(chan, X11Connection, chan);\r
871     xconn->input_wanted = wanted;\r
872     if (xconn->s)\r
873         sk_set_frozen(xconn->s, !xconn->input_wanted);\r
876 static void x11_send_init_error(struct X11Connection *xconn,\r
877                                 const char *err_message)\r
879     char *full_message;\r
880     int msglen, msgsize;\r
881     unsigned char *reply;\r
883     full_message = dupprintf("%s X11 proxy: %s\n", appname, err_message);\r
885     msglen = strlen(full_message);\r
886     reply = snewn(8 + msglen+1 + 4, unsigned char); /* include zero */\r
887     msgsize = (msglen + 3) & ~3;\r
888     reply[0] = 0;              /* failure */\r
889     reply[1] = msglen;         /* length of reason string */\r
890     memcpy(reply + 2, xconn->firstpkt + 2, 4);  /* major/minor proto vsn */\r
891     PUT_16BIT_X11(xconn->firstpkt[0], reply + 6, msgsize >> 2);/* data len */\r
892     memset(reply + 8, 0, msgsize);\r
893     memcpy(reply + 8, full_message, msglen);\r
894     sshfwd_write(xconn->c, reply, 8 + msgsize);\r
895     sshfwd_write_eof(xconn->c);\r
896     xconn->no_data_sent_to_x_client = false;\r
897     sfree(reply);\r
898     sfree(full_message);\r
901 static bool x11_parse_ip(const char *addr_string, unsigned long *ip)\r
904     /*\r
905      * See if we can make sense of this string as an IPv4 address, for\r
906      * XDM-AUTHORIZATION-1 purposes.\r
907      */\r
908     int i[4];\r
909     if (addr_string &&\r
910         4 == sscanf(addr_string, "%d.%d.%d.%d", i+0, i+1, i+2, i+3)) {\r
911         *ip = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3];\r
912         return true;\r
913     } else {\r
914         return false;\r
915     }\r
918 /*\r
919  * Called to send data down the raw connection.\r
920  */\r
921 static size_t x11_send(\r
922     Channel *chan, bool is_stderr, const void *vdata, size_t len)\r
924     assert(chan->vt == &X11Connection_channelvt);\r
925     X11Connection *xconn = container_of(chan, X11Connection, chan);\r
926     const char *data = (const char *)vdata;\r
928     /*\r
929      * Read the first packet.\r
930      */\r
931     while (len > 0 && xconn->data_read < 12)\r
932         xconn->firstpkt[xconn->data_read++] = (unsigned char) (len--, *data++);\r
933     if (xconn->data_read < 12)\r
934         return 0;\r
936     /*\r
937      * If we have not allocated the auth_protocol and auth_data\r
938      * strings, do so now.\r
939      */\r
940     if (!xconn->auth_protocol) {\r
941         char endian = xconn->firstpkt[0];\r
942         xconn->auth_plen = GET_16BIT_X11(endian, xconn->firstpkt + 6);\r
943         xconn->auth_dlen = GET_16BIT_X11(endian, xconn->firstpkt + 8);\r
944         xconn->auth_psize = (xconn->auth_plen + 3) & ~3;\r
945         xconn->auth_dsize = (xconn->auth_dlen + 3) & ~3;\r
946         /* Leave room for a terminating zero, to make our lives easier. */\r
947         xconn->auth_protocol = snewn(xconn->auth_psize + 1, char);\r
948         xconn->auth_data = snewn(xconn->auth_dsize, unsigned char);\r
949     }\r
951     /*\r
952      * Read the auth_protocol and auth_data strings.\r
953      */\r
954     while (len > 0 &&\r
955            xconn->data_read < 12 + xconn->auth_psize)\r
956         xconn->auth_protocol[xconn->data_read++ - 12] = (len--, *data++);\r
957     while (len > 0 &&\r
958            xconn->data_read < 12 + xconn->auth_psize + xconn->auth_dsize)\r
959         xconn->auth_data[xconn->data_read++ - 12 -\r
960                       xconn->auth_psize] = (unsigned char) (len--, *data++);\r
961     if (xconn->data_read < 12 + xconn->auth_psize + xconn->auth_dsize)\r
962         return 0;\r
964     /*\r
965      * If we haven't verified the authorisation, do so now.\r
966      */\r
967     if (!xconn->verified) {\r
968         const char *err;\r
969         struct X11FakeAuth *auth_matched = NULL;\r
970         unsigned long peer_ip;\r
971         int peer_port;\r
972         int protomajor, protominor;\r
973         void *greeting;\r
974         int greeting_len;\r
975         unsigned char *socketdata;\r
976         int socketdatalen;\r
977         char new_peer_addr[32];\r
978         int new_peer_port;\r
979         char endian = xconn->firstpkt[0];\r
981         protomajor = GET_16BIT_X11(endian, xconn->firstpkt + 2);\r
982         protominor = GET_16BIT_X11(endian, xconn->firstpkt + 4);\r
984         assert(!xconn->s);\r
986         xconn->auth_protocol[xconn->auth_plen] = '\0';  /* ASCIZ */\r
988         peer_ip = 0;                   /* placate optimiser */\r
989         if (x11_parse_ip(xconn->peer_addr, &peer_ip))\r
990             peer_port = xconn->peer_port;\r
991         else\r
992             peer_port = -1; /* signal no peer address data available */\r
994         err = x11_verify(peer_ip, peer_port,\r
995                          xconn->authtree, xconn->auth_protocol,\r
996                          xconn->auth_data, xconn->auth_dlen, &auth_matched);\r
997         if (err) {\r
998             x11_send_init_error(xconn, err);\r
999             return 0;\r
1000         }\r
1001         assert(auth_matched);\r
1003         /*\r
1004          * If this auth points to a connection-sharing downstream\r
1005          * rather than an X display we know how to connect to\r
1006          * directly, pass it off to the sharing module now. (This will\r
1007          * have the side effect of freeing xconn.)\r
1008          */\r
1009         if (auth_matched->share_cs) {\r
1010             sshfwd_x11_sharing_handover(xconn->c, auth_matched->share_cs,\r
1011                                         auth_matched->share_chan,\r
1012                                         xconn->peer_addr, xconn->peer_port,\r
1013                                         xconn->firstpkt[0],\r
1014                                         protomajor, protominor, data, len);\r
1015             return 0;\r
1016         }\r
1018         /*\r
1019          * Now we know we're going to accept the connection, and what\r
1020          * X display to connect to. Actually connect to it.\r
1021          */\r
1022         xconn->chan.initial_fixed_window_size = 0;\r
1023         sshfwd_window_override_removed(xconn->c);\r
1024         xconn->disp = auth_matched->disp;\r
1025         xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),\r
1026                                   xconn->disp->realhost, xconn->disp->port,\r
1027                                   false, true, false, false, &xconn->plug,\r
1028                                   sshfwd_get_conf(xconn->c));\r
1029         if ((err = sk_socket_error(xconn->s)) != NULL) {\r
1030             char *err_message = dupprintf("unable to connect to"\r
1031                                           " forwarded X server: %s", err);\r
1032             x11_send_init_error(xconn, err_message);\r
1033             sfree(err_message);\r
1034             return 0;\r
1035         }\r
1037         /*\r
1038          * Write a new connection header containing our replacement\r
1039          * auth data.\r
1040          */\r
1041         socketdatalen = 0;             /* placate compiler warning */\r
1042         socketdata = sk_getxdmdata(xconn->s, &socketdatalen);\r
1043         if (socketdata && socketdatalen==6) {\r
1044             sprintf(new_peer_addr, "%d.%d.%d.%d", socketdata[0],\r
1045                     socketdata[1], socketdata[2], socketdata[3]);\r
1046             new_peer_port = GET_16BIT_MSB_FIRST(socketdata + 4);\r
1047         } else {\r
1048             strcpy(new_peer_addr, "0.0.0.0");\r
1049             new_peer_port = 0;\r
1050         }\r
1052         greeting = x11_make_greeting(xconn->firstpkt[0],\r
1053                                      protomajor, protominor,\r
1054                                      xconn->disp->localauthproto,\r
1055                                      xconn->disp->localauthdata,\r
1056                                      xconn->disp->localauthdatalen,\r
1057                                      new_peer_addr, new_peer_port,\r
1058                                      &greeting_len);\r
1060         sk_write(xconn->s, greeting, greeting_len);\r
1062         smemclr(greeting, greeting_len);\r
1063         sfree(greeting);\r
1065         /*\r
1066          * Now we're done.\r
1067          */\r
1068         xconn->verified = true;\r
1069     }\r
1071     /*\r
1072      * After initialisation, just copy data simply.\r
1073      */\r
1075     return sk_write(xconn->s, data, len);\r
1078 static void x11_send_eof(Channel *chan)\r
1080     assert(chan->vt == &X11Connection_channelvt);\r
1081     X11Connection *xconn = container_of(chan, X11Connection, chan);\r
1083     if (xconn->s) {\r
1084         sk_write_eof(xconn->s);\r
1085     } else {\r
1086         /*\r
1087          * If EOF is received from the X client before we've got to\r
1088          * the point of actually connecting to an X server, then we\r
1089          * should send an EOF back to the client so that the\r
1090          * forwarded channel will be terminated.\r
1091          */\r
1092         if (xconn->c)\r
1093             sshfwd_write_eof(xconn->c);\r
1094     }\r
1097 static char *x11_log_close_msg(Channel *chan)\r
1099     return dupstr("Forwarded X11 connection terminated");\r
1102 /*\r
1103  * Utility functions used by connection sharing to convert textual\r
1104  * representations of an X11 auth protocol name + hex cookie into our\r
1105  * usual integer protocol id and binary auth data.\r
1106  */\r
1107 int x11_identify_auth_proto(ptrlen protoname)\r
1109     int protocol;\r
1111     for (protocol = 1; protocol < lenof(x11_authnames); protocol++)\r
1112         if (ptrlen_eq_string(protoname, x11_authnames[protocol]))\r
1113             return protocol;\r
1114     return -1;\r
1117 void *x11_dehexify(ptrlen hexpl, int *outlen)\r
1119     int len, i;\r
1120     unsigned char *ret;\r
1122     len = hexpl.len / 2;\r
1123     ret = snewn(len, unsigned char);\r
1125     for (i = 0; i < len; i++) {\r
1126         char bytestr[3];\r
1127         unsigned val = 0;\r
1128         bytestr[0] = ((const char *)hexpl.ptr)[2*i];\r
1129         bytestr[1] = ((const char *)hexpl.ptr)[2*i+1];\r
1130         bytestr[2] = '\0';\r
1131         sscanf(bytestr, "%x", &val);\r
1132         ret[i] = val;\r
1133     }\r
1135     *outlen = len;\r
1136     return ret;\r
1139 /*\r
1140  * Construct an X11 greeting packet, including making up the right\r
1141  * authorisation data.\r
1142  */\r
1143 void *x11_make_greeting(int endian, int protomajor, int protominor,\r
1144                         int auth_proto, const void *auth_data, int auth_len,\r
1145                         const char *peer_addr, int peer_port,\r
1146                         int *outlen)\r
1148     unsigned char *greeting;\r
1149     unsigned char realauthdata[64];\r
1150     const char *authname;\r
1151     const unsigned char *authdata;\r
1152     int authnamelen, authnamelen_pad;\r
1153     int authdatalen, authdatalen_pad;\r
1154     int greeting_len;\r
1156     authname = x11_authnames[auth_proto];\r
1157     authnamelen = strlen(authname);\r
1158     authnamelen_pad = (authnamelen + 3) & ~3;\r
1160     if (auth_proto == X11_MIT) {\r
1161         authdata = auth_data;\r
1162         authdatalen = auth_len;\r
1163     } else if (auth_proto == X11_XDM && auth_len == 16) {\r
1164         time_t t;\r
1165         unsigned long peer_ip = 0;\r
1167         x11_parse_ip(peer_addr, &peer_ip);\r
1169         authdata = realauthdata;\r
1170         authdatalen = 24;\r
1171         memset(realauthdata, 0, authdatalen);\r
1172         memcpy(realauthdata, auth_data, 8);\r
1173         PUT_32BIT_MSB_FIRST(realauthdata+8, peer_ip);\r
1174         PUT_16BIT_MSB_FIRST(realauthdata+12, peer_port);\r
1175         t = time(NULL);\r
1176         PUT_32BIT_MSB_FIRST(realauthdata+14, t);\r
1178         des_encrypt_xdmauth((char *)auth_data + 9, realauthdata, authdatalen);\r
1179     } else {\r
1180         authdata = realauthdata;\r
1181         authdatalen = 0;\r
1182     }\r
1184     authdatalen_pad = (authdatalen + 3) & ~3;\r
1185     greeting_len = 12 + authnamelen_pad + authdatalen_pad;\r
1187     greeting = snewn(greeting_len, unsigned char);\r
1188     memset(greeting, 0, greeting_len);\r
1189     greeting[0] = endian;\r
1190     PUT_16BIT_X11(endian, greeting+2, protomajor);\r
1191     PUT_16BIT_X11(endian, greeting+4, protominor);\r
1192     PUT_16BIT_X11(endian, greeting+6, authnamelen);\r
1193     PUT_16BIT_X11(endian, greeting+8, authdatalen);\r
1194     memcpy(greeting+12, authname, authnamelen);\r
1195     memcpy(greeting+12+authnamelen_pad, authdata, authdatalen);\r
1197     smemclr(realauthdata, sizeof(realauthdata));\r
1199     *outlen = greeting_len;\r
1200     return greeting;\r