1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-06 03:50:04 +02:00

VNC authentication works, although there is no UI for adding passwords

yet. Direct database manipulation is required for now.
This commit is contained in:
Simo Kinnunen 2015-10-13 03:14:18 +09:00
parent a902c66131
commit 5b5520b705
8 changed files with 332 additions and 32 deletions

View file

@ -8,6 +8,9 @@ var uuid = require('node-uuid')
var jpeg = require('jpeg-turbo')
var logger = require('../../../../util/logger')
var grouputil = require('../../../../util/grouputil')
var wire = require('../../../../wire')
var wireutil = require('../../../../wire/util')
var lifecycle = require('../../../../util/lifecycle')
var VncServer = require('./util/server')
@ -15,11 +18,75 @@ var VncConnection = require('./util/connection')
var PointerTranslator = require('./util/pointertranslator')
module.exports = syrup.serial()
.dependency(require('../../support/router'))
.dependency(require('../../support/push'))
.dependency(require('../screen/stream'))
.dependency(require('../touch'))
.define(function(options, screenStream, touch) {
.dependency(require('../group'))
.dependency(require('../solo'))
.define(function(options, router, push, screenStream, touch, group, solo) {
var log = logger.createLogger('device:plugins:vnc')
function vncAuthHandler(data) {
log.info('VNC authentication attempt using "%s"', data.fingerprint)
var resolver = Promise.defer()
function notify() {
group.get()
.then(function(currentGroup) {
push.send([
solo.channel
, wireutil.envelope(new wire.JoinGroupByVncAuthResponseMessage(
options.serial
, data.response.toString('hex')
, currentGroup.group
))
])
})
.catch(grouputil.NoGroupError, function() {
push.send([
solo.channel
, wireutil.envelope(new wire.JoinGroupByVncAuthResponseMessage(
options.serial
, data.response.toString('hex')
))
])
})
}
function joinListener(newGroup, identifier) {
if (!data.response.equals(new Buffer(identifier || '', 'hex'))) {
resolver.reject(new Error('Someone else took the device'))
}
}
function autojoinListener(identifier, joined) {
if (data.response.equals(new Buffer(identifier, 'hex'))) {
if (joined) {
resolver.resolve()
}
else {
resolver.reject(new Error('Device is already in use'))
}
}
}
group.on('join', joinListener)
group.on('autojoin', autojoinListener)
router.on(wire.VncAuthResponsesUpdatedMessage, notify)
notify()
return resolver.promise
.timeout(5000)
.finally(function() {
group.removeListener('join', joinListener)
group.removeListener('autojoin', autojoinListener)
router.removeListener(wire.VncAuthResponsesUpdatedMessage, notify)
})
}
function createServer() {
log.info('Starting VNC server on port %d', options.vncPort)
@ -27,6 +94,11 @@ module.exports = syrup.serial()
name: options.serial
, width: options.vncInitialSize[0]
, height: options.vncInitialSize[1]
, security: [{
type: VncConnection.SECURITY_VNC
, challenge: new Buffer(16).fill(0)
, auth: vncAuthHandler
}]
}
var vnc = new VncServer(net.createServer({
@ -57,6 +129,8 @@ module.exports = syrup.serial()
return createServer()
.then(function(vnc) {
vnc.on('connection', function(conn) {
log.info('New VNC connection from %s', conn.conn.remoteAddress)
var id = util.format('vnc-%s', uuid.v4())
var connState = {
@ -89,23 +163,6 @@ module.exports = syrup.serial()
touch.touchCommit()
})
function vncStartListener(frameProducer) {
return new Promise(function(resolve/*, reject*/) {
connState.frameWidth = frameProducer.banner.virtualWidth
connState.frameHeight = frameProducer.banner.virtualHeight
resolve()
})
}
function vncFrameListener(frame) {
return new Promise(function(resolve/*, reject*/) {
connState.lastFrame = frame
connState.lastFrameTime = Date.now()
maybeSendFrame()
resolve()
})
}
function maybeSendFrame() {
if (!connState.updateRequests) {
return
@ -142,6 +199,27 @@ module.exports = syrup.serial()
connState.sentFrameTime = connState.lastFrameTime
}
function vncStartListener(frameProducer) {
return new Promise(function(resolve/*, reject*/) {
connState.frameWidth = frameProducer.banner.virtualWidth
connState.frameHeight = frameProducer.banner.virtualHeight
resolve()
})
}
function vncFrameListener(frame) {
return new Promise(function(resolve/*, reject*/) {
connState.lastFrame = frame
connState.lastFrameTime = Date.now()
maybeSendFrame()
resolve()
})
}
function groupLeaveListener() {
conn.end()
}
conn.on('authenticated', function() {
screenStream.updateProjection(
options.vncInitialSize[0], options.vncInitialSize[1])
@ -157,7 +235,7 @@ module.exports = syrup.serial()
})
conn.on('formatchange', function(format) {
var same = os.endianness() == 'BE' == format.bigEndianFlag
var same = os.endianness() === 'BE' === format.bigEndianFlag
switch (format.bitsPerPixel) {
case 8:
connState.frameConfig = {
@ -191,7 +269,14 @@ module.exports = syrup.serial()
conn.on('close', function() {
screenStream.broadcastSet.remove(id)
group.removeListener('leave', groupLeaveListener)
})
conn.on('userActivity', function() {
group.keepalive()
})
group.on('leave', groupLeaveListener)
})
lifecycle.observe(function() {

View file

@ -1,10 +1,13 @@
var util = require('util')
var os = require('os')
var crypto = require('crypto')
var EventEmitter = require('eventemitter3').EventEmitter
var debug = require('debug')('vnc:connection')
var Promise = require('bluebird')
var PixelFormat = require('./pixelformat')
var vncauth = require('../../../../../util/vncauth')
function VncConnection(conn, options) {
this.options = options
@ -21,7 +24,15 @@ function VncConnection(conn, options) {
this._changeState(VncConnection.STATE_NEED_CLIENT_VERSION)
this._serverVersion = VncConnection.V3_008
this._serverSupportedSecurity = [VncConnection.SECURITY_NONE]
this._serverSupportedSecurity = this.options.security
this._serverSupportedSecurityByType =
this.options.security.reduce(
function(map, method) {
map[method.type] = method
return map
}
, Object.create(null)
)
this._serverWidth = this.options.width
this._serverHeight = this.options.height
this._serverPixelFormat = new PixelFormat({
@ -45,12 +56,16 @@ function VncConnection(conn, options) {
this._clientEncodings = []
this._clientCutTextLength = 0
this._authChallenge = this.options.challenge || crypto.randomBytes(16)
this.conn = conn
.on('error', this._bound._errorListener)
.on('readable', this._bound._readableListener)
.on('end', this._bound._endListener)
.on('close', this._bound._closeListener)
this._blockingOps = []
this._writeServerVersion()
this._read()
}
@ -80,6 +95,7 @@ var StateReverse = Object.create(null), State = {
STATE_NEED_CLIENT_VERSION: 10
, STATE_NEED_CLIENT_SECURITY: 20
, STATE_NEED_CLIENT_INIT: 30
, STATE_NEED_CLIENT_VNC_AUTH: 31
, STATE_NEED_CLIENT_MESSAGE: 40
, STATE_NEED_CLIENT_MESSAGE_SETPIXELFORMAT: 50
, STATE_NEED_CLIENT_MESSAGE_SETENCODINGS: 60
@ -171,18 +187,12 @@ VncConnection.prototype._writeSupportedSecurity = function() {
chunk[0] = this._serverSupportedSecurity.length
this._serverSupportedSecurity.forEach(function(security, i) {
chunk[1 + i] = security
chunk[1 + i] = security.type
})
this._write(chunk)
}
VncConnection.prototype._writeSelectedSecurity = function() {
var chunk = new Buffer(4)
chunk.writeUInt32BE(VncConnection.SECURITY_NONE, 0)
this._write(chunk)
}
VncConnection.prototype._writeSecurityResult = function(result, reason) {
var chunk
switch (result) {
@ -224,11 +234,40 @@ VncConnection.prototype._writeServerInit = function() {
this._write(chunk)
}
VncConnection.prototype._writeVncAuthChallenge = function() {
var vncSec = this._serverSupportedSecurityByType[VncConnection.SECURITY_VNC]
debug('vnc auth challenge', vncSec.challenge)
this._write(vncSec.challenge)
}
VncConnection.prototype._readableListener = function() {
this._read()
}
VncConnection.prototype._read = function() {
Promise.all(this._blockingOps).bind(this)
.then(this._unguardedRead)
}
VncConnection.prototype._auth = function(type, data) {
var security = this._serverSupportedSecurityByType[type]
this._blockingOps.push(
security.auth(data).bind(this)
.then(function() {
this._changeState(VncConnection.STATE_NEED_CLIENT_INIT)
this._writeSecurityResult(VncConnection.SECURITYRESULT_OK)
this.emit('authenticated')
this._read()
})
.catch(function() {
this._writeSecurityResult(
VncConnection.SECURITYRESULT_FAIL, 'Authentication failure')
this.end()
})
)
}
VncConnection.prototype._unguardedRead = function() {
var chunk, lo, hi
while (this._append(this.conn.read())) {
do {
@ -250,14 +289,35 @@ VncConnection.prototype._read = function() {
if ((chunk = this._consume(1))) {
if ((this._clientSecurity = this._parseSecurity(chunk)) === null) {
this._writeSecurityResult(
VncConnection.SECURITYRESULT_FAIL, 'Unsupported security type')
VncConnection.SECURITYRESULT_FAIL, 'Unimplemented security type')
this.end()
return
}
debug('client security', this._clientSecurity)
this._writeSecurityResult(VncConnection.SECURITYRESULT_OK)
this.emit('authenticated')
this._changeState(VncConnection.STATE_NEED_CLIENT_INIT)
if (!(this._clientSecurity in this._serverSupportedSecurityByType)) {
this._writeSecurityResult(
VncConnection.SECURITYRESULT_FAIL, 'Unsupported security type')
this.end()
return
}
switch (this._clientSecurity) {
case VncConnection.SECURITY_NONE:
this._auth(VncConnection.SECURITY_NONE)
return
case VncConnection.SECURITY_VNC:
this._writeVncAuthChallenge()
this._changeState(VncConnection.STATE_NEED_CLIENT_VNC_AUTH)
break
}
}
break
case VncConnection.STATE_NEED_CLIENT_VNC_AUTH:
if ((chunk = this._consume(16))) {
this._auth(VncConnection.SECURITY_VNC, {
response: chunk
, fingerprint: vncauth.format(chunk)
})
return
}
break
case VncConnection.STATE_NEED_CLIENT_INIT:
@ -284,14 +344,17 @@ VncConnection.prototype._read = function() {
VncConnection.STATE_NEED_CLIENT_MESSAGE_FBUPDATEREQUEST)
break
case VncConnection.CLIENT_MESSAGE_KEYEVENT:
this.emit('userActivity')
this._changeState(
VncConnection.STATE_NEED_CLIENT_MESSAGE_KEYEVENT)
break
case VncConnection.CLIENT_MESSAGE_POINTEREVENT:
this.emit('userActivity')
this._changeState(
VncConnection.STATE_NEED_CLIENT_MESSAGE_POINTEREVENT)
break
case VncConnection.CLIENT_MESSAGE_CLIENTCUTTEXT:
this.emit('userActivity')
this._changeState(
VncConnection.STATE_NEED_CLIENT_MESSAGE_CLIENTCUTTEXT)
break