From 2d93a6a7bab08f2cdbb94f396955e8d99a4a137d Mon Sep 17 00:00:00 2001
From: Apollon Oikonomopoulos <apollon@noc.grnet.gr>
Date: Tue, 12 Oct 2010 18:08:06 +0300
Subject: [PATCH] Set list of trusted SSL CAs for client to verify

As per SSL_CTX_set_client_CA_list(3SSL), set the list of acceptable CAs
advertised to SSL clients to include the server's own certificate. This
evidently fixes the pycurl/gnutls RPC client.

During the TLS Handshake, when client verification is requested, the
Server sends a CertificateRequest message which states that the client
should send a valid certificate as a response. The CertificateRequest
message contains a section called "certificate_authorities", which,
according to the standard, is a list of the Distinguished Names (DNs) of
acceptable certification authorities. The client uses this list to send
a certificate signed by one of the acceptable CAs.

Under OpenSSL's server implementation, this list must be set manually
using some appropriate call, otherwise the list is empty. TLS 1.0[1]
does not state whether the list may be left blank, whereas TLS 1.1[2]
and 1.2[3] state that in case the list is blank, then the client *may*
send any certificate of a valid type (valid types are specified
elsewhere in the handshake).

OpenSSL clients seem to obey the behaviour specified in TLS 1.1+,
whereas at least curl+GnuTLS does not send any certificates if the list
is empty (which is not wrong per the spec, but also evidently not
configurable).

[1] http://tools.ietf.org/html/rfc2246
[2] http://tools.ietf.org/html/rfc4346
[3] http://tools.ietf.org/html/rfc5246

Signed-off-by: Apollon Oikonomopoulos <apollon@noc.grnet.gr>
Reviewed-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
---
 lib/http/__init__.py | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/lib/http/__init__.py b/lib/http/__init__.py
index 8767272be..036c13f6d 100644
--- a/lib/http/__init__.py
+++ b/lib/http/__init__.py
@@ -550,6 +550,7 @@ class HttpSslParams(object):
     """
     self.ssl_key_pem = utils.ReadFile(ssl_key_path)
     self.ssl_cert_pem = utils.ReadFile(ssl_cert_path)
+    self.ssl_cert_path = ssl_cert_path
 
   def GetKey(self):
     return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
@@ -609,6 +610,15 @@ class HttpBase(object):
                      OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                      self._SSLVerifyCallback)
 
+      # Also add our certificate as a trusted CA to be sent to the client.
+      # This is required at least for GnuTLS clients to work.
+      try:
+        # This will fail for PyOpenssl versions before 0.10
+        ctx.add_client_ca(self._ssl_cert)
+      except AttributeError:
+        # Fall back to letting OpenSSL read the certificate file directly.
+        ctx.load_client_ca(ssl_params.ssl_cert_path)
+
     return OpenSSL.SSL.Connection(ctx, sock)
 
   def GetSslCiphers(self): # pylint: disable-msg=R0201
-- 
GitLab