1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Support for XDM-AUTHORIZATION-1 at the SSH server end, making use of

the remote IP/port data provided by the server for forwarded
connections. Disabled by default, since it's incompatible with SSH2,
probably incompatible with some X clients, and tickles a bug in
at least one version of OpenSSH.

[originally from svn r2554]
This commit is contained in:
Simon Tatham 2003-01-12 14:11:38 +00:00
parent 05ae857752
commit fee1624c69
8 changed files with 201 additions and 45 deletions

View File

@ -1,4 +1,4 @@
\versionid $Id: config.but,v 1.47 2002/12/18 16:23:10 simon Exp $
\versionid $Id: config.but,v 1.48 2003/01/12 14:11:38 simon Exp $
\C{config} Configuring PuTTY
@ -1887,6 +1887,53 @@ display location} box.
See \k{using-x-forwarding} for more information about X11
forwarding.
\S2{config-ssh-x11auth} Remote X11 authentication
\cfg{winhelp-topic}{ssh.tunnels.x11auth}
If you are using X11 forwarding, the virtual X server created on the
SSH server machine will be protected by authorisation data. This
data is invented, and checked, by PuTTY.
The usual authorisation method used for this is called
\cw{MIT-MAGIC-COOKIE-1}. This is a simple password-style protocol:
the X client sends some cookie data to the server, and the server
checks that it matches the real cookie. The cookie data is sent over
an unencrypted X11 connection; so if you allow a client on a third
machine to access the virtual X server, then the cookie will be sent
in the clear.
PuTTY offers the alternative protocol \cw{XDM-AUTHORIZATION-1}. This
is a cryptographically authenticated protocol: the data sent by the
X client is different every time, and it depends on the IP address
and port of the client's end of the connection and is also stamped
with the current time. So an eavesdropper who captures an
\cw{XDM-AUTHORIZATION-1} string cannot immediately re-use it for
their own X connection.
PuTTY's support for \cw{XDM-AUTHORIZATION-1} is a somewhat
experimental feature, and may encounter several problems:
\b Some X clients probably do not even support
\cw{XDM-AUTHORIZATION-1}, so they will not know what to do with the
data PuTTY has provided.
\b This authentication mechanism will only work in SSH v2. In SSH
v1, the SSH server does not tell the client the source address of
a forwarded connection in a machine-readable format, so it's
impossible to verify the \cw{XDM-AUTHORIZATION-1} data.
\b You may find this feature causes problems with some SSH servers,
which will not clean up \cw{XDM-AUTHORIZATION-1} data after a
session, so that if you then connect to the same server using
a client which only does \cw{MIT-MAGIC-COOKIE-1} and are allocated
the same remote display number, you might find that out-of-date
authentication data is still present on your server and your X
connections fail.
PuTTY's default is \cw{MIT-MAGIC-COOKIE-1}. If you change it, you
should be sure you know what you're doing.
\S{config-ssh-portfwd} Port forwarding
\cfg{winhelp-topic}{ssh.tunnels.portfwd}

View File

@ -344,6 +344,7 @@ struct config_tag {
/* X11 forwarding */
int x11_forward;
char x11_display[128];
int x11_auth;
/* port forwarding */
int lport_acceptall; /* accept conns from hosts other than localhost */
int rport_acceptall; /* same for remote forwarded ports (SSH2 only) */

View File

@ -292,6 +292,7 @@ void save_settings(char *section, int do_host, Config * cfg)
write_setting_i(sesskey, "BlinkText", cfg->blinktext);
write_setting_i(sesskey, "X11Forward", cfg->x11_forward);
write_setting_s(sesskey, "X11Display", cfg->x11_display);
write_setting_i(sesskey, "X11AuthType", cfg->x11_auth);
write_setting_i(sesskey, "LocalPortAcceptAll", cfg->lport_acceptall);
write_setting_i(sesskey, "RemotePortAcceptAll", cfg->rport_acceptall);
{
@ -582,6 +583,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
gppi(sesskey, "X11Forward", 0, &cfg->x11_forward);
gpps(sesskey, "X11Display", "localhost:0", cfg->x11_display,
sizeof(cfg->x11_display));
gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth);
gppi(sesskey, "LocalPortAcceptAll", 0, &cfg->lport_acceptall);
gppi(sesskey, "RemotePortAcceptAll", 0, &cfg->rport_acceptall);

19
ssh.c
View File

@ -3018,7 +3018,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
char proto[20], data[64];
logevent("Requesting X11 forwarding");
ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
data, sizeof(data));
data, sizeof(data), cfg.x11_auth);
x11_get_real_auth(ssh->x11auth, cfg.x11_display);
if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
@ -3277,7 +3277,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
c->ssh = ssh;
if (x11_init(&c->u.x11.s, cfg.x11_display, c,
ssh->x11auth) != NULL) {
ssh->x11auth, NULL, -1) != NULL) {
logevent("opening X11 forward connection failed");
sfree(c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
@ -5068,7 +5068,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
char proto[20], data[64];
logevent("Requesting X11 forwarding");
ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
data, sizeof(data));
data, sizeof(data), cfg.x11_auth);
x11_get_real_auth(ssh->x11auth, cfg.x11_display);
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
@ -5714,6 +5714,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
} else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN) {
char *type;
int typelen;
char *peeraddr;
int peeraddrlen;
int port;
char *error = NULL;
struct ssh_channel *c;
unsigned remid, winsize, pktsize;
@ -5724,16 +5727,24 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
remid = ssh2_pkt_getuint32(ssh);
winsize = ssh2_pkt_getuint32(ssh);
pktsize = ssh2_pkt_getuint32(ssh);
ssh2_pkt_getstring(ssh, &peeraddr, &peeraddrlen);
port = ssh2_pkt_getuint32(ssh);
if (typelen == 3 && !memcmp(type, "x11", 3)) {
char *addrstr = smalloc(peeraddrlen+1);
memcpy(addrstr, peeraddr, peeraddrlen);
peeraddr[peeraddrlen] = '\0';
if (!ssh->X11_fwd_enabled)
error = "X11 forwarding is not enabled";
else if (x11_init(&c->u.x11.s, cfg.x11_display, c,
ssh->x11auth) != NULL) {
ssh->x11auth, addrstr, port) != NULL) {
error = "Unable to open an X11 connection";
} else {
c->type = CHAN_X11;
}
sfree(addrstr);
} else if (typelen == 15 &&
!memcmp(type, "forwarded-tcpip", 15)) {
struct ssh_rportfwd pf, *realpf;

5
ssh.h
View File

@ -266,10 +266,10 @@ extern void pfd_unthrottle(Socket s);
extern void pfd_override_throttle(Socket s, int enable);
/* Exports from x11fwd.c */
extern char *x11_init(Socket *, char *, void *, void *);
extern char *x11_init(Socket *, char *, void *, void *, const char *, int);
extern void x11_close(Socket);
extern int x11_send(Socket, char *, int);
extern void *x11_invent_auth(char *, int, char *, int);
extern void *x11_invent_auth(char *, int, char *, int, int);
extern void x11_unthrottle(Socket s);
extern void x11_override_throttle(Socket s, int enable);
extern int x11_get_screen_number(char *display);
@ -368,6 +368,7 @@ void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk,
int len);
void des_encrypt_xdmauth(unsigned char *key, unsigned char *blk, int len);
void des_decrypt_xdmauth(unsigned char *key, unsigned char *blk, int len);
/*
* For progress updates in the key generation utility.

View File

@ -897,10 +897,9 @@ void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
memset(ourkeys, 0, sizeof(ourkeys));
}
void des_encrypt_xdmauth(unsigned char *keydata, unsigned char *blk, int len)
static void des_keysetup_xdmauth(unsigned char *keydata, DESContext *dc)
{
unsigned char key[8];
DESContext dc;
int i, nbits, j;
unsigned int bits;
@ -918,11 +917,23 @@ void des_encrypt_xdmauth(unsigned char *keydata, unsigned char *blk, int len)
nbits -= 7;
}
des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4),
&dc);
des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), dc);
}
void des_encrypt_xdmauth(unsigned char *keydata, unsigned char *blk, int len)
{
DESContext dc;
des_keysetup_xdmauth(keydata, &dc);
des_cbc_encrypt(blk, blk, 24, &dc);
}
void des_decrypt_xdmauth(unsigned char *keydata, unsigned char *blk, int len)
{
DESContext dc;
des_keysetup_xdmauth(keydata, &dc);
des_cbc_decrypt(blk, blk, 24, &dc);
}
static const struct ssh2_cipher ssh_3des_ssh2 = {
des3_make_context, des3_free_context, des3_iv, des3_key,
des3_ssh2_encrypt_blk, des3_ssh2_decrypt_blk,

View File

@ -620,6 +620,9 @@ enum { IDCX_ABOUT =
IDC_X11_FORWARD,
IDC_X11_DISPSTATIC,
IDC_X11_DISPLAY,
IDC_X11AUTHSTATIC,
IDC_X11MIT,
IDC_X11XDM,
IDC_LPORT_ALL,
IDC_RPORT_ALL,
IDC_PFWDSTATIC,
@ -1025,6 +1028,10 @@ char *help_context_cmd(int id)
case IDC_X11_DISPSTATIC:
case IDC_X11_DISPLAY:
return "JI(`',`ssh.tunnels.x11')";
case IDC_X11AUTHSTATIC:
case IDC_X11MIT:
case IDC_X11XDM:
return "JI(`',`ssh.tunnels.x11auth')";
case IDC_PFWDSTATIC:
case IDC_PFWDSTATIC2:
case IDC_PFWDREMOVE:
@ -1347,6 +1354,8 @@ static void init_dlg_ctrls(HWND hwnd, int keepsess)
CheckDlgButton(hwnd, IDC_X11_FORWARD, cfg.x11_forward);
SetDlgItemText(hwnd, IDC_X11_DISPLAY, cfg.x11_display);
CheckRadioButton(hwnd, IDC_X11MIT, IDC_X11XDM,
cfg.x11_auth == X11_MIT ? IDC_X11MIT : IDC_X11XDM);
CheckDlgButton(hwnd, IDC_LPORT_ALL, cfg.lport_acceptall);
CheckDlgButton(hwnd, IDC_RPORT_ALL, cfg.rport_acceptall);
@ -2046,8 +2055,12 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
IDC_TITLE_TUNNELS);
beginbox(&cp, "X11 forwarding", IDC_BOX_TUNNELS1);
checkbox(&cp, "&Enable X11 forwarding", IDC_X11_FORWARD);
multiedit(&cp, "&X display location", IDC_X11_DISPSTATIC,
IDC_X11_DISPLAY, 50, NULL);
staticedit(&cp, "&X display location", IDC_X11_DISPSTATIC,
IDC_X11_DISPLAY, 50);
radioline(&cp, "Remote X11 a&uthentication protocol",
IDC_X11AUTHSTATIC, 2,
"MIT-Magic-Cookie-1", IDC_X11MIT,
"XDM-Authorization-1", IDC_X11XDM, NULL);
endbox(&cp);
beginbox(&cp, "Port forwarding", IDC_BOX_TUNNELS2);
checkbox(&cp, "Local ports accept connections from o&ther hosts",
@ -3535,6 +3548,16 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
GetDlgItemText(hwnd, IDC_X11_DISPLAY, cfg.x11_display,
sizeof(cfg.x11_display) - 1);
break;
case IDC_X11MIT:
case IDC_X11XDM:
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) {
if (IsDlgButtonChecked(hwnd, IDC_X11MIT))
cfg.x11_auth = X11_MIT;
else if (IsDlgButtonChecked(hwnd, IDC_X11XDM))
cfg.x11_auth = X11_XDM;
}
break;
case IDC_PFWDADD:
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) {

124
x11fwd.c
View File

@ -13,18 +13,18 @@
((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 )
(cp)[0] = (char)(value), \
(cp)[1] = (char)((value) >> 8), \
(cp)[2] = (char)((value) >> 16), \
(cp)[3] = (char)((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 )
(cp)[0] = (char)(value), \
(cp)[1] = (char)((value) >> 8) )
#define GET_32BIT_MSB_FIRST(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
@ -33,18 +33,18 @@
((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) )
(cp)[0] = (char)((value) >> 24), \
(cp)[1] = (char)((value) >> 16), \
(cp)[2] = (char)((value) >> 8), \
(cp)[3] = (char)(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) )
(cp)[0] = (char)((value) >> 8), \
(cp)[1] = (char)(value) )
#define GET_16BIT(endian, cp) \
(endian=='B' ? GET_16BIT_MSB_FIRST(cp) : GET_16BIT_LSB_FIRST(cp))
@ -72,23 +72,35 @@ struct X11Private {
int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;
int verified;
int throttled, throttle_override;
unsigned long peer_ip;
int peer_port;
void *c; /* data used by ssh.c */
Socket s;
};
void *x11_invent_auth(char *proto, int protomaxlen,
char *data, int datamaxlen)
char *data, int datamaxlen, int proto_id)
{
struct X11Auth *auth = smalloc(sizeof(struct X11Auth));
char ourdata[64];
int i;
auth->fakeproto = X11_MIT;
if (proto_id == X11_MIT) {
auth->fakeproto = X11_MIT;
/* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */
auth->fakelen = 16;
for (i = 0; i < 16; i++)
auth->fakedata[i] = random_byte();
/* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */
auth->fakelen = 16;
for (i = 0; i < 16; i++)
auth->fakedata[i] = random_byte();
} else {
assert(proto_id == X11_XDM);
auth->fakeproto = X11_XDM;
/* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */
auth->fakelen = 16;
for (i = 0; i < 16; i++)
auth->fakedata[i] = (i == 8 ? 0 : random_byte());
}
/* Now format for the recipient. */
strncpy(proto, x11_authnames[auth->fakeproto], protomaxlen);
@ -116,19 +128,44 @@ void x11_get_real_auth(void *authv, char *display)
auth->realdata, &auth->reallen);
}
static int x11_verify(struct X11Auth *auth,
char *proto, unsigned char *data, int dlen)
static char *x11_verify(unsigned long peer_ip, int peer_port,
struct X11Auth *auth, char *proto,
unsigned char *data, int dlen)
{
if (strcmp(proto, x11_authnames[auth->fakeproto]) != 0)
return 0; /* wrong protocol attempted */
return "wrong authentication protocol attempted";
if (auth->fakeproto == X11_MIT) {
if (dlen != auth->fakelen)
return 0; /* cookie was wrong length */
return "MIT-MAGIC-COOKIE-1 data was wrong length";
if (memcmp(auth->fakedata, data, dlen) != 0)
return 0; /* cookie was wrong cookie! */
return "MIT-MAGIC-COOKIE-1 data did not match";
}
if (auth->fakeproto == X11_XDM) {
unsigned long t;
time_t tim;
int i;
if (dlen != 24)
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(auth->fakedata+9, data, 24);
if (memcmp(auth->fakedata, 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 */
if ((int)GET_16BIT_MSB_FIRST(data+12) != peer_port)
return "XDM-AUTHORIZATION-1 data failed check"; /* port wrong */
t = GET_32BIT_MSB_FIRST(data+14);
for (i = 18; i < 24; i++)
if (data[i] != 0) /* zero padding wrong */
return "XDM-AUTHORIZATION-1 data failed check";
tim = time(NULL);
if (abs(t - tim) > 20*60) /* 20 minute clock skew should be OK */
return "XDM-AUTHORIZATION-1 time stamp was too far out";
}
/* implement other protocols here if ever required */
return 1;
return NULL;
}
static int x11_closing(Plug plug, char *error_msg, int error_code,
@ -189,7 +226,8 @@ int x11_get_screen_number(char *display)
* Returns an error message, or NULL on success.
* also, fills the SocketsStructure
*/
char *x11_init(Socket * s, char *display, void *c, void *auth)
char *x11_init(Socket * s, char *display, void *c, void *auth,
const char *peeraddr, int peerport)
{
static const struct plug_function_table fn_table = {
x11_closing,
@ -253,6 +291,21 @@ char *x11_init(Socket * s, char *display, void *c, void *auth)
return err;
}
/*
* See if we can make sense of the peer address we were given.
*/
{
int i[4];
if (peeraddr &&
4 == sscanf(peeraddr, "%d.%d.%d.%d", i+0, i+1, i+2, i+3)) {
pr->peer_ip = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3];
pr->peer_port = peerport;
} else {
pr->peer_ip = 0;
pr->peer_port = -1;
}
}
sk_set_private_ptr(*s, pr);
sk_addr_free(addr);
return NULL;
@ -343,21 +396,26 @@ int x11_send(Socket s, char *data, int len)
* If we haven't verified the authentication, do so now.
*/
if (!pr->verified) {
int ret;
char *err;
pr->auth_protocol[pr->auth_plen] = '\0'; /* ASCIZ */
ret = x11_verify(pr->auth, pr->auth_protocol,
err = x11_verify(pr->peer_ip, pr->peer_port,
pr->auth, 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;
if (err) {
char *message;
int msglen, msgsize;
unsigned char *reply;
message = dupprintf("PuTTY X11 proxy: %s", err);
msglen = strlen(message);
reply = smalloc(8 + msglen+1 + 4); /* include zero byte */
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 */
@ -367,6 +425,8 @@ int x11_send(Socket s, char *data, int len)
sshfwd_write(pr->c, (char *)reply, 8 + msgsize);
sshfwd_close(pr->c);
x11_close(s);
sfree(reply);
sfree(message);
return 0;
}