def sendGlobalRequest(self, request, data, wantReply=0): """ Send a global request for this connection. Current this is only used for remote->local TCP forwarding. @type request: L{bytes} @type data: L{bytes} @type wantReply: L{bool} @rtype C{Deferred}/L{None} """ self.transport.sendPacket( MSG_GLOBAL_REQUEST, common.NS(request) + (wantReply and b'\xff' or b'\x00') + data) if wantReply: d = defer.Deferred() self.deferreds['global'].append(d) return d
def test_CHANNEL_REQUEST_failure(self): """ Test that channel requests that fail send MSG_CHANNEL_FAILURE. """ channel = TestChannel() self._openChannel(channel) d = self.conn.ssh_CHANNEL_REQUEST('\x00\x00\x00\x00' + common.NS('test') + '\xff') def check(result): self.assertEqual( self.transport.packets, [(connection.MSG_CHANNEL_FAILURE, '\x00\x00\x00\xff')]) d.addCallback(self.fail) d.addErrback(check) return d
def test_fromPrivateBlobDSA(self): """ A private DSA key is correctly generated from a private key blob. """ dsaBlob = ( common.NS(b'ssh-dss') + common.MP(keydata.DSAData['p']) + common.MP(keydata.DSAData['q']) + common.MP(keydata.DSAData['g']) + common.MP(keydata.DSAData['y']) + common.MP(keydata.DSAData['x']) ) dsaKey = keys.Key._fromString_PRIVATE_BLOB(dsaBlob) self.assertFalse(dsaKey.isPublic()) self.assertEqual(keydata.DSAData, dsaKey.data())
def sendExtendedData(self, channel, dataType, data): """ Send extended data to a channel. This should not normally be used: instead use channel.writeExtendedData(data, dataType) as it manages the window automatically. @type channel: subclass of L{SSHChannel} @type dataType: L{int} @type data: L{bytes} """ if channel.localClosed: return # we're already closed self.transport.sendPacket( MSG_CHANNEL_EXTENDED_DATA, struct.pack(">2L", self.channelsToRemoteChannel[channel], dataType) + common.NS(data), )
def test_lookupSubsystemDoesNotNeedISession(self): """ Previously, if one only wanted to implement a subsystem, an ISession adapter wasn't needed because subsystems were looked up using the lookupSubsystem method on the avatar. """ s = session.SSHSession(avatar=SubsystemOnlyAvatar(), conn=StubConnection()) ret = s.request_subsystem(common.NS('subsystem') + 'data') self.assertTrue(ret) self.assertIsNot(s.client, None) self.assertIs(s.conn.closes.get(s), None) s.eofReceived() self.assertTrue(s.conn.closes.get(s)) # these should not raise errors s.loseConnection() s.closed()
def test_requestExecWithData(self): """ When a client executes a command, it should be able to give pass data back and forth. """ ret = self.session.requestReceived('exec', common.NS('repeat hello')) self.assertTrue(ret) self.assertSessionIsStubSession() self.session.dataReceived('some data') self.assertEquals(self.session.session.execTransport.data, 'some data') self.assertEquals(self.session.conn.data[self.session], ['hello', 'some data', '\r\n']) self.session.eofReceived() self.session.closeReceived() self.session.closed() self.assertTrue(self.session.session.execTransport.closed) self.assertEquals(self.session.conn.requests[self.session], [('exit-status', '\x00\x00\x00\x00', False)])
def test_fromPrivateBlobRSA(self): """ A private RSA key is correctly generated from a private key blob. """ rsaBlob = ( common.NS(b'ssh-rsa') + common.MP(keydata.RSAData['n']) + common.MP(keydata.RSAData['e']) + common.MP(keydata.RSAData['d']) + common.MP(keydata.RSAData['u']) + common.MP(keydata.RSAData['p']) + common.MP(keydata.RSAData['q']) ) rsaKey = keys.Key._fromString_PRIVATE_BLOB(rsaBlob) self.assertFalse(rsaKey.isPublic()) self.assertEqual(keydata.RSAData, rsaKey.data())
def test_fromBlobRSA(self): """ A public RSA key is correctly generated from a public key blob. """ rsaPublicData = { 'n': keydata.RSAData['n'], 'e': keydata.RSAData['e'], } rsaBlob = ( common.NS(b'ssh-rsa') + common.MP(rsaPublicData['e']) + common.MP(rsaPublicData['n']) ) rsaKey = keys.Key.fromString(rsaBlob) self.assertTrue(rsaKey.isPublic()) self.assertEqual(rsaPublicData, rsaKey.data())
def channelOpen(self, data): self.data = [] self.running = True if self.start_defer: log.debug("Channel %s is open, calling deferred", self.id) self.start_defer.callback(self) self.conn.sendRequest(self, 'exec', common.NS(self.command), wantReply=True).addCallback( self._cbExecSendRequest) else: # A missing start defer means that we are no longer expected to do anything when the channel opens # It probably means we gave up on this connection and failed the job, but later the channel opened up # correctly. log.warning("Channel open delayed, giving up and closing") self.loseConnection()
def openChannel(self, channel, extra=''): """ Open a new channel on this connection. @type channel: subclass of C{SSHChannel} @type extra: C{str} """ log.msg('opening channel %s with %s %s' % (self.localChannelID, channel.localWindowSize, channel.localMaxPacket)) self.transport.sendPacket( MSG_CHANNEL_OPEN, common.NS(channel.name) + struct.pack('>3L', self.localChannelID, channel.localWindowSize, channel.localMaxPacket) + extra) channel.id = self.localChannelID self.channels[self.localChannelID] = channel self.localChannelID += 1
def test_sendExtendedData(self): """ Test that channel extended data messages are sent in the right format. """ channel = TestChannel() self._openChannel(channel) self.conn.sendExtendedData(channel, 1, b"test") channel.localClosed = True self.conn.sendExtendedData(channel, 2, b"test2") self.assertEqual( self.transport.packets, [ ( connection.MSG_CHANNEL_EXTENDED_DATA, b"\x00\x00\x00\xff" + b"\x00\x00\x00\x01" + common.NS(b"test"), ) ], )
def test_openChannel(self): """ Test that open channel messages are sent in the right format. """ channel = TestChannel() self.conn.openChannel(channel, b"aaaa") self.assertEqual( self.transport.packets, [ ( connection.MSG_CHANNEL_OPEN, common.NS(b"TestChannel") + b"\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x80\x00aaaa", ) ], ) self.assertEqual(channel.id, 0) self.assertEqual(self.conn.localChannelID, 1)
def test_privateBlobRSA(self): """ L{keys.Key.privateBlob} returns the SSH protocol-level format of an RSA private key. """ from cryptography.hazmat.primitives.asymmetric import rsa numbers = self.rsaObj.private_numbers() u = rsa.rsa_crt_iqmp(numbers.q, numbers.p) self.assertEqual( keys.Key(self.rsaObj).privateBlob(), common.NS(b'ssh-rsa') + common.MP(self.rsaObj.private_numbers().public_numbers.n) + common.MP(self.rsaObj.private_numbers().public_numbers.e) + common.MP(self.rsaObj.private_numbers().d) + common.MP(u) + common.MP(self.rsaObj.private_numbers().p) + common.MP(self.rsaObj.private_numbers().q) )
def test_CHANNEL_OPEN(self): """ Test that open channel packets cause a channel to be created and opened or a failure message to be returned. """ del self.transport.avatar self.conn.ssh_CHANNEL_OPEN( common.NS(b"TestChannel") + b"\x00\x00\x00\x01" * 4) self.assertTrue(self.conn.channel.gotOpen) self.assertEqual(self.conn.channel.conn, self.conn) self.assertEqual(self.conn.channel.data, b"\x00\x00\x00\x01") self.assertEqual(self.conn.channel.specificData, b"\x00\x00\x00\x01") self.assertEqual(self.conn.channel.remoteWindowLeft, 1) self.assertEqual(self.conn.channel.remoteMaxPacket, 1) self.assertEqual( self.transport.packets, [( connection.MSG_CHANNEL_OPEN_CONFIRMATION, b"\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00" b"\x00\x00\x80\x00", )], ) self.transport.packets = [] self.conn.ssh_CHANNEL_OPEN( common.NS(b"BadChannel") + b"\x00\x00\x00\x02" * 4) self.flushLoggedErrors() self.assertEqual( self.transport.packets, [( connection.MSG_CHANNEL_OPEN_FAILURE, b"\x00\x00\x00\x02\x00\x00\x00\x03" + common.NS(b"unknown channel") + common.NS(b""), )], ) self.transport.packets = [] self.conn.ssh_CHANNEL_OPEN( common.NS(b"ErrorChannel") + b"\x00\x00\x00\x02" * 4) self.flushLoggedErrors() self.assertEqual( self.transport.packets, [( connection.MSG_CHANNEL_OPEN_FAILURE, b"\x00\x00\x00\x02\x00\x00\x00\x02" + common.NS(b"unknown failure") + common.NS(b""), )], )
def test_fromBlobDSA(self): """ A public DSA key is correctly generated from a public key blob. """ dsaPublicData = { 'p': keydata.DSAData['p'], 'q': keydata.DSAData['q'], 'g': keydata.DSAData['g'], 'y': keydata.DSAData['y'], } dsaBlob = (common.NS(b'ssh-dss') + common.MP(dsaPublicData['p']) + common.MP(dsaPublicData['q']) + common.MP(dsaPublicData['g']) + common.MP(dsaPublicData['y'])) dsaKey = keys.Key.fromString(dsaBlob) self.assertTrue(dsaKey.isPublic()) self.assertEqual(dsaPublicData, dsaKey.data())
def test_sessionClose(self): """ Closing a session should notify an SFTP subsystem launched by that session. """ # make a session testSession = session.SSHSession(conn=FakeConn(), avatar=self.avatar) # start an SFTP subsystem on the session testSession.request_subsystem(common.NS(b'sftp')) sftpServer = testSession.client.transport.proto # intercept connectionLost so we can check that it's called self.interceptConnectionLost(sftpServer) # close session testSession.closeReceived() self.assertSFTPConnectionLost()
def openChannel(self, channel, extra=b''): """ Open a new channel on this connection. @type channel: subclass of C{SSHChannel} @type extra: L{bytes} """ self._log.info( 'opening channel {id} with {localWindowSize} {localMaxPacket}', id=self.localChannelID, localWindowSize=channel.localWindowSize, localMaxPacket=channel.localMaxPacket) self.transport.sendPacket( MSG_CHANNEL_OPEN, common.NS(channel.name) + struct.pack('>3L', self.localChannelID, channel.localWindowSize, channel.localMaxPacket) + extra) channel.id = self.localChannelID self.channels[self.localChannelID] = channel self.localChannelID += 1
def test_requestExecWithData(self): """ When a client executes a command, it should be able to give pass data back and forth. """ ret = self.session.requestReceived(b"exec", common.NS(b"repeat hello")) self.assertTrue(ret) self.assertSessionIsStubSession() self.session.dataReceived(b"some data") self.assertEqual(self.session.session.execTransport.data, b"some data") self.assertEqual(self.session.conn.data[self.session], [b"hello", b"some data", b"\r\n"]) self.session.eofReceived() self.session.closeReceived() self.session.closed() self.assertTrue(self.session.session.execTransport.closed) self.assertEqual( self.session.conn.requests[self.session], [(b"exit-status", b"\x00\x00\x00\x00", False)], )
def sendRequest(self, channel, requestType, data, wantReply=0): """ Send a request to a channel. @type channel: subclass of C{SSHChannel} @type requestType: C{str} @type data: C{str} @type wantReply: C{bool} @rtype C{Deferred}/C{None} """ if channel.localClosed: return log.msg('sending request %s' % requestType) self.transport.sendPacket( MSG_CHANNEL_REQUEST, struct.pack('>L', self.channelsToRemoteChannel[channel]) + common.NS(requestType) + chr(wantReply) + data) if wantReply: d = defer.Deferred() self.deferreds.setdefault(channel.id, []).append(d) return d
def test_lookupSubsystem_data(self): """ After having looked up a subsystem, data should be passed along to the client. Additionally, subsystems were passed the entire request packet as data, instead of just the additional data. We check for the additional tidle to verify that the data passed through the client. """ #self.session.dataReceived('1') # subsystems didn't get extended data #self.session.extReceived(connection.EXTENDED_DATA_STDERR, '2') self.session.requestReceived('subsystem', common.NS('TestSubsystem') + 'data') self.assertEquals(self.session.conn.data[self.session], ['\x00\x00\x00\x0dTestSubsystemdata~']) self.session.dataReceived('more data') self.assertEquals(self.session.conn.data[self.session][-1], 'more data~')
def _fromString_PRIVATE_LSH(cls, data): """ Return a private key corresponding to this LSH private key string. The LSH private key string format is:: <s-expression: ('private-key', (<key type>, (<name>, <value>)+))> The names for a RSA (key type 'rsa-pkcs1-sha1') key are: n, e, d, p, q. The names for a DSA (key type 'dsa') key are: y, g, p, q, x. @type data: L{bytes} @param data: The key data. @return: A new key. @rtype: L{twisted.conch.ssh.keys.Key} @raises BadKeyError: if the key type is unknown """ sexp = sexpy.parse(data) assert sexp[0] == b'private-key' kd = {} for name, data in sexp[1][1:]: kd[name] = common.getMP(common.NS(data))[0] if sexp[1][0] == b'dsa': assert len(kd) == 5, len(kd) return cls._fromDSAComponents(y=kd[b'y'], g=kd[b'g'], p=kd[b'p'], q=kd[b'q'], x=kd[b'x']) elif sexp[1][0] == b'rsa-pkcs1': assert len(kd) == 8, len(kd) if kd[b'p'] > kd[b'q']: # Make p smaller than q kd[b'p'], kd[b'q'] = kd[b'q'], kd[b'p'] return cls._fromRSAComponents(n=kd[b'n'], e=kd[b'e'], d=kd[b'd'], p=kd[b'p'], q=kd[b'q']) else: raise BadKeyError('unknown lsh key type %s' % (sexp[1][0], ))
def sendRequest(self, channel, requestType, data, wantReply=0): """ Send a request to a channel. @type channel: subclass of C{SSHChannel} @type requestType: L{bytes} @type data: L{bytes} @type wantReply: L{bool} @rtype C{Deferred}/L{None} """ if channel.localClosed: return self._log.debug('sending request {requestType}', requestType=requestType) self.transport.sendPacket( MSG_CHANNEL_REQUEST, struct.pack('>L', self.channelsToRemoteChannel[channel]) + common.NS(requestType) + (b'\1' if wantReply else b'\0') + data) if wantReply: d = defer.Deferred() self.deferreds.setdefault(channel.id, []).append(d) return d
def channelOpen(self, unused): """ Initialize the channel and send our command to the device. @param unused: unused (unused) @type unused: string @return: Twisted channel @rtype: Twisted channel """ log.debug( "%s channel %s Opening command channel for %s", self.targetIp, self.id, self.command, ) self.data = "" self.stderr = "" # Notes for sendRequest: # 'exec' - execute the following command and exit # common.NS() - encodes the command as a length-prefixed string # wantReply - reply to let us know the process has been started try: result = yield self.conn.sendRequest(self, "exec", common.NS(self.command), wantReply=1) except Exception as e: log.warn( "%s channel %s failed during command execution with error: %s", self.targetIp, self.id, e, ) defer.returnValue([]) defer.returnValue(result)
def verify(self, signature, data): """ Returns true if the signature for data is valid for this Key. @type signature: C{str} @type data: C{str} @rtype: C{bool} """ if len(signature) == 40: # DSA key with no padding signatureType, signature = 'ssh-dss', common.NS(signature) else: signatureType, signature = common.getNS(signature) if signatureType != self.sshType(): return False if self.type() == 'RSA': numbers = common.getMP(signature) digest = pkcs1Digest(data, self.keyObject.size() / 8) elif self.type() == 'DSA': signature = common.getNS(signature)[0] numbers = [Util.number.bytes_to_long(n) for n in signature[:20], signature[20:]] digest = sha1(data).digest() return self.keyObject.verify(digest, numbers)
def channelOpen(self, unused): """ Initialize the channel and send our command to the device. @param unused: unused (unused) @type unused: string @return: Twisted channel @rtype: Twisted channel """ log.debug('%s channel %s Opening command channel for %s', self.targetIp, self.conn.localChannelID, self.command) self.data = '' self.stderr = '' # Notes for sendRequest: # 'exec' - execute the following command and exit # common.NS() - encodes the command as a length-prefixed string # wantReply - reply to let us know the process has been started d = self.conn.sendRequest(self, 'exec', common.NS(self.command), wantReply=1) return d
def _fromString_PUBLIC_LSH(Class, data): """ Return a public key corresponding to this LSH public key string. The LSH public key string format is:: <s-expression: ('public-key', (<key type>, (<name, <value>)+))> The names for a RSA (key type 'rsa-pkcs1-sha1') key are: n, e. The names for a DSA (key type 'dsa') key are: y, g, p, q. @type data: C{str} @return: a C{Crypto.PublicKey.pubkey.pubkey} object @raises BadKeyError: if the key type is unknown """ sexp = sexpy.parse(base64.decodestring(data[1:-1])) assert sexp[0] == 'public-key' kd = {} for name, data in sexp[1][1:]: kd[name] = common.getMP(common.NS(data))[0] if sexp[1][0] == 'dsa': return Class(DSA.construct((kd['y'], kd['g'], kd['p'], kd['q']))) elif sexp[1][0] == 'rsa-pkcs1-sha1': return Class(RSA.construct((kd['n'], kd['e']))) else: raise BadKeyError('unknown lsh key type %s' % sexp[1][0])
def cbChannel(channel): self.channel = channel return channel.conn.sendRequest(channel, 'exec', common.NS('echo hello'), 1)
def cbChannel(channel): self.channel = channel return self.assertFailure( channel.conn.sendRequest(channel, 'exec', common.NS('jumboliah'), 1), Exception)
def cbNotCrazyFailed(ignored): channel = self.channel return channel.conn.sendRequest(channel, 'subsystem', common.NS('crazy'), 1)
def cbSubsystem(channel): self.channel = channel return self.assertFailure( channel.conn.sendRequest(channel, 'subsystem', common.NS('not-crazy'), 1), Exception)