+ if (!display || !*display) {
+ localcopy = platform_get_x_display();
+ if (!localcopy || !*localcopy) {
+ sfree(localcopy);
+ localcopy = dupstr(":0"); /* plausible default for any platform */
+ }
+ } else
+ localcopy = dupstr(display);
+
+ /*
+ * Parse the display name.
+ *
+ * We expect this to have one of the following forms:
+ *
+ * - the standard X format which looks like
+ * [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ]
+ * (X11 also permits a double colon to indicate DECnet, but
+ * that's not our problem, thankfully!)
+ *
+ * - only seen in the wild on MacOS (so far): a pathname to a
+ * Unix-domain socket, which will typically and confusingly
+ * end in ":0", and which I'm currently distinguishing from
+ * the standard scheme by noting that it starts with '/'.
+ */
+ if (localcopy[0] == '/') {
+ disp->unixsocketpath = localcopy;
+ disp->unixdomain = TRUE;
+ disp->hostname = NULL;
+ disp->displaynum = -1;
+ disp->screennum = 0;
+ } else {
+ char *colon, *dot, *slash;
+ char *protocol, *hostname;
+
+ colon = strrchr(localcopy, ':');
+ if (!colon) {
+ sfree(disp);
+ sfree(localcopy);
+ return NULL; /* FIXME: report a specific error? */
+ }
+
+ *colon++ = '\0';
+ dot = strchr(colon, '.');
+ if (dot)
+ *dot++ = '\0';
+
+ disp->displaynum = atoi(colon);
+ if (dot)
+ disp->screennum = atoi(dot);
+ else
+ disp->screennum = 0;
+
+ protocol = NULL;
+ hostname = localcopy;
+ if (colon > localcopy) {
+ slash = strchr(localcopy, '/');
+ if (slash) {
+ *slash++ = '\0';
+ protocol = localcopy;
+ hostname = slash;
+ }
+ }
+
+ disp->hostname = *hostname ? dupstr(hostname) : NULL;
+
+ if (protocol)
+ disp->unixdomain = (!strcmp(protocol, "local") ||
+ !strcmp(protocol, "unix"));
+ else
+ disp->unixdomain = platform_uses_x11_unix_by_default;
+
+ if (!disp->hostname && !disp->unixdomain)
+ disp->hostname = dupstr("localhost");
+
+ disp->unixsocketpath = NULL;
+
+ sfree(localcopy);
+ }
+
+ /*
+ * Look up the display hostname, if we need to.
+ */
+ if (disp->unixdomain) {
+ disp->addr = platform_get_x11_unix_address(disp->unixsocketpath,
+ disp->displaynum);
+ if (disp->unixsocketpath)
+ disp->realhost = dupstr(disp->unixsocketpath);
+ else
+ disp->realhost = dupprintf("unix:%d", disp->displaynum);
+ disp->port = 0;
+ } else {
+ const char *err;
+
+ disp->port = 6000 + disp->displaynum;
+ disp->addr = name_lookup(disp->hostname, disp->port,
+ &disp->realhost, cfg, ADDRTYPE_UNSPEC);
+
+ if ((err = sk_addr_error(disp->addr)) != NULL) {
+ sk_addr_free(disp->addr);
+ sfree(disp->hostname);
+ sfree(disp->unixsocketpath);
+ return NULL; /* FIXME: report an error */
+ }
+ }
+
+ /*
+ * Invent the remote authorisation details.
+ */
+ if (authtype == X11_MIT) {
+ disp->remoteauthproto = X11_MIT;
+
+ /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */
+ disp->remoteauthdata = snewn(16, unsigned char);
+ for (i = 0; i < 16; i++)
+ disp->remoteauthdata[i] = random_byte();
+ disp->remoteauthdatalen = 16;
+
+ disp->xdmseen = NULL;
+ } else {
+ assert(authtype == X11_XDM);
+ disp->remoteauthproto = X11_XDM;
+
+ /* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */
+ disp->remoteauthdata = snewn(16, unsigned char);
+ for (i = 0; i < 16; i++)
+ disp->remoteauthdata[i] = (i == 8 ? 0 : random_byte());
+ disp->remoteauthdatalen = 16;
+
+ disp->xdmseen = newtree234(xdmseen_cmp);
+ }
+ disp->remoteauthprotoname = dupstr(x11_authnames[disp->remoteauthproto]);
+ disp->remoteauthdatastring = snewn(disp->remoteauthdatalen * 2 + 1, char);
+ for (i = 0; i < disp->remoteauthdatalen; i++)
+ sprintf(disp->remoteauthdatastring + i*2, "%02x",
+ disp->remoteauthdata[i]);
+
+ /*
+ * Fetch the local authorisation details.
+ */
+ disp->localauthproto = X11_NO_AUTH;
+ disp->localauthdata = NULL;
+ disp->localauthdatalen = 0;
+ platform_get_x11_auth(disp, cfg);
+
+ return disp;
+}
+
+void x11_free_display(struct X11Display *disp)
+{
+ if (disp->xdmseen != NULL) {
+ struct XDMSeen *seen;
+ while ((seen = delpos234(disp->xdmseen, 0)) != NULL)
+ sfree(seen);
+ freetree234(disp->xdmseen);
+ }
+ sfree(disp->hostname);
+ sfree(disp->unixsocketpath);
+ if (disp->localauthdata)
+ memset(disp->localauthdata, 0, disp->localauthdatalen);
+ sfree(disp->localauthdata);
+ if (disp->remoteauthdata)
+ memset(disp->remoteauthdata, 0, disp->remoteauthdatalen);
+ sfree(disp->remoteauthdata);
+ sfree(disp->remoteauthprotoname);
+ sfree(disp->remoteauthdatastring);
+ sk_addr_free(disp->addr);
+ sfree(disp);
+}
+
+#define XDM_MAXSKEW 20*60 /* 20 minute clock skew should be OK */
+
+static char *x11_verify(unsigned long peer_ip, int peer_port,
+ struct X11Display *disp, char *proto,
+ unsigned char *data, int dlen)
+{
+ if (strcmp(proto, x11_authnames[disp->remoteauthproto]) != 0)
+ return "wrong authorisation protocol attempted";
+ if (disp->remoteauthproto == X11_MIT) {
+ if (dlen != disp->remoteauthdatalen)
+ return "MIT-MAGIC-COOKIE-1 data was wrong length";
+ if (memcmp(disp->remoteauthdata, data, dlen) != 0)
+ return "MIT-MAGIC-COOKIE-1 data did not match";
+ }
+ if (disp->remoteauthproto == X11_XDM) {
+ unsigned long t;
+ time_t tim;
+ int i;
+ struct XDMSeen *seen, *ret;
+
+ 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(disp->remoteauthdata+9, data, 24);
+ if (memcmp(disp->remoteauthdata, 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) > XDM_MAXSKEW)
+ return "XDM-AUTHORIZATION-1 time stamp was too far out";
+ seen = snew(struct XDMSeen);
+ seen->time = t;
+ memcpy(seen->clientid, data+8, 6);
+ assert(disp->xdmseen != NULL);
+ ret = add234(disp->xdmseen, seen);
+ if (ret != seen) {
+ sfree(seen);
+ return "XDM-AUTHORIZATION-1 data replayed";
+ }
+ /* While we're here, purge entries too old to be replayed. */
+ for (;;) {
+ seen = index234(disp->xdmseen, 0);
+ assert(seen != NULL);
+ if (t - seen->time <= XDM_MAXSKEW)
+ break;
+ sfree(delpos234(disp->xdmseen, 0));
+ }
+ }
+ /* implement other protocols here if ever required */
+ return NULL;