diff options
| -rw-r--r-- | services/Makefile | 14 | ||||
| -rw-r--r-- | services/authorized_keys | 1 | ||||
| -rw-r--r-- | services/checkers.py | 25 | ||||
| -rw-r--r-- | services/services.txt | 2 | ||||
| -rw-r--r-- | services/test.py | 108 | 
5 files changed, 150 insertions, 0 deletions
| diff --git a/services/Makefile b/services/Makefile new file mode 100644 index 00000000..a68f095d --- /dev/null +++ b/services/Makefile @@ -0,0 +1,14 @@ +help:;@cat Makefile + +export authorized_keys_file := authorized_keys +export services_file := services.txt +export host_key_file := test.key + +test-client: +	ssh localhost -p 1337 2>/dev/null + +test-server: +	python test.py + +$(host_key_file): +	ssh-keygen -t rsa -P '' -f $@ diff --git a/services/authorized_keys b/services/authorized_keys new file mode 100644 index 00000000..dcb8bfeb --- /dev/null +++ b/services/authorized_keys @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7YrLdnXDRU2XEdZDu1BmgiT0Vaxplf3bfvSm+5o3g4AcR2yCv7h2D633c9uA0gq52EJ3V5m8B1ZcxqA0zqDptKwx+ZTMUGDls7StH5xpJyk9j5gf8DzyDLQPQG2IYszCH+8esKjo3BOFxfey8NaX+k6gvQsG3lyV0PjLvvIy4gDuMn6dPZfVAlwNYFOUNgwpku3W3A0d+UFyVjt3/sgZxM+8C3y6QE1gwT5/NfBbHM5vaEqjHcVq1ui+7a4iOXFGKkZDcd7EX6cQZSbCzZL7sZ0OmB1WpAsDCvIXfzX1YfNA0sso7ldSF6ZUGNgwEk1LootnQlCK/dfbM+i62SZ+1 tv@iiso diff --git a/services/checkers.py b/services/checkers.py new file mode 100644 index 00000000..dbfe1323 --- /dev/null +++ b/services/checkers.py @@ -0,0 +1,25 @@ + +import base64, binascii +from twisted.python.filepath import FilePath +from twisted.conch.checkers import SSHPublicKeyDatabase + + +class PublicKeyChecker(SSHPublicKeyDatabase): + +    def __init__(self, filename): +        self.filepath = FilePath(filename) + +    def getAuthorizedKeysFiles(self, credentials): +        return [self.filepath] + +    def checkKey(self, credentials): +        for line in self.filepath.open(): +            parts = line.split() +            if len(parts) < 2: +                continue +            try: +                if base64.decodestring(parts[1]) == credentials.blob: +                    return True +            except binascii.Error: +                continue +        return False diff --git a/services/services.txt b/services/services.txt new file mode 100644 index 00000000..a2b97670 --- /dev/null +++ b/services/services.txt @@ -0,0 +1,2 @@ +# this is a comment +TODO declare proper services format diff --git a/services/test.py b/services/test.py new file mode 100644 index 00000000..06340a54 --- /dev/null +++ b/services/test.py @@ -0,0 +1,108 @@ +#! /usr/bin/env python + +from os import environ as env + +authorized_keys_file = env.get('authorized_keys_file', '/dev/null') +services_file = env.get('services_file', '/dev/null') +host_key_file = env.get('host_key_file', '/dev/null') +host_key_pub_file = host_key_file + '.pub' + + +from checkers import PublicKeyChecker +from twisted.conch.avatar import ConchUser +from twisted.conch.ssh.connection import SSHConnection +from twisted.conch.ssh.factory import SSHFactory +from twisted.conch.ssh.keys import Key +from twisted.conch.ssh.session import SSHSession, ISession, wrapProtocol +from twisted.conch.ssh.userauth import SSHUserAuthServer +from twisted.cred.error import UnauthorizedLogin +from twisted.cred.portal import IRealm, Portal +from twisted.internet.protocol import Protocol +from twisted.internet.reactor import listenTCP, run +from twisted.python.components import registerAdapter +from zope.interface import implements + +from twisted.python.log import startLogging +from sys import stderr +startLogging(stderr) + + +class MyRealm: +    implements(IRealm) + +    def requestAvatar(self, avatarId, mind, *interfaces): +        return interfaces[0], MyUser(), lambda: None + + +class MyUser(ConchUser): +    def __init__(self): +        ConchUser.__init__(self) +        self.channelLookup.update({ 'session': SSHSession }) + + +class MySession: +     +    def __init__(self, avatar): +        pass + +    def getPty(self, term, windowSize, attrs): +        pass +     +    def execCommand(self, proto, cmd): +        raise Exception("no executing commands") + +    def openShell(self, trans): +        ep = MyProtocol() +        ep.makeConnection(trans) +        trans.makeConnection(wrapProtocol(ep)) + +    def eofReceived(self): +        pass + +    def closed(self): +        pass + + +registerAdapter(MySession, MyUser, ISession) + + +def slurpTextfile(filename): +    file = open(filename, 'r') +    try: +        return file.read() +    finally: +        file.close() + +class MyProtocol(Protocol): +    def connectionMade(self): +        data = slurpTextfile(services_file).replace('\n', '\r\n') +        self.transport.write(data) +        self.transport.loseConnection() + +    #def dataReceived(self, data): +    #    if data == '\r': +    #        data = '\r\n' +    #    elif data == '\x03': #^C +    #        self.transport.loseConnection() +    #        return +    #    self.transport.write(data) + + +class MyFactory(SSHFactory): +    privateKeys = { +    'ssh-rsa': Key.fromFile(filename=host_key_file) +    } +    publicKeys = { +    'ssh-rsa': Key.fromFile(filename=host_key_pub_file) +    } +    services = { +        'ssh-userauth': SSHUserAuthServer, +        'ssh-connection': SSHConnection +    } + +if __name__ == '__main__': +    portal = Portal(MyRealm()) +    portal.registerChecker(PublicKeyChecker(authorized_keys_file)) +    MyFactory.portal = portal +    listenTCP(1337, MyFactory()) +    run() | 
