示例#1
0
    def __init__(self, encrypted=None, certData=None, options={}):
        service.MultiService.__init__(self)
        if encrypted is None:
            if crypto:
                encrypted = True
            else:
                encrypted = False
        assert encrypted in (True, False)
        self.options = options
        self.listeners = []
        self.locationHints = []
        self.encrypted = encrypted
        if encrypted and not crypto:
            raise RuntimeError(
                "crypto for PB is not available, " "try importing twisted.pb.crypto and see " "what happens"
            )
        if encrypted:
            if certData:
                cert = crypto.sslverify.PrivateCertificate.loadPEM(certData)
            else:
                cert = self.createCertificate()
            self.myCertificate = cert
            self.tubID = crypto.digest32(cert.digest("sha1"))
        else:
            self.myCertificate = None
            self.tubID = None

        # local Referenceables
        self.nameToReference = {}
        self.referenceToName = {}
        # remote stuff. Most of these use a TubRef (or NoAuthTubRef) as a
        # dictionary key
        self.tubConnectors = {}  # maps TubRef to a TubConnector
        self.waitingForBrokers = {}  # maps TubRef to list of Deferreds
        self.brokers = {}  # maps TubRef to a Broker that connects to them
        self.unencryptedBrokers = []  # inbound Brokers without TubRefs
示例#2
0
    def __init__(self, encrypted=None, certData=None, options={}):
        service.MultiService.__init__(self)
        if encrypted is None:
            if crypto:
                encrypted = True
            else:
                encrypted = False
        assert encrypted in (True, False)
        self.options = options
        self.listeners = []
        self.locationHints = []
        self.encrypted = encrypted
        if encrypted and not crypto:
            raise RuntimeError("crypto for PB is not available, "
                               "try importing twisted.pb.crypto and see "
                               "what happens")
        if encrypted:
            if certData:
                cert = crypto.sslverify.PrivateCertificate.loadPEM(certData)
            else:
                cert = self.createCertificate()
            self.myCertificate = cert
            self.tubID = crypto.digest32(cert.digest("sha1"))
        else:
            self.myCertificate = None
            self.tubID = None

        # local Referenceables
        self.nameToReference = {}
        self.referenceToName = {}
        # remote stuff. Most of these use a TubRef (or NoAuthTubRef) as a
        # dictionary key
        self.tubConnectors = {}  # maps TubRef to a TubConnector
        self.waitingForBrokers = {}  # maps TubRef to list of Deferreds
        self.brokers = {}  # maps TubRef to a Broker that connects to them
        self.unencryptedBrokers = []  # inbound Brokers without TubRefs
示例#3
0
    def evaluateHello(self, offer):
        """Evaluate the HELLO message sent by the other side. We compare
        TubIDs, and the higher value becomes the 'master' and makes the
        negotiation decisions.

        This method returns a tuple of DECISION,PARAMS. There are a few
        different possibilities:

         We are the master, we make a negotiation decision: DECISION is the
         block of data to send back to the non-master side, PARAMS are the
         connection parameters we will use ourselves.

         We are the master, we can't accomodate their request: raise
         NegotiationError

         We are not the master: DECISION is None
        """

        if self.debugNegotiation:
            log.msg("evaluateHello(isClient=%s): offer=%s" % (self.isClient,
                                                              offer,))
        version = offer.get('banana-negotiation-version')
        if version != '1':
            raise NegotiationError("Unrecognized version number, "
                                   "'%s' not '1', in %s"
                                   % (version, offer))

        forced = False
        f = offer.get('negotiation-forced', None)
        if f and f.lower() == "true":
            forced = True
        # 'forced' means the client is on a one-way link (or is really
        # stubborn) and has already made up its mind about the connection
        # parameters. If we are unable to handle exactly what they have
        # offered, we must hang up.
        assert not forced # TODO: implement


        # glyph says: look at Juice, it does rfc822 parsing, startTLS,
        # switch-to-other-protocol, etc. grep for retrieveConnection in q2q.

        # TODO: oh, if we see an HTTP client, send a good HTTP error like
        # "protocol not supported", or maybe even an HTML page that explains
        # what a PB server is

        # there are four distinct dicts here:
        #  self.negotiationOffer: what we want
        #  clientOffer: what they sent to us, the client's requests.
        #  serverOffer: what we send to them, the server's decision
        #  self.negotiationResults: the negotiated settings
        #
        # [my-tub-id] is not present in self.negotiationResults
        # the server's tubID is in [my-tub-id] for both self.negotiationOffer
        # and serverOffer
        # the client's tubID is in [my-tub-id] for clientOffer

        myTubID = self.myTubID

        theirTubID = offer.get("my-tub-id")
        if self.theirCertificate is None:
            # no client certificate
            if theirTubID is not None:
                # this is where a poor MitM attack is detected, one which
                # doesn't even pretend to encrypt the connection
                raise BananaError("you must use a certificate to claim a "
                                  "TubID")
        else:
            # verify that their claimed TubID matches their SSL certificate.
            # TODO: handle chains
            digest = crypto.digest32(self.theirCertificate.digest("sha1"))
            if digest != theirTubID:
                # this is where a good MitM attack is detected, one which
                # encrypts the connection but which of course uses the wrong
                # certificate
                raise BananaError("TubID mismatch")

        if theirTubID:
            theirTubRef = referenceable.TubRef(theirTubID)
        else:
            theirTubRef = None # unencrypted
        self.theirTubRef = theirTubRef # for use by non-master side, later

        if self.isClient and self.target.encrypted:
            # verify that we connected to the Tub we expected to. If we
            # weren't trying to connect to an encrypted tub, then don't
            # bother checking.. we just accept whoever we managed to connect
            # to.
            if theirTubRef != self.target:
                # TODO: how (if at all) should this error message be
                # communicated to the other side?
                raise BananaError("connected to the wrong Tub")

        if myTubID is None and theirTubID is None:
            iAmTheMaster = not self.isClient
        elif myTubID is None:
            iAmTheMaster = False
        elif theirTubID is None:
            iAmTheMaster = True
        else:
            # this is the most common case
            iAmTheMaster = myTubID > theirTubID

        if self.debugNegotiation:
            log.msg("iAmTheMaster: %s" % iAmTheMaster)

        decision, params = None, None

        if iAmTheMaster:
            # we get to decide everything
            decision = {}

            # first, do we continue with this connection? we might
            # have an existing connection for this particular tub
            if theirTubRef and theirTubRef in self.tub.brokers:
                # there is an existing connection, so drop this one
                if self.debugNegotiation:
                    log.msg(" abandoning the connection: we already have one")
                raise NegotiationError("Duplicate connection")

            # combine their 'offer' and our own self.negotiationOffer to come
            # up with a 'decision' to be sent back to the other end, and the
            # 'params' to be used on our connection
            decision = {}
            decision['banana-decision-version'] = "1"

            ignoredKeys = ["my-tub-id"]

            us = dict([(k, self.negotiationOffer[k])
                       for k in self.negotiationOffer.keys()
                       if k not in ignoredKeys])
            them = dict([(k, offer[k])
                         for k in offer.keys()
                         if k not in ignoredKeys])

            if them != us:
                raise NegotiationError("our negotiation offers are different")

            params = {}

        else:
            # otherwise, the other side gets to decide
            pass


        if iAmTheMaster:
            # I am the master, so I send the decision
            if self.debugNegotiation:
                log.msg("Negotiation.sendDecision: %s" % decision)
            # now we send the decision and switch to Banana. they might hang
            # up.
            self.sendDecision(decision, params)
        else:
            # I am not the master, I receive the decision
            self.phase = DECIDING
示例#4
0
    def evaluateHello(self, offer):
        """Evaluate the HELLO message sent by the other side. We compare
        TubIDs, and the higher value becomes the 'master' and makes the
        negotiation decisions.

        This method returns a tuple of DECISION,PARAMS. There are a few
        different possibilities:

         We are the master, we make a negotiation decision: DECISION is the
         block of data to send back to the non-master side, PARAMS are the
         connection parameters we will use ourselves.

         We are the master, we can't accomodate their request: raise
         NegotiationError

         We are not the master: DECISION is None
        """

        if self.debugNegotiation:
            log.msg("evaluateHello(isClient=%s): offer=%s" % (
                self.isClient,
                offer,
            ))
        version = offer.get('banana-negotiation-version')
        if version != '1':
            raise NegotiationError("Unrecognized version number, "
                                   "'%s' not '1', in %s" % (version, offer))

        forced = False
        f = offer.get('negotiation-forced', None)
        if f and f.lower() == "true":
            forced = True
        # 'forced' means the client is on a one-way link (or is really
        # stubborn) and has already made up its mind about the connection
        # parameters. If we are unable to handle exactly what they have
        # offered, we must hang up.
        assert not forced  # TODO: implement

        # glyph says: look at Juice, it does rfc822 parsing, startTLS,
        # switch-to-other-protocol, etc. grep for retrieveConnection in q2q.

        # TODO: oh, if we see an HTTP client, send a good HTTP error like
        # "protocol not supported", or maybe even an HTML page that explains
        # what a PB server is

        # there are four distinct dicts here:
        #  self.negotiationOffer: what we want
        #  clientOffer: what they sent to us, the client's requests.
        #  serverOffer: what we send to them, the server's decision
        #  self.negotiationResults: the negotiated settings
        #
        # [my-tub-id] is not present in self.negotiationResults
        # the server's tubID is in [my-tub-id] for both self.negotiationOffer
        # and serverOffer
        # the client's tubID is in [my-tub-id] for clientOffer

        myTubID = self.myTubID

        theirTubID = offer.get("my-tub-id")
        if self.theirCertificate is None:
            # no client certificate
            if theirTubID is not None:
                # this is where a poor MitM attack is detected, one which
                # doesn't even pretend to encrypt the connection
                raise BananaError("you must use a certificate to claim a "
                                  "TubID")
        else:
            # verify that their claimed TubID matches their SSL certificate.
            # TODO: handle chains
            digest = crypto.digest32(self.theirCertificate.digest("sha1"))
            if digest != theirTubID:
                # this is where a good MitM attack is detected, one which
                # encrypts the connection but which of course uses the wrong
                # certificate
                raise BananaError("TubID mismatch")

        if theirTubID:
            theirTubRef = referenceable.TubRef(theirTubID)
        else:
            theirTubRef = None  # unencrypted
        self.theirTubRef = theirTubRef  # for use by non-master side, later

        if self.isClient and self.target.encrypted:
            # verify that we connected to the Tub we expected to. If we
            # weren't trying to connect to an encrypted tub, then don't
            # bother checking.. we just accept whoever we managed to connect
            # to.
            if theirTubRef != self.target:
                # TODO: how (if at all) should this error message be
                # communicated to the other side?
                raise BananaError("connected to the wrong Tub")

        if myTubID is None and theirTubID is None:
            iAmTheMaster = not self.isClient
        elif myTubID is None:
            iAmTheMaster = False
        elif theirTubID is None:
            iAmTheMaster = True
        else:
            # this is the most common case
            iAmTheMaster = myTubID > theirTubID

        if self.debugNegotiation:
            log.msg("iAmTheMaster: %s" % iAmTheMaster)

        decision, params = None, None

        if iAmTheMaster:
            # we get to decide everything
            decision = {}

            # first, do we continue with this connection? we might
            # have an existing connection for this particular tub
            if theirTubRef and theirTubRef in self.tub.brokers:
                # there is an existing connection, so drop this one
                if self.debugNegotiation:
                    log.msg(" abandoning the connection: we already have one")
                raise NegotiationError("Duplicate connection")

            # combine their 'offer' and our own self.negotiationOffer to come
            # up with a 'decision' to be sent back to the other end, and the
            # 'params' to be used on our connection
            decision = {}
            decision['banana-decision-version'] = "1"

            ignoredKeys = ["my-tub-id"]

            us = dict([(k, self.negotiationOffer[k])
                       for k in self.negotiationOffer.keys()
                       if k not in ignoredKeys])
            them = dict([(k, offer[k]) for k in offer.keys()
                         if k not in ignoredKeys])

            if them != us:
                raise NegotiationError("our negotiation offers are different")

            params = {}

        else:
            # otherwise, the other side gets to decide
            pass

        if iAmTheMaster:
            # I am the master, so I send the decision
            if self.debugNegotiation:
                log.msg("Negotiation.sendDecision: %s" % decision)
            # now we send the decision and switch to Banana. they might hang
            # up.
            self.sendDecision(decision, params)
        else:
            # I am not the master, I receive the decision
            self.phase = DECIDING