From 9c964e8579f6597d1bfdacd6dcf8d531bfece511 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 22 Jan 2001 11:34:52 +0000 Subject: [PATCH] Add X11 forwarding, mainly thanks to Andreas Schultz git-svn-id: svn://svn.tartarus.org/sgt/putty@878 cda61777-01e9-0310-a592-d414129be87e --- Makefile | 10 ++- putty.h | 3 + settings.c | 5 ++ ssh.c | 107 +++++++++++++++++++++++- windlg.c | 38 +++++++++ winnet.c | 2 + x11fwd.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 437 insertions(+), 4 deletions(-) create mode 100644 x11fwd.c diff --git a/Makefile b/Makefile index 2dedf302..42ae666a 100644 --- a/Makefile +++ b/Makefile @@ -81,6 +81,7 @@ MOBJ2 = tree234.$(OBJ) OBJS1 = sshcrc.$(OBJ) sshdes.$(OBJ) sshmd5.$(OBJ) sshrsa.$(OBJ) sshrand.$(OBJ) OBJS2 = sshsha.$(OBJ) sshblowf.$(OBJ) noise.$(OBJ) sshdh.$(OBJ) sshdss.$(OBJ) OBJS3 = sshbn.$(OBJ) sshpubk.$(OBJ) ssh.$(OBJ) pageantc.$(OBJ) sshzlib.$(OBJ) +OBJS4 = x11fwd.$(OBJ) ##-- objects pageant PAGE1 = pageant.$(OBJ) sshrsa.$(OBJ) sshpubk.$(OBJ) sshdes.$(OBJ) sshbn.$(OBJ) PAGE2 = sshmd5.$(OBJ) version.$(OBJ) tree234.$(OBJ) misc.$(OBJ) @@ -119,7 +120,7 @@ SOCK2 = ws2_32.lib all: putty.exe puttytel.exe pscp.exe plink.exe pageant.exe puttygen.exe -putty.exe: $(GOBJS1) $(GOBJS2) $(LOBJS1) $(POBJS) $(MOBJS) $(MOBJ2) $(OBJS1) $(OBJS2) $(OBJS3) $(PRESRC) putty.rsp +putty.exe: $(GOBJS1) $(GOBJS2) $(LOBJS1) $(POBJS) $(MOBJS) $(MOBJ2) $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(PRESRC) putty.rsp link $(LFLAGS) -out:putty.exe -map:putty.map @putty.rsp puttytel.exe: $(GOBJS1) $(GOBJS2) $(LOBJS1) $(TOBJS) $(MOBJS) $(MOBJ2) $(PRESRC) puttytel.rsp @@ -131,10 +132,10 @@ pageant.exe: $(PAGE1) $(PAGE2) $(PAGERC) pageant.rsp puttygen.exe: $(GEN1) $(GEN2) $(GEN3) $(GEN4) $(GENRC) puttygen.rsp link $(LFLAGS) -out:puttygen.exe -map:puttygen.map @puttygen.rsp -pscp.exe: $(SOBJS) $(MOBJS) $(MOBJ2) $(OBJS1) $(OBJS2) $(OBJS3) $(SRESRC) pscp.rsp +pscp.exe: $(SOBJS) $(MOBJS) $(MOBJ2) $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(SRESRC) pscp.rsp link $(LFLAGS) -out:pscp.exe -map:pscp.map @pscp.rsp -plink.exe: $(LOBJS1) $(POBJS) $(PLOBJS) $(MOBJS) $(MOBJ2) $(OBJS1) $(OBJS2) $(OBJS3) $(LRESRC) plink.rsp +plink.exe: $(LOBJS1) $(POBJS) $(PLOBJS) $(MOBJS) $(MOBJ2) $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(LRESRC) plink.rsp link $(LFLAGS) -out:plink.exe -map:plink.map @plink.rsp putty.rsp: makefile @@ -148,6 +149,7 @@ putty.rsp: makefile echo $(OBJS1) >> putty.rsp echo $(OBJS2) >> putty.rsp echo $(OBJS3) >> putty.rsp + echo $(OBJS4) >> putty.rsp echo $(PRESRC) >> putty.rsp echo $(LIBS1) >> putty.rsp echo $(LIBS2) >> putty.rsp @@ -194,6 +196,7 @@ pscp.rsp: makefile echo $(OBJS1) >> pscp.rsp echo $(OBJS2) >> pscp.rsp echo $(OBJS3) >> pscp.rsp + echo $(OBJS4) >> pscp.rsp echo $(SRESRC) >> pscp.rsp echo $(LIBS1) >> pscp.rsp echo $(LIBS2) >> pscp.rsp @@ -209,6 +212,7 @@ plink.rsp: makefile echo $(OBJS1) >> plink.rsp echo $(OBJS2) >> plink.rsp echo $(OBJS3) >> plink.rsp + echo $(OBJS4) >> plink.rsp echo $(LRESRC) >> plink.rsp echo $(LIBS1) >> plink.rsp echo $(LIBS2) >> plink.rsp diff --git a/putty.h b/putty.h index 34cf0696..e2b6473c 100644 --- a/putty.h +++ b/putty.h @@ -209,6 +209,9 @@ typedef struct { int xlat_88592w1250; int xlat_88592cp852; int xlat_capslockcyr; + /* X11 forwarding */ + int x11_forward; + char x11_display[128]; } Config; /* diff --git a/settings.c b/settings.c index 67fd748e..eba645cf 100644 --- a/settings.c +++ b/settings.c @@ -142,6 +142,8 @@ void save_settings (char *section, int do_host, Config *cfg) { write_setting_i (sesskey, "LockSize", cfg->locksize); write_setting_i (sesskey, "BCE", cfg->bce); write_setting_i (sesskey, "BlinkText", cfg->blinktext); + write_setting_i (sesskey, "X11Forward", cfg->x11_forward); + write_setting_s (sesskey, "X11Display", cfg->x11_display); close_settings_w(sesskey); } @@ -306,6 +308,9 @@ void load_settings (char *section, int do_host, Config *cfg) { gppi (sesskey, "LockSize", 0, &cfg->locksize); gppi (sesskey, "BCE", 0, &cfg->bce); gppi (sesskey, "BlinkText", 0, &cfg->blinktext); + gppi (sesskey, "X11Forward", 0, &cfg->x11_forward); + gpps (sesskey, "X11Display", "localhost:0", cfg->x11_display, + sizeof(cfg->x11_display)); close_settings_r(sesskey); } diff --git a/ssh.c b/ssh.c index f75041ba..4023dd6d 100644 --- a/ssh.c +++ b/ssh.c @@ -46,10 +46,15 @@ #define SSH1_MSG_CHANNEL_DATA 23 /* 0x17 */ #define SSH1_MSG_CHANNEL_CLOSE 24 /* 0x18 */ #define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* 0x19 */ +#define SSH1_SMSG_X11_OPEN 27 /* 0x1b */ +#define SSH1_CMSG_PORT_FORWARD_REQUEST 28 /* 0x1c */ +#define SSH1_MSG_PORT_OPEN 29 /* 0x1d */ #define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30 /* 0x1e */ #define SSH1_SMSG_AGENT_OPEN 31 /* 0x1f */ -#define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */ #define SSH1_MSG_IGNORE 32 /* 0x20 */ +#define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */ +#define SSH1_CMSG_X11_REQUEST_FORWARDING 34 /* 0x22 */ +#define SSH1_CMSG_AUTH_RHOSTS_RSA 35 /* 0x23 */ #define SSH1_MSG_DEBUG 36 /* 0x24 */ #define SSH1_CMSG_REQUEST_COMPRESSION 37 /* 0x25 */ #define SSH1_CMSG_AUTH_TIS 39 /* 0x27 */ @@ -160,6 +165,11 @@ extern const struct ssh_cipher ssh_des; extern const struct ssh_cipher ssh_blowfish_ssh1; extern const struct ssh_cipher ssh_blowfish_ssh2; +extern char *x11_init (Socket *, char *, void *, char **); +extern void x11_close (Socket); +extern void x11_send (Socket , char *, int); +extern void x11_invent_auth(char *, int, char *, int); + /* * Ciphers for SSH2. We miss out single-DES because it isn't * supported; also 3DES and Blowfish are both done differently from @@ -214,6 +224,9 @@ struct ssh_channel { unsigned char msglen[4]; int lensofar, totallen; } a; + struct ssh_x11_channel { + Socket s; + } x11; struct ssh2_data_channel { unsigned char *outbuffer; unsigned outbuflen, outbufsize; @@ -238,6 +251,7 @@ static Socket s = NULL; static unsigned char session_key[32]; static int ssh1_compressing; static int ssh_agentfwd_enabled; +static int ssh_X11_fwd_enabled; static const struct ssh_cipher *cipher = NULL; static const struct ssh_cipher *cscipher = NULL; static const struct ssh_cipher *sccipher = NULL; @@ -1719,6 +1733,25 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) crFinish(1); } +void sshfwd_close(struct ssh_channel *c) { + + if (c) { + send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END); + logevent("X11 connection terminated"); + c->closes = 1; + c->u.x11.s = NULL; + } +} + +void sshfwd_write(struct ssh_channel *c, char *buf, int len) { + + send_packet(SSH1_MSG_CHANNEL_DATA, + PKT_INT, c->remoteid, + PKT_INT, len, + PKT_DATA, buf, len, + PKT_END); +} + static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { crBegin; @@ -1745,6 +1778,26 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { } } + if (cfg.x11_forward) { + char proto[20], data[64]; + logevent("Requesting X11 forwarding"); + x11_invent_auth(proto, sizeof(proto), data, sizeof(data)); + send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING, + PKT_STR, proto, PKT_STR, data, + PKT_INT, 0, + PKT_END); + do { crReturnV; } while (!ispkt); + if (pktin.type != SSH1_SMSG_SUCCESS && pktin.type != SSH1_SMSG_FAILURE) { + bombout(("Protocol confusion")); + crReturnV; + } else if (pktin.type == SSH1_SMSG_FAILURE) { + logevent("X11 forwarding refused"); + } else { + logevent("X11 forwarding enabled"); + ssh_X11_fwd_enabled = TRUE; + } + } + if (!cfg.nopty) { send_packet(SSH1_CMSG_REQUEST_PTY, PKT_STR, cfg.termtype, @@ -1805,6 +1858,49 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { ssh_state = SSH_STATE_CLOSED; logevent("Received disconnect request"); crReturnV; + } else if (pktin.type == SSH1_SMSG_X11_OPEN) { + /* Remote side is trying to open a channel to talk to our + * X-Server. Give them back a local channel number. */ + unsigned i; + struct ssh_channel *c, *d; + enum234 e; + + logevent("Received X11 connect request"); + /* Refuse if X11 forwarding is disabled. */ + if (!ssh_X11_fwd_enabled) { + send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE, + PKT_INT, GET_32BIT(pktin.body), + PKT_END); + logevent("Rejected X11 connect request"); + } else { + char *rh; + + c = smalloc(sizeof(struct ssh_channel)); + + if ( x11_init(&c->u.x11.s, cfg.x11_display, c, &rh) != NULL ) { + logevent("opening X11 forward connection failed"); + sfree(c); + send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE, + PKT_INT, GET_32BIT(pktin.body), + PKT_END); + } else { + logevent("opening X11 forward connection succeeded"); + for (i=1, d = first234(ssh_channels, &e); d; d = next234(&e)) { + if (d->localid > i) + break; /* found a free number */ + i = d->localid + 1; + } + c->remoteid = GET_32BIT(pktin.body); + c->localid = i; + c->closes = 0; + c->type = SSH1_SMSG_X11_OPEN;/* identify channel type */ + add234(ssh_channels, c); + send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, + PKT_INT, c->remoteid, PKT_INT, c->localid, + PKT_END); + logevent("Opened X11 forward channel"); + } + } } else if (pktin.type == SSH1_SMSG_AGENT_OPEN) { /* Remote side is trying to open a channel to talk to our * agent. Give them back a local channel number. */ @@ -1845,6 +1941,12 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { int closetype; closetype = (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2); send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END); + if ((c->closes == 0) && (c->type == SSH1_SMSG_X11_OPEN)) { + logevent("X11 connection closed"); + assert(c->u.x11.s != NULL); + x11_close(c->u.x11.s); + c->u.x11.s = NULL; + } c->closes |= closetype; if (c->closes == 3) { del234(ssh_channels, c); @@ -1860,6 +1962,9 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { c = find234(ssh_channels, &i, ssh_channelfind); if (c) { switch(c->type) { + case SSH1_SMSG_X11_OPEN: + x11_send(c->u.x11.s, p, len); + break; case SSH1_SMSG_AGENT_OPEN: /* Data for an agent message. Buffer it. */ while (len > 0) { diff --git a/windlg.c b/windlg.c index f3f7dfc1..abc09022 100644 --- a/windlg.c +++ b/windlg.c @@ -401,6 +401,13 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue, IDC_VTPOORMAN, translationpanelend, + tunnelspanelstart, + IDC_BOX_TUNNELS, IDC_BOXT_TUNNELS, + IDC_X11_FORWARD, + IDC_X11_DISPSTATIC, + IDC_X11_DISPLAY, + tunnelspanelend, + controlendvalue }; @@ -579,6 +586,9 @@ static void init_dlg_ctrls(HWND hwnd) { cfg.vtmode == VT_OEMANSI ? IDC_VTOEMANSI : cfg.vtmode == VT_OEMONLY ? IDC_VTOEMONLY : IDC_VTPOORMAN); + + CheckDlgButton (hwnd, IDC_X11_FORWARD, cfg.x11_forward); + SetDlgItemText (hwnd, IDC_X11_DISPLAY, cfg.x11_display); } static void hide(HWND hwnd, int hide, int minid, int maxid) { @@ -1064,6 +1074,22 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, treeview_insert(&tvfaff, 1, "SSH"); } } + /* The Tunnels panel. Accelerators used: [acgo] ex */ + { + struct ctlpos tp; + ctlposinit(&tp, hwnd, 80, 3, 13); + if (dlgtype == 0) { + beginbox(&tp, "X11 forwarding options", + IDC_BOX_TUNNELS, IDC_BOXT_TUNNELS); + checkbox(&tp, "&Enable X11 forwarding", + IDC_X11_FORWARD); + multiedit(&tp, "&X display location", IDC_X11_DISPSTATIC, + IDC_X11_DISPLAY, 50, NULL); + endbox(&tp); + + treeview_insert(&tvfaff, 2, "Tunnels"); + } + } init_dlg_ctrls(hwnd); for (i = 0; i < nsessions; i++) @@ -1124,6 +1150,8 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, hide(hwnd, FALSE, windowpanelstart, windowpanelend); if (!strcmp(buffer, "Appearance")) hide(hwnd, FALSE, appearancepanelstart, appearancepanelend); + if (!strcmp(buffer, "Tunnels")) + hide(hwnd, FALSE, tunnelspanelstart, tunnelspanelend); if (!strcmp(buffer, "Connection")) hide(hwnd, FALSE, connectionpanelstart, connectionpanelend); if (!strcmp(buffer, "Telnet")) @@ -1848,6 +1876,16 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg, IsDlgButtonChecked (hwnd, IDC_VTOEMONLY) ? VT_OEMONLY : VT_POORMAN); break; + case IDC_X11_FORWARD: + if (HIWORD(wParam) == BN_CLICKED || + HIWORD(wParam) == BN_DOUBLECLICKED) + cfg.x11_forward = IsDlgButtonChecked (hwnd, IDC_X11_FORWARD); + break; + case IDC_X11_DISPLAY: + if (HIWORD(wParam) == EN_CHANGE) + GetDlgItemText (hwnd, IDC_X11_DISPLAY, cfg.x11_display, + sizeof(cfg.x11_display)-1); + break; } return 0; case WM_CLOSE: diff --git a/winnet.c b/winnet.c index 53a40e81..372845d5 100644 --- a/winnet.c +++ b/winnet.c @@ -423,6 +423,8 @@ Socket sk_new(SockAddr addr, int port, int privport, sk_receiver_t receiver) { } void sk_close(Socket s) { + extern char *do_select(SOCKET skt, int startup); + del234(sktree, s); do_select(s->s, 0); closesocket(s->s); diff --git a/x11fwd.c b/x11fwd.c new file mode 100644 index 00000000..c537f2da --- /dev/null +++ b/x11fwd.c @@ -0,0 +1,276 @@ +#include +#include +#include + +#include "putty.h" +#include "ssh.h" + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#define GET_32BIT_LSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0]) | \ + ((unsigned long)(unsigned char)(cp)[1] << 8) | \ + ((unsigned long)(unsigned char)(cp)[2] << 16) | \ + ((unsigned long)(unsigned char)(cp)[3] << 24)) + +#define PUT_32BIT_LSB_FIRST(cp, value) ( \ + (cp)[0] = (value), \ + (cp)[1] = (value) >> 8, \ + (cp)[2] = (value) >> 16, \ + (cp)[3] = (value) >> 24 ) + +#define GET_16BIT_LSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0]) | \ + ((unsigned long)(unsigned char)(cp)[1] << 8)) + +#define PUT_16BIT_LSB_FIRST(cp, value) ( \ + (cp)[0] = (value), \ + (cp)[1] = (value) >> 8 ) + +#define GET_32BIT_MSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0] << 24) | \ + ((unsigned long)(unsigned char)(cp)[1] << 16) | \ + ((unsigned long)(unsigned char)(cp)[2] << 8) | \ + ((unsigned long)(unsigned char)(cp)[3])) + +#define PUT_32BIT_MSB_FIRST(cp, value) ( \ + (cp)[0] = (value) >> 24, \ + (cp)[1] = (value) >> 16, \ + (cp)[2] = (value) >> 8, \ + (cp)[3] = (value) ) + +#define GET_16BIT_MSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0] << 8) | \ + ((unsigned long)(unsigned char)(cp)[1])) + +#define PUT_16BIT_MSB_FIRST(cp, value) ( \ + (cp)[0] = (value) >> 8, \ + (cp)[1] = (value) ) + +#define GET_16BIT(endian, cp) \ + (endian=='B' ? GET_16BIT_MSB_FIRST(cp) : GET_16BIT_LSB_FIRST(cp)) + +#define PUT_16BIT(endian, cp, val) \ + (endian=='B' ? PUT_16BIT_MSB_FIRST(cp, val) : PUT_16BIT_LSB_FIRST(cp, val)) + +extern void sshfwd_close(void *); +extern void sshfwd_write(void *, char *, int); + +struct X11Private { + unsigned char firstpkt[12]; /* first X data packet */ + char *auth_protocol; + unsigned char *auth_data; + int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize; + int verified; + void *c; /* data used by ssh.c */ +}; + +void x11_close (Socket s); + +static unsigned char x11_authdata[64]; +static int x11_authdatalen; + +void x11_invent_auth(char *proto, int protomaxlen, + char *data, int datamaxlen) { + char ourdata[64]; + int i; + + /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */ + x11_authdatalen = 16; + for (i = 0; i < 16; i++) + x11_authdata[i] = random_byte(); + + /* Now format for the recipient. */ + strncpy(proto, "MIT-MAGIC-COOKIE-1", protomaxlen); + ourdata[0] = '\0'; + for (i = 0; i < x11_authdatalen; i++) + sprintf(ourdata+strlen(ourdata), "%02x", x11_authdata[i]); + strncpy(data, ourdata, datamaxlen); +} + +static int x11_verify(char *proto, unsigned char *data, int dlen) { + if (strcmp(proto, "MIT-MAGIC-COOKIE-1") != 0) + return 0; /* wrong protocol attempted */ + if (dlen != x11_authdatalen) + return 0; /* cookie was wrong length */ + if (memcmp(x11_authdata, data, dlen) != 0) + return 0; /* cookie was wrong cookie! */ + return 1; +} + +static int x11_receive (Socket s, int urgent, char *data, int len) { + struct X11Private *pr = (struct X11Private *)sk_get_private_ptr(s); + + if (!len) { + /* Connection has closed. */ + sshfwd_close(pr->c); + x11_close(s); + return 1; + } + sshfwd_write(pr->c, data, len); + return 1; +} + +/* + * Called to set up the raw connection. + * + * Returns an error message, or NULL on success. + * also, fills the SocketsStructure + * + * Also places the canonical host name into `realhost'. + */ +char *x11_init (Socket *s, char *display, void *c, char **realhost) { + SockAddr addr; + int port; + char *err; + char host[128]; + int n, displaynum; + struct X11Private *pr; + + /* + * Split up display name into host and display-number parts. + */ + n = strcspn(display, ":"); + if (display[n]) + displaynum = atoi(display+n+1); + else + displaynum = 0; /* sensible default */ + if (n > sizeof(host)-1) + n = sizeof(host)-1; + strncpy(host, display, n); + host[n] = '\0'; + + /* + * Try to find host. + */ + addr = sk_namelookup(host, realhost); + if ( (err = sk_addr_error(addr)) ) + return err; + + port = 6000 + displaynum; + + /* + * Open socket. + */ + *s = sk_new(addr, port, 0, x11_receive); + if ( (err = sk_socket_error(*s)) ) + return err; + + pr = (struct X11Private *)smalloc(sizeof(struct X11Private)); + pr->auth_protocol = NULL; + pr->verified = 0; + pr->data_read = 0; + pr->c = c; + + sk_set_private_ptr(*s, pr); + sk_addr_free(addr); + return NULL; +} + +void x11_close (Socket s) { + struct X11Private *pr = (struct X11Private *)sk_get_private_ptr(s); + + if (pr->auth_protocol) { + sfree(pr->auth_protocol); + sfree(pr->auth_data); + } + + sfree(pr); + + sk_close(s); +} + +/* + * Called to send data down the raw connection. + */ +void x11_send (Socket s, char *data, int len) { + struct X11Private *pr = (struct X11Private *)sk_get_private_ptr(s); + + if (s == NULL) + return; + + /* + * Read the first packet. + */ + while (len > 0 && pr->data_read < 12) + pr->firstpkt[pr->data_read++] = (unsigned char)(len--, *data++); + if (pr->data_read < 12) + return; + + /* + * If we have not allocated the auth_protocol and auth_data + * strings, do so now. + */ + if (!pr->auth_protocol) { + pr->auth_plen = GET_16BIT(pr->firstpkt[0], pr->firstpkt+6); + pr->auth_dlen = GET_16BIT(pr->firstpkt[0], pr->firstpkt+8); + pr->auth_psize = (pr->auth_plen + 3) &~ 3; + pr->auth_dsize = (pr->auth_dlen + 3) &~ 3; + /* Leave room for a terminating zero, to make our lives easier. */ + pr->auth_protocol = (char *)smalloc(pr->auth_psize+1); + pr->auth_data = (char *)smalloc(pr->auth_dsize); + } + + /* + * Read the auth_protocol and auth_data strings. + */ + while (len > 0 && pr->data_read < 12 + pr->auth_psize) + pr->auth_protocol[pr->data_read++ - 12] = (len--, *data++); + while (len > 0 && pr->data_read < 12 + pr->auth_psize + pr->auth_dsize) + pr->auth_data[pr->data_read++ - 12 - + pr->auth_psize] = (unsigned char)(len--, *data++); + if (pr->data_read < 12 + pr->auth_psize + pr->auth_dsize) + return; + + /* + * If we haven't verified the authentication, do so now. + */ + if (!pr->verified) { + int ret; + + pr->auth_protocol[pr->auth_plen] = '\0'; /* ASCIZ */ + ret = x11_verify(pr->auth_protocol, pr->auth_data, pr->auth_dlen); + + /* + * If authentication failed, construct and send an error + * packet, then terminate the connection. + */ + if (!ret) { + char message[] = "Authentication failed at PuTTY X11 proxy"; + unsigned char reply[8 + sizeof(message) + 4]; + int msglen = sizeof(message)-1; /* skip zero byte */ + int msgsize = (msglen+3) &~ 3; + reply[0] = 0; /* failure */ + reply[1] = msglen; /* length of reason string */ + memcpy(reply+2, pr->firstpkt+2, 4); /* major/minor proto vsn */ + PUT_16BIT(pr->firstpkt[0], reply+6, msglen >> 2); /* data len */ + memset(reply+8, 0, msgsize); + memcpy(reply+8, message, msglen); + sshfwd_write(pr->c, reply, 8+msgsize); + sshfwd_close(pr->c); + x11_close(s); + return; + } + + /* + * Now we know we're going to accept the connection. Strip + * the auth data. (TODO: if we ever work out how, we should + * replace some real auth data in here.) + */ + PUT_16BIT(pr->firstpkt[0], pr->firstpkt+6, 0); /* auth proto */ + PUT_16BIT(pr->firstpkt[0], pr->firstpkt+8, 0); /* auth data */ + sk_write(s, pr->firstpkt, 12); + pr->verified = 1; + } + + /* + * After initialisation, just copy data simply. + */ + + sk_write(s, data, len); +} -- 2.11.0