x11authfile.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Functions to handle .Xauthority files.
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <assert.h>
  7. #include "putty.h"
  8. #include "ssh.h"
  9. static ptrlen BinarySource_get_string_xauth(BinarySource *src)
  10. {
  11. size_t len = get_uint16(src);
  12. return get_data(src, len);
  13. }
  14. #define get_string_xauth(src) \
  15. BinarySource_get_string_xauth(BinarySource_UPCAST(src))
  16. static void BinarySink_put_stringpl_xauth(BinarySink *bs, ptrlen pl)
  17. {
  18. assert((pl.len >> 16) == 0);
  19. put_uint16(bs, pl.len);
  20. put_datapl(bs, pl);
  21. }
  22. #define put_stringpl_xauth(bs, ptrlen) \
  23. BinarySink_put_stringpl_xauth(BinarySink_UPCAST(bs),ptrlen)
  24. void x11_get_auth_from_authfile(struct X11Display *disp,
  25. Filename *authfilename)
  26. {
  27. FILE *authfp;
  28. char *buf;
  29. int size;
  30. BinarySource src[1];
  31. int family, protocol;
  32. ptrlen addr, protoname, data;
  33. char *displaynum_string;
  34. int displaynum;
  35. bool ideal_match = false;
  36. char *ourhostname;
  37. /* A maximally sized (wildly implausible) .Xauthority record
  38. * consists of a 16-bit integer to start with, then four strings,
  39. * each of which has a 16-bit length field followed by that many
  40. * bytes of data (i.e. up to 0xFFFF bytes). */
  41. const size_t MAX_RECORD_SIZE = 2 + 4 * (2+0xFFFF);
  42. /* We'll want a buffer of twice that size (see below). */
  43. const size_t BUF_SIZE = 2 * MAX_RECORD_SIZE;
  44. /*
  45. * Normally we should look for precisely the details specified in
  46. * `disp'. However, there's an oddity when the display is local:
  47. * displays like "localhost:0" usually have their details stored
  48. * in a Unix-domain-socket record (even if there isn't actually a
  49. * real Unix-domain socket available, as with OpenSSH's proxy X11
  50. * server).
  51. *
  52. * This is apparently a fudge to get round the meaninglessness of
  53. * "localhost" in a shared-home-directory context -- xauth entries
  54. * for Unix-domain sockets already disambiguate this by storing
  55. * the *local* hostname in the conveniently-blank hostname field,
  56. * but IP "localhost" records couldn't do this. So, typically, an
  57. * IP "localhost" entry in the auth database isn't present and if
  58. * it were it would be ignored.
  59. *
  60. * However, we don't entirely trust that (say) Windows X servers
  61. * won't rely on a straight "localhost" entry, bad idea though
  62. * that is; so if we can't find a Unix-domain-socket entry we'll
  63. * fall back to an IP-based entry if we can find one.
  64. */
  65. bool localhost = !disp->unixdomain && sk_address_is_local(disp->addr);
  66. authfp = f_open(authfilename, "rb", false);
  67. if (!authfp)
  68. return;
  69. ourhostname = get_hostname();
  70. /*
  71. * Allocate enough space to hold two maximally sized records, so
  72. * that a full record can start anywhere in the first half. That
  73. * way we avoid the accidentally-quadratic algorithm that would
  74. * arise if we moved everything to the front of the buffer after
  75. * consuming each record; instead, we only move everything to the
  76. * front after our current position gets past the half-way mark.
  77. * Before then, there's no need to move anyway; so this guarantees
  78. * linear time, in that every byte written into this buffer moves
  79. * at most once (because every move is from the second half of the
  80. * buffer to the first half).
  81. */
  82. buf = snewn(BUF_SIZE, char);
  83. size = fread(buf, 1, BUF_SIZE, authfp);
  84. BinarySource_BARE_INIT(src, buf, size);
  85. while (!ideal_match) {
  86. bool match = false;
  87. if (src->pos >= MAX_RECORD_SIZE) {
  88. size -= src->pos;
  89. memcpy(buf, buf + src->pos, size);
  90. size += fread(buf + size, 1, BUF_SIZE - size, authfp);
  91. BinarySource_BARE_INIT(src, buf, size);
  92. }
  93. family = get_uint16(src);
  94. addr = get_string_xauth(src);
  95. displaynum_string = mkstr(get_string_xauth(src));
  96. displaynum = displaynum_string[0] ? atoi(displaynum_string) : -1;
  97. sfree(displaynum_string);
  98. protoname = get_string_xauth(src);
  99. data = get_string_xauth(src);
  100. if (get_err(src))
  101. break;
  102. /*
  103. * Now we have a full X authority record in memory. See
  104. * whether it matches the display we're trying to
  105. * authenticate to.
  106. *
  107. * The details we've just read should be interpreted as
  108. * follows:
  109. *
  110. * - 'family' is the network address family used to
  111. * connect to the display. 0 means IPv4; 6 means IPv6;
  112. * 256 means Unix-domain sockets.
  113. *
  114. * - 'addr' is the network address itself. For IPv4 and
  115. * IPv6, this is a string of binary data of the
  116. * appropriate length (respectively 4 and 16 bytes)
  117. * representing the address in big-endian format, e.g.
  118. * 7F 00 00 01 means IPv4 localhost. For Unix-domain
  119. * sockets, this is the host name of the machine on
  120. * which the Unix-domain display resides (so that an
  121. * .Xauthority file on a shared file system can contain
  122. * authority entries for Unix-domain displays on
  123. * several machines without them clashing).
  124. *
  125. * - 'displaynum' is the display number. An empty display
  126. * number is a wildcard for any display number.
  127. *
  128. * - 'protoname' is the authorisation protocol, encoded as
  129. * its canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
  130. * "XDM-AUTHORIZATION-1" or something we don't recognise).
  131. *
  132. * - 'data' is the actual authorisation data, stored in
  133. * binary form.
  134. */
  135. if (disp->displaynum < 0 ||
  136. (displaynum >= 0 && disp->displaynum != displaynum))
  137. continue; /* not the one */
  138. for (protocol = 1; protocol < lenof(x11_authnames); protocol++)
  139. if (ptrlen_eq_string(protoname, x11_authnames[protocol]))
  140. break;
  141. if (protocol == lenof(x11_authnames))
  142. continue; /* don't recognise this protocol, look for another */
  143. switch (family) {
  144. case 0: /* IPv4 */
  145. if (!disp->unixdomain &&
  146. sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {
  147. char buf[4];
  148. sk_addrcopy(disp->addr, buf);
  149. if (addr.len == 4 && !memcmp(addr.ptr, buf, 4)) {
  150. match = true;
  151. /* If this is a "localhost" entry, note it down
  152. * but carry on looking for a Unix-domain entry. */
  153. ideal_match = !localhost;
  154. }
  155. }
  156. break;
  157. case 6: /* IPv6 */
  158. if (!disp->unixdomain &&
  159. sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {
  160. char buf[16];
  161. sk_addrcopy(disp->addr, buf);
  162. if (addr.len == 16 && !memcmp(addr.ptr, buf, 16)) {
  163. match = true;
  164. ideal_match = !localhost;
  165. }
  166. }
  167. break;
  168. case 256: /* Unix-domain / localhost */
  169. if ((disp->unixdomain || localhost)
  170. && ourhostname && ptrlen_eq_string(addr, ourhostname)) {
  171. /* A matching Unix-domain socket is always the best
  172. * match. */
  173. match = true;
  174. ideal_match = true;
  175. }
  176. break;
  177. }
  178. if (match) {
  179. /* Current best guess -- may be overridden if !ideal_match */
  180. disp->localauthproto = protocol;
  181. sfree(disp->localauthdata); /* free previous guess, if any */
  182. disp->localauthdata = snewn(data.len, unsigned char);
  183. memcpy(disp->localauthdata, data.ptr, data.len);
  184. disp->localauthdatalen = data.len;
  185. }
  186. }
  187. fclose(authfp);
  188. smemclr(buf, 2 * MAX_RECORD_SIZE);
  189. sfree(buf);
  190. sfree(ourhostname);
  191. }
  192. void x11_format_auth_for_authfile(
  193. BinarySink *bs, SockAddr *addr, int display_no,
  194. ptrlen authproto, ptrlen authdata)
  195. {
  196. if (sk_address_is_special_local(addr)) {
  197. char *ourhostname = get_hostname();
  198. put_uint16(bs, 256); /* indicates Unix-domain socket */
  199. put_stringpl_xauth(bs, ptrlen_from_asciz(ourhostname));
  200. sfree(ourhostname);
  201. } else if (sk_addrtype(addr) == ADDRTYPE_IPV4) {
  202. char ipv4buf[4];
  203. sk_addrcopy(addr, ipv4buf);
  204. put_uint16(bs, 0); /* indicates IPv4 */
  205. put_stringpl_xauth(bs, make_ptrlen(ipv4buf, 4));
  206. } else if (sk_addrtype(addr) == ADDRTYPE_IPV6) {
  207. char ipv6buf[16];
  208. sk_addrcopy(addr, ipv6buf);
  209. put_uint16(bs, 6); /* indicates IPv6 */
  210. put_stringpl_xauth(bs, make_ptrlen(ipv6buf, 16));
  211. } else {
  212. unreachable("Bad address type in x11_format_auth_for_authfile");
  213. }
  214. {
  215. char *numberbuf = dupprintf("%d", display_no);
  216. put_stringpl_xauth(bs, ptrlen_from_asciz(numberbuf));
  217. sfree(numberbuf);
  218. }
  219. put_stringpl_xauth(bs, authproto);
  220. put_stringpl_xauth(bs, authdata);
  221. }