diff --git a/.hgsigs b/.hgsigs --- a/.hgsigs +++ b/.hgsigs @@ -128,3 +128,4 @@ f85de28eae32e7d3064b1a1321309071bbaaa069 a56296f55a5e1038ea5016dace2076b693c28a56 0 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 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -141,3 +141,4 @@ f85de28eae32e7d3064b1a1321309071bbaaa069 a56296f55a5e1038ea5016dace2076b693c28a56 3.8.1 aaabed77791a75968a12b8c43ad263631a23ee81 3.8.2 a9764ab80e11bcf6a37255db7dd079011f767c6c 3.8.3 +26a5d605b8683a292bb89aea11f37a81b06ac016 3.8.4 diff --git a/i18n/pt_BR.po b/i18n/pt_BR.po --- a/i18n/pt_BR.po +++ b/i18n/pt_BR.po @@ -22714,12 +22714,16 @@ msgstr "reescreve ``http://server/foo-hg 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 @@ msgstr "" " 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 \n" +":Organization: Mercurial\n" +":Manual section: 8\n" +":Manual group: Mercurial Manual" +msgstr "" +":Author: Thomas Arendsen Hein \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 " +msgstr "" +"Autor\n" +"\"\"\"\"\"\n" +"Escrito por Matt Mackall " + +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 @@ msgstr "" ":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 @@ msgstr "" "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 @@ msgstr "" "|hgignore(5)|_, |hgrc(5)|_" msgid "" -"Author\n" -"\"\"\"\"\"\"\n" -"Written by Matt Mackall " -msgstr "" -"Autor\n" -"\"\"\"\"\"\n" -"Escrito por Matt Mackall " - -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 @@ msgstr "" 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 \"
\" tag before the end of\n" " every line except the last." @@ -33004,6 +33094,15 @@ msgstr "procurar mudanças remotas" 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 = *" diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt --- a/mercurial/help/config.txt +++ b/mercurial/help/config.txt @@ -1559,8 +1559,9 @@ rewrite it. Groups can be matched in ``p 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`` ----------------- diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py --- a/mercurial/sslutil.py +++ b/mercurial/sslutil.py @@ -11,6 +11,7 @@ from __future__ import absolute_import import hashlib import os +import re import ssl import sys @@ -315,6 +316,57 @@ def wrapsocket(sock, keyfile, certfile, 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 @@ def _verifycert(cert, hostname): ''' 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 diff --git a/tests/test-url.py b/tests/test-url.py --- a/tests/test-url.py +++ b/tests/test-url.py @@ -1,3 +1,4 @@ +# coding=utf-8 from __future__ import absolute_import, print_function import doctest @@ -50,8 +51,7 @@ check(_verifycert(san_cert, 'example.com # 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(None, 'example.com'), 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', ''), + ) +} +check(_verifycert(c, 'linuxfr.org'), None) +check(_verifycert(c, 'linuxfr.com'), None) +# Not a "DNS" entry +check(_verifycert(c, ''), + '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