Patchwork [RFC] ssl: load CA certificates from system's store by default on Python 2.7.9

login
register
mail settings
Submitter Yuya Nishihara
Date Feb. 26, 2015, 3:14 p.m.
Message ID <7ee972a1ee715e252f85.1424963689@mimosa>
Download mbox | patch
Permalink /patch/7835/
State Superseded
Commit 760a86865f806024af5f4ec45ab7b96e334122dc
Headers show

Comments

Yuya Nishihara - Feb. 26, 2015, 3:14 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1424958853 -32400
#      Thu Feb 26 22:54:13 2015 +0900
# Node ID 7ee972a1ee715e252f850b285e19c51fd8881e2e
# Parent  8d338a372e6888cdd3c18fe142b01b5349664d5e
ssl: load CA certificates from system's store by default on Python 2.7.9

This will make it easy to manage in-house CA certificates, which are often
used in corporate environment and installed into Windows' certs store.

It explicitly checks "cacerts is None" to honor --insecure option that sets
web.cacerts = ''. Another option is to use dummycert.pem as a flag to call
load_default_certs().
Sean Farley - Feb. 26, 2015, 6:10 p.m.
Yuya Nishihara writes:

> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1424958853 -32400
> #      Thu Feb 26 22:54:13 2015 +0900
> # Node ID 7ee972a1ee715e252f850b285e19c51fd8881e2e
> # Parent  8d338a372e6888cdd3c18fe142b01b5349664d5e
> ssl: load CA certificates from system's store by default on Python 2.7.9
>
> This will make it easy to manage in-house CA certificates, which are often
> used in corporate environment and installed into Windows' certs store.
>
> It explicitly checks "cacerts is None" to honor --insecure option that sets
> web.cacerts = ''. Another option is to use dummycert.pem as a flag to call
> load_default_certs().

For what it's worth, this fixes issue4500 from what I could test.
Yuya Nishihara - March 3, 2015, 3:47 p.m.
On Fri, 27 Feb 2015 00:14:49 +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1424958853 -32400
> #      Thu Feb 26 22:54:13 2015 +0900
> # Node ID 7ee972a1ee715e252f850b285e19c51fd8881e2e
> # Parent  8d338a372e6888cdd3c18fe142b01b5349664d5e
> ssl: load CA certificates from system's store by default on Python 2.7.9
> 
> This will make it easy to manage in-house CA certificates, which are often
> used in corporate environment and installed into Windows' certs store.
> 
> It explicitly checks "cacerts is None" to honor --insecure option that sets
> web.cacerts = ''. Another option is to use dummycert.pem as a flag to call
> load_default_certs().

Please ignore this. It seems I forgot to update test-https.t which would need
something like DISABLEOSXDUMMYCERT="--config=web.cacerts=" on Python 2.7.9.

Patch

diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
--- a/mercurial/sslutil.py
+++ b/mercurial/sslutil.py
@@ -10,12 +10,15 @@  import os, sys
 
 from mercurial import util
 from mercurial.i18n import _
+
+_hasdefaultcacerts = False
 try:
     # avoid using deprecated/broken FakeSocket in python 2.6
     import ssl
     CERT_REQUIRED = ssl.CERT_REQUIRED
     try:
         ssl_context = ssl.SSLContext
+        _hasdefaultcacerts = util.safehasattr(ssl_context, 'load_default_certs')
 
         def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=ssl.CERT_NONE,
                             ca_certs=None, serverhostname=None):
@@ -35,6 +38,8 @@  try:
             sslcontext.verify_mode = cert_reqs
             if ca_certs is not None:
                 sslcontext.load_verify_locations(cafile=ca_certs)
+            elif _hasdefaultcacerts:
+                sslcontext.load_default_certs()
 
             sslsocket = sslcontext.wrap_socket(sock,
                                                server_hostname=serverhostname)
@@ -145,7 +150,7 @@  def sslkwargs(ui, host):
             ui.debug('using %s to enable OS X system CA\n' % dummycert)
             ui.setconfig('web', 'cacerts', dummycert, 'dummy')
             cacerts = dummycert
-    if cacerts:
+    if cacerts or (_hasdefaultcacerts and cacerts is None):
         kws.update({'ca_certs': cacerts,
                     'cert_reqs': CERT_REQUIRED,
                     })
@@ -194,7 +199,7 @@  class validator(object):
                                  hint=_('check hostfingerprint configuration'))
             self.ui.debug('%s certificate matched fingerprint %s\n' %
                           (host, nicefingerprint))
-        elif cacerts:
+        elif cacerts or (_hasdefaultcacerts and cacerts is None):
             msg = _verifycert(peercert2, host)
             if msg:
                 raise util.Abort(_('%s certificate error: %s') % (host, msg),