From 550286de8802515b97358c9632f585677862a01b Mon Sep 17 00:00:00 2001 From: yvesf Date: Fri, 11 Mar 2011 20:14:09 +0100 Subject: simple ssh server with fortunes --- sshsimpleserver.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 sshsimpleserver.py (limited to 'sshsimpleserver.py') diff --git a/sshsimpleserver.py b/sshsimpleserver.py new file mode 100644 index 0000000..3254ef6 --- /dev/null +++ b/sshsimpleserver.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Twisted Matrix Laboratories. +# See LICENSE for details. + +from twisted.cred import portal, checkers +from twisted.conch import error, avatar +from twisted.conch.checkers import SSHPublicKeyDatabase +from twisted.conch.ssh import factory, userauth, connection, keys, session +from twisted.internet import reactor, protocol, defer +from twisted.python import log +from zope.interface import implements +import sys +import random +log.startLogging(sys.stderr) + +header = """Linux kischt 2.6.48-5-amd23 #1 SMP Wed Jan 12 04:22:50 UTC 2011 i687 + +The programs included with the Debian GNU/Linux system are free software; +the exact distribution terms for each program are described in the +individual files in /usr/share/doc/*/copyright. + +Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent +permitted by applicable law. +Follow the white rabbit. +Last login: Fri Mar 11 15:33:45 2011 from 127.0.0.1 +bash-# """ + +fortunes = open("fortunes").read().split("\n%") + +class ExampleAvatar(avatar.ConchUser): + def __init__(self, username): + avatar.ConchUser.__init__(self) + self.username = username + self.channelLookup.update({'session':session.SSHSession}) + +class ExampleRealm: + implements(portal.IRealm) + + def requestAvatar(self, avatarId, mind, *interfaces): + return interfaces[0], ExampleAvatar(avatarId), lambda: None + +class EchoProtocol(protocol.Protocol): + def makeConnection(self, transport): + protocol.Protocol.makeConnection(self, transport) + self.transport.write( "\r\n".join(header.split("\n"))) + self.state = "count" + self.ctr = 0 + + def dataReceived(self, data): + if data == '\x03': #^C + self.transport.loseConnection() + return + elif self.state == "count": #echo mode + self.ctr += 1 + if self.ctr > 10: + self.state = "bomb" + print "received: ",data + if data == '\r': + data = '\n\r' + self.transport.write(data) + elif self.state == "bomb": + self.transport.write( "\r\n".join( random.choice(fortunes).split("\n") ) ) + self.transport.write("\n\r") + +publicKey = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEArzJx8OYOnJmzf4tfBEvLi8DVPrJ3/c9k2I/Az64fxjHf9imyRJbixtQhlH9lfNjUIx+4LmrJH5QNRsFporcHDKOTwTTYLh5KmRpslkYHRivcJSkbh/C+BR3utDS555mV' + +privateKey = """-----BEGIN RSA PRIVATE KEY----- +MIIByAIBAAJhAK8ycfDmDpyZs3+LXwRLy4vA1T6yd/3PZNiPwM+uH8Yx3/YpskSW +4sbUIZR/ZXzY1CMfuC5qyR+UDUbBaaK3Bwyjk8E02C4eSpkabJZGB0Yr3CUpG4fw +vgUd7rQ0ueeZlQIBIwJgbh+1VZfr7WftK5lu7MHtqE1S1vPWZQYE3+VUn8yJADyb +Z4fsZaCrzW9lkIqXkE3GIY+ojdhZhkO1gbG0118sIgphwSWKRxK0mvh6ERxKqIt1 +xJEJO74EykXZV4oNJ8sjAjEA3J9r2ZghVhGN6V8DnQrTk24Td0E8hU8AcP0FVP+8 +PQm/g/aXf2QQkQT+omdHVEJrAjEAy0pL0EBH6EVS98evDCBtQw22OZT52qXlAwZ2 +gyTriKFVoqjeEjt3SZKKqXHSApP/AjBLpF99zcJJZRq2abgYlf9lv1chkrWqDHUu +DZttmYJeEfiFBBavVYIF1dOlZT0G8jMCMBc7sOSZodFnAiryP+Qg9otSBjJ3bQML +pSTqy7c3a2AScC/YyOwkDaICHnnD3XyjMwIxALRzl0tQEKMXs6hH8ToUdlLROCrP +EhQ0wahUTCk1gKA4uPD6TMTChavbh4K63OvbKg== +-----END RSA PRIVATE KEY-----""" + + +class InMemoryPublicKeyChecker(SSHPublicKeyDatabase): + + def checkKey(self, credentials): + return credentials.username == 'user' and \ + keys.Key.fromString(data=publicKey).blob() == credentials.blob + +class ExampleSession: + def __init__(self, avatar): + """ + We don't use it, but the adapter is passed the avatar as its first + argument. + """ + + def getPty(self, term, windowSize, attrs): + pass + + def execCommand(self, proto, cmd): + raise Exception("no executing commands") + + def windowChanged(self, *a): + pass + + def openShell(self, trans): + ep = EchoProtocol() + ep.makeConnection(trans) + trans.makeConnection(session.wrapProtocol(ep)) + + def eofReceived(self): + pass + + def closed(self): + pass + +from twisted.python import components +components.registerAdapter(ExampleSession, ExampleAvatar, session.ISession) + +class ExampleFactory(factory.SSHFactory): + publicKeys = { + 'ssh-rsa': keys.Key.fromString(data=publicKey) + } + privateKeys = { + 'ssh-rsa': keys.Key.fromString(data=privateKey) + } + services = { + 'ssh-userauth': userauth.SSHUserAuthServer, + 'ssh-connection': connection.SSHConnection + } + + +portal = portal.Portal(ExampleRealm()) +passwdDB = checkers.InMemoryUsernamePasswordDatabaseDontUse() +passwdDB.addUser('user', 'password') +portal.registerChecker(passwdDB) +portal.registerChecker(InMemoryPublicKeyChecker()) +ExampleFactory.portal = portal + +if __name__ == '__main__': + reactor.listenTCP(2222, ExampleFactory()) + reactor.run() -- cgit v1.2.1