123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /*
- * Functions to handle .Xauthority files.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- #include "putty.h"
- #include "ssh.h"
- static 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))
- static void BinarySink_put_stringpl_xauth(BinarySink *bs, ptrlen pl)
- {
- assert((pl.len >> 16) == 0);
- put_uint16(bs, pl.len);
- put_datapl(bs, pl);
- }
- #define put_stringpl_xauth(bs, ptrlen) \
- BinarySink_put_stringpl_xauth(BinarySink_UPCAST(bs),ptrlen)
- void x11_get_auth_from_authfile(struct X11Display *disp,
- Filename *authfilename)
- {
- FILE *authfp;
- char *buf;
- int size;
- BinarySource src[1];
- int family, protocol;
- ptrlen addr, protoname, data;
- char *displaynum_string;
- int displaynum;
- bool ideal_match = false;
- char *ourhostname;
- /* A maximally sized (wildly implausible) .Xauthority record
- * consists of a 16-bit integer to start with, then four strings,
- * each of which has a 16-bit length field followed by that many
- * bytes of data (i.e. up to 0xFFFF bytes). */
- const size_t MAX_RECORD_SIZE = 2 + 4 * (2+0xFFFF);
- /* We'll want a buffer of twice that size (see below). */
- const size_t BUF_SIZE = 2 * MAX_RECORD_SIZE;
- /*
- * Normally we should look for precisely the details specified in
- * `disp'. However, there's an oddity when the display is local:
- * displays like "localhost:0" usually have their details stored
- * in a Unix-domain-socket record (even if there isn't actually a
- * real Unix-domain socket available, as with OpenSSH's proxy X11
- * server).
- *
- * This is apparently a fudge to get round the meaninglessness of
- * "localhost" in a shared-home-directory context -- xauth entries
- * for Unix-domain sockets already disambiguate this by storing
- * the *local* hostname in the conveniently-blank hostname field,
- * but IP "localhost" records couldn't do this. So, typically, an
- * IP "localhost" entry in the auth database isn't present and if
- * it were it would be ignored.
- *
- * However, we don't entirely trust that (say) Windows X servers
- * won't rely on a straight "localhost" entry, bad idea though
- * that is; so if we can't find a Unix-domain-socket entry we'll
- * fall back to an IP-based entry if we can find one.
- */
- bool localhost = !disp->unixdomain && sk_address_is_local(disp->addr);
- authfp = f_open(authfilename, "rb", false);
- if (!authfp)
- return;
- ourhostname = get_hostname();
- /*
- * 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) {
- bool 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 = displaynum_string[0] ? atoi(displaynum_string) : -1;
- 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
- * whether it matches the display we're trying to
- * authenticate to.
- *
- * The details we've just read should be interpreted as
- * follows:
- *
- * - 'family' is the network address family used to
- * connect to the display. 0 means IPv4; 6 means IPv6;
- * 256 means Unix-domain sockets.
- *
- * - '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.
- * 7F 00 00 01 means IPv4 localhost. For Unix-domain
- * sockets, this is the host name of the machine on
- * which the Unix-domain display resides (so that an
- * .Xauthority file on a shared file system can contain
- * authority entries for Unix-domain displays on
- * several machines without them clashing).
- *
- * - 'displaynum' is the display number. An empty display
- * number is a wildcard for any display number.
- *
- * - '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).
- *
- * - 'data' is the actual authorisation data, stored in
- * binary form.
- */
- if (disp->displaynum < 0 ||
- (displaynum >= 0 && disp->displaynum != displaynum))
- continue; /* not the one */
- for (protocol = 1; protocol < lenof(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 */
- switch (family) {
- case 0: /* IPv4 */
- if (!disp->unixdomain &&
- sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {
- char buf[4];
- sk_addrcopy(disp->addr, buf);
- 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. */
- ideal_match = !localhost;
- }
- }
- break;
- case 6: /* IPv6 */
- if (!disp->unixdomain &&
- sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {
- char buf[16];
- sk_addrcopy(disp->addr, buf);
- if (addr.len == 16 && !memcmp(addr.ptr, buf, 16)) {
- match = true;
- ideal_match = !localhost;
- }
- }
- break;
- case 256: /* Unix-domain / localhost */
- if ((disp->unixdomain || localhost)
- && ourhostname && ptrlen_eq_string(addr, ourhostname)) {
- /* A matching Unix-domain socket is always the best
- * match. */
- match = true;
- ideal_match = true;
- }
- break;
- }
- if (match) {
- /* Current best guess -- may be overridden if !ideal_match */
- disp->localauthproto = protocol;
- sfree(disp->localauthdata); /* free previous guess, if any */
- disp->localauthdata = snewn(data.len, unsigned char);
- memcpy(disp->localauthdata, data.ptr, data.len);
- disp->localauthdatalen = data.len;
- }
- }
- fclose(authfp);
- smemclr(buf, 2 * MAX_RECORD_SIZE);
- sfree(buf);
- sfree(ourhostname);
- }
- void x11_format_auth_for_authfile(
- BinarySink *bs, SockAddr *addr, int display_no,
- ptrlen authproto, ptrlen authdata)
- {
- if (sk_address_is_special_local(addr)) {
- char *ourhostname = get_hostname();
- put_uint16(bs, 256); /* indicates Unix-domain socket */
- put_stringpl_xauth(bs, ptrlen_from_asciz(ourhostname));
- sfree(ourhostname);
- } else if (sk_addrtype(addr) == ADDRTYPE_IPV4) {
- char ipv4buf[4];
- sk_addrcopy(addr, ipv4buf);
- put_uint16(bs, 0); /* indicates IPv4 */
- put_stringpl_xauth(bs, make_ptrlen(ipv4buf, 4));
- } else if (sk_addrtype(addr) == ADDRTYPE_IPV6) {
- char ipv6buf[16];
- sk_addrcopy(addr, ipv6buf);
- put_uint16(bs, 6); /* indicates IPv6 */
- put_stringpl_xauth(bs, make_ptrlen(ipv6buf, 16));
- } else {
- unreachable("Bad address type in x11_format_auth_for_authfile");
- }
- {
- char *numberbuf = dupprintf("%d", display_no);
- put_stringpl_xauth(bs, ptrlen_from_asciz(numberbuf));
- sfree(numberbuf);
- }
- put_stringpl_xauth(bs, authproto);
- put_stringpl_xauth(bs, authdata);
- }
|