merge with stable
authorMatt Mackall <mpm@selenic.com>
Fri, 01 Jul 2016 16:02:56 -0500
changeset 29459 fd93b15b5c30
parent 29458 59058549a611 (current diff)
parent 29454 ddbff1435e7b (diff)
child 29461 7d88fde2309f
merge with stable
mercurial/help/config.txt
mercurial/sslutil.py
--- a/.hgsigs	Fri Jul 01 13:54:35 2016 +0800
+++ b/.hgsigs	Fri Jul 01 16:02:56 2016 -0500
@@ -128,3 +128,4 @@
 a56296f55a5e1038ea5016dace2076b693c28a56 0 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
+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
--- a/.hgtags	Fri Jul 01 13:54:35 2016 +0800
+++ b/.hgtags	Fri Jul 01 16:02:56 2016 -0500
@@ -141,3 +141,4 @@
 a56296f55a5e1038ea5016dace2076b693c28a56 3.8.1
 aaabed77791a75968a12b8c43ad263631a23ee81 3.8.2
 a9764ab80e11bcf6a37255db7dd079011f767c6c 3.8.3
+26a5d605b8683a292bb89aea11f37a81b06ac016 3.8.4
--- a/i18n/pt_BR.po	Fri Jul 01 13:54:35 2016 +0800
+++ b/i18n/pt_BR.po	Fri Jul 01 16:02:56 2016 -0500
@@ -22714,12 +22714,16 @@
 
 msgid ""
 "Relative subrepository paths are first made absolute, and the\n"
-"rewrite rules are then applied on the full (absolute) path. The rules\n"
-"are applied in definition order."
+"rewrite rules are then applied on the full (absolute) path. If ``pattern``\n"
+"doesn't match the full path, an attempt is made to apply it on the\n"
+"relative path alone. The rules are applied in definition order."
 msgstr ""
 "Caminhos relativos para sub-repositórios são primeiramente tornados\n"
 "absolutos, e em seguida as regras de reescrita são aplicadas no\n"
-"caminho absoluto completo. As regras são aplicadas na ordem de definição."
+"caminho absoluto completo.\n"
+"Se ``pattern`` não coincidir com o caminho completo, é feita uma\n"
+"tentativa para aplicá-lo no caminho relativo sozinho.\n"
+"As regras são aplicadas na ordem de definição."
 
 msgid ""
 "``templatealias``\n"
@@ -25625,6 +25629,163 @@
 "    Veja 'Parent, working directory'.\n"
 
 msgid ""
+"========\n"
+" hg-ssh\n"
+"========"
+msgstr ""
+"========\n"
+" hg-ssh\n"
+"========"
+
+msgid ""
+"----------------------------------------\n"
+"restricted ssh login shell for Mercurial\n"
+"----------------------------------------"
+msgstr ""
+"--------------------------------------------\n"
+"shell de login ssh restrito para o Mercurial--------------------------------------------"
+
+msgid ""
+":Author:         Thomas Arendsen Hein <thomas@intevation.de>\n"
+":Organization:   Mercurial\n"
+":Manual section: 8\n"
+":Manual group:   Mercurial Manual"
+msgstr ""
+":Author:         Thomas Arendsen Hein <thomas@intevation.de>\n"
+":Organization:   Mercurial\n"
+":Manual section: 8\n"
+":Manual group:   Mercurial Manual"
+
+#. do not translate: .. contents::
+msgid ""
+".. contents::\n"
+"   :backlinks: top\n"
+"   :class: htmlonly\n"
+"   :depth: 1"
+msgstr ""
+".. contents::\n"
+"   :backlinks: top\n"
+"   :class: htmlonly\n"
+"   :depth: 1"
+
+msgid ""
+"Synopsis\n"
+"\"\"\"\"\"\"\"\"\n"
+"**hg-ssh** repositories..."
+msgstr ""
+"Sinopse\n"
+"\"\"\"\"\"\"\"\n"
+"**hg-ssh** repositórios..."
+
+msgid ""
+"Description\n"
+"\"\"\"\"\"\"\"\"\"\"\"\n"
+"**hg-ssh** is a wrapper for ssh access to a limited set of mercurial repos."
+msgstr ""
+"Descrição\n"
+"\"\"\"\"\"\"\"\"\"\n"
+"**hg-ssh** é um wrapper para acesso ssh a um conjunto limitado de\n"
+"repositórios do Mercurial."
+
+msgid ""
+"To be used in ~/.ssh/authorized_keys with the \"command\" option, see sshd(8):\n"
+"command=\"hg-ssh path/to/repo1 /path/to/repo2 ~/repo3 ~user/repo4\" ssh-dss ...\n"
+"(probably together with these other useful options:\n"
+"no-port-forwarding,no-X11-forwarding,no-agent-forwarding)"
+msgstr ""
+"Para ser usado em ~/.ssh/authorized_keys com a opção \"command\", veja sshd(8):\n"
+"command=\"hg-ssh path/to/repo1 /path/to/repo2 ~/repo3 ~user/repo4\" ssh-dss ...\n"
+"(provavelmente incluindo estas outras opções úteis:\n"
+"no-port-forwarding,no-X11-forwarding,no-agent-forwarding)"
+
+msgid ""
+"This allows pull/push over ssh from/to the repositories given as arguments."
+msgstr ""
+"Isto possibilita pull/push através de ssh de/para repositórios passados como\n"
+"argumentos."
+
+msgid ""
+"If all your repositories are subdirectories of a common directory, you can\n"
+"allow shorter paths with:\n"
+"command=\"cd path/to/my/repositories && hg-ssh repo1 subdir/repo2\""
+msgstr ""
+"Se os repositórios forem sub-diretórios de um diretírio comum, você pode\n"
+"permitir caminhos mais curtos com:\n"
+"command=\"cd caminho/para/repositórios && hg-ssh repo1 subdir/repo2\""
+
+msgid ""
+"You can use pattern matching of your normal shell, e.g.:\n"
+"command=\"cd repos && hg-ssh user/thomas/* projects/{mercurial,foo}\""
+msgstr ""
+"Você pode usar a correspondência de padrões de seu shell, por exemplo:\n"
+"command=\"cd repos && hg-ssh user/thomas/* projetos/{mercurial,foo}\""
+
+msgid ""
+"You can also add a --read-only flag to allow read-only access to a key, e.g.:\n"
+"command=\"hg-ssh --read-only repos/\\*\""
+msgstr ""
+"Você também pode adicionar a opção --read-only para permitir acesso somente\n"
+"leitura a uma chave, por exemplo:\n"
+"command=\"hg-ssh --read-only repos/\\*\""
+
+msgid ""
+"Bugs\n"
+"\"\"\"\"\n"
+"Probably lots, please post them to the mailing list (see Resources_\n"
+"below) when you find them."
+msgstr ""
+"Bugs\n"
+"\"\"\"\"\n"
+"Provavelmente vários, por favor informe-os na lista de discussão\n"
+"(veja a seção Recursos_ abaixo)."
+
+msgid ""
+"See Also\n"
+"\"\"\"\"\"\"\"\"\n"
+"|hg(1)|_"
+msgstr ""
+"Veja Também\n"
+"\"\"\"\"\"\"\"\"\"\"\"\n"
+"|hg(1)|_"
+
+msgid ""
+"Author\n"
+"\"\"\"\"\"\"\n"
+"Written by Matt Mackall <mpm@selenic.com>"
+msgstr ""
+"Autor\n"
+"\"\"\"\"\"\n"
+"Escrito por Matt Mackall <mpm@selenic.com>"
+
+msgid ""
+"Resources\n"
+"\"\"\"\"\"\"\"\"\"\n"
+"Main Web Site: https://mercurial-scm.org/"
+msgstr ""
+"Recursos\n"
+"\"\"\"\"\"\"\"\"\n"
+"Página Principal: https://mercurial-scm.org/"
+
+msgid "Source code repository: http://selenic.com/hg"
+msgstr "Repositório de código fonte: http://selenic.com/hg"
+
+msgid "Mailing list: http://selenic.com/mailman/listinfo/mercurial"
+msgstr "Lista de discussão: http://selenic.com/mailman/listinfo/mercurial"
+
+msgid ""
+"Copying\n"
+"\"\"\"\"\"\"\"\n"
+"Copyright (C) 2005-2016 Matt Mackall.\n"
+"Free use of this software is granted under the terms of the GNU General\n"
+"Public License version 2 or any later version."
+msgstr ""
+"Cópia\n"
+"\"\"\"\"\"\n"
+"Copyright (C) 2005-2016 Matt Mackall.\n"
+"Garante-se livre uso deste software nos termos da licença\n"
+"GNU General Public License, versão 2 ou qualquer versão posterior."
+
+msgid ""
 "====\n"
 " hg\n"
 "===="
@@ -25653,18 +25814,6 @@
 ":Manual section: 1\n"
 ":Manual group:   Mercurial Manual"
 
-#. do not translate: .. contents::
-msgid ""
-".. contents::\n"
-"   :backlinks: top\n"
-"   :class: htmlonly\n"
-"   :depth: 1"
-msgstr ""
-".. contents::\n"
-"   :backlinks: top\n"
-"   :class: htmlonly\n"
-"   :depth: 1"
-
 msgid ""
 "\n"
 "Synopsis\n"
@@ -25829,17 +25978,6 @@
 "rastreado pelo Mercurial, ele será sobrescrito."
 
 msgid ""
-"Bugs\n"
-"\"\"\"\"\n"
-"Probably lots, please post them to the mailing list (see Resources_\n"
-"below) when you find them."
-msgstr ""
-"Bugs\n"
-"\"\"\"\"\n"
-"Provavelmente vários, por favor informe-os na lista de discussão\n"
-"(veja a seção Recursos_ abaixo)."
-
-msgid ""
 "See Also\n"
 "\"\"\"\"\"\"\"\"\n"
 "|hgignore(5)|_, |hgrc(5)|_"
@@ -25849,43 +25987,6 @@
 "|hgignore(5)|_, |hgrc(5)|_"
 
 msgid ""
-"Author\n"
-"\"\"\"\"\"\"\n"
-"Written by Matt Mackall <mpm@selenic.com>"
-msgstr ""
-"Autor\n"
-"\"\"\"\"\"\n"
-"Escrito por Matt Mackall <mpm@selenic.com>"
-
-msgid ""
-"Resources\n"
-"\"\"\"\"\"\"\"\"\"\n"
-"Main Web Site: https://mercurial-scm.org/"
-msgstr ""
-"Recursos\n"
-"\"\"\"\"\"\"\"\"\n"
-"Página Principal: https://mercurial-scm.org/"
-
-msgid "Source code repository: http://selenic.com/hg"
-msgstr "Repositório de código fonte: http://selenic.com/hg"
-
-msgid "Mailing list: http://selenic.com/mailman/listinfo/mercurial"
-msgstr "Lista de discussão: http://selenic.com/mailman/listinfo/mercurial"
-
-msgid ""
-"Copying\n"
-"\"\"\"\"\"\"\"\n"
-"Copyright (C) 2005-2016 Matt Mackall.\n"
-"Free use of this software is granted under the terms of the GNU General\n"
-"Public License version 2 or any later version."
-msgstr ""
-"Cópia\n"
-"\"\"\"\"\"\n"
-"Copyright (C) 2005-2016 Matt Mackall.\n"
-"Garante-se livre uso deste software nos termos da licença\n"
-"GNU General Public License, versão 2 ou qualquer versão posterior."
-
-msgid ""
 "==========\n"
 " hgignore\n"
 "=========="
@@ -31696,17 +31797,6 @@
 msgid ".hgtags merged successfully\n"
 msgstr ".hgtags mesclado com successo\n"
 
-#, python-format
-msgid "%s, line %s: %s\n"
-msgstr "%s, linha %s: %s\n"
-
-msgid "cannot parse entry"
-msgstr "não é possível decodificar entrada"
-
-#, python-format
-msgid "node '%s' is not well formed"
-msgstr "nó '%s' não é bem formado"
-
 msgid ""
 ":addbreaks: Any text. Add an XHTML \"<br />\" tag before the end of\n"
 "    every line except the last."
@@ -33004,6 +33094,15 @@
 msgid "number of cpus must be an integer"
 msgstr "o número de cpus deve ser um inteiro"
 
+#~ msgid "%s, line %s: %s\n"
+#~ msgstr "%s, linha %s: %s\n"
+
+#~ msgid "cannot parse entry"
+#~ msgstr "não é possível decodificar entrada"
+
+#~ msgid "node '%s' is not well formed"
+#~ msgstr "nó '%s' não é bem formado"
+
 #~ msgid ""
 #~ "  [blackbox]\n"
 #~ "  track = *"
--- a/mercurial/help/config.txt	Fri Jul 01 13:54:35 2016 +0800
+++ b/mercurial/help/config.txt	Fri Jul 01 16:02:56 2016 -0500
@@ -1559,8 +1559,9 @@
 rewrites ``http://server/foo-hg/`` into ``http://hg.server/foo/``.
 
 Relative subrepository paths are first made absolute, and the
-rewrite rules are then applied on the full (absolute) path. The rules
-are applied in definition order.
+rewrite rules are then applied on the full (absolute) path. If ``pattern``
+doesn't match the full path, an attempt is made to apply it on the
+relative path alone. The rules are applied in definition order.
 
 ``templatealias``
 -----------------
--- a/mercurial/sslutil.py	Fri Jul 01 13:54:35 2016 +0800
+++ b/mercurial/sslutil.py	Fri Jul 01 16:02:56 2016 -0500
@@ -11,6 +11,7 @@
 
 import hashlib
 import os
+import re
 import ssl
 import sys
 
@@ -315,6 +316,57 @@
 
     return sslsocket
 
+class wildcarderror(Exception):
+    """Represents an error parsing wildcards in DNS name."""
+
+def _dnsnamematch(dn, hostname, maxwildcards=1):
+    """Match DNS names according RFC 6125 section 6.4.3.
+
+    This code is effectively copied from CPython's ssl._dnsname_match.
+
+    Returns a bool indicating whether the expected hostname matches
+    the value in ``dn``.
+    """
+    pats = []
+    if not dn:
+        return False
+
+    pieces = dn.split(r'.')
+    leftmost = pieces[0]
+    remainder = pieces[1:]
+    wildcards = leftmost.count('*')
+    if wildcards > maxwildcards:
+        raise wildcarderror(
+            _('too many wildcards in certificate DNS name: %s') % dn)
+
+    # speed up common case w/o wildcards
+    if not wildcards:
+        return dn.lower() == hostname.lower()
+
+    # RFC 6125, section 6.4.3, subitem 1.
+    # The client SHOULD NOT attempt to match a presented identifier in which
+    # the wildcard character comprises a label other than the left-most label.
+    if leftmost == '*':
+        # When '*' is a fragment by itself, it matches a non-empty dotless
+        # fragment.
+        pats.append('[^.]+')
+    elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
+        # RFC 6125, section 6.4.3, subitem 3.
+        # The client SHOULD NOT attempt to match a presented identifier
+        # where the wildcard character is embedded within an A-label or
+        # U-label of an internationalized domain name.
+        pats.append(re.escape(leftmost))
+    else:
+        # Otherwise, '*' matches any dotless string, e.g. www*
+        pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
+
+    # add the remaining fragments, ignore any wildcards
+    for frag in remainder:
+        pats.append(re.escape(frag))
+
+    pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
+    return pat.match(hostname) is not None
+
 def _verifycert(cert, hostname):
     '''Verify that cert (in socket.getpeercert() format) matches hostname.
     CRLs is not handled.
@@ -323,33 +375,46 @@
     '''
     if not cert:
         return _('no certificate received')
-    dnsname = hostname.lower()
-    def matchdnsname(certname):
-        return (certname == dnsname or
-                '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
 
+    dnsnames = []
     san = cert.get('subjectAltName', [])
-    if san:
-        certnames = [value.lower() for key, value in san if key == 'DNS']
-        for name in certnames:
-            if matchdnsname(name):
-                return None
-        if certnames:
-            return _('certificate is for %s') % ', '.join(certnames)
+    for key, value in san:
+        if key == 'DNS':
+            try:
+                if _dnsnamematch(value, hostname):
+                    return
+            except wildcarderror as e:
+                return e.message
+
+            dnsnames.append(value)
 
-    # subject is only checked when subjectAltName is empty
-    for s in cert.get('subject', []):
-        key, value = s[0]
-        if key == 'commonName':
-            try:
-                # 'subject' entries are unicode
-                certname = value.lower().encode('ascii')
-            except UnicodeEncodeError:
-                return _('IDN in certificate not supported')
-            if matchdnsname(certname):
-                return None
-            return _('certificate is for %s') % certname
-    return _('no commonName or subjectAltName found in certificate')
+    if not dnsnames:
+        # The subject is only checked when there is no DNS in subjectAltName.
+        for sub in cert.get('subject', []):
+            for key, value in sub:
+                # According to RFC 2818 the most specific Common Name must
+                # be used.
+                if key == 'commonName':
+                    # 'subject' entries are unicide.
+                    try:
+                        value = value.encode('ascii')
+                    except UnicodeEncodeError:
+                        return _('IDN in certificate not supported')
+
+                    try:
+                        if _dnsnamematch(value, hostname):
+                            return
+                    except wildcarderror as e:
+                        return e.message
+
+                    dnsnames.append(value)
+
+    if len(dnsnames) > 1:
+        return _('certificate is for %s') % ', '.join(dnsnames)
+    elif len(dnsnames) == 1:
+        return _('certificate is for %s') % dnsnames[0]
+    else:
+        return _('no commonName or subjectAltName found in certificate')
 
 def _plainapplepython():
     """return true if this seems to be a pure Apple Python that
--- a/tests/test-url.py	Fri Jul 01 13:54:35 2016 +0800
+++ b/tests/test-url.py	Fri Jul 01 16:02:56 2016 -0500
@@ -1,3 +1,4 @@
+# coding=utf-8
 from __future__ import absolute_import, print_function
 
 import doctest
@@ -50,8 +51,7 @@
 # Avoid some pitfalls
 check(_verifycert(cert('*.foo'), 'foo'),
       'certificate is for *.foo')
-check(_verifycert(cert('*o'), 'foo'),
-      'certificate is for *o')
+check(_verifycert(cert('*o'), 'foo'), None)
 
 check(_verifycert({'subject': ()},
                   'example.com'),
@@ -63,6 +63,177 @@
 check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'),
       'IDN in certificate not supported')
 
+# The following tests are from CPython's test_ssl.py.
+check(_verifycert(cert('example.com'), 'example.com'), None)
+check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None)
+check(_verifycert(cert('example.com'), 'www.example.com'),
+      'certificate is for example.com')
+check(_verifycert(cert('example.com'), '.example.com'),
+      'certificate is for example.com')
+check(_verifycert(cert('example.com'), 'example.org'),
+      'certificate is for example.com')
+check(_verifycert(cert('example.com'), 'exampleXcom'),
+      'certificate is for example.com')
+check(_verifycert(cert('*.a.com'), 'foo.a.com'), None)
+check(_verifycert(cert('*.a.com'), 'bar.foo.a.com'),
+      'certificate is for *.a.com')
+check(_verifycert(cert('*.a.com'), 'a.com'),
+      'certificate is for *.a.com')
+check(_verifycert(cert('*.a.com'), 'Xa.com'),
+      'certificate is for *.a.com')
+check(_verifycert(cert('*.a.com'), '.a.com'),
+      'certificate is for *.a.com')
+
+# only match one left-most wildcard
+check(_verifycert(cert('f*.com'), 'foo.com'), None)
+check(_verifycert(cert('f*.com'), 'f.com'), None)
+check(_verifycert(cert('f*.com'), 'bar.com'),
+      'certificate is for f*.com')
+check(_verifycert(cert('f*.com'), 'foo.a.com'),
+      'certificate is for f*.com')
+check(_verifycert(cert('f*.com'), 'bar.foo.com'),
+      'certificate is for f*.com')
+
+# NULL bytes are bad, CVE-2013-4073
+check(_verifycert(cert('null.python.org\x00example.org'),
+                  'null.python.org\x00example.org'), None)
+check(_verifycert(cert('null.python.org\x00example.org'),
+                  'example.org'),
+      'certificate is for null.python.org\x00example.org')
+check(_verifycert(cert('null.python.org\x00example.org'),
+                  'null.python.org'),
+      'certificate is for null.python.org\x00example.org')
+
+# error cases with wildcards
+check(_verifycert(cert('*.*.a.com'), 'bar.foo.a.com'),
+      'certificate is for *.*.a.com')
+check(_verifycert(cert('*.*.a.com'), 'a.com'),
+      'certificate is for *.*.a.com')
+check(_verifycert(cert('*.*.a.com'), 'Xa.com'),
+      'certificate is for *.*.a.com')
+check(_verifycert(cert('*.*.a.com'), '.a.com'),
+      'certificate is for *.*.a.com')
+
+check(_verifycert(cert('a.*.com'), 'a.foo.com'),
+      'certificate is for a.*.com')
+check(_verifycert(cert('a.*.com'), 'a..com'),
+      'certificate is for a.*.com')
+check(_verifycert(cert('a.*.com'), 'a.com'),
+      'certificate is for a.*.com')
+
+# wildcard doesn't match IDNA prefix 'xn--'
+idna = u'püthon.python.org'.encode('idna').decode('ascii')
+check(_verifycert(cert(idna), idna), None)
+check(_verifycert(cert('x*.python.org'), idna),
+      'certificate is for x*.python.org')
+check(_verifycert(cert('xn--p*.python.org'), idna),
+      'certificate is for xn--p*.python.org')
+
+# wildcard in first fragment and  IDNA A-labels in sequent fragments
+# are supported.
+idna = u'www*.pythön.org'.encode('idna').decode('ascii')
+check(_verifycert(cert(idna),
+                  u'www.pythön.org'.encode('idna').decode('ascii')),
+      None)
+check(_verifycert(cert(idna),
+                  u'www1.pythön.org'.encode('idna').decode('ascii')),
+      None)
+check(_verifycert(cert(idna),
+                  u'ftp.pythön.org'.encode('idna').decode('ascii')),
+      'certificate is for www*.xn--pythn-mua.org')
+check(_verifycert(cert(idna),
+                  u'pythön.org'.encode('idna').decode('ascii')),
+      'certificate is for www*.xn--pythn-mua.org')
+
+c = {
+    'notAfter': 'Jun 26 21:41:46 2011 GMT',
+    'subject': (((u'commonName', u'linuxfrz.org'),),),
+    'subjectAltName': (
+        ('DNS', 'linuxfr.org'),
+        ('DNS', 'linuxfr.com'),
+        ('othername', '<unsupported>'),
+    )
+}
+check(_verifycert(c, 'linuxfr.org'), None)
+check(_verifycert(c, 'linuxfr.com'), None)
+# Not a "DNS" entry
+check(_verifycert(c, '<unsupported>'),
+      'certificate is for linuxfr.org, linuxfr.com')
+# When there is a subjectAltName, commonName isn't used
+check(_verifycert(c, 'linuxfrz.org'),
+      'certificate is for linuxfr.org, linuxfr.com')
+
+# A pristine real-world example
+c = {
+    'notAfter': 'Dec 18 23:59:59 2011 GMT',
+    'subject': (
+        ((u'countryName', u'US'),),
+        ((u'stateOrProvinceName', u'California'),),
+        ((u'localityName', u'Mountain View'),),
+        ((u'organizationName', u'Google Inc'),),
+        ((u'commonName', u'mail.google.com'),),
+    ),
+}
+check(_verifycert(c, 'mail.google.com'), None)
+check(_verifycert(c, 'gmail.com'), 'certificate is for mail.google.com')
+
+# Only commonName is considered
+check(_verifycert(c, 'California'), 'certificate is for mail.google.com')
+
+# Neither commonName nor subjectAltName
+c = {
+    'notAfter': 'Dec 18 23:59:59 2011 GMT',
+    'subject': (
+        ((u'countryName', u'US'),),
+        ((u'stateOrProvinceName', u'California'),),
+        ((u'localityName', u'Mountain View'),),
+        ((u'organizationName', u'Google Inc'),),
+    ),
+}
+check(_verifycert(c, 'mail.google.com'),
+      'no commonName or subjectAltName found in certificate')
+
+# No DNS entry in subjectAltName but a commonName
+c = {
+    'notAfter': 'Dec 18 23:59:59 2099 GMT',
+    'subject': (
+        ((u'countryName', u'US'),),
+        ((u'stateOrProvinceName', u'California'),),
+        ((u'localityName', u'Mountain View'),),
+        ((u'commonName', u'mail.google.com'),),
+    ),
+    'subjectAltName': (('othername', 'blabla'),),
+}
+check(_verifycert(c, 'mail.google.com'), None)
+
+# No DNS entry subjectAltName and no commonName
+c = {
+    'notAfter': 'Dec 18 23:59:59 2099 GMT',
+    'subject': (
+        ((u'countryName', u'US'),),
+        ((u'stateOrProvinceName', u'California'),),
+        ((u'localityName', u'Mountain View'),),
+        ((u'organizationName', u'Google Inc'),),
+    ),
+    'subjectAltName': (('othername', 'blabla'),),
+}
+check(_verifycert(c, 'google.com'),
+      'no commonName or subjectAltName found in certificate')
+
+# Empty cert / no cert
+check(_verifycert(None, 'example.com'), 'no certificate received')
+check(_verifycert({}, 'example.com'), 'no certificate received')
+
+# avoid denials of service by refusing more than one
+# wildcard per fragment.
+check(_verifycert({'subject': (((u'commonName', u'a*b.com'),),)},
+                  'axxb.com'), None)
+check(_verifycert({'subject': (((u'commonName', u'a*b.co*'),),)},
+                  'axxb.com'), 'certificate is for a*b.co*')
+check(_verifycert({'subject': (((u'commonName', u'a*b*.com'),),)},
+                  'axxbxxc.com'),
+      'too many wildcards in certificate DNS name: a*b*.com')
+
 def test_url():
     """
     >>> from mercurial.util import url