예제 #1
0
    def open(self):
        try:
            res0 = self._resolveAddr()
            for res in res0:
                (
                    sock_family,
                    sock_type,
                ) = res[0:2]
                ip_port = res[4]
                plain_sock = socket.socket(sock_family, sock_type)
                try:
                    self.handle = ssl.wrap_socket(plain_sock,
                                                  keyfile=self.keyfile,
                                                  certfile=self.certfile,
                                                  ssl_version=self.SSL_VERSION,
                                                  do_handshake_on_connect=True,
                                                  ca_certs=self.ca_certs,
                                                  cert_reqs=self.cert_reqs)
                except ssl.SSLError as e:
                    message = 'SSLError: Error in certificate or key file(s): %s' % e
                    raise TTransportException(
                        type=TTransportException.NOT_OPEN, message=message)
                self.handle.settimeout(self._timeout)
                try:
                    self.handle.connect(ip_port)
                except ssl.SSLError as e:
                    if self.validate:
                        self.handle.close()
                        self.validate = False
                        self.cert_reqs = ssl.CERT_NONE
                        self.ssl_exception = e
                        return self.open()
                    raise e
                except socket.error as e:
                    if res is not res0[-1]:
                        continue
                    else:
                        raise e
                break

        except socket.error as e:
            if self._unix_socket:
                message = 'Could not connect to secure socket %s' % self._unix_socket
            else:
                message = 'Could not connect to %s:%d' % (self.host, self.port)
            raise TTransportException(type=TTransportException.NOT_OPEN,
                                      message=message)
        if self.validate:
            self._validate_cert()
        elif self.ssl_exception is not None:
            self._do_pinning(self.handle.getpeercert(True), self.ssl_exception)
        elif self.accept_once:
            if self.handle.getpeercert(True) in self.accept_once:
                return
            self.handle.close()
            self.validate = True
            self.cert_reqs = ssl.CERT_REQUIRED
            return self.open()
예제 #2
0
 def _validate_cert(self):
     """
     Internal method to validate the peer's SSL certificate.
     raises OnepCertificateException if the certificate fails validation.
     """
     cert = self.handle.getpeercert()
     self.peercert = cert
     self.log.debug('cert: %s' % cert)
     dns_names = []
     ip_addrs = []
     common_names = []
     unstructured_addrs = []
     (
         dns_names,
         ip_addrs,
         common_names,
         unstructured_addrs,
     ) = self._get_fields()
     if HostIpCheck(self.host).is_ipaddress():
         if len(ip_addrs) > 0:
             self._validate_ip_addrs(ip_addrs,
                                     CertificateMatchType.IP_ADDRESS)
         elif len(unstructured_addrs) > 0:
             self._validate_ip_addrs(
                 unstructured_addrs,
                 CertificateMatchType.UNSTRUCTURED_ADDRESS)
         else:
             self.log.warn(
                 'Certificate provided neither ip address nor unstructured address'
             )
             e = TTransportException(
                 type=TTransportException.UNKNOWN,
                 message=
                 'Certificate provided neither ip address nor unstructured address'
             )
             self._do_pinning(self.handle.getpeercert(True), e)
     elif len(dns_names) > 0:
         self._validate_names(dns_names, CertificateMatchType.DNS_NAME)
     elif len(common_names) > 0:
         self._validate_names(common_names,
                              CertificateMatchType.COMMON_NAME)
     else:
         self.log.warn(
             'Certificate provided neither dns name nor common name')
         e = TTransportException(
             type=TTransportException.UNKNOWN,
             message='Certificate provided neither dns name nor common name'
         )
         self._do_pinning(self.handle.getpeercert(True), e)
예제 #3
0
    def _validate_names(self, names, type):
        self.log.debug('host: %s self.host %s' % (names, self.host))
        for name in names:
            if name.lower() == self.host.lower():
                self.is_valid = True
                return

        if type == CertificateMatchType.COMMON_NAME:
            field = 'commonName'
        else:
            field = 'subjectAltName'
        self.log.warn(
            'Host name we connected to "%s" doesn\'t match certificate provided dns names: %s'
            % (self.host, names))
        e = TTransportException(
            type=TTransportException.UNKNOWN,
            message=
            'Host name we connected to "%s" doesn\'t match certificate provided %s: %s'
            % (self.host, field, names))
        self._do_pinning(self.handle.getpeercert(True), e)
예제 #4
0
    def _validate_ip_addrs(self, addrs, type):
        self.log.debug('ip addr: %s self.host %s' % (addrs, self.host))
        for addr in addrs:
            if HostIpCheck(str(addr)) == HostIpCheck(self.host):
                self.is_valid = True
                return

        self.log.warn(
            'Host name we connected to "%s" doesn\'t match certificate provided ip address "%s"'
            % (self.host, addrs))
        if type == CertificateMatchType.UNSTRUCTURED_ADDRESS:
            field = 'unstructuredAddress'
        else:
            field = 'subjectAltName'
        e = TTransportException(
            type=TTransportException.UNKNOWN,
            message=
            'Host name we connected to "%s" doesn\'t match certificate provided "%s %s"'
            % (self.host, field, addrs))
        self._do_pinning(self.handle.getpeercert(True), e)
예제 #5
0
 def _do_pinning(self, cert, cause):
     if cert is None:
         self.log.error(
             'TLS pinning was attempted but there was no certificate to pin.'
         )
         raise TTransportException(type=TTransportException.NOT_OPEN,
                                   message=str(cause))
     try:
         entry = tlspinning._get_entry(self.pinning_file, self.host)
     except:
         entry = None
     hash_type = None
     fingerprint = None
     changed = False
     if entry is not None:
         hash_type = entry[1]
         try:
             md = CiscoTSSLSocket._hashtypes[hash_type]()
         except KeyError:
             self.log.warn(
                 "The entry '%s' in the pinning file '%s' could not be used."
                 % (entry[0], self.pinning_file))
             entry = None
         else:
             md.update(cert)
             hexdigest = md.hexdigest().upper()
             fingerprint = ':'.join((hexdigest[i:(i + 2)]
                                     for i in range(0, len(hexdigest), 2)))
             if entry[2].upper() == fingerprint:
                 return
             changed = True
     if entry is None:
         hash_type = 'SHA-1'
         md = hashlib.sha1()
         md.update(cert)
         hexdigest = md.hexdigest().upper()
         fingerprint = ':'.join(
             (hexdigest[i:(i + 2)] for i in range(0, len(hexdigest), 2)))
     decision = tlspinning.DecisionType.REJECT
     if self.unverified_handler is not None:
         decision = self.unverified_handler.handle_verify(
             self.host, hash_type, fingerprint, changed)
     if decision == tlspinning.DecisionType.ACCEPT_AND_PIN:
         if self.pinning_file:
             try:
                 tlspinning._update_entry(
                     self.pinning_file, (self.host, hash_type, fingerprint))
             except Exception as e:
                 self.log.error(
                     "Failed to pin '%s' to pinning file '%s':  %s" %
                     (self.host, self.pinning_file, e))
         else:
             self.log.error(
                 "No pinning file was given so '%s' could not be pinned." %
                 self.host)
         self.accept_once.append(cert)
         return
     if decision == tlspinning.DecisionType.ACCEPT_ONCE:
         self.accept_once.append(cert)
         return
     raise TTransportException(type=TTransportException.UNKNOWN,
                               message=str(cause))