~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/_urllib2_wrappers.py

  • Committer: Vincent Ladeuil
  • Date: 2016-01-27 13:36:17 UTC
  • mto: This revision was merged to the branch mainline in revision 6614.
  • Revision ID: v.ladeuil+lp@free.fr-20160127133617-gteit32e0nu3938n
Use ssl module for the match_hostname function

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2012 Canonical Ltd
 
1
# Copyright (C) 2006-2013, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
398
398
        self._wrap_socket_for_reporting(self.sock)
399
399
 
400
400
 
401
 
# These two methods were imported from Python 3.2's ssl module
402
 
 
403
 
def _dnsname_to_pat(dn, max_wildcards=1):
404
 
    pats = []
405
 
    for frag in dn.split(r'.'):
406
 
        if frag.count('*') > max_wildcards:
407
 
            # Python Issue #17980: avoid denials of service by refusing more
408
 
            # than one wildcard per fragment.  A survery of established
409
 
            # policy among SSL implementations showed it to be a
410
 
            # reasonable choice.
411
 
            raise ValueError(
412
 
                "too many wildcards in certificate DNS name: " + repr(dn))
413
 
        if frag == '*':
414
 
            # When '*' is a fragment by itself, it matches a non-empty dotless
415
 
            # fragment.
416
 
            pats.append('[^.]+')
417
 
        else:
418
 
            # Otherwise, '*' matches any dotless fragment.
419
 
            frag = re.escape(frag)
420
 
            pats.append(frag.replace(r'\*', '[^.]*'))
421
 
    return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
422
 
 
423
 
 
424
 
def match_hostname(cert, hostname):
425
 
    """Verify that *cert* (in decoded format as returned by
426
 
    SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 rules
427
 
    are mostly followed, but IP addresses are not accepted for *hostname*.
428
 
 
429
 
    CertificateError is raised on failure. On success, the function
430
 
    returns nothing.
431
 
    """
432
 
    if not cert:
433
 
        raise ValueError("empty or no certificate")
434
 
    dnsnames = []
435
 
    san = cert.get('subjectAltName', ())
436
 
    for key, value in san:
437
 
        if key == 'DNS':
438
 
            if _dnsname_to_pat(value).match(hostname):
439
 
                return
440
 
            dnsnames.append(value)
441
 
    if not san:
442
 
        # The subject is only checked when subjectAltName is empty
443
 
        for sub in cert.get('subject', ()):
444
 
            for key, value in sub:
445
 
                # XXX according to RFC 2818, the most specific Common Name
446
 
                # must be used.
447
 
                if key == 'commonName':
448
 
                    if _dnsname_to_pat(value).match(hostname):
449
 
                        return
450
 
                    dnsnames.append(value)
451
 
    if len(dnsnames) > 1:
452
 
        raise errors.CertificateError(
453
 
            "hostname %r doesn't match either of %s"
454
 
            % (hostname, ', '.join(map(repr, dnsnames))))
455
 
    elif len(dnsnames) == 1:
456
 
        raise errors.CertificateError("hostname %r doesn't match %r" %
457
 
                                      (hostname, dnsnames[0]))
458
 
    else:
459
 
        raise errors.CertificateError("no appropriate commonName or "
460
 
            "subjectAltName fields were found")
461
 
 
462
 
 
463
401
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
464
402
 
465
403
    def __init__(self, host, port=None, key_file=None, cert_file=None,
515
453
            raise
516
454
        if cert_reqs == ssl.CERT_REQUIRED:
517
455
            peer_cert = ssl_sock.getpeercert()
518
 
            match_hostname(peer_cert, host)
 
456
            ssl.match_hostname(peer_cert, host)
519
457
 
520
458
        # Wrap the ssl socket before anybody use it
521
459
        self._wrap_socket_for_reporting(ssl_sock)
833
771
                    % (request, request.connection.sock.getsockname())
834
772
            response = connection.getresponse()
835
773
            convert_to_addinfourl = True
836
 
        except (ssl.SSLError, errors.CertificateError):
 
774
        except (ssl.SSLError, ssl.CertificateError):
837
775
            # Something is wrong with either the certificate or the hostname,
838
776
            # re-trying won't help
839
777
            raise