Revision b14f759e

b/lib/http.py
209 209
    return serializer.LoadJson(data)
210 210

  
211 211

  
212
class _HttpSocketBase(object):
213
  """Base class for HTTP server and client.
214

  
215
  """
216
  def __init__(self):
217
    self._using_ssl = None
218
    self._ssl_cert = None
219
    self._ssl_key = None
220

  
221
  def _CreateSocket(self, ssl_key_path, ssl_cert_path, ssl_verify_peer):
222
    """Creates a TCP socket and initializes SSL if needed.
223

  
224
    @type ssl_key_path: string
225
    @param ssl_key_path: Path to file containing SSL key in PEM format
226
    @type ssl_cert_path: string
227
    @param ssl_cert_path: Path to file containing SSL certificate in PEM format
228
    @type ssl_verify_peer: bool
229
    @param ssl_verify_peer: Whether to require client certificate and compare
230
                            it with our certificate
231

  
232
    """
233
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
234

  
235
    # Should we enable SSL?
236
    self._using_ssl = (ssl_cert_path and ssl_key_path)
237

  
238
    if not self._using_ssl:
239
      return sock
240

  
241
    ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
242
    ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2)
243

  
244
    ssl_key_pem = utils.ReadFile(ssl_key_path)
245
    ssl_cert_pem = utils.ReadFile(ssl_cert_path)
246

  
247
    cr = OpenSSL.crypto
248
    self._ssl_cert = cr.load_certificate(cr.FILETYPE_PEM, ssl_cert_pem)
249
    self._ssl_key = cr.load_privatekey(cr.FILETYPE_PEM, ssl_key_pem)
250
    del cr
251

  
252
    ctx.use_privatekey(self._ssl_key)
253
    ctx.use_certificate(self._ssl_cert)
254
    ctx.check_privatekey()
255

  
256
    if ssl_verify_peer:
257
      ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
258
                     OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
259
                     self._SSLVerifyCallback)
260

  
261
    return OpenSSL.SSL.Connection(ctx, sock)
262

  
263
  def _SSLVerifyCallback(self, conn, cert, errnum, errdepth, ok):
264
    """Verify the certificate provided by the peer
265

  
266
    We only compare fingerprints. The client must use the same certificate as
267
    we do on our side.
268

  
269
    """
270
    assert self._ssl_cert and self._ssl_key, "SSL not initialized"
271

  
272
    return (self._ssl_cert.digest("sha1") == cert.digest("sha1") and
273
            self._ssl_cert.digest("md5") == cert.digest("md5"))
274

  
275

  
212 276
class _HttpConnectionHandler(object):
213 277
  """Implements server side of HTTP
214 278

  
......
487 551
    logging.debug("HTTP POST data: %s", self.request_post_data)
488 552

  
489 553

  
490
class HttpServer(object):
554
class HttpServer(_HttpSocketBase):
491 555
  """Generic HTTP server class
492 556

  
493 557
  Users of this class must subclass it and override the HandleRequest function.
......
514 578
                            it with our certificate
515 579

  
516 580
    """
581
    _HttpSocketBase.__init__(self)
582

  
517 583
    self.mainloop = mainloop
518 584
    self.local_address = local_address
519 585
    self.port = port
520 586

  
521
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
522

  
523
    if ssl_cert_path and ssl_key_path:
524
      ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
525
      ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2)
526

  
527
      ssl_key_pem = utils.ReadFile(ssl_key_path)
528
      ssl_cert_pem = utils.ReadFile(ssl_cert_path)
529

  
530
      cr = OpenSSL.crypto
531
      self._ssl_cert = cr.load_certificate(cr.FILETYPE_PEM, ssl_cert_pem)
532
      self._ssl_key = cr.load_privatekey(cr.FILETYPE_PEM, ssl_key_pem)
533
      del cr
534

  
535
      ctx.use_privatekey(self._ssl_key)
536
      ctx.use_certificate(self._ssl_cert)
537
      ctx.check_privatekey()
587
    self.socket = self._CreateSocket(ssl_key_path, ssl_cert_path, ssl_verify_peer)
538 588

  
539
      if ssl_verify_peer:
540
        ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
541
                       OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
542
                       self._VerifyCallback)
589
    # Allow port to be reused
590
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
543 591

  
544
      self.socket = OpenSSL.SSL.Connection(ctx, sock)
592
    if self._using_ssl:
545 593
      self._fileio_class = _SSLFileObject
546 594
    else:
547
      self.socket = sock
548 595
      self._fileio_class = socket._fileobject
549 596

  
550
    # Allow port to be reused
551
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
552

  
553 597
    self._children = []
554 598

  
555 599
    mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN)
556 600
    mainloop.RegisterSignal(self)
557 601

  
558
  def _VerifyCallback(self, conn, cert, errno, errdepth, ok):
559
    """Verify the certificate provided by the peer
560

  
561
    We only compare fingerprints. The client must use the same certificate as
562
    we do on the server side.
563

  
564
    """
565
    return (self._ssl_cert.digest("sha1") == cert.digest("sha1") and
566
            self._ssl_cert.digest("md5") == cert.digest("md5"))
567

  
568 602
  def Start(self):
569 603
    self.socket.bind((self.local_address, self.port))
570 604
    self.socket.listen(5)

Also available in: Unified diff