From e8883275bfee9316f1fe2f96336a98f2d2e240c1 Mon Sep 17 00:00:00 2001 From: Ian Scott Date: Mon, 21 Mar 2016 16:00:52 -0700 Subject: [PATCH 1/4] Fix auth for remote hosts and handle different auth families - Use the remote address to find the auth cookie instead of the local hostname - Read family type as big endian in Xauthority file - Don't upgrade 127.0.0.1 TCP connections to unix domain socket (libX11 and xcb don't do this) - Don't error if no auth cookie is found; continue without auth --- lib/auth.js | 26 +++++++++++++++++++------- lib/handshake.js | 4 ++-- lib/xcore.js | 15 +++++++++------ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/auth.js b/lib/auth.js index 264a3f2..1546e5d 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,4 +1,5 @@ -// TODO: http://en.wikipedia.org/wiki/X_Window_authorization +// TODO: differentiate between auth types (i.e., MIT-MAGIC-COOKIE-1 and XDM-AUTHORIZATION-1) +// and choose the best based on the algorithm in libXau's XauGetBestAuthByAddr var fs = require('fs'); var Buffer = require('buffer').Buffer; @@ -15,7 +16,7 @@ var typeToName = { 1: 'DECnet', 2: 'Chaos', 5: 'ServerInterpreted', - 6: 'InternetV6' + 6: 'Internet6' }; function parseXauth( buf ) @@ -27,7 +28,7 @@ function parseXauth( buf ) while (offset < buf.length) { var cookie = {}; - cookie.type = buf.readUInt16LE(offset); + cookie.type = buf.readUInt16BE(offset); if (!typeToName[cookie.type]) { console.warn('Unknown address type'); } @@ -73,8 +74,16 @@ function readXauthority(cb) { }); } -module.exports = function( display, host, cb ) +module.exports = function( display, host, socketFamily, cb ) { + var family; + if (socketFamily === 'IPv4') { + family = 0; // Internet + } else if (socketFamily === 'IPv6') { + family = 6; // Internet6 + } else { + family = 256; // Local + } readXauthority(function(err, data) { if(err) return cb(err); @@ -84,15 +93,18 @@ module.exports = function( display, host, cb ) authData: '' }); } - var auth = parseXauth(data); for (var cookieNum in auth) { var cookie = auth[cookieNum]; - if ((typeToName[cookie.family] === 'Wild' || cookie.address === host) && + if ((typeToName[cookie.family] === 'Wild' || (cookie.type === family && cookie.address === host)) && (cookie.display.length === 0 || cookie.display === display)) return cb( null, cookie ); } - cb(new Error('No auth cookie matching display=' + display + ' and host=' + host)); + // If no cookie is found, proceed without authentication + cb(null, { + authName: '', + authData: '' + }); }); }; diff --git a/lib/handshake.js b/lib/handshake.js index 45f08b0..9fbb623 100644 --- a/lib/handshake.js +++ b/lib/handshake.js @@ -189,9 +189,9 @@ function getByteOrder() { } } -function writeClientHello(stream, displayNum, authHost) +function writeClientHello(stream, displayNum, authHost, authFamily) { - getAuthString( displayNum, authHost, function( err, cookie ) { + getAuthString( displayNum, authHost, authFamily, function( err, cookie ) { if (err) { throw err; } diff --git a/lib/xcore.js b/lib/xcore.js index f4744a7..5c68b1c 100644 --- a/lib/xcore.js +++ b/lib/xcore.js @@ -31,7 +31,6 @@ function XClient(displayNum, screenNum, options) this.displayNum = displayNum; this.screenNum = screenNum; - this.authHost = os.hostname(); } util.inherits(XClient, EventEmitter); @@ -39,6 +38,13 @@ XClient.prototype.init = function(stream) { this.stream = stream; + this.authHost = stream.remoteAddress; + this.authFamily = stream.remoteFamily; + if (!this.authHost || this.authHost === '127.0.0.1' || this.authHost === '::1') { + this.authHost = os.hostname(); + this.authFamily = null; + } + var pack_stream = new PackStream(); // data received from stream is dispached to @@ -524,7 +530,7 @@ XClient.prototype.expectReplyHeader = function() XClient.prototype.startHandshake = function() { var client = this; - handshake.writeClientHello(this.pack_stream, this.displayNum, this.authHost); + handshake.writeClientHello(this.pack_stream, this.displayNum, this.authHost, this.authFamily); handshake.readServerHello(this.pack_stream, function(display) { // TODO: readServerHello can set error state in display @@ -560,8 +566,6 @@ module.exports.createClient = function(options, initCb) throw new Error("Cannot parse display"); var host = displayMatch[1]; - if (!host) - host = '127.0.0.1'; var displayNum = displayMatch[2]; if (!displayNum) @@ -586,10 +590,9 @@ module.exports.createClient = function(options, initCb) { socketPath = display; } - } else if(host == '127.0.0.1') //TODO check if it's consistent with xlib (DISPLAY=127.0.0.1:0 -> local unix socket or port 6000?) + } else if(!host) socketPath = '/tmp/.X11-unix/X' + displayNum; } - //socketPath = '/tmp/.X11-unix/X' + displayNum; var client = new XClient(displayNum, screenNum, options); var connectStream = function() { From c694394b6d3210c13d77f9c86061b1a1c8b11885 Mon Sep 17 00:00:00 2001 From: Ian Scott Date: Fri, 13 May 2016 18:25:31 -0700 Subject: [PATCH 2/4] Test xauth in Travis tests --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 58649b7..5828bfb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,14 @@ before_script: - - "export DISPLAY=:99.0" - - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -nolisten $NOLISTEN" + - "export XAUTHORITY=/tmp/.Xauthority-Xvfb" + - "xauth add :99 . $(mcookie)" + - "xauth add 127.0.0.2:99 . $(mcookie)" + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -nolisten $NOLISTEN -auth $XAUTHORITY" + - "sleep 1" env: - - NOLISTEN=tcp - - NOLISTEN=unix + - NOLISTEN=tcp DISPLAY=:99.0 + - NOLISTEN=unix DISPLAY=:99.0 + - NOLISTEN=unix DISPLAY=127.0.0.2:99.0 language: node_js node_js: From a42f0437681f90ea232a866736fed1406f1a2a89 Mon Sep 17 00:00:00 2001 From: Ian Scott Date: Fri, 13 May 2016 18:26:40 -0700 Subject: [PATCH 3/4] Emit error when handshake fails, such as with bad xauth --- lib/handshake.js | 2 +- lib/xcore.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/handshake.js b/lib/handshake.js index 9fbb623..254837b 100644 --- a/lib/handshake.js +++ b/lib/handshake.js @@ -90,7 +90,7 @@ function readScreens(bl, display, cbDisplayReady) if (display.screen.length == display.screen_num) { delete display.screen_num; - cbDisplayReady(display); + cbDisplayReady(null, display); return; } else { readScreens(bl, display, cbDisplayReady); diff --git a/lib/xcore.js b/lib/xcore.js index 5c68b1c..519c665 100644 --- a/lib/xcore.js +++ b/lib/xcore.js @@ -531,10 +531,12 @@ XClient.prototype.startHandshake = function() { var client = this; handshake.writeClientHello(this.pack_stream, this.displayNum, this.authHost, this.authFamily); - handshake.readServerHello(this.pack_stream, function(display) + handshake.readServerHello(this.pack_stream, function(err, display) { - // TODO: readServerHello can set error state in display - // emit error in that case + if (err) { + client.emit('error', err); + return; + } client.expectReplyHeader(); client.display = display; display.client = client; From 1f5e2310a6dc9b29f5525e2d6bcbd4c33cb1d693 Mon Sep 17 00:00:00 2001 From: Ian Scott Date: Mon, 16 May 2016 09:45:39 -0700 Subject: [PATCH 4/4] Fix remoteFamily on Node v0.10 --- lib/xcore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/xcore.js b/lib/xcore.js index 519c665..cf073e4 100644 --- a/lib/xcore.js +++ b/lib/xcore.js @@ -39,7 +39,8 @@ XClient.prototype.init = function(stream) this.stream = stream; this.authHost = stream.remoteAddress; - this.authFamily = stream.remoteFamily; + // Node v0.10.x does not have stream.remoteFamily, so dig in to find it + this.authFamily = stream._getpeername ? stream._getpeername().family : stream.remoteFamily; if (!this.authHost || this.authHost === '127.0.0.1' || this.authHost === '::1') { this.authHost = os.hostname(); this.authFamily = null;