SSL certificate verification for untrusted CA
bug description
If the xmpp server is using SSL certificate which is signed with untrusted CA, this xmpp server can't be used in Gajim because on login Gajim will again and again ask for confirmation of the certificate. The Pidgin client has not problem with this xmpp server and after initial confirmation of the certificate the logins are without any error message.
How to reproduce this bug in Gajim using Prosody XMPP server
-
generate the CA and xmpp server certificate: a. Generate the CA certificate with private key. b. Generate a certificate request for XMPP server together with private key. c. Sign the XMPP server certificate request using CA and generate the XMPP certificate from the request.
openssl req -x509 -nodes -days 100 \ -newkey rsa:4096 -out ca.crt -keyout ca.priv -sha1 \ -subj '/CN=Demo Certification Authority/' openssl req -new -nodes -days 100 -newkey rsa:2048 -out localhost.req \ -keyout localhost.priv -sha1 -subj '/CN=localhost/O=xmpp server/' openssl x509 -req -in localhost.req -out localhost.crt \ -CA ca.crt -CAkey ca.priv -set_serial 10 -sha1
-
Set the certificates in prosody configuration files
ssl = { cafile = "/etc/prosody/certs/ca.crt"; key = "/etc/prosody/certs/localhost.priv"; certificate = "/etc/prosody/certs/localhost.crt"; }
-
After starting Prosody check the certificate using OpenSSL s_client
openssl s_client -connect 'localhost:5222' -starttls xmpp
-
The output of this command should contain the SSL certificate chain
CONNECTED(00000003) depth=1 CN = Demo Certification Authority verify error:num=19:self signed certificate in certificate chain verify return:0 --- Certificate chain 0 s:/CN=localhost/O=xmpp server i:/CN=Demo Certification Authority 1 s:/CN=Demo Certification Authority i:/CN=Demo Certification Authority
-
Now try to login multiple times to the localhost server using Gajim. On each login you get a SSL error, SSL error: Self signed certificate in certificate chain, or SSL certificate fingerprint has changed.
Setting "Ignore this error for this certificate" will not correct this problem and updating the SSL certificate fingerprint will not correct this problem.
bug analysis
The problem is in _ssl_verify_callback
, this method is called for each certificate in certificate chain. For the server certificate, for intermediate CAs and root CAs.
In Gajim source code in src/common/connection.py the method Connection.connection_accepted
assumes that only single fingerprint is present. But _ssl_verify_callback
causes multiple fingerprints. The only fingerprint which is interesting for Gajim and should be display to user is the xmpp server certificate not the CA certificate fingerprints.
fix recommendation
Store only the fingerprint of the xmpp server in _ssl_verify_callback
, ignore CA fingerprints
diff -r e88ec6cb0884 nbxmpp/tls_nb.py
--- a/nbxmpp/tls_nb.py Sat Nov 30 11:29:05 2013 +0100
+++ b/nbxmpp/tls_nb.py Mon Dec 02 11:36:10 2013 +0100
@@ -446,10 +446,11 @@
def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok):
# Exceptions can't propagate up through this callback, so print them here.
try:
- self._owner.ssl_fingerprint_sha1.append(cert.digest('sha1'))
- self._owner.ssl_certificate.append(cert)
- self._owner.ssl_errnum.append(errnum)
- self._owner.ssl_cert_pem.append(OpenSSL.crypto.dump_certificate(
+ if depth == 0:
+ self._owner.ssl_fingerprint_sha1.append(cert.digest('sha1'))
+ self._owner.ssl_certificate.append(cert)
+ self._owner.ssl_errnum.append(errnum)
+ self._owner.ssl_cert_pem.append(OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert))
return True
except: