Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(5508)

Side by Side Diff: boto/https_connection.py

Issue 4425052: Adds optional support for SSL server certificate validation
Patch Set: Created 13 years, 11 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2007,2011 Google Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15
16 # This file is derived from
17 # http://code.google.com/p/googleappengine/source/browse/trunk/python/google/app engine/tools/https_wrapper.py?r=136
Mike Schwartz 2011/04/19 23:49:13 I get a 404 when I try this URL
18
19
20 """Extensions to allow HTTPS requests with SSL certificate validation."""
21
22
23 import httplib
24 import re
25 import socket
26 import ssl
27
28 import boto
29
30 class InvalidCertificateException(httplib.HTTPException):
Mike Schwartz 2011/04/19 23:49:13 Most of the rest of the boto code puts the excepti
xtof.g 2011/04/20 16:18:38 Well, this one is a subclass of a httplib exceptio
31 """Raised when a certificate is provided with an invalid hostname."""
32
33 def __init__(self, host, cert, reason):
34 """Constructor.
35
36 Args:
37 host: The hostname the connection was made to.
38 cert: The SSL certificate (as a dictionary) the host returned.
39 """
40 httplib.HTTPException.__init__(self)
41 self.host = host
42 self.cert = cert
43 self.reason = reason
44
45 def __str__(self):
46 return ('Host %s returned an invalid certificate (%s): %s' %
47 (self.host, self.reason, self.cert))
48
49 def GetValidHostsForCert(cert):
50 """Returns a list of valid host globs for an SSL certificate.
51
52 Args:
53 cert: A dictionary representing an SSL certificate.
54 Returns:
55 list: A list of valid host globs.
56 """
57 if 'subjectAltName' in cert:
58 return [x[1] for x in cert['subjectAltName'] if x[0].lower() == 'dns']
59 else:
60 return [x[0][1] for x in cert['subject']
61 if x[0][0].lower() == 'commonname']
62
63 def ValidateCertificateHostname(cert, hostname):
64 """Validates that a given hostname is valid for an SSL certificate.
65
66 Args:
67 cert: A dictionary representing an SSL certificate.
68 hostname: The hostname to test.
69 Returns:
70 bool: Whether or not the hostname is valid for this certificate.
71 """
72 hosts = GetValidHostsForCert(cert)
73 boto.log.debug(
74 "validating server certificate: hostname=%s, certificate hosts=%s",
75 hostname, hosts)
76 for host in hosts:
77 host_re = host.replace('.', '\.').replace('*', '[^.]*')
78 if re.search('^%s$' % (host_re,), hostname, re.I):
79 return True
80 return False
81
82
83 class CertValidatingHTTPSConnection(httplib.HTTPConnection):
84 """An HTTPConnection that connects over SSL and validates certificates."""
85
86 default_port = httplib.HTTPS_PORT
87
88 def __init__(self, host, port=None, key_file=None, cert_file=None,
89 ca_certs=None, strict=None, **kwargs):
90 """Constructor.
91
92 Args:
93 host: The hostname. Can be in 'host:port' form.
94 port: The port. Defaults to 443.
95 key_file: A file containing the client's private key
96 cert_file: A file containing the client's certificates
97 ca_certs: A file contianing a set of concatenated certificate authority
98 certs for validating the server against.
99 strict: When true, causes BadStatusLine to be raised if the status line
100 can't be parsed as a valid HTTP/1.0 or 1.1 status line.
101 """
102 httplib.HTTPConnection.__init__(self, host, port, strict, **kwargs)
103 self.key_file = key_file
104 self.cert_file = cert_file
105 self.ca_certs = ca_certs
106
107 def connect(self):
108 "Connect to a host on a given (SSL) port."
109 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
110 sock.connect((self.host, self.port))
111 boto.log.debug("wrapping ssl socket; CA certificate file=%s",·
112 self.ca_certs)
113 self.sock = ssl.wrap_socket(sock, keyfile=self.key_file,
114 certfile=self.cert_file,
115 cert_reqs=ssl.CERT_REQUIRED,
116 ca_certs=self.ca_certs)
117 cert = self.sock.getpeercert()
118 hostname = self.host.split(':', 0)[0]
119 if not ValidateCertificateHostname(cert, hostname):
120 raise InvalidCertificateException(hostname, cert, 'hostname mismatch')
121
122
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b