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

Rewrite .Xauthority parsing using BinarySource.

This rewrite replaces a particularly hairy macro-based system.
This commit is contained in:
Simon Tatham 2018-05-29 22:41:37 +01:00
parent ae3edcdfc0
commit 5acd523ae6

103
x11fwd.c
View File

@ -429,15 +429,28 @@ static const char *x11_verify(unsigned long peer_ip, int peer_port,
return NULL;
}
ptrlen BinarySource_get_string_xauth(BinarySource *src)
{
size_t len = get_uint16(src);
return get_data(src, len);
}
#define get_string_xauth(src) \
BinarySource_get_string_xauth(BinarySource_UPCAST(src))
void x11_get_auth_from_authfile(struct X11Display *disp,
const char *authfilename)
{
FILE *authfp;
char *buf, *ptr, *str[4];
int len[4];
char *buf;
int size;
BinarySource src[1];
int family, protocol;
ptrlen addr, protoname, data;
char *displaynum_string;
int displaynum;
int ideal_match = FALSE;
char *ourhostname;
const size_t MAX_RECORD_SIZE = 0x80, BUF_SIZE = 2 * MAX_RECORD_SIZE;
/*
* Normally we should look for precisely the details specified in
@ -468,29 +481,41 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
ourhostname = get_hostname();
/* Records in .Xauthority contain four strings of up to 64K each */
buf = snewn(65537 * 4, char);
/*
* Allocate enough space to hold two maximally sized records, so
* that a full record can start anywhere in the first half. That
* way we avoid the accidentally-quadratic algorithm that would
* arise if we moved everything to the front of the buffer after
* consuming each record; instead, we only move everything to the
* front after our current position gets past the half-way mark.
* Before then, there's no need to move anyway; so this guarantees
* linear time, in that every byte written into this buffer moves
* at most once (because every move is from the second half of the
* buffer to the first half).
*/
buf = snewn(BUF_SIZE, char);
size = fread(buf, 1, BUF_SIZE, authfp);
BinarySource_BARE_INIT(src, buf, size);
while (!ideal_match) {
int c, i, j, match = FALSE;
#define GET do { c = fgetc(authfp); if (c == EOF) goto done; c = (unsigned char)c; } while (0)
/* Expect a big-endian 2-byte number giving address family */
GET; family = c;
GET; family = (family << 8) | c;
/* Then expect four strings, each composed of a big-endian 2-byte
* length field followed by that many bytes of data */
ptr = buf;
for (i = 0; i < 4; i++) {
GET; len[i] = c;
GET; len[i] = (len[i] << 8) | c;
str[i] = ptr;
for (j = 0; j < len[i]; j++) {
GET; *ptr++ = c;
}
*ptr++ = '\0';
}
#undef GET
int match = FALSE;
if (src->pos >= MAX_RECORD_SIZE) {
size -= src->pos;
memcpy(buf, buf + src->pos, size);
size += fread(buf + size, 1, BUF_SIZE - size, authfp);
BinarySource_BARE_INIT(src, buf, size);
}
family = get_uint16(src);
addr = get_string_xauth(src);
displaynum_string = mkstr(get_string_xauth(src));
displaynum = atoi(displaynum_string);
sfree(displaynum_string);
protoname = get_string_xauth(src);
data = get_string_xauth(src);
if (get_err(src))
break;
/*
* Now we have a full X authority record in memory. See
@ -504,7 +529,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
* connect to the display. 0 means IPv4; 6 means IPv6;
* 256 means Unix-domain sockets.
*
* - str[0] is the network address itself. For IPv4 and
* - 'addr' is the network address itself. For IPv4 and
* IPv6, this is a string of binary data of the
* appropriate length (respectively 4 and 16 bytes)
* representing the address in big-endian format, e.g.
@ -515,24 +540,23 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
* authority entries for Unix-domain displays on
* several machines without them clashing).
*
* - str[1] is the display number. I've no idea why
* - 'displaynum' is the display number. I've no idea why
* .Xauthority stores this as a string when it has a
* perfectly good integer format, but there we go.
*
* - str[2] is the authorisation method, encoded as its
* canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
* "XDM-AUTHORIZATION-1" or something we don't
* recognise).
* - 'protoname' is the authorisation protocol, encoded as
* its canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
* "XDM-AUTHORIZATION-1" or something we don't recognise).
*
* - str[3] is the actual authorisation data, stored in
* - 'data' is the actual authorisation data, stored in
* binary form.
*/
if (disp->displaynum < 0 || disp->displaynum != atoi(str[1]))
if (disp->displaynum < 0 || disp->displaynum != displaynum)
continue; /* not the one */
for (protocol = 1; protocol < lenof(x11_authnames); protocol++)
if (!strcmp(str[2], x11_authnames[protocol]))
if (ptrlen_eq_string(protoname, x11_authnames[protocol]))
break;
if (protocol == lenof(x11_authnames))
continue; /* don't recognise this protocol, look for another */
@ -543,7 +567,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {
char buf[4];
sk_addrcopy(disp->addr, buf);
if (len[0] == 4 && !memcmp(str[0], buf, 4)) {
if (addr.len == 4 && !memcmp(addr.ptr, buf, 4)) {
match = TRUE;
/* If this is a "localhost" entry, note it down
* but carry on looking for a Unix-domain entry. */
@ -556,7 +580,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {
char buf[16];
sk_addrcopy(disp->addr, buf);
if (len[0] == 16 && !memcmp(str[0], buf, 16)) {
if (addr.len == 16 && !memcmp(addr.ptr, buf, 16)) {
match = TRUE;
ideal_match = !localhost;
}
@ -564,7 +588,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
break;
case 256: /* Unix-domain / localhost */
if ((disp->unixdomain || localhost)
&& ourhostname && !strcmp(ourhostname, str[0]))
&& ourhostname && ptrlen_eq_string(addr, ourhostname))
/* A matching Unix-domain socket is always the best
* match. */
match = ideal_match = TRUE;
@ -575,15 +599,14 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
/* Current best guess -- may be overridden if !ideal_match */
disp->localauthproto = protocol;
sfree(disp->localauthdata); /* free previous guess, if any */
disp->localauthdata = snewn(len[3], unsigned char);
memcpy(disp->localauthdata, str[3], len[3]);
disp->localauthdatalen = len[3];
disp->localauthdata = snewn(data.len, unsigned char);
memcpy(disp->localauthdata, data.ptr, data.len);
disp->localauthdatalen = data.len;
}
}
done:
fclose(authfp);
smemclr(buf, 65537 * 4);
smemclr(buf, 2 * MAX_RECORD_SIZE);
sfree(buf);
sfree(ourhostname);
}