/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2016-02-28 10:59:18 UTC
  • Revision ID: teddy@recompile.se-20160228105918-tb8pt2p5j0tkcls3
Handle GnuTLS errors and partial sends in gnutls "module".

* mandos (GnuTLS.E_INTERRUPTED, GnuTLS.E_AGAIN): New.
  (GnuTLS.Error): Set error code as "code" attribute.
  (GnuTLS.ClientSession.send): Handle partial sends with a loop.
  (GnuTLS._retry_on_error): New function.
  (GnuTLS.record_send, GnuTLS.handshake, GnuTLS.bye): Set "errcheck"
                                                      attribute to
                                                    "_retry_on_error".
  (ClientHandler.handle): Remove loop for handling partial sends;
                          GnuTLS.ClientSession.send() will do that.

Show diffs side-by-side

added added

removed removed

Lines of Context:
450
450
    
451
451
    # Constants
452
452
    E_SUCCESS = 0
 
453
    E_INTERRUPTED = -52
 
454
    E_AGAIN = -28
453
455
    CRT_OPENPGP = 2
454
456
    CLIENT = 2
455
457
    SHUT_RDWR = 0
483
485
        # We need to use the class name "GnuTLS" here, since this
484
486
        # exception might be raised from within GnuTLS.__init__,
485
487
        # which is called before the assignment to the "gnutls"
486
 
        # global variable happens.
 
488
        # global variable has happened.
487
489
        def __init__(self, message = None, code = None, args=()):
488
490
            # Default usage is by a message string, but if a return
489
491
            # code is passed, convert it to a string with
490
492
            # gnutls.strerror()
 
493
            self.code = code
491
494
            if message is None and code is not None:
492
495
                message = GnuTLS.strerror(code)
493
496
            return super(GnuTLS.Error, self).__init__(
531
534
        
532
535
        def send(self, data):
533
536
            data = bytes(data)
534
 
            if not data:
535
 
                return 0
536
 
            return gnutls.record_send(self._c_object, data, len(data))
 
537
            data_len = len(data)
 
538
            while data_len > 0:
 
539
                data_len -= gnutls.record_send(self._c_object,
 
540
                                               data[-data_len:],
 
541
                                               data_len)
537
542
        
538
543
        def bye(self):
539
544
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
540
545
    
541
 
    # Error handling function
 
546
    # Error handling functions
542
547
    def _error_code(result):
543
548
        """A function to raise exceptions on errors, suitable
544
549
        for the 'restype' attribute on ctypes functions"""
548
553
            raise gnutls.CertificateSecurityError(code = result)
549
554
        raise gnutls.Error(code = result)
550
555
    
 
556
    def _retry_on_error(result, func, arguments):
 
557
        """A function to retry on some errors, suitable
 
558
        for the 'errcheck' attribute on ctypes functions"""
 
559
        while result < 0:
 
560
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
 
561
                return _error_code(result)
 
562
            result = func(*arguments)
 
563
        return result
 
564
    
551
565
    # Unless otherwise indicated, the function declarations below are
552
566
    # all from the gnutls/gnutls.h C header file.
553
567
    
569
583
    record_send.argtypes = [session_t, ctypes.c_void_p,
570
584
                            ctypes.c_size_t]
571
585
    record_send.restype = ctypes.c_ssize_t
 
586
    record_send.errcheck = _retry_on_error
572
587
    
573
588
    certificate_allocate_credentials = (
574
589
        _library.gnutls_certificate_allocate_credentials)
620
635
    handshake = _library.gnutls_handshake
621
636
    handshake.argtypes = [session_t]
622
637
    handshake.restype = _error_code
 
638
    handshake.errcheck = _retry_on_error
623
639
    
624
640
    transport_set_ptr = _library.gnutls_transport_set_ptr
625
641
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
628
644
    bye = _library.gnutls_bye
629
645
    bye.argtypes = [session_t, close_request_t]
630
646
    bye.restype = _error_code
 
647
    bye.errcheck = _retry_on_error
631
648
    
632
649
    check_version = _library.gnutls_check_version
633
650
    check_version.argtypes = [ctypes.c_char_p]
662
679
                                                ctypes.c_size_t)]
663
680
    openpgp_crt_get_fingerprint.restype = _error_code
664
681
    
665
 
    # Remove non-public function
666
 
    del _error_code
 
682
    # Remove non-public functions
 
683
    del _error_code, _retry_on_error
667
684
# Create the global "gnutls" object, simulating a module
668
685
gnutls = GnuTLS()
669
686
 
2215
2232
                    else:
2216
2233
                        delay -= time2 - time
2217
2234
                
2218
 
                sent_size = 0
2219
 
                while sent_size < len(client.secret):
2220
 
                    try:
2221
 
                        sent = session.send(client.secret[sent_size:])
2222
 
                    except gnutls.Error as error:
2223
 
                        logger.warning("gnutls send failed",
2224
 
                                       exc_info=error)
2225
 
                        return
2226
 
                    logger.debug("Sent: %d, remaining: %d", sent,
2227
 
                                 len(client.secret) - (sent_size
2228
 
                                                       + sent))
2229
 
                    sent_size += sent
 
2235
                try:
 
2236
                    session.send(client.secret)
 
2237
                except gnutls.Error as error:
 
2238
                    logger.warning("gnutls send failed",
 
2239
                                   exc_info = error)
 
2240
                    return
2230
2241
                
2231
2242
                logger.info("Sending secret to %s", client.name)
2232
2243
                # bump the timeout using extended_timeout