Package rhn :: Module SSL
[hide private]
[frames] | no frames]

Source Code for Module rhn.SSL

  1  # 
  2  # Higher-level SSL objects used by rpclib 
  3  # 
  4  # Copyright (c) 2002--2020 Red Hat, Inc. 
  5  # 
  6  # Author: Mihai Ibanescu <misa@redhat.com> 
  7  # 
  8  # In addition, as a special exception, the copyright holders give 
  9  # permission to link the code of portions of this program with the 
 10  # OpenSSL library under certain conditions as described in each 
 11  # individual source file, and distribute linked combinations 
 12  # including the two. 
 13  # You must obey the GNU General Public License in all respects 
 14  # for all of the code used other than OpenSSL.  If you modify 
 15  # file(s) with this exception, you may extend this exception to your 
 16  # version of the file(s), but you are not obligated to do so.  If you 
 17  # do not wish to do so, delete this exception statement from your 
 18  # version.  If you delete this exception statement from all source 
 19  # files in the program, then also delete it here. 
 20   
 21   
 22  """ 
 23  rhn.SSL builds an abstraction on top of the objects provided by pyOpenSSL 
 24  """ 
 25   
 26  from OpenSSL import SSL 
 27  # SSL.crypto is provided to other modules 
 28  from OpenSSL import crypto 
 29  import os 
 30   
 31  import socket 
 32  import select 
 33  from rhn.i18n import bstr 
 34  import sys 
 35   
 36  DEFAULT_TIMEOUT = 120 
 37   
 38  if hasattr(socket, 'sslerror'): 
 39      socket_error = socket.sslerror 
 40  else: 
 41      from ssl import socket_error 
 42   
43 -class SSLSocket:
44 """ 45 Class that wraps a pyOpenSSL Connection object, adding more methods 46 """
47 - def __init__(self, socket, trusted_certs=None):
48 # SSL.Context object 49 self._ctx = None 50 # SSL.Connection object 51 self._connection = None 52 self._sock = socket 53 self._trusted_certs = [] 54 # convert None to empty list 55 trusted_certs = trusted_certs or [] 56 for f in trusted_certs: 57 self.add_trusted_cert(f) 58 # SSL method to use 59 self._ssl_method = SSL.SSLv23_METHOD 60 # Flags to pass to the SSL layer 61 self._ssl_verify_flags = SSL.VERIFY_PEER 62 63 # Buffer size for reads 64 self._buffer_size = 8192 65 66 # Position, for tell() 67 self._pos = 0 68 # Buffer 69 self._buffer = bstr("") 70 71 # Flag to show if makefile() was called 72 self._makefile_called = 0 73 74 self._closed = None
75
76 - def add_trusted_cert(self, file):
77 """ 78 Adds a trusted certificate to the certificate store of the SSL context 79 object. 80 """ 81 if not os.access(file, os.R_OK): 82 raise ValueError("Unable to read certificate file %s" % file) 83 self._trusted_certs.append(file.encode("utf-8"))
84
85 - def init_ssl(self, server_name=None):
86 """ 87 Initializes the SSL connection. 88 """ 89 self._check_closed() 90 # Get a context 91 self._ctx = SSL.Context(self._ssl_method) 92 self._ctx.set_options(SSL.OP_NO_SSLv2) 93 self._ctx.set_options(SSL.OP_NO_SSLv3) 94 if self._trusted_certs: 95 # We have been supplied with trusted CA certs 96 for f in self._trusted_certs: 97 self._ctx.load_verify_locations(f) 98 else: 99 # Reset the verify flags 100 self._ssl_verify_flags = 0 101 102 self._ctx.set_verify(self._ssl_verify_flags, ssl_verify_callback) 103 if hasattr(SSL, "OP_DONT_INSERT_EMPTY_FRAGMENTS"): 104 # Certain SSL implementations break when empty fragments are 105 # initially sent (even if sending them is compliant to 106 # SSL 3.0 and TLS 1.0 specs). Play it safe and disable this 107 # feature (openssl 0.9.6e and later) 108 self._ctx.set_options(SSL.OP_DONT_INSERT_EMPTY_FRAGMENTS) 109 110 # Init the connection 111 self._connection = SSL.Connection(self._ctx, self._sock) 112 # Set server name if defined. This allows connections to 113 # SNI-enabled servers 114 if server_name is not None: 115 self._connection.set_tlsext_host_name(server_name.encode("utf8")) 116 # Place the connection in client mode 117 self._connection.set_connect_state()
118
119 - def makefile(self, mode, bufsize=None):
120 """ 121 Returns self, since we are a file-like object already 122 """ 123 if bufsize: 124 self._buffer_size = bufsize 125 126 # Increment the counter with the number of times we've called makefile 127 # - we don't want close to actually close things until all the objects 128 # that originally called makefile() are gone 129 self._makefile_called = self._makefile_called + 1 130 return self
131
132 - def close(self):
133 """ 134 Closes the SSL connection 135 """ 136 # XXX Normally sock.makefile does a dup() on the socket file 137 # descriptor; httplib relies on this, but there is no dup for an ssl 138 # connection; so we have to count how may times makefile() was called 139 if self._closed: 140 # Nothing to do 141 return 142 if not self._makefile_called: 143 self._really_close() 144 return 145 self._makefile_called = self._makefile_called - 1
146 147 # BZ 1464157 - Python 3 http attempts to call this method during close, 148 # at least add it empty
149 - def flush(self):
150 pass
151
152 - def _really_close(self):
153 # No connection was established 154 if self._connection is None: 155 return 156 get_state = None 157 try: 158 get_state = getattr(self._connection, 'state_string') 159 except AttributeError: 160 get_state = getattr(self._connection, 'get_state_string') 161 162 if get_state is not None: 163 # for Python 3 164 if sys.version_info[0] == 3: 165 if get_state() == b'SSL negotiation finished successfully': 166 self._connection.shutdown() 167 # for Python 2 168 else: 169 if get_state() == 'SSL negotiation finished successfully': 170 self._connection.shutdown() 171 172 self._connection.close() 173 self._closed = 1
174
175 - def _check_closed(self):
176 if self._closed: 177 raise ValueError("I/O operation on closed file")
178
179 - def __getattr__(self, name):
180 if hasattr(self._connection, name): 181 return getattr(self._connection, name) 182 raise AttributeError(name)
183 184 # File methods
185 - def isatty(self):
186 """ 187 Returns false always. 188 """ 189 return 0
190
191 - def tell(self):
192 return self._pos
193
194 - def seek(self, pos, mode=0):
195 raise NotImplementedError("seek")
196
197 - def read(self, amt=None):
198 """ 199 Reads up to amt bytes from the SSL connection. 200 """ 201 self._check_closed() 202 # Initially, the buffer size is the default buffer size. 203 # Unfortunately, pending() does not return meaningful data until 204 # recv() is called, so we only adjust the buffer size after the 205 # first read 206 buffer_size = self._buffer_size 207 208 buffer_length = len(self._buffer) 209 # Read only the specified amount of data 210 while buffer_length < amt or amt is None: 211 # if amt is None (read till the end), fills in self._buffer 212 if amt is not None: 213 buffer_size = min(amt - buffer_length, buffer_size) 214 215 try: 216 data = self._connection.recv(buffer_size) 217 218 self._buffer = self._buffer + data 219 buffer_length = len(self._buffer) 220 221 # More bytes to read? 222 pending = self._connection.pending() 223 if pending == 0: 224 # we're done here 225 break 226 except SSL.ZeroReturnError: 227 # Nothing more to be read 228 break 229 except SSL.SysCallError: 230 e = sys.exc_info()[1] 231 print("SSL exception", e.args) 232 break 233 except SSL.WantWriteError: 234 self._poll(select.POLLOUT, 'read') 235 except SSL.WantReadError: 236 self._poll(select.POLLIN, 'read') 237 238 if amt: 239 ret = self._buffer[:amt] 240 self._buffer = self._buffer[amt:] 241 else: 242 ret = self._buffer 243 self._buffer = bstr("") 244 245 self._pos = self._pos + len(ret) 246 return ret
247
248 - def readinto(self, buf):
249 buf[:] = self.read(len(buf)) 250 return len(buf)
251
252 - def _poll(self, filter_type, caller_name):
253 poller = select.poll() 254 poller.register(self._sock, filter_type) 255 res = poller.poll(self._sock.gettimeout() * 1000) 256 if res == []: 257 raise TimeoutException("Connection timed out on %s" % caller_name)
258
259 - def write(self, data):
260 """ 261 Writes to the SSL connection. 262 """ 263 self._check_closed() 264 265 # XXX Should use sendall 266 # sent = self._connection.sendall(data) 267 origlen = len(data) 268 while True: 269 try: 270 sent = self._connection.send(data) 271 if sent == len(data): 272 break 273 data = data[sent:] 274 except SSL.WantWriteError: 275 self._poll(select.POLLOUT, 'write') 276 except SSL.WantReadError: 277 self._poll(select.POLLIN, 'write') 278 279 return origlen
280
281 - def recv(self, amt):
282 return self.read(amt)
283 284 send = write 285 286 sendall = write 287
288 - def readline(self, length=None):
289 """ 290 Reads a single line (up to `length' characters long) from the SSL 291 connection. 292 """ 293 self._check_closed() 294 while True: 295 # charcount contains the number of chars to be outputted (or None 296 # if none to be outputted at this time) 297 charcount = None 298 i = self._buffer.find(bstr('\n')) 299 if i >= 0: 300 # Go one char past newline 301 charcount = i + 1 302 elif length and len(self._buffer) >= length: 303 charcount = length 304 305 if charcount is not None: 306 ret = self._buffer[:charcount] 307 self._buffer = self._buffer[charcount:] 308 self._pos = self._pos + len(ret) 309 return ret 310 311 # Determine the number of chars to be read next 312 bufsize = self._buffer_size 313 if length: 314 # we know length > len(self._buffer) 315 bufsize = min(self._buffer_size, length - len(self._buffer)) 316 317 try: 318 data = self._connection.recv(bufsize) 319 self._buffer = self._buffer + data 320 except SSL.ZeroReturnError: 321 # Nothing more to be read 322 break 323 except SSL.WantWriteError: 324 self._poll(select.POLLOUT, 'readline') 325 except SSL.WantReadError: 326 self._poll(select.POLLIN, 'readline') 327 328 # We got here if we're done reading, so return everything 329 ret = self._buffer 330 self._buffer = "" 331 self._pos = self._pos + len(ret) 332 return ret
333 334
335 -def ssl_verify_callback(conn, cert, errnum, depth, ok):
336 """ 337 Verify callback, which will be called for each certificate in the 338 certificate chain. 339 """ 340 # Nothing by default 341 return ok
342
343 -class TimeoutException(SSL.Error, socket.timeout):
344
345 - def __init__(self, *args):
346 self.args = args
347
348 - def __str__(self):
349 return "Timeout Exception"
350