ssl.py 23.5 KB
Newer Older
1 2 3
# Wrapper module for _ssl, providing some additional facilities
# implemented in Python.  Written by Bill Janssen.

4
"""This module provides some more Pythonic support for SSL.
5 6 7

Object types:

8
  SSLSocket -- subtype of socket.socket which does SSL over the socket
9 10 11

Exceptions:

12
  SSLError -- exception raised for I/O errors
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

Functions:

  cert_time_to_seconds -- convert time string used for certificate
                          notBefore and notAfter functions to integer
                          seconds past the Epoch (the time values
                          returned from time.time())

  fetch_server_certificate (HOST, PORT) -- fetch the certificate provided
                          by the server running on HOST at port PORT.  No
                          validation of the certificate is performed.

Integer constants:

SSL_ERROR_ZERO_RETURN
SSL_ERROR_WANT_READ
SSL_ERROR_WANT_WRITE
SSL_ERROR_WANT_X509_LOOKUP
SSL_ERROR_SYSCALL
SSL_ERROR_SSL
SSL_ERROR_WANT_CONNECT

SSL_ERROR_EOF
SSL_ERROR_INVALID_ERROR_CODE

The following group define certificate requirements that one side is
allowing/requiring from the other side:

CERT_NONE - no certificates from the other side are required (or will
            be looked at if provided)
CERT_OPTIONAL - certificates are not required, but if provided will be
                validated, and if validation fails, the connection will
                also fail
CERT_REQUIRED - certificates are required, and will be validated, and
                if validation fails, the connection will also fail

The following constants identify various SSL protocol variants:

PROTOCOL_SSLv2
PROTOCOL_SSLv3
PROTOCOL_SSLv23
PROTOCOL_TLSv1
"""

57
import textwrap
58
import re
59 60

import _ssl             # if we can't import it, let the error propagate
61

62
from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
63 64 65 66 67
from _ssl import _SSLContext
from _ssl import (
    SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
    SSLSyscallError, SSLEOFError,
    )
68
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
69 70
from _ssl import (
    OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1,
71
    OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE
72
    )
73 74 75 76
try:
    from _ssl import OP_NO_COMPRESSION
except ImportError:
    pass
77 78 79 80
try:
    from _ssl import OP_SINGLE_ECDH_USE
except ImportError:
    pass
81
from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
82 83 84 85 86 87 88 89 90 91 92
from _ssl import (
    SSL_ERROR_ZERO_RETURN,
    SSL_ERROR_WANT_READ,
    SSL_ERROR_WANT_WRITE,
    SSL_ERROR_WANT_X509_LOOKUP,
    SSL_ERROR_SYSCALL,
    SSL_ERROR_SSL,
    SSL_ERROR_WANT_CONNECT,
    SSL_ERROR_EOF,
    SSL_ERROR_INVALID_ERROR_CODE,
    )
93
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN
94 95
from _ssl import (PROTOCOL_SSLv3, PROTOCOL_SSLv23,
                  PROTOCOL_TLSv1)
96 97
from _ssl import _OPENSSL_API_VERSION

98 99 100 101 102 103 104
_PROTOCOL_NAMES = {
    PROTOCOL_TLSv1: "TLSv1",
    PROTOCOL_SSLv23: "SSLv23",
    PROTOCOL_SSLv3: "SSLv3",
}
try:
    from _ssl import PROTOCOL_SSLv2
105
    _SSLv2_IF_EXISTS = PROTOCOL_SSLv2
106
except ImportError:
107
    _SSLv2_IF_EXISTS = None
108 109
else:
    _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2"
110 111

from socket import getnameinfo as _getnameinfo
112
from socket import error as socket_error
113
from socket import socket, AF_INET, SOCK_STREAM, create_connection
114
import base64        # for DER-to-PEM translation
115
import traceback
116
import errno
117

118 119 120 121
if _ssl.HAS_TLS_UNIQUE:
    CHANNEL_BINDING_TYPES = ['tls-unique']
else:
    CHANNEL_BINDING_TYPES = []
122

123 124 125 126
# Disable weak or insecure ciphers by default
# (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')
_DEFAULT_CIPHERS = 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2'

127

128 129 130 131
class CertificateError(ValueError):
    pass


132 133 134 135 136
def _dnsname_match(dn, hostname, max_wildcards=1):
    """Matching according to RFC 6125, section 6.4.3

    http://tools.ietf.org/html/rfc6125#section-6.4.3
    """
137
    pats = []
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    if not dn:
        return False

    leftmost, *remainder = dn.split(r'.')

    wildcards = leftmost.count('*')
    if wildcards > max_wildcards:
        # Issue #17980: avoid denials of service by refusing more
        # than one wildcard per fragment.  A survery of established
        # policy among SSL implementations showed it to be a
        # reasonable choice.
        raise CertificateError(
            "too many wildcards in certificate DNS name: " + repr(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)
179 180 181 182


def match_hostname(cert, hostname):
    """Verify that *cert* (in decoded format as returned by
183 184
    SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 and RFC 6125
    rules are followed, but IP addresses are not accepted for *hostname*.
185 186 187 188 189 190 191 192 193 194

    CertificateError is raised on failure. On success, the function
    returns nothing.
    """
    if not cert:
        raise ValueError("empty or no certificate")
    dnsnames = []
    san = cert.get('subjectAltName', ())
    for key, value in san:
        if key == 'DNS':
195
            if _dnsname_match(value, hostname):
196 197
                return
            dnsnames.append(value)
198 199 200
    if not dnsnames:
        # The subject is only checked when there is no dNSName entry
        # in subjectAltName
201 202 203 204 205
        for sub in cert.get('subject', ()):
            for key, value in sub:
                # XXX according to RFC 2818, the most specific Common Name
                # must be used.
                if key == 'commonName':
206
                    if _dnsname_match(value, hostname):
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
                        return
                    dnsnames.append(value)
    if len(dnsnames) > 1:
        raise CertificateError("hostname %r "
            "doesn't match either of %s"
            % (hostname, ', '.join(map(repr, dnsnames))))
    elif len(dnsnames) == 1:
        raise CertificateError("hostname %r "
            "doesn't match %r"
            % (hostname, dnsnames[0]))
    else:
        raise CertificateError("no appropriate commonName or "
            "subjectAltName fields were found")


222 223 224 225 226 227 228
class SSLContext(_SSLContext):
    """An SSLContext holds various SSL-related configuration options and
    data, such as certificates and possibly a private key."""

    __slots__ = ('protocol',)

    def __new__(cls, protocol, *args, **kwargs):
229 230 231 232
        self = _SSLContext.__new__(cls, protocol)
        if protocol != _SSLv2_IF_EXISTS:
            self.set_ciphers(_DEFAULT_CIPHERS)
        return self
233 234 235 236 237 238

    def __init__(self, protocol):
        self.protocol = protocol

    def wrap_socket(self, sock, server_side=False,
                    do_handshake_on_connect=True,
239 240
                    suppress_ragged_eofs=True,
                    server_hostname=None):
241 242 243
        return SSLSocket(sock=sock, server_side=server_side,
                         do_handshake_on_connect=do_handshake_on_connect,
                         suppress_ragged_eofs=suppress_ragged_eofs,
244
                         server_hostname=server_hostname,
245 246
                         _context=self)

247 248 249 250 251 252 253 254 255 256 257
    def set_npn_protocols(self, npn_protocols):
        protos = bytearray()
        for protocol in npn_protocols:
            b = bytes(protocol, 'ascii')
            if len(b) == 0 or len(b) > 255:
                raise SSLError('NPN protocols must be 1 to 255 in length')
            protos.append(len(b))
            protos.extend(b)

        self._set_npn_protocols(protos)

258 259

class SSLSocket(socket):
260 261 262 263
    """This class implements a subtype of socket.socket that wraps
    the underlying OS socket in an SSL context when necessary, and
    provides read and write methods over that channel."""

264
    def __init__(self, sock=None, keyfile=None, certfile=None,
265
                 server_side=False, cert_reqs=CERT_NONE,
266 267 268
                 ssl_version=PROTOCOL_SSLv23, ca_certs=None,
                 do_handshake_on_connect=True,
                 family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None,
269
                 suppress_ragged_eofs=True, npn_protocols=None, ciphers=None,
270
                 server_hostname=None,
271
                 _context=None):
272

273 274 275
        if _context:
            self.context = _context
        else:
276 277 278
            if server_side and not certfile:
                raise ValueError("certfile must be specified for server-side "
                                 "operations")
279 280
            if keyfile and not certfile:
                raise ValueError("certfile must be specified")
281 282 283 284 285 286 287 288
            if certfile and not keyfile:
                keyfile = certfile
            self.context = SSLContext(ssl_version)
            self.context.verify_mode = cert_reqs
            if ca_certs:
                self.context.load_verify_locations(ca_certs)
            if certfile:
                self.context.load_cert_chain(certfile, keyfile)
289 290
            if npn_protocols:
                self.context.set_npn_protocols(npn_protocols)
291 292 293 294 295 296 297 298
            if ciphers:
                self.context.set_ciphers(ciphers)
            self.keyfile = keyfile
            self.certfile = certfile
            self.cert_reqs = cert_reqs
            self.ssl_version = ssl_version
            self.ca_certs = ca_certs
            self.ciphers = ciphers
299 300 301
        if server_side and server_hostname:
            raise ValueError("server_hostname can only be specified "
                             "in client mode")
302
        self.server_side = server_side
303
        self.server_hostname = server_hostname
304 305
        self.do_handshake_on_connect = do_handshake_on_connect
        self.suppress_ragged_eofs = suppress_ragged_eofs
306
        connected = False
307
        if sock is not None:
308 309 310 311
            socket.__init__(self,
                            family=sock.family,
                            type=sock.type,
                            proto=sock.proto,
312
                            fileno=sock.fileno())
313
            self.settimeout(sock.gettimeout())
314 315 316 317 318 319 320 321
            # see if it's connected
            try:
                sock.getpeername()
            except socket_error as e:
                if e.errno != errno.ENOTCONN:
                    raise
            else:
                connected = True
322
            sock.detach()
323 324 325 326 327
        elif fileno is not None:
            socket.__init__(self, fileno=fileno)
        else:
            socket.__init__(self, family=family, type=type, proto=proto)

328 329
        self._closed = False
        self._sslobj = None
330
        self._connected = connected
331 332
        if connected:
            # create the SSL object
333
            try:
334 335
                self._sslobj = self.context._wrap_socket(self, server_side,
                                                         server_hostname)
336
                if do_handshake_on_connect:
337 338 339 340
                    timeout = self.gettimeout()
                    if timeout == 0.0:
                        # non-blocking
                        raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
341
                    self.do_handshake()
342

343 344 345 346
            except socket_error as x:
                self.close()
                raise x

347 348 349 350
    def dup(self):
        raise NotImplemented("Can't dup() %s instances" %
                             self.__class__.__name__)

351 352 353
    def _checkClosed(self, msg=None):
        # raise an exception here if you wish to check for spurious closes
        pass
354

355
    def read(self, len=0, buffer=None):
356 357 358
        """Read up to LEN bytes and return them.
        Return zero-length string on EOF."""

359 360
        self._checkClosed()
        try:
361 362
            if buffer is not None:
                v = self._sslobj.read(len, buffer)
363
            else:
364 365
                v = self._sslobj.read(len or 1024)
            return v
366 367
        except SSLError as x:
            if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:
368
                if buffer is not None:
369 370 371
                    return 0
                else:
                    return b''
372 373
            else:
                raise
374 375

    def write(self, data):
376 377 378
        """Write DATA to the underlying SSL channel.  Returns
        number of bytes of DATA actually transmitted."""

379
        self._checkClosed()
380 381
        return self._sslobj.write(data)

382
    def getpeercert(self, binary_form=False):
383 384 385 386 387
        """Returns a formatted version of the data in the
        certificate provided by the other end of the SSL channel.
        Return None if no certificate was provided, {} if a
        certificate was provided, but not validated."""

388
        self._checkClosed()
389 390
        return self._sslobj.peer_certificate(binary_form)

391 392 393 394 395 396 397
    def selected_npn_protocol(self):
        self._checkClosed()
        if not self._sslobj or not _ssl.HAS_NPN:
            return None
        else:
            return self._sslobj.selected_npn_protocol()

398
    def cipher(self):
399
        self._checkClosed()
400 401 402 403
        if not self._sslobj:
            return None
        else:
            return self._sslobj.cipher()
404

405 406 407 408 409 410 411
    def compression(self):
        self._checkClosed()
        if not self._sslobj:
            return None
        else:
            return self._sslobj.compression()

412
    def send(self, data, flags=0):
413
        self._checkClosed()
414 415 416 417 418
        if self._sslobj:
            if flags != 0:
                raise ValueError(
                    "non-zero flags not allowed in calls to send() on %s" %
                    self.__class__)
419 420 421 422 423 424 425 426 427 428 429 430
            while True:
                try:
                    v = self._sslobj.write(data)
                except SSLError as x:
                    if x.args[0] == SSL_ERROR_WANT_READ:
                        return 0
                    elif x.args[0] == SSL_ERROR_WANT_WRITE:
                        return 0
                    else:
                        raise
                else:
                    return v
431 432
        else:
            return socket.send(self, data, flags)
433

434
    def sendto(self, data, flags_or_addr, addr=None):
435
        self._checkClosed()
436
        if self._sslobj:
437
            raise ValueError("sendto not allowed on instances of %s" %
438
                             self.__class__)
439 440
        elif addr is None:
            return socket.sendto(self, data, flags_or_addr)
441
        else:
442
            return socket.sendto(self, data, flags_or_addr, addr)
443

444 445 446 447 448 449
    def sendmsg(self, *args, **kwargs):
        # Ensure programs don't send data unencrypted if they try to
        # use this method.
        raise NotImplementedError("sendmsg not allowed on instances of %s" %
                                  self.__class__)

450
    def sendall(self, data, flags=0):
451
        self._checkClosed()
452
        if self._sslobj:
453 454 455 456
            if flags != 0:
                raise ValueError(
                    "non-zero flags not allowed in calls to sendall() on %s" %
                    self.__class__)
457 458 459 460 461 462
            amount = len(data)
            count = 0
            while (count < amount):
                v = self.send(data[count:])
                count += v
            return amount
463 464
        else:
            return socket.sendall(self, data, flags)
465

466
    def recv(self, buflen=1024, flags=0):
467
        self._checkClosed()
468 469 470
        if self._sslobj:
            if flags != 0:
                raise ValueError(
471 472 473
                    "non-zero flags not allowed in calls to recv() on %s" %
                    self.__class__)
            return self.read(buflen)
474 475
        else:
            return socket.recv(self, buflen, flags)
476

477
    def recv_into(self, buffer, nbytes=None, flags=0):
478 479 480 481 482 483 484 485
        self._checkClosed()
        if buffer and (nbytes is None):
            nbytes = len(buffer)
        elif nbytes is None:
            nbytes = 1024
        if self._sslobj:
            if flags != 0:
                raise ValueError(
486 487
                  "non-zero flags not allowed in calls to recv_into() on %s" %
                  self.__class__)
488
            return self.read(nbytes, buffer)
489 490 491
        else:
            return socket.recv_into(self, buffer, nbytes, flags)

492
    def recvfrom(self, buflen=1024, flags=0):
493
        self._checkClosed()
494
        if self._sslobj:
495
            raise ValueError("recvfrom not allowed on instances of %s" %
496 497
                             self.__class__)
        else:
498
            return socket.recvfrom(self, buflen, flags)
499

500 501 502 503 504 505 506 507
    def recvfrom_into(self, buffer, nbytes=None, flags=0):
        self._checkClosed()
        if self._sslobj:
            raise ValueError("recvfrom_into not allowed on instances of %s" %
                             self.__class__)
        else:
            return socket.recvfrom_into(self, buffer, nbytes, flags)

508 509 510 511 512 513 514 515
    def recvmsg(self, *args, **kwargs):
        raise NotImplementedError("recvmsg not allowed on instances of %s" %
                                  self.__class__)

    def recvmsg_into(self, *args, **kwargs):
        raise NotImplementedError("recvmsg_into not allowed on instances of "
                                  "%s" % self.__class__)

516
    def pending(self):
517 518 519 520 521 522
        self._checkClosed()
        if self._sslobj:
            return self._sslobj.pending()
        else:
            return 0

523
    def shutdown(self, how):
524
        self._checkClosed()
525
        self._sslobj = None
526
        socket.shutdown(self, how)
527

528
    def unwrap(self):
529 530 531 532 533 534 535
        if self._sslobj:
            s = self._sslobj.shutdown()
            self._sslobj = None
            return s
        else:
            raise ValueError("No SSL wrapper around " + str(self))

536
    def _real_close(self):
537
        self._sslobj = None
538
        # self._closed = True
539
        socket._real_close(self)
540

541
    def do_handshake(self, block=False):
542 543
        """Perform a TLS/SSL handshake."""

544
        timeout = self.gettimeout()
545
        try:
546 547
            if timeout == 0.0 and block:
                self.settimeout(None)
548
            self._sslobj.do_handshake()
549 550
        finally:
            self.settimeout(timeout)
551

552
    def _real_connect(self, addr, connect_ex):
553 554
        if self.server_side:
            raise ValueError("can't connect in server-side mode")
555 556
        # Here we assume that the socket is client-side, and not
        # connected at the time of the call.  We connect it, then wrap it.
557
        if self._connected:
558
            raise ValueError("attempt to connect already-connected SSLSocket!")
559
        self._sslobj = self.context._wrap_socket(self, False, self.server_hostname)
560
        try:
561 562
            if connect_ex:
                rc = socket.connect_ex(self, addr)
563
            else:
564 565 566 567 568 569 570 571 572 573
                rc = None
                socket.connect(self, addr)
            if not rc:
                if self.do_handshake_on_connect:
                    self.do_handshake()
                self._connected = True
            return rc
        except socket_error:
            self._sslobj = None
            raise
574 575 576 577 578 579 580 581 582 583

    def connect(self, addr):
        """Connects to remote ADDR, and then wraps the connection in
        an SSL channel."""
        self._real_connect(addr, False)

    def connect_ex(self, addr):
        """Connects to remote ADDR, and then wraps the connection in
        an SSL channel."""
        return self._real_connect(addr, True)
584 585

    def accept(self):
586 587 588 589 590
        """Accepts a new connection from a remote client, and returns
        a tuple containing that new connection wrapped with a server-side
        SSL channel, and the address of the remote client."""

        newsock, addr = socket.accept(self)
591 592 593 594 595
        newsock = self.context.wrap_socket(newsock,
                    do_handshake_on_connect=self.do_handshake_on_connect,
                    suppress_ragged_eofs=self.suppress_ragged_eofs,
                    server_side=True)
        return newsock, addr
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
    def get_channel_binding(self, cb_type="tls-unique"):
        """Get channel binding data for current connection.  Raise ValueError
        if the requested `cb_type` is not supported.  Return bytes of the data
        or None if the data is not available (e.g. before the handshake).
        """
        if cb_type not in CHANNEL_BINDING_TYPES:
            raise ValueError("Unsupported channel binding type")
        if cb_type != "tls-unique":
            raise NotImplementedError(
                            "{0} channel binding type not implemented"
                            .format(cb_type))
        if self._sslobj is None:
            return None
        return self._sslobj.tls_unique_cb()

612

613 614
def wrap_socket(sock, keyfile=None, certfile=None,
                server_side=False, cert_reqs=CERT_NONE,
615
                ssl_version=PROTOCOL_SSLv23, ca_certs=None,
616
                do_handshake_on_connect=True,
617 618
                suppress_ragged_eofs=True,
                ciphers=None):
619

620
    return SSLSocket(sock=sock, keyfile=keyfile, certfile=certfile,
621
                     server_side=server_side, cert_reqs=cert_reqs,
622
                     ssl_version=ssl_version, ca_certs=ca_certs,
623
                     do_handshake_on_connect=do_handshake_on_connect,
624 625
                     suppress_ragged_eofs=suppress_ragged_eofs,
                     ciphers=ciphers)
626

627 628 629
# some utility functions

def cert_time_to_seconds(cert_time):
630 631 632 633
    """Takes a date-time string in standard ASN1_print form
    ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return
    a Python time value in seconds past the epoch."""

634 635 636
    import time
    return time.mktime(time.strptime(cert_time, "%b %d %H:%M:%S %Y GMT"))

637 638 639 640 641 642 643
PEM_HEADER = "-----BEGIN CERTIFICATE-----"
PEM_FOOTER = "-----END CERTIFICATE-----"

def DER_cert_to_PEM_cert(der_cert_bytes):
    """Takes a certificate in binary DER format and returns the
    PEM version of it as a string."""

644 645 646 647
    f = str(base64.standard_b64encode(der_cert_bytes), 'ASCII', 'strict')
    return (PEM_HEADER + '\n' +
            textwrap.fill(f, 64) + '\n' +
            PEM_FOOTER + '\n')
648 649 650 651 652 653 654 655 656 657 658 659

def PEM_cert_to_DER_cert(pem_cert_string):
    """Takes a certificate in ASCII PEM format and returns the
    DER-encoded version of it as a byte sequence"""

    if not pem_cert_string.startswith(PEM_HEADER):
        raise ValueError("Invalid PEM encoding; must start with %s"
                         % PEM_HEADER)
    if not pem_cert_string.strip().endswith(PEM_FOOTER):
        raise ValueError("Invalid PEM encoding; must end with %s"
                         % PEM_FOOTER)
    d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
660
    return base64.decodebytes(d.encode('ASCII', 'strict'))
661

662
def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None):
663 664 665 666 667 668 669 670 671 672
    """Retrieve the certificate from the server at the specified address,
    and return it as a PEM-encoded string.
    If 'ca_certs' is specified, validate the server cert against it.
    If 'ssl_version' is specified, use it in the connection attempt."""

    host, port = addr
    if (ca_certs is not None):
        cert_reqs = CERT_REQUIRED
    else:
        cert_reqs = CERT_NONE
673 674
    s = create_connection(addr)
    s = wrap_socket(s, ssl_version=ssl_version,
675 676 677 678 679
                    cert_reqs=cert_reqs, ca_certs=ca_certs)
    dercert = s.getpeercert(True)
    s.close()
    return DER_cert_to_PEM_cert(dercert)

680
def get_protocol_name(protocol_code):
681
    return _PROTOCOL_NAMES.get(protocol_code, '<unknown>')