]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Prepare to have multiple X11 auth cookies valid at once.
authorSimon Tatham <anakin@pobox.com>
Sun, 17 Nov 2013 14:05:10 +0000 (14:05 +0000)
committerSimon Tatham <anakin@pobox.com>
Sun, 17 Nov 2013 14:05:10 +0000 (14:05 +0000)
Rather than the top-level component of X forwarding being an
X11Display structure which owns some auth data, it's now a collection
of X11FakeAuth structures, each of which owns a display. The idea is
that when we receive an X connection, we wait to see which of our
available auth cookies it matches, and then connect to whatever X
display that auth cookie identifies. At present the tree will only
have one thing in it; this is all groundwork for later changes.

[originally from svn r10079]

ssh.c
ssh.h
sshdes.c
x11fwd.c

diff --git a/ssh.c b/ssh.c
index fd3d4cf50dcdcc996281e1a5acfdc1d2ac66cdf1..ec05f0ac82b372ef40546881b808ff81f3057a88 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -786,6 +786,8 @@ struct ssh_tag {
     Pkt_ACtx pkt_actx;
 
     struct X11Display *x11disp;
+    struct X11FakeAuth *x11auth;
+    tree234 *x11authtree;
 
     int version;
     int conn_throttle_count;
@@ -4890,7 +4892,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
        c = snew(struct ssh_channel);
        c->ssh = ssh;
 
-       if ((err = x11_init(&c->u.x11.xconn, ssh->x11disp, c,
+       if ((err = x11_init(&c->u.x11.xconn, ssh->x11authtree, c,
                             NULL, -1)) != NULL) {
            logeventf(ssh, "Opening X11 forward connection failed: %s", err);
             sfree(err);
@@ -5259,36 +5261,47 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
        }
     }
 
-    if (conf_get_int(ssh->conf, CONF_x11_forward) &&
-       (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
-                                         conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) {
-       logevent("Requesting X11 forwarding");
-       if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
-           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
-                       PKT_STR, ssh->x11disp->remoteauthprotoname,
-                       PKT_STR, ssh->x11disp->remoteauthdatastring,
-                       PKT_INT, ssh->x11disp->screennum,
-                       PKT_END);
-       } else {
-           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
-                       PKT_STR, ssh->x11disp->remoteauthprotoname,
-                       PKT_STR, ssh->x11disp->remoteauthdatastring,
-                       PKT_END);
-       }
-       do {
-           crReturnV;
-       } while (!pktin);
-       if (pktin->type != SSH1_SMSG_SUCCESS
-           && pktin->type != SSH1_SMSG_FAILURE) {
-           bombout(("Protocol confusion"));
-           crStopV;
-       } else if (pktin->type == SSH1_SMSG_FAILURE) {
-           logevent("X11 forwarding refused");
-       } else {
-           logevent("X11 forwarding enabled");
-           ssh->X11_fwd_enabled = TRUE;
-           ssh->packet_dispatch[SSH1_SMSG_X11_OPEN] = ssh1_smsg_x11_open;
-       }
+    if (conf_get_int(ssh->conf, CONF_x11_forward)) {
+        ssh->x11disp =
+            x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+                              ssh->conf);
+        if (!ssh->x11disp) {
+            /* FIXME: return an error message from x11_setup_display */
+            logevent("X11 forwarding not enabled: unable to"
+                     " initialise X display");
+        } else {
+            ssh->x11auth = x11_invent_fake_auth
+                (ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth));
+            ssh->x11auth->disp = ssh->x11disp;
+
+            logevent("Requesting X11 forwarding");
+            if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
+                send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
+                            PKT_STR, ssh->x11auth->protoname,
+                            PKT_STR, ssh->x11auth->datastring,
+                            PKT_INT, ssh->x11disp->screennum,
+                            PKT_END);
+            } else {
+                send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
+                            PKT_STR, ssh->x11auth->protoname,
+                            PKT_STR, ssh->x11auth->datastring,
+                            PKT_END);
+            }
+            do {
+                crReturnV;
+            } while (!pktin);
+            if (pktin->type != SSH1_SMSG_SUCCESS
+                && pktin->type != SSH1_SMSG_FAILURE) {
+                bombout(("Protocol confusion"));
+                crStopV;
+            } else if (pktin->type == SSH1_SMSG_FAILURE) {
+                logevent("X11 forwarding refused");
+            } else {
+                logevent("X11 forwarding enabled");
+                ssh->X11_fwd_enabled = TRUE;
+                ssh->packet_dispatch[SSH1_SMSG_X11_OPEN] = ssh1_smsg_x11_open;
+            }
+        }
     }
 
     ssh_setup_portfwd(ssh, ssh->conf);
@@ -7563,7 +7576,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
 
        if (!ssh->X11_fwd_enabled)
            error = "X11 forwarding is not enabled";
-       else if ((x11err = x11_init(&c->u.x11.xconn, ssh->x11disp, c,
+       else if ((x11err = x11_init(&c->u.x11.xconn, ssh->x11authtree, c,
                                    addrstr, peerport)) != NULL) {
            logeventf(ssh, "Local X11 connection failed: %s", x11err);
             sfree(x11err);
@@ -7700,8 +7713,8 @@ static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin,
     pktout = ssh2_chanreq_init(ssh->mainchan, "x11-req",
                                ssh2_setup_x11, s);
     ssh2_pkt_addbool(pktout, 0);              /* many connections */
-    ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthprotoname);
-    ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthdatastring);
+    ssh2_pkt_addstring(pktout, ssh->x11auth->protoname);
+    ssh2_pkt_addstring(pktout, ssh->x11auth->datastring);
     ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum);
     ssh2_pkt_send(ssh, pktout);
 
@@ -9449,12 +9462,22 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
         */
 
        /* Potentially enable X11 forwarding. */
-       if (conf_get_int(ssh->conf, CONF_x11_forward) &&
-           (ssh->x11disp =
-            x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
-                              conf_get_int(ssh->conf, CONF_x11_auth),
-                              ssh->conf)))
-           ssh2_setup_x11(ssh->mainchan, NULL, NULL);
+       if (conf_get_int(ssh->conf, CONF_x11_forward)) {
+            ssh->x11disp =
+                x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+                                  ssh->conf);
+            if (!ssh->x11disp) {
+                /* FIXME: return an error message from x11_setup_display */
+                logevent("X11 forwarding not enabled: unable to"
+                         " initialise X display");
+            } else {
+                ssh->x11auth = x11_invent_fake_auth
+                    (ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth));
+                ssh->x11auth->disp = ssh->x11disp;
+
+                ssh2_setup_x11(ssh->mainchan, NULL, NULL);
+            }
+        }
 
        /* Potentially enable agent forwarding. */
        if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists())
@@ -9805,6 +9828,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
     ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
     ssh->x11disp = NULL;
+    ssh->x11auth = NULL;
+    ssh->x11authtree = newtree234(x11_authcmp);
     ssh->v1_compressing = FALSE;
     ssh->v2_outgoing_sequence = 0;
     ssh->ssh1_rdpkt_crstate = 0;
@@ -9883,6 +9908,7 @@ static void ssh_free(void *handle)
     Ssh ssh = (Ssh) handle;
     struct ssh_channel *c;
     struct ssh_rportfwd *pf;
+    struct X11FakeAuth *auth;
 
     if (ssh->v1_cipher_ctx)
        ssh->cipher->free_context(ssh->v1_cipher_ctx);
@@ -9960,6 +9986,9 @@ static void ssh_free(void *handle)
     sfree(ssh->deferred_send_data);
     if (ssh->x11disp)
        x11_free_display(ssh->x11disp);
+    while ((auth = delpos234(ssh->x11authtree, 0)) != NULL)
+        x11_free_fake_auth(auth);
+    freetree234(ssh->x11authtree);
     sfree(ssh->do_ssh_init_state);
     sfree(ssh->do_ssh1_login_state);
     sfree(ssh->do_ssh2_transport_state);
diff --git a/ssh.h b/ssh.h
index ee629ee58d193485c02c7eb2827ef08d48c94373..8e2a908d4c550716e707bb627e5201cb10b6fd1f 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -372,24 +372,36 @@ struct X11Display {
     int port;
     char *realhost;
 
-    /* Auth details we invented for the virtual display on the SSH server. */
-    int remoteauthproto;
-    unsigned char *remoteauthdata;
-    int remoteauthdatalen;
-    char *remoteauthprotoname;
-    char *remoteauthdatastring;
-
     /* Our local auth details for talking to the real X display. */
     int localauthproto;
     unsigned char *localauthdata;
     int localauthdatalen;
+};
+struct X11FakeAuth {
+    /* Auth details we invented for a virtual display on the SSH server. */
+    int proto;
+    unsigned char *data;
+    int datalen;
+    char *protoname;
+    char *datastring;
+
+    /* The encrypted form of the first block, in XDM-AUTHORIZATION-1.
+     * Used as part of the key when these structures are organised
+     * into a tree. See x11_invent_fake_auth for explanation. */
+    unsigned char *xa1_firstblock;
 
     /*
      * Used inside x11fwd.c to remember recently seen
      * XDM-AUTHORIZATION-1 strings, to avoid replay attacks.
      */
     tree234 *xdmseen;
+
+    /*
+     * What to do with an X connection matching this auth data.
+     */
+    struct X11Display *disp;
 };
+int x11_authcmp(void *av, void *bv); /* for putting X11FakeAuth in a tree234 */
 /*
  * x11_setup_display() parses the display variable and fills in an
  * X11Display structure. Some remote auth details are invented;
@@ -397,11 +409,12 @@ struct X11Display {
  * authorisation protocol to use at the remote end. The local auth
  * details are looked up by calling platform_get_x11_auth.
  */
-extern struct X11Display *x11_setup_display(char *display, int authtype,
-                                           Conf *);
+extern struct X11Display *x11_setup_display(char *display, Conf *);
 void x11_free_display(struct X11Display *disp);
+struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype);
+void x11_free_fake_auth(struct X11FakeAuth *auth);
 struct X11Connection;                  /* opaque outside x11fwd.c */
-extern char *x11_init(struct X11Connection **, struct X11Display *,
+extern char *x11_init(struct X11Connection **, tree234 *authtree,
                       void *, const char *, int);
 extern void x11_close(struct X11Connection *);
 extern int x11_send(struct X11Connection *, char *, int);
index f2736691fbbe952078fbfaf6f1c43a4505d5bd79..81aee8ba0594643a0bb5ac77d8b8102ad40e7c2d 100644 (file)
--- a/sshdes.c
+++ b/sshdes.c
@@ -934,7 +934,7 @@ void des_encrypt_xdmauth(const unsigned char *keydata,
 {
     DESContext dc;
     des_keysetup_xdmauth(keydata, &dc);
-    des_cbc_encrypt(blk, 24, &dc);
+    des_cbc_encrypt(blk, len, &dc);
 }
 
 void des_decrypt_xdmauth(const unsigned char *keydata,
@@ -942,7 +942,7 @@ void des_decrypt_xdmauth(const unsigned char *keydata,
 {
     DESContext dc;
     des_keysetup_xdmauth(keydata, &dc);
-    des_cbc_decrypt(blk, 24, &dc);
+    des_cbc_decrypt(blk, len, &dc);
 }
 
 static const struct ssh2_cipher ssh_3des_ssh2 = {
index 80a0206c0ebf954ee6ec0535281e8b8c733c4fc5..c9b09d7cebad25b87fc8b96ea35e6cab19bcc8b4 100644 (file)
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -30,6 +30,7 @@ struct X11Connection {
     const struct plug_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
     unsigned char firstpkt[12];               /* first X data packet */
+    tree234 *authtree;
     struct X11Display *disp;
     char *auth_protocol;
     unsigned char *auth_data;
@@ -69,11 +70,102 @@ static const struct plug_function_table dummy_plug = {
     dummy_plug_sent, dummy_plug_accepting
 };
 
-struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf)
+struct X11FakeAuth *x11_invent_fake_auth(tree234 *authtree, int authtype)
+{
+    struct X11FakeAuth *auth = snew(struct X11FakeAuth);
+    int i;
+
+    if (authtype == X11_MIT) {
+       auth->proto = X11_MIT;
+
+       /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */
+        auth->datalen = 16;
+       auth->data = snewn(auth->datalen, unsigned char);
+        auth->xa1_firstblock = NULL;
+
+        while (1) {
+            for (i = 0; i < auth->datalen; i++)
+                auth->data[i] = random_byte();
+            if (add234(authtree, auth) == auth)
+                break;
+        }
+
+       auth->xdmseen = NULL;
+    } else {
+       assert(authtype == X11_XDM);
+       auth->proto = X11_XDM;
+
+       /* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */
+       auth->datalen = 16;
+       auth->data = snewn(auth->datalen, unsigned char);
+        auth->xa1_firstblock = snewn(8, unsigned char);
+        memset(auth->xa1_firstblock, 0, 8);
+
+        while (1) {
+            for (i = 0; i < auth->datalen; i++)
+                auth->data[i] = (i == 8 ? 0 : random_byte());
+            memcpy(auth->xa1_firstblock, auth->data, 8);
+            des_encrypt_xdmauth(auth->data + 9, auth->xa1_firstblock, 8);
+            if (add234(authtree, auth) == auth)
+                break;
+        }
+
+        auth->xdmseen = newtree234(xdmseen_cmp);
+    }
+    auth->protoname = dupstr(x11_authnames[auth->proto]);
+    auth->datastring = snewn(auth->datalen * 2 + 1, char);
+    for (i = 0; i < auth->datalen; i++)
+       sprintf(auth->datastring + i*2, "%02x",
+               auth->data[i]);
+
+    return auth;
+}
+
+void x11_free_fake_auth(struct X11FakeAuth *auth)
+{
+    if (auth->data)
+       smemclr(auth->data, auth->datalen);
+    sfree(auth->data);
+    sfree(auth->protoname);
+    sfree(auth->datastring);
+    sfree(auth->xa1_firstblock);
+    if (auth->xdmseen != NULL) {
+       struct XDMSeen *seen;
+       while ((seen = delpos234(auth->xdmseen, 0)) != NULL)
+           sfree(seen);
+       freetree234(auth->xdmseen);
+    }
+    sfree(auth);
+}
+
+int x11_authcmp(void *av, void *bv)
+{
+    struct X11FakeAuth *a = (struct X11FakeAuth *)av;
+    struct X11FakeAuth *b = (struct X11FakeAuth *)bv;
+
+    if (a->proto < b->proto)
+        return -1;
+    else if (a->proto > b->proto)
+        return +1;
+
+    if (a->proto == X11_MIT) {
+        if (a->datalen < b->datalen)
+            return -1;
+        else if (a->datalen > b->datalen)
+            return +1;
+
+        return memcmp(a->data, b->data, a->datalen);
+    } else {
+        assert(a->proto == X11_XDM);
+
+        return memcmp(a->xa1_firstblock, b->xa1_firstblock, 8);
+    }
+}
+
+struct X11Display *x11_setup_display(char *display, Conf *conf)
 {
     struct X11Display *disp = snew(struct X11Display);
     char *localcopy;
-    int i;
 
     if (!display || !*display) {
        localcopy = platform_get_x_display();
@@ -213,37 +305,6 @@ struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf)
        disp->port = 0;
     }
 
-    /*
-     * Invent the remote authorisation details.
-     */
-    if (authtype == X11_MIT) {
-       disp->remoteauthproto = X11_MIT;
-
-       /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */
-       disp->remoteauthdata = snewn(16, unsigned char);
-       for (i = 0; i < 16; i++)
-           disp->remoteauthdata[i] = random_byte();
-       disp->remoteauthdatalen = 16;
-
-       disp->xdmseen = NULL;
-    } else {
-       assert(authtype == X11_XDM);
-       disp->remoteauthproto = X11_XDM;
-
-       /* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */
-       disp->remoteauthdata = snewn(16, unsigned char);
-       for (i = 0; i < 16; i++)
-           disp->remoteauthdata[i] = (i == 8 ? 0 : random_byte());
-       disp->remoteauthdatalen = 16;
-
-       disp->xdmseen = newtree234(xdmseen_cmp);
-    }
-    disp->remoteauthprotoname = dupstr(x11_authnames[disp->remoteauthproto]);
-    disp->remoteauthdatastring = snewn(disp->remoteauthdatalen * 2 + 1, char);
-    for (i = 0; i < disp->remoteauthdatalen; i++)
-       sprintf(disp->remoteauthdatastring + i*2, "%02x",
-               disp->remoteauthdata[i]);
-
     /*
      * Fetch the local authorisation details.
      */
@@ -257,22 +318,11 @@ struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf)
 
 void x11_free_display(struct X11Display *disp)
 {
-    if (disp->xdmseen != NULL) {
-       struct XDMSeen *seen;
-       while ((seen = delpos234(disp->xdmseen, 0)) != NULL)
-           sfree(seen);
-       freetree234(disp->xdmseen);
-    }
     sfree(disp->hostname);
     sfree(disp->unixsocketpath);
     if (disp->localauthdata)
        smemclr(disp->localauthdata, disp->localauthdatalen);
     sfree(disp->localauthdata);
-    if (disp->remoteauthdata)
-       smemclr(disp->remoteauthdata, disp->remoteauthdatalen);
-    sfree(disp->remoteauthdata);
-    sfree(disp->remoteauthprotoname);
-    sfree(disp->remoteauthdatastring);
     sk_addr_free(disp->addr);
     sfree(disp);
 }
@@ -280,18 +330,37 @@ void x11_free_display(struct X11Display *disp)
 #define XDM_MAXSKEW 20*60      /* 20 minute clock skew should be OK */
 
 static char *x11_verify(unsigned long peer_ip, int peer_port,
-                       struct X11Display *disp, char *proto,
-                       unsigned char *data, int dlen)
+                       tree234 *authtree, char *proto,
+                       unsigned char *data, int dlen,
+                        struct X11FakeAuth **auth_ret)
 {
-    if (strcmp(proto, x11_authnames[disp->remoteauthproto]) != 0)
-       return "wrong authorisation protocol attempted";
-    if (disp->remoteauthproto == X11_MIT) {
-        if (dlen != disp->remoteauthdatalen)
-            return "MIT-MAGIC-COOKIE-1 data was wrong length";
-        if (memcmp(disp->remoteauthdata, data, dlen) != 0)
-            return "MIT-MAGIC-COOKIE-1 data did not match";
+    struct X11FakeAuth match_dummy;    /* for passing to find234 */
+    struct X11FakeAuth *auth;
+
+    /*
+     * First, do a lookup in our tree to find the only authorisation
+     * record that _might_ match.
+     */
+    if (!strcmp(proto, x11_authnames[X11_MIT])) {
+        match_dummy.proto = X11_MIT;
+        match_dummy.datalen = dlen;
+        match_dummy.data = data;
+    } else if (!strcmp(proto, x11_authnames[X11_XDM])) {
+        match_dummy.proto = X11_XDM;
+        match_dummy.xa1_firstblock = data;
+    } else {
+        return "Unsupported authorisation protocol";
     }
-    if (disp->remoteauthproto == X11_XDM) {
+
+    if ((auth = find234(authtree, &match_dummy, 0)) == NULL)
+        return "Authorisation not recognised";
+
+    /*
+     * If we're using MIT-MAGIC-COOKIE-1, that was all we needed. If
+     * we're doing XDM-AUTHORIZATION-1, though, we have to check the
+     * rest of the auth data.
+     */
+    if (auth->proto == X11_XDM) {
        unsigned long t;
        time_t tim;
        int i;
@@ -301,8 +370,8 @@ static char *x11_verify(unsigned long peer_ip, int peer_port,
             return "XDM-AUTHORIZATION-1 data was wrong length";
        if (peer_port == -1)
             return "cannot do XDM-AUTHORIZATION-1 without remote address data";
-       des_decrypt_xdmauth(disp->remoteauthdata+9, data, 24);
-        if (memcmp(disp->remoteauthdata, data, 8) != 0)
+       des_decrypt_xdmauth(auth->data+9, data, 24);
+        if (memcmp(auth->data, data, 8) != 0)
             return "XDM-AUTHORIZATION-1 data failed check"; /* cookie wrong */
        if (GET_32BIT_MSB_FIRST(data+8) != peer_ip)
             return "XDM-AUTHORIZATION-1 data failed check";   /* IP wrong */
@@ -318,22 +387,24 @@ static char *x11_verify(unsigned long peer_ip, int peer_port,
        seen = snew(struct XDMSeen);
        seen->time = t;
        memcpy(seen->clientid, data+8, 6);
-       assert(disp->xdmseen != NULL);
-       ret = add234(disp->xdmseen, seen);
+       assert(auth->xdmseen != NULL);
+       ret = add234(auth->xdmseen, seen);
        if (ret != seen) {
            sfree(seen);
            return "XDM-AUTHORIZATION-1 data replayed";
        }
        /* While we're here, purge entries too old to be replayed. */
        for (;;) {
-           seen = index234(disp->xdmseen, 0);
+           seen = index234(auth->xdmseen, 0);
            assert(seen != NULL);
            if (t - seen->time <= XDM_MAXSKEW)
                break;
-           sfree(delpos234(disp->xdmseen, 0));
+           sfree(delpos234(auth->xdmseen, 0));
        }
     }
     /* implement other protocols here if ever required */
+
+    *auth_ret = auth;
     return NULL;
 }
 
@@ -584,7 +655,7 @@ int x11_get_screen_number(char *display)
  * a dynamically allocated error message string.
  */
 extern char *x11_init(struct X11Connection **xconnret,
-                      struct X11Display *disp, void *c,
+                      tree234 *authtree, void *c,
                       const char *peeraddr, int peerport)
 {
     static const struct plug_function_table fn_table = {
@@ -603,7 +674,7 @@ extern char *x11_init(struct X11Connection **xconnret,
     xconn = *xconnret = snew(struct X11Connection);
     xconn->fn = &fn_table;
     xconn->auth_protocol = NULL;
-    xconn->disp = disp;
+    xconn->authtree = authtree;
     xconn->verified = 0;
     xconn->data_read = 0;
     xconn->throttled = xconn->throttle_override = 0;
@@ -611,12 +682,14 @@ extern char *x11_init(struct X11Connection **xconnret,
     xconn->c = c;
 
     /*
-     * We don't actually open a local socket to the X server just yet.
-     * Instead, we'll wait until we see the incoming authentication
-     * data, which may tell us we have to divert this X forwarding
-     * channel to a connection-sharing downstream rather than handling
-     * it ourself.
+     * We don't actually open a local socket to the X server just yet,
+     * because we don't know which one it is. Instead, we'll wait
+     * until we see the incoming authentication data, which may tell
+     * us what display to connect to, or whether we have to divert
+     * this X forwarding channel to a connection-sharing downstream
+     * rather than handling it ourself.
      */
+    xconn->disp = NULL;
     xconn->s = NULL;
 
     /*
@@ -745,22 +818,25 @@ int x11_send(struct X11Connection *xconn, char *data, int len)
      */
     if (!xconn->verified) {
        const char *err;
+        struct X11FakeAuth *auth_matched = NULL;
 
         assert(!xconn->s);
 
        xconn->auth_protocol[xconn->auth_plen] = '\0';  /* ASCIZ */
        err = x11_verify(xconn->peer_ip, xconn->peer_port,
-                        xconn->disp, xconn->auth_protocol,
-                        xconn->auth_data, xconn->auth_dlen);
+                        xconn->authtree, xconn->auth_protocol,
+                        xconn->auth_data, xconn->auth_dlen, &auth_matched);
        if (err) {
             x11_send_init_error(xconn, err);
             return 0;
         }
+        assert(auth_matched);
 
         /*
-         * Now we know we're going to accept the connection. Actually
-         * connect to the X server.
+         * Now we know we're going to accept the connection, and what
+         * X display to connect to. Actually connect to it.
          */
+        xconn->disp = auth_matched->disp;
         xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
                                   xconn->disp->realhost, xconn->disp->port, 
                                   0, 1, 0, 0, (Plug) xconn,