Ticket #1902: conch-sshbugs.patch
File conch-sshbugs.patch, 9.6 KB (added by , 16 years ago) |
---|
-
twisted/conch/test/test_ssh.py
313 313 def getService(self, trans, name): 314 314 return factory.SSHFactory.getService(self, trans, name) 315 315 316 class ConchTestBuggyServerFactory(ConchTestServerFactory): 317 def buildProtocol(self, addr): 318 proto = ConchTestBuggyServer() 319 proto.supportPublicKeys = self.privateKeys.keys() 320 proto.factory = self 321 self.proto = proto 322 return proto 323 316 324 class ConchTestBase: 317 325 318 326 done = 0 … … 343 351 ConchTestBase.connectionLost(self, reason) 344 352 transport.SSHServerTransport.connectionLost(self, reason) 345 353 354 class BuggyKeyFuncs: 355 def __init__(self): 356 # Dirty hack. An instance of this class pretends to be the 357 # keys module, and provides various buggy behaviours to 358 # emulate various crappy SSH implementations 359 360 # Copy the key funcs we *don't* have from "keys" 361 from twisted.conch.ssh import keys 362 for o in dir(keys): 363 if not hasattr(self, o): 364 setattr(self, o, getattr(keys, o)) 365 366 def signData(self, obj, data): 367 mapping = { 368 'ssh-rsa': self.signData_rsa, 369 'ssh-dss': self.signData_dsa 370 } 371 objType = self.objectType(obj) 372 #return common.NS(objType)+mapping[objType](obj, data) 373 # SSH.com does not return the sigtype - just the sigdata 374 return mapping[objType](obj, data) 375 376 class ConchTestBuggyServer(ConchTestBase, transport.SSHServerTransport): 377 supportedPublicKeys = ['ssh-dss',] 378 379 def connectionMade(self): 380 self.keys = BuggyKeyFuncs() 381 # FIXME: should be a super().connectionMade() call? 382 transport.SSHServerTransport.connectionMade(self) 383 384 def sendPacket(self, messageType, payload): 385 if messageType==transport.MSG_SERVICE_ACCEPT: 386 # EMULATE BUG - SSH.com does not put the service name in the accept 387 payload = '' 388 389 # FIXME: should be a super().sendPacket() call? 390 transport.SSHServerTransport.sendPacket(self, messageType, payload) 391 346 392 class ConchTestClient(ConchTestBase, transport.SSHClientTransport): 347 393 348 394 def connectionLost(self, reason): … … 350 396 transport.SSHClientTransport.connectionLost(self, reason) 351 397 352 398 def verifyHostKey(self, key, fp): 353 unittest.assertEquals(key, keys.getPublicKeyString(data = publicRSA_openssh)) 354 unittest.assertEquals(fp,'3d:13:5f:cb:c9:79:8a:93:06:27:65:bc:3d:0b:8f:af') 399 unittest.assertIn( 400 key, 401 ( 402 keys.getPublicKeyString(data = publicRSA_openssh), 403 keys.getPublicKeyString(data = publicDSA_openssh), 404 ) 405 ) 406 unittest.assertIn( 407 fp, 408 ( 409 '3d:13:5f:cb:c9:79:8a:93:06:27:65:bc:3d:0b:8f:af', 410 '16:af:52:0e:e1:a9:0b:78:e7:e8:51:52:91:13:ae:6a', 411 ) 412 ) 355 413 return defer.succeed(1) 356 414 357 415 def connectionSecure(self): … … 698 756 if not Crypto: 699 757 skip = "can't run w/o PyCrypto" 700 758 759 def testBuggyServerOurClient(self): 760 """test a fake/buggy server against the Conch client 761 """ 762 realm = ConchTestRealm() 763 p = portal.Portal(realm) 764 sshpc = ConchTestSSHChecker() 765 sshpc.registerChecker(ConchTestPasswordChecker()) 766 sshpc.registerChecker(ConchTestPublicKeyChecker()) 767 p.registerChecker(sshpc) 768 fac = ConchTestBuggyServerFactory() 769 fac.portal = p 770 fac.startFactory() 771 self.server = fac.buildProtocol(None) 772 self.clientTransport = LoopbackRelay(self.server) 773 self.client = ConchTestClient() 774 self.serverTransport = LoopbackRelay(self.client) 775 776 self.server.makeConnection(self.serverTransport) 777 self.client.makeConnection(self.clientTransport) 778 779 while self.serverTransport.buffer or self.clientTransport.buffer: 780 log.callWithContext({'system': 'serverTransport'}, 781 self.serverTransport.clearBuffer) 782 log.callWithContext({'system': 'clientTransport'}, 783 self.clientTransport.clearBuffer) 784 self.failIf(self.server.done and self.client.done) 785 701 786 def testOurServerOurClient(self): 702 787 """test the Conch server against the Conch client 703 788 """ -
twisted/conch/ssh/transport.py
35 35 36 36 # sibling importsa 37 37 from common import NS, getNS, MP, getMP, _MPpow, ffs, entropy # ease of use 38 import keys39 38 40 39 41 40 class SSHTransportBase(protocol.Protocol): … … 80 79 log.msg('connection lost') 81 80 82 81 def connectionMade(self): 82 # Get our key/signature functions - subclasses may override these 83 # by setting the instance/class variable first e.g. test cases 84 if not hasattr(self, 'keys'): 85 import keys 86 self.keys = keys 87 83 88 self.transport.write('%s\r\n'%(self.ourVersionString)) 84 89 self.sendKexInit() 85 90 … … 345 350 h.update(sharedSecret) 346 351 exchangeHash = h.digest() 347 352 self.sendPacket(MSG_KEXDH_REPLY, NS(self.factory.publicKeys[self.keyAlg])+ \ 348 MP(f)+NS( keys.signData(self.factory.privateKeys[self.keyAlg], exchangeHash)))353 MP(f)+NS(self.keys.signData(self.factory.privateKeys[self.keyAlg], exchangeHash))) 349 354 self._keySetup(sharedSecret, exchangeHash) 350 355 elif self.kexAlg == 'diffie-hellman-group-exchange-sha1': 351 356 self.kexAlg = 'diffie-hellman-group-exchange-sha1-old' … … 408 413 h.update(sharedSecret) 409 414 exchangeHash = h.digest() 410 415 self.sendPacket(MSG_KEX_DH_GEX_REPLY, NS(self.factory.publicKeys[self.keyAlg])+ \ 411 MP(f)+NS( keys.signData(self.factory.privateKeys[self.keyAlg], exchangeHash)))416 MP(f)+NS(self.keys.signData(self.factory.privateKeys[self.keyAlg], exchangeHash))) 412 417 self._keySetup(sharedSecret, exchangeHash) 413 418 414 419 def ssh_NEWKEYS(self, packet): … … 510 515 self.sendPacket(MSG_KEX_DH_GEX_INIT, MP(self.DHpubKey)) 511 516 512 517 def _continueGEX_GROUP(self, ignored, pubKey, f, signature): 513 serverKey = keys.getPublicKeyObject(pubKey)518 serverKey = self.keys.getPublicKeyObject(pubKey) 514 519 sharedSecret = _MPpow(f, self.x, DH_PRIME) 515 520 h = sha.new() 516 521 h.update(NS(self.ourVersionString)) … … 522 527 h.update(MP(f)) 523 528 h.update(sharedSecret) 524 529 exchangeHash = h.digest() 525 if not keys.verifySignature(serverKey, signature, exchangeHash):530 if not self.keys.verifySignature(serverKey, signature, exchangeHash): 526 531 self.sendDisconnect(DISCONNECT_KEY_EXCHANGE_FAILED, 'bad signature') 527 532 return 528 533 self._keySetup(sharedSecret, exchangeHash) … … 537 542 d.addErrback(lambda unused, self=self: self.sendDisconnect(DISCONNECT_HOST_KEY_NOT_VERIFIABLE, 'bad host key')) 538 543 539 544 def _continueGEX_REPLY(self, ignored, pubKey, f, signature): 540 serverKey = keys.getPublicKeyObject(pubKey)545 serverKey = self.keys.getPublicKeyObject(pubKey) 541 546 sharedSecret = _MPpow(f, self.x, self.p) 542 547 h = sha.new() 543 548 h.update(NS(self.ourVersionString)) … … 552 557 h.update(MP(f)) 553 558 h.update(sharedSecret) 554 559 exchangeHash = h.digest() 555 if not keys.verifySignature(serverKey, signature, exchangeHash):560 if not self.keys.verifySignature(serverKey, signature, exchangeHash): 556 561 self.sendDisconnect(DISCONNECT_KEY_EXCHANGE_FAILED, 'bad signature') 557 562 return 558 563 self._keySetup(sharedSecret, exchangeHash) … … 591 596 self.connectionSecure() 592 597 593 598 def ssh_SERVICE_ACCEPT(self, packet): 599 if len(packet) == 0: # SSH.com bug, empty SERVICE ACCEPT packet 600 self.setService(self.instance) 601 return 594 602 name = getNS(packet)[0] 595 603 if name != self.instance.name: 596 604 self.sendDisconnect(DISCONNECT_PROTOCOL_ERROR, "received accept for service we did not request") -
twisted/conch/ssh/keys.py
429 429 'ssh-dss': verifySignature_dsa, 430 430 } 431 431 objType = objectType(obj) 432 if len(sig) == 40: # SSH.com bug, no header 433 return mapping[objType](obj, sig, data) 432 434 sigType, sigData = common.getNS(sig) 433 435 if objType != sigType: # object and signature are not of same type 434 436 return 0 … … 439 441 return obj.verify(pkcs1Digest(data, lenSig(obj)), sigTuple) 440 442 441 443 def verifySignature_dsa(obj, sig, data): 442 sig = common.getNS(sig)[0] 444 if len(sig) != 40: # SSH.com bug, no header 445 sig = common.getNS(sig)[0] 443 446 assert(len(sig) == 40) 444 447 l = len(sig)/2 445 448 sigTuple = map(Util.number.bytes_to_long, [sig[: l], sig[l:]])