From: Simon Tatham Date: Sun, 17 Nov 2013 14:05:10 +0000 (+0000) Subject: Prepare to have multiple X11 auth cookies valid at once. X-Git-Tag: 0.64~125 X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=commitdiff_plain;h=cc4fbe33bcb76271b9b59d2edd124a962b026095;p=PuTTY.git Prepare to have multiple X11 auth cookies valid at once. 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] --- diff --git a/ssh.c b/ssh.c index fd3d4cf5..ec05f0ac 100644 --- 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 ee629ee5..8e2a908d 100644 --- 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); diff --git a/sshdes.c b/sshdes.c index f2736691..81aee8ba 100644 --- 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 = { diff --git a/x11fwd.c b/x11fwd.c index 80a0206c..c9b09d7c 100644 --- 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,