import struct
from twisted.conch.ssh import common, transport, userauth, connection, keys
from twisted.conch.ssh import channel, session
from twisted.internet import defer, protocol, error
from twisted.python import failure
class SimpleTransport(transport.SSHClientTransport):
def __init__(self, d):
"""
d is a Deferred called back when the basic transport setup is done.
it is called back with this object.
"""
self._d = d
self._authClient = None
def verifyHostKey(self, hostKey, fingerprint):
self._hostKey = hostKey
self._fingerprint = fingerprint
return defer.succeed(1)
def connectionSecure(self):
d = self._d
del self._d
d.callback(self)
def _getAuthClient(self):
d = defer.Deferred()
self._conn = SimpleConnection()
self._authClient = SimpleAuthClient(d, self._conn)
self.requestService(self._authClient)
return d
def getHostKey(self):
"""
Called by the client to get the host key.
Returns (keyType, keyData, fingerprint)
"""
t, k = common.getNS(self._hostKey)
return (t, self._hostKey, self._fingerprint)
def isAuthenticated(self):
"""
Returns True if we are authenticated, else False.
"""
if not self._authClient: return False
return self._authClient._authenticated
def authPassword(self, username, password):
if not self._authClient:
d = self._getAuthClient()
d.addCallback(lambda x:self.authPassword(username, password))
else:
self._authClient.user = username
d = self._authClient._d = defer.Deferred()
self._authClient.getPassword = lambda:defer.succeed(password)
self._authClient.tryAuth('password')
return d
def authPublicKey(self, username, privateKey):
if not self._authClient:
d = self._getAuthClient()
d.addCallback(lambda x:self.authPublicKey(username, privateKey))
else:
self._authClient.user = username
d = self._authClient._d = defer.Deferred()
privObj = keys.getPrivateKeyObject(data=privateKey)
pubKey = keys.makePublicKeyBlob(privObj)
self._authClient.getPublicKey = lambda:pubKey
self._authClient.getPrivateKey = lambda:defer.succeed(privObj)
self._authClient.tryAuth('publickey')
return d
def openSession(self):
d = defer.Deferred()
c = SimpleSession(conn=self._conn)
c._d = d
self._conn.openChannel(c)
return d
class SimpleAuthClient(userauth.SSHUserAuthClient):
def __init__(self, d, instance):
userauth.SSHUserAuthClient.__init__(self, None, instance)
self._d = d
self._authenticated = False
def serviceStarted(self):
d = self._d
del self._d
d.callback(None)
def ssh_USERAUTH_SUCCESS(self, packet):
d = self._d
del self._d
self.transport.setService(self.instance)
self._authenticated = True
self.ssh_USERAUTH_SUCCESS = lambda x: None
d.callback(None)
def ssh_USERAUTH_FAILURE(self, packet):
canContinue, partial = common.getNS(packet)
partial = ord(partial)
if partial:
d = self._d
del self._d
d.callback(None)
else:
d.errback(error.ConchError())
class SimpleConnection(connection.SSHConnection): pass
class SimpleSession(channel.SSHChannel):
name = 'session'
def channelOpen(self, data):
d = self._d
del self._d
self.specificData = data
d.callback(self)
def openFailed(self, reason):
d = self._d
del self._d
d.errback(reason)
def setClient(self, client):
if isinstance(client, protocol.ProcessProtocol):
self.dataReceived = client.outReceived
self.extReceived = client.errReceived
self.eofReceived = client.inConnectionLost
def _exit_status(status):
code = struct.unpack('!L', status[:4])[0]
if code == 0:
err = error.ProcessDone(None)
else:
err = error.ProcessTerminated(code)
client.processEnded(failure.Failure(err))
return True
self.request_exit_status = _exit_status
else:
self.dataReceived = client.dataReceived
self.closed = lambda:client.connectionLost(protocol.connectionDone)
client.makeConnection(self)
def requestPTY(self, term='xterm', width=80, height=24, xpixel=0, ypixel=0, modes='', wantReply = 0):
data = session.packRequest_pty_req(term, (width, heght, xpixel, ypixel), modes)
return self.conn.sendRequest(self, 'pty-req', data, wantReply)
def openShell(self, wantReply = 0):
return self.conn.sendRequest(self, 'shell', '', wantReply)
def openExec(self, program, wantReply = 0):
return self.conn.sendRequest(self, 'exec', common.NS(program), wantReply)