ssl: on OS X, use a dummy cert to trick Python/OpenSSL to use system CA certs
This will give PKI-secure behaviour out of the box, without any configuration.
Setting web.cacerts to any value or empty will disable this trick.
This dummy cert trick only works on OS X 10.6+, but 10.5 had Python 2.5 which
didn't have certificate validation at all.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/dummycert.pem Fri Sep 26 02:19:48 2014 +0200
@@ -0,0 +1,56 @@
+A dummy certificate that will make OS X 10.6+ Python use the system CA
+certificate store:
+
+-----BEGIN CERTIFICATE-----
+MIIBIzCBzgIJANjmj39sb3FmMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNVBAMTDmhn
+LmV4YW1wbGUuY29tMB4XDTE0MDgzMDA4NDU1OVoXDTE0MDgyOTA4NDU1OVowGTEX
+MBUGA1UEAxMOaGcuZXhhbXBsZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEA
+mh/ZySGlcq0ALNLmA1gZqt61HruywPrRk6WyrLJRgt+X7OP9FFlEfl2tzHfzqvmK
+CtSQoPINWOdAJMekBYFgKQIDAQABMA0GCSqGSIb3DQEBBQUAA0EAF9h49LkSqJ6a
+IlpogZuUHtihXeKZBsiktVIDlDccYsNy0RSh9XxUfhk+XMLw8jBlYvcltSXdJ7We
+aKdQRekuMQ==
+-----END CERTIFICATE-----
+
+This certificate was generated to be syntactically valid but never be usable;
+it expired before it became valid.
+
+Created as:
+
+ $ cat > cn.conf << EOT
+ > [req]
+ > distinguished_name = req_distinguished_name
+ > [req_distinguished_name]
+ > commonName = Common Name
+ > commonName_default = no.example.com
+ > EOT
+ $ openssl req -nodes -new -x509 -keyout /dev/null \
+ > -out dummycert.pem -days -1 -config cn.conf -subj '/CN=hg.example.com'
+
+To verify the content of this certificate:
+
+ $ openssl x509 -in dummycert.pem -noout -text
+ Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 15629337334278746470 (0xd8e68f7f6c6f7166)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hg.example.com
+ Validity
+ Not Before: Aug 30 08:45:59 2014 GMT
+ Not After : Aug 29 08:45:59 2014 GMT
+ Subject: CN=hg.example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (512 bit)
+ Modulus:
+ 00:9a:1f:d9:c9:21:a5:72:ad:00:2c:d2:e6:03:58:
+ 19:aa:de:b5:1e:bb:b2:c0:fa:d1:93:a5:b2:ac:b2:
+ 51:82:df:97:ec:e3:fd:14:59:44:7e:5d:ad:cc:77:
+ f3:aa:f9:8a:0a:d4:90:a0:f2:0d:58:e7:40:24:c7:
+ a4:05:81:60:29
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 17:d8:78:f4:b9:12:a8:9e:9a:22:5a:68:81:9b:94:1e:d8:a1:
+ 5d:e2:99:06:c8:a4:b5:52:03:94:37:1c:62:c3:72:d1:14:a1:
+ f5:7c:54:7e:19:3e:5c:c2:f0:f2:30:65:62:f7:25:b5:25:dd:
+ 27:b5:9e:68:a7:50:45:e9:2e:31
--- a/mercurial/sslutil.py Fri Sep 26 02:19:47 2014 +0200
+++ b/mercurial/sslutil.py Fri Sep 26 02:19:48 2014 +0200
@@ -6,7 +6,7 @@
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-import os
+import os, sys
from mercurial import util
from mercurial.i18n import _
@@ -104,6 +104,13 @@
cacerts = util.expandpath(cacerts)
if not os.path.exists(cacerts):
raise util.Abort(_('could not find web.cacerts: %s') % cacerts)
+ elif cacerts is None and sys.platform == 'darwin' and not util.mainfrozen():
+ dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
+ if os.path.exists(dummycert):
+ ui.debug('using %s to enable OS X system CA\n' % dummycert)
+ ui.setconfig('web', 'cacerts', dummycert, 'dummy')
+ cacerts = dummycert
+ if cacerts:
kws.update({'ca_certs': cacerts,
'cert_reqs': CERT_REQUIRED,
})
--- a/setup.py Fri Sep 26 02:19:47 2014 +0200
+++ b/setup.py Fri Sep 26 02:19:48 2014 +0200
@@ -481,7 +481,8 @@
cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
- 'help/*.txt']}
+ 'help/*.txt',
+ 'dummycert.pem']}
def ordinarypath(p):
return p and p[0] != '.' and p[-1] != '~'
--- a/tests/hghave.py Fri Sep 26 02:19:47 2014 +0200
+++ b/tests/hghave.py Fri Sep 26 02:19:48 2014 +0200
@@ -332,6 +332,10 @@
def has_aix():
return sys.platform.startswith("aix")
+@check("osx", "OS X")
+def has_osx():
+ return sys.platform == 'darwin'
+
@check("absimport", "absolute_import in __future__")
def has_absimport():
import __future__
--- a/tests/test-https.t Fri Sep 26 02:19:47 2014 +0200
+++ b/tests/test-https.t Fri Sep 26 02:19:48 2014 +0200
@@ -115,9 +115,20 @@
#endif
$ cd ..
+OS X has a dummy CA cert that enables use of the system CA store
+
+ $ DISABLEOSXDUMMYCERT=
+#if osx
+ $ hg clone https://localhost:$HGPORT/ copy-pull
+ abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
+ [255]
+
+ $ DISABLEOSXDUMMYCERT="--config=web.cacerts="
+#endif
+
clone via pull
- $ hg clone https://localhost:$HGPORT/ copy-pull
+ $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLEOSXDUMMYCERT
warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
requesting all changes
adding changesets
@@ -143,7 +154,7 @@
$ cd copy-pull
$ echo '[hooks]' >> .hg/hgrc
$ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup" >> .hg/hgrc
- $ hg pull
+ $ hg pull $DISABLEOSXDUMMYCERT
warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
pulling from https://localhost:$HGPORT/
searching for changes