| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
""" |
|---|
| 5 |
A Factory for SSH servers, along with an OpenSSHFactory to use the same |
|---|
| 6 |
data sources as OpenSSH. |
|---|
| 7 |
|
|---|
| 8 |
Maintainer: Paul Swartz |
|---|
| 9 |
""" |
|---|
| 10 |
|
|---|
| 11 |
try: |
|---|
| 12 |
import resource |
|---|
| 13 |
except ImportError: |
|---|
| 14 |
resource = None |
|---|
| 15 |
|
|---|
| 16 |
from twisted.internet import protocol |
|---|
| 17 |
from twisted.python import log |
|---|
| 18 |
from twisted.python.reflect import qual |
|---|
| 19 |
|
|---|
| 20 |
from twisted.conch import error |
|---|
| 21 |
from twisted.conch.ssh import keys |
|---|
| 22 |
import transport, userauth, connection |
|---|
| 23 |
|
|---|
| 24 |
import random |
|---|
| 25 |
import warnings |
|---|
| 26 |
|
|---|
| 27 |
class SSHFactory(protocol.Factory): |
|---|
| 28 |
|
|---|
| 29 |
protocol = transport.SSHServerTransport |
|---|
| 30 |
|
|---|
| 31 |
services = { |
|---|
| 32 |
'ssh-userauth':userauth.SSHUserAuthServer, |
|---|
| 33 |
'ssh-connection':connection.SSHConnection |
|---|
| 34 |
} |
|---|
| 35 |
def startFactory(self): |
|---|
| 36 |
|
|---|
| 37 |
if resource: |
|---|
| 38 |
resource.setrlimit(resource.RLIMIT_CORE, (0,0)) |
|---|
| 39 |
else: |
|---|
| 40 |
log.msg('INSECURE: unable to disable core dumps.') |
|---|
| 41 |
if not hasattr(self,'publicKeys'): |
|---|
| 42 |
self.publicKeys = self.getPublicKeys() |
|---|
| 43 |
for keyType, value in self.publicKeys.items(): |
|---|
| 44 |
if isinstance(value, str): |
|---|
| 45 |
warnings.warn("Returning a mapping from strings to " |
|---|
| 46 |
"strings from getPublicKeys()/publicKeys (in %s) " |
|---|
| 47 |
"is deprecated. Return a mapping from " |
|---|
| 48 |
"strings to Key objects instead." % |
|---|
| 49 |
(qual(self.__class__)), |
|---|
| 50 |
DeprecationWarning, stacklevel=1) |
|---|
| 51 |
self.publicKeys[keyType] = keys.Key.fromString(value) |
|---|
| 52 |
if not hasattr(self,'privateKeys'): |
|---|
| 53 |
self.privateKeys = self.getPrivateKeys() |
|---|
| 54 |
for keyType, value in self.privateKeys.items(): |
|---|
| 55 |
if not isinstance(value, keys.Key): |
|---|
| 56 |
warnings.warn("Returning a mapping from strings to " |
|---|
| 57 |
"PyCrypto key objects from " |
|---|
| 58 |
"getPrivateKeys()/privateKeys (in %s) " |
|---|
| 59 |
"is deprecated. Return a mapping from " |
|---|
| 60 |
"strings to Key objects instead." % |
|---|
| 61 |
(qual(self.__class__),), |
|---|
| 62 |
DeprecationWarning, stacklevel=1) |
|---|
| 63 |
self.privateKeys[keyType] = keys.Key(value) |
|---|
| 64 |
if not self.publicKeys or not self.privateKeys: |
|---|
| 65 |
raise error.ConchError('no host keys, failing') |
|---|
| 66 |
if not hasattr(self,'primes'): |
|---|
| 67 |
self.primes = self.getPrimes() |
|---|
| 68 |
|
|---|
| 69 |
def buildProtocol(self, addr): |
|---|
| 70 |
t = protocol.Factory.buildProtocol(self, addr) |
|---|
| 71 |
t.supportedPublicKeys = self.privateKeys.keys() |
|---|
| 72 |
if not self.primes: |
|---|
| 73 |
log.msg('disabling diffie-hellman-group-exchange because we ' |
|---|
| 74 |
'cannot find moduli file') |
|---|
| 75 |
ske = t.supportedKeyExchanges[:] |
|---|
| 76 |
ske.remove('diffie-hellman-group-exchange-sha1') |
|---|
| 77 |
t.supportedKeyExchanges = ske |
|---|
| 78 |
return t |
|---|
| 79 |
|
|---|
| 80 |
def getPublicKeys(self): |
|---|
| 81 |
""" |
|---|
| 82 |
Called when the factory is started to get the public portions of the |
|---|
| 83 |
servers host keys. Returns a dictionary mapping SSH key types to |
|---|
| 84 |
public key strings. |
|---|
| 85 |
|
|---|
| 86 |
@rtype: C{dict} |
|---|
| 87 |
""" |
|---|
| 88 |
raise NotImplementedError('getPublicKeys unimplemented') |
|---|
| 89 |
|
|---|
| 90 |
def getPrivateKeys(self): |
|---|
| 91 |
""" |
|---|
| 92 |
Called when the factory is started to get the private portions of the |
|---|
| 93 |
servers host keys. Returns a dictionary mapping SSH key types to |
|---|
| 94 |
C{Crypto.PublicKey.pubkey.pubkey} objects. |
|---|
| 95 |
|
|---|
| 96 |
@rtype: C{dict} |
|---|
| 97 |
""" |
|---|
| 98 |
raise NotImplementedError('getPrivateKeys unimplemented') |
|---|
| 99 |
|
|---|
| 100 |
def getPrimes(self): |
|---|
| 101 |
""" |
|---|
| 102 |
Called when the factory is started to get Diffie-Hellman generators and |
|---|
| 103 |
primes to use. Returns a dictionary mapping number of bits to lists |
|---|
| 104 |
of tuple of (generator, prime). |
|---|
| 105 |
|
|---|
| 106 |
@rtype: C{dict} |
|---|
| 107 |
""" |
|---|
| 108 |
|
|---|
| 109 |
def getDHPrime(self, bits): |
|---|
| 110 |
""" |
|---|
| 111 |
Return a tuple of (g, p) for a Diffe-Hellman process, with p being as |
|---|
| 112 |
close to bits bits as possible. |
|---|
| 113 |
|
|---|
| 114 |
@type bits: C{int} |
|---|
| 115 |
@rtype: C{tuple} |
|---|
| 116 |
""" |
|---|
| 117 |
primesKeys = self.primes.keys() |
|---|
| 118 |
primesKeys.sort(lambda x, y: cmp(abs(x - bits), abs(y - bits))) |
|---|
| 119 |
realBits = primesKeys[0] |
|---|
| 120 |
return random.choice(self.primes[realBits]) |
|---|
| 121 |
|
|---|
| 122 |
def getService(self, transport, service): |
|---|
| 123 |
""" |
|---|
| 124 |
Return a class to use as a service for the given transport. |
|---|
| 125 |
|
|---|
| 126 |
@type transport: L{transport.SSHServerTransport} |
|---|
| 127 |
@type service: C{str} |
|---|
| 128 |
@rtype: subclass of L{service.SSHService} |
|---|
| 129 |
""" |
|---|
| 130 |
if service == 'ssh-userauth' or hasattr(transport, 'avatar'): |
|---|
| 131 |
return self.services[service] |
|---|