async def test_abort_on_socket_failed(hass): """Test we abort of we have errors during socket creation.""" flow = init_config_flow(hass) with patch("socket.create_connection", side_effect=socket.gaierror()): result = await flow.async_step_user({CONF_HOST: HOST}) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {CONF_HOST: "resolve_failed"} with patch("socket.create_connection", side_effect=socket.timeout()): result = await flow.async_step_user({CONF_HOST: HOST}) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {CONF_HOST: "connection_timeout"} with patch( "socket.create_connection", side_effect=ssl.CertificateError( f"{HOST} doesn't match somethingelse.com"), ): result = await flow.async_step_user({CONF_HOST: HOST}) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {CONF_HOST: "wrong_host"} with patch("socket.create_connection", side_effect=ssl.CertificateError("different error")): result = await flow.async_step_user({CONF_HOST: HOST}) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {CONF_HOST: "certificate_error"} with patch("socket.create_connection", side_effect=ssl.SSLError()): result = await flow.async_step_user({CONF_HOST: HOST}) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["errors"] == {CONF_HOST: "certificate_error"}
def match_hostname(cert, hostname): """Verify that *cert* (in decoded format as returned by SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 rules are followed, but IP addresses are not accepted for *hostname*. CertificateError is raised on failure. On success, the function returns nothing. """ if not cert: raise ValueError("empty or no certificate, match_hostname needs a " "SSL socket or SSL context with either " "CERT_OPTIONAL or CERT_REQUIRED") try: host_ip = ipaddress.ip_address(hostname) except ValueError: # Not an IP address (common case) host_ip = None dnsnames = [] san = cert.get('subjectAltName', ()) for key, value in san: if key == 'DNS': if host_ip is None and ssl._dnsname_match(value, hostname): return dnsnames.append(value) elif key == 'IP Address': if host_ip is not None and _ipaddress_match(value, host_ip): return dnsnames.append(value) if not dnsnames: # The subject is only checked when there is no dNSName entry # in subjectAltName for sub in cert.get('subject', ()): for key, value in sub: # XXX according to RFC 2818, the most specific Common Name # must be used. if key == 'commonName': if ssl._dnsname_match(value, hostname): return dnsnames.append(value) if len(dnsnames) > 1: raise ssl.CertificateError("hostname %r " "doesn't match either of %s" % (hostname, ', '.join(map(repr, dnsnames))) ) elif len(dnsnames) == 1: raise ssl.CertificateError("hostname %r " "doesn't match %r" % (hostname, dnsnames[0])) else: raise ssl.CertificateError("no appropriate commonName or " "subjectAltName fields were found")
def _validate_certificate(self, opened_socket): cert = opened_socket.getpeercert() ssl.match_hostname(cert, self._host) before = datetime.strptime(cert["notBefore"], "%b %d %H:%M:%S %Y %Z") after = datetime.strptime(cert["notAfter"], "%b %d %H:%M:%S %Y %Z") if not before < datetime.utcnow() < after: raise ssl.CertificateError("certificate expired")
def wrap_socket(self, *args, **kwargs): sock = super().wrap_socket(*args, **kwargs) cert = sock.getpeercert(True) if not self.cert is None: if self.cert != cert: raise ssl.CertificateError( 'Certificate is not equal to expected') return sock
def check_certificate_fingerprint(conn, trusted_fingerprints): cert = conn.getpeercert(binary_form=True) for algorithm in FINGERPRINT_ALGORITHMS: h = hashlib.new(algorithm) h.update(cert) if h.hexdigest() in trusted_fingerprints: return raise ssl.CertificateError('No matching fingerprint.')
def test_cert_error_ssl_module_absent(self, mocker): class Dummy(Exception): pass open_url = mocker.patch.object(http, "open_url") open_url.side_effect = ssl.CertificateError("Invalid") mocker.patch.object(http, "CertificateError", Dummy) with pytest.raises(ssl.CertificateError): http.request("GET", "example.com/bad")
def notify_certificate(self, commonname: str, certdata: bytes) -> None: cert = x509.load_der_x509_certificate(certdata) pem = ssl.DER_cert_to_PEM_cert(certdata) certnames = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME) for name in certnames: if name.value.lower() == commonname: self.certs[commonname] = pem break else: raise ssl.CertificateError( "Certificate has no matching common name" )
def tls_cert_tofu(wrapped, accept_certs, sname): global KNOWN_TLS_HOSTS cert = tls_sock_cert_sha256(wrapped) if accept_certs and cert not in accept_certs: raise ssl.CertificateError('Unrecognized certificate: %s' % cert) skey = md5_hex(sname) if skey not in KNOWN_TLS_HOSTS: KNOWN_TLS_HOSTS[skey] = {'server': sname} KNOWN_TLS_HOSTS[skey].use_web_ca = (accept_certs is None) if cert not in KNOWN_TLS_HOSTS[skey].accept_certs: KNOWN_TLS_HOSTS[skey].accept_certs.append(cert)
def main(): """ Exercise 3 Client """ # Create a standard TCP Socket sock = socket.socket(socket.AF_INET) # Create SSL context which holds the parameters for any sessions context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) context.check_hostname = False context.load_verify_locations(CA_CERT) # We can wrap in an SSL context first, then connect conn = context.wrap_socket(sock, server_hostname=LOCAL_HOST) try: # Handshake - conn is an SSLSocket conn.connect((LOCAL_HOST, LOCAL_PORT)) # Verify against pinned hash dat = conn.getpeercert(binary_form=True) hashalg = hashlib.sha256() hashalg.update(dat) cert_hash = hashalg.digest() with open(PINNED_FILE, 'rb') as pinned: pinned_hash = pinned.read() if cert_hash != pinned_hash: raise ssl.CertificateError("Non-pinned certificate!") # What parameters were established? print("Negotiated session using cipher suite: {0}\n".format( conn.cipher()[0])) # We are sending a 4 byte int, convert into bytes conn.send((100124796).to_bytes(4, 'big')) # Receive a number back from the server server_response = conn.recv(1024) # Unpickle the bytes from the server into a datatypes.BankCustomer customer_returned = pickle.loads(server_response) print(customer_returned) except ssl.CertificateError: print("The host's certificate has not been pinned by this application") finally: conn.close()
def error(self, error): raw_error_str = str(error) if "SSL23_GET_SERVER_HELLO" in str( error) or "SSL3_GET_RECORD" in raw_error_str: error = T("This server does not allow SSL on this port") # Catch certificate errors if type( error ) == ssl.CertificateError or "CERTIFICATE_VERIFY_FAILED" in raw_error_str: # Log the raw message for debug purposes logging.info("Certificate error for host %s: %s", self.nw.server.host, raw_error_str) # Try to see if we should catch this message and provide better text if "hostname" in raw_error_str: raw_error_str = T( "Certificate hostname mismatch: the server hostname is not listed in the certificate. This is a server issue." ) elif "certificate verify failed" in raw_error_str: raw_error_str = T( "Certificate not valid. This is most probably a server issue." ) # Reformat error error = T("Server %s uses an untrusted certificate [%s]") % ( self.nw.server.host, raw_error_str) error = "%s - %s: %s" % (error, T("Wiki"), "https://sabnzbd.org/certificate-errors") # Prevent throwing a lot of errors or when testing server if error not in self.nw.server.warning and not self.blocking: logging.error(error) # Pass to server-test if self.blocking: raise ssl.CertificateError(error) # Blocking = server-test, pass directly to display code if self.blocking: raise socket.error(errno.ECONNREFUSED, str(error)) else: msg = "Failed to connect: %s" % (str(error)) msg = "%s %s@%s:%s" % (msg, self.nw.thrdnum, self.host, self.port) self.error_msg = msg logging.info(msg) self.nw.server.warning = msg
def starttls(self): assert 'ssl' in globals() network_config = getattr(conf.supybot.networks, self.irc.network) certfile = network_config.certfile() if not certfile: certfile = conf.supybot.protocols.irc.certfile() if not certfile: certfile = None elif not os.path.isfile(certfile): drivers.log.warning('Could not find cert file %s.' % certfile) certfile = None if self.currentServer.force_tls_verification \ and not self.anyCertValidationEnabled(): verifyCertificates = True else: verifyCertificates = conf.supybot.protocols.ssl.verifyCertificates( ) if not self.currentServer.force_tls_verification \ and not self.anyCertValidationEnabled(): drivers.log.warning( 'Not checking SSL certificates, connections ' 'are vulnerable to man-in-the-middle attacks. Set ' 'supybot.protocols.ssl.verifyCertificates to "true" ' 'to enable validity checks.') try: self.conn = utils.net.ssl_wrap_socket( self.conn, logger=drivers.log, hostname=self.currentServer.hostname, certfile=certfile, verify=verifyCertificates, trusted_fingerprints=network_config.ssl.serverFingerprints(), ca_file=network_config.ssl.authorityCertificate(), ) except ssl.CertificateError as e: drivers.log.error( ('Certificate validation failed when ' 'connecting to %s: %s\n' 'This means either someone is doing a man-in-the-middle ' 'attack on your connection, or the server\'s certificate is ' 'not in your trusted fingerprints list.') % (self.irc.network, e.args[0])) raise ssl.CertificateError( 'Aborting because of failed certificate ' 'verification.')
def _validate_ssl_cert(self): if self.ca_certs is not None: cert = self._socket.getpeercert() # get cert as dictionary ssl.match_hostname(cert, self.host) else: cert = self._socket.getpeercert(True) # get binary cert sha512_hash = hashlib.sha512(cert).hexdigest() if self.host in self.known_hosts: hash_ = self.known_hosts[self.host] if sha512_hash != hash_: self.disconnect() e = ('SSL certificate does not match the one from the ' 'known_hosts file. Most likely the server has changed' ' its certificate and you have to delete the old line' ' from the known_hosts file. Be careful, this could ' 'also mean that you are being attacked!\nOld hash: ' '%s\nNew hash: %s' % (hash_, sha512_hash)) raise ssl.CertificateError(e) else: self.disconnect() raise UnknownCertError(self.host, sha512_hash, hashlib.sha1(cert).hexdigest())
def connect(self): """ initiates the connection to the server set in self.host:self.port and returns a generator object. >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667) >>> g = cli.connect() >>> while 1: ... next(g) """ try: retries = 0 while True: try: self.socket = socket.create_connection( ("{0}".format(self.host), self.port), source_address=("{0}".format(self.bindhost), 0)) break except socket.error as e: retries += 1 self.stream_handler('Error: {0}'.format(e), level="warning") if retries > 3: sys.exit(1) if self.use_ssl: ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) if self.cipher_list: try: ctx.set_ciphers(self.cipher_list) except Exception: self.stream_handler( "No ciphers could be selected from the cipher list. TLS is not available.", level="warning") self.stream_handler( "Use `openssl ciphers' to see which ciphers are available on this system.", level="warning") raise # explicitly disable old protocols ctx.options |= ssl.OP_NO_SSLv2 ctx.options |= ssl.OP_NO_SSLv3 ctx.options |= ssl.OP_NO_TLSv1 # explicitly disable compression (CRIME attack) ctx.options |= ssl.OP_NO_COMPRESSION # TLS session tickets harm forward secrecy ctx.options |= ssl.OP_NO_TICKET if self.cert_verify and not self.cert_fp: ctx.verify_mode = ssl.CERT_REQUIRED if not self.cert_fp: ctx.check_hostname = True ctx.load_default_certs() elif not self.cert_verify and not self.cert_fp: self.stream_handler( "**NOT** validating the server's TLS certificate! Set SSL_VERIFY or SSL_CERTFP in botconfig.py.", level="warning") if self.client_certfile: # if client_keyfile is not specified, the ssl module will look to the client_certfile for it. try: # specify blank password to ensure that encrypted certs will outright fail rather than prompting for password on stdin # in a scenario where a user does !update or !restart, they will be unable to type in such a password and effectively kill the bot # until someone can SSH in to restart it via CLI. ctx.load_cert_chain(self.client_certfile, self.client_keyfile, password="") self.stream_handler( "Connecting with a TLS client certificate", level="info") except Exception as error: self.stream_handler( "Unable to load client cert/key pair: {0}".format( error), level="error") raise try: self.socket = ctx.wrap_socket(self.socket, server_hostname=self.host) except Exception as error: self.stream_handler( "Could not connect with TLS: {0}".format(error), level="error") raise if self.cert_fp: valid_fps = set( fp.replace(":", "").lower() for fp in self.cert_fp) peercert = self.socket.getpeercert(True) h = hashlib.new("sha256") h.update(peercert) peercertfp = h.hexdigest() if peercertfp not in valid_fps: self.stream_handler( "Certificate fingerprint {0} did not match any expected fingerprints" .format(peercertfp), level="error") raise ssl.CertificateError( "Certificate fingerprint {0} did not match any expected fingerprints" .format(peercertfp)) self.stream_handler( "Server certificate fingerprint matched {0}".format( peercertfp), level="info") self.stream_handler("Connected with cipher {0}".format( self.socket.cipher()[0]), level="info") if not self.blocking: self.socket.setblocking(0) self.send("CAP LS 302") if self.server_pass and "{password}" in self.server_pass and self.password and not self.sasl_auth: # If not using SASL, try to send the NickServ password during connect via PASS message = "PASS :{0}".format(self.server_pass).format( account=self.authname if self.authname else self.nickname, password=self.password) self.send(message, log="PASS :[redacted]") elif self.server_pass and "{password}" not in self.server_pass: # If {password} isn't present, then we likely have a connect password, so send that regardless of SASL message = "PASS :{0}".format(self.server_pass) self.send(message, log="PASS :[redacted]") self.send("NICK", self.nickname) self.user(self.ident, self.real_name) if self.connect_cb: try: self.connect_cb(self) except Exception as e: sys.stderr.write(traceback.format_exc()) raise e buffer = bytes() while not self._end: try: buffer += self.socket.recv(1024) except socket.error as e: if False and not self.blocking and e.errno == 11: pass else: sys.stderr.write(traceback.format_exc()) raise e else: data = buffer.split(bytes("\n", "utf_8")) buffer = data.pop() for el in data: prefix, command, args = parse_raw_irc_command(el) try: enc = "utf8" fargs = [ arg.decode(enc) for arg in args if isinstance(arg, bytes) ] except UnicodeDecodeError: enc = "latin1" fargs = [ arg.decode(enc) for arg in args if isinstance(arg, bytes) ] try: largs = list(args) if prefix is not None: prefix = prefix.decode(enc) self.stream_handler( "<--- receive {0} {1} ({2})".format( prefix, command, ", ".join(fargs)), level="debug") # for i,arg in enumerate(largs): # if arg is not None: largs[i] = arg.decode(enc) if command in self.command_handler: self.command_handler[command](self, prefix, *fargs) elif "" in self.command_handler: self.command_handler[""](self, prefix, command, *fargs) except Exception as e: sys.stderr.write(traceback.format_exc()) raise e # ? yield True finally: if self.socket: self.stream_handler('closing socket') self.socket.close() yield False
def test_cert_error_ssl_module_present(self, mocker): open_url = mocker.patch.object(http, "open_url") open_url.side_effect = ssl.CertificateError("Invalid") with pytest.raises(errors.HttpError): http.request("GET", "example.com/bad")
def test_certificate_error(self, mock: MagicMock) -> None: exc = ssl.CertificateError() mock.return_value = URLOpenMock(b"msg", exc) with self.assertRaises(www.WWWError): www.WWW("https://example.com").get()
def request(*args, **kwargs): raise ssl.CertificateError()