# Twisted, the Framework of Your Internet # Copyright (C) 2001-2002 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """A Factory for SSH servers, along with an OpenSSHFactory to use the same data sources as OpenSSH. This module is unstable. Maintainer: U{Paul Swartz} """ import md5, os try: import resource except ImportError: resource = None try: import PAM except: pass else: # PAM requires threading from twisted.python import threadable threadable.init(1) from twisted.internet import protocol from twisted.python import log import common, keys, transport, primes, connection, userauth from twisted.conch import error class SSHFactory(protocol.Factory): services = { 'ssh-userauth':userauth.SSHUserAuthServer, 'ssh-connection':connection.SSHConnection } def startFactory(self): # disable coredumps if resource: resource.setrlimit(resource.RLIMIT_CORE, (0,0)) else: log.msg('INSECURE: unable to disable core dumps.') if not hasattr(self,'publicKeys'): self.publicKeys = self.getPublicKeys() if not hasattr(self,'privateKeys'): self.privateKeys = self.getPrivateKeys() if not self.publicKeys or not self.privateKeys: raise error.ConchError('no host keys, failing') if not hasattr(self,'primes'): self.primes = self.getPrimes() if not self.primes: log.msg('disabling diffie-hellman-group-exchange because we cannot find moduli file') transport.SSHServerTransport.supportedKeyExchanges.remove('diffie-hellman-group-exchange-sha1') def buildProtocol(self, addr): t = transport.SSHServerTransport() t.supportedPublicKeys = self.privateKeys.keys() #if not self.primes: # t.supportedKeyExchanges.remove('diffie-hellman-group-exchange-sha1') t.factory = self return t def getPublicKeys(self): """ Called when the factory is started to get the public portions of the servers host keys. Returns a dictionary mapping SSH key types to public key strings. @rtype: C{dict} """ raise NotImplementedError def getPrivateKeys(self): """ Called when the factory is started to get the private portions of the servers host keys. Returns a dictionary mapping SSH key types to C{Crypto.PublicKey.pubkey.pubkey} objects. @rtype: C{dict} """ raise NotImplementedError def getPrimes(self): """ Called when the factory is started to get Diffie-Hellman generators and primes to use. Returns a dictionary mapping number of bits to lists of tuple of (generator, prime). @rtype: C{dict} """ def getDHPrime(self, bits): """ Return a tuple of (g, p) for a Diffe-Hellman process, with p being as close to bits bits as possible. @type bits: C{int} @rtype: C{tuple} """ return primes.getDHPrimeOfBits(self.primes, bits) def getService(self, transport, service): """ Return a class to use as a service for the given transport. @type transport: C{transport.SSHServerTransport} @type service: C{stR} @rtype: subclass of {service.SSHService} """ if transport.isAuthorized or service == 'ssh-userauth': return self.services[service] class OpenSSHFactory(SSHFactory): dataRoot = '/usr/local/etc' moduliRoot = '/usr/local/etc' # for openbsd which puts moduli in a different # directory from keys def getPublicKeys(self): ks = {} for file in os.listdir(self.dataRoot): if file[:9] == 'ssh_host_' and file[-8:]=='_key.pub': try: k = keys.getPublicKeyString(self.dataRoot+'/'+file) t = common.getNS(k)[0] ks[t] = k except: log.msg('bad public key file %s' % file) return ks def getPrivateKeys(self): ks = {} for file in os.listdir(self.dataRoot): if file[:9] == 'ssh_host_' and file[-4:]=='_key': try: k = keys.getPrivateKeyObject(self.dataRoot+'/'+file) t = keys.objectType(k) ks[t] = k except: log.msg('bad private key file %s' % file) return ks def getPrimes(self): try: return primes.parseModuliFile(self.moduliRoot+'/moduli') except IOError: return None