예제 #1
0
    def write(self, data):
        """Write data to connection

        Write data as string of bytes.

        Arguments:
        data -- buffer containing data to be written

        Return value:
        number of bytes actually transmitted
        """
        #Time test by johann
        try:
            tInicio = time.time()
            ret = self._wrap_socket_library_call(
                lambda: SSL_write(self._ssl.value, data), ERR_WRITE_TIMEOUT)
            tFim = time.time()
            if globalvars.handshakeDone:
                globalvars.tSSLWrite += tFim - tInicio
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
                raise_ssl_error(ERR_PORT_UNREACHABLE, err)
            raise
        if ret:
            self._handshake_done = True
        return ret
예제 #2
0
    def shutdown(self):
        """Shut down the DTLS connection

        This method attemps to complete a bidirectional shutdown between
        peers. For non-blocking sockets, it should be called repeatedly until
        it no longer raises continuation request exceptions.
        """

        if hasattr(self, "_listening"):
            # Listening server-side sockets cannot be shut down
            return

        try:
            self._wrap_socket_library_call(
                lambda: SSL_shutdown(self._ssl.value), ERR_READ_TIMEOUT)
        except openssl_error() as err:
            if err.result == 0:
                # close-notify alert was just sent; wait for same from peer
                # Note: while it might seem wise to suppress further read-aheads
                # with SSL_set_read_ahead here, doing so causes a shutdown
                # failure (ret: -1, SSL_ERROR_SYSCALL) on the DTLS shutdown
                # initiator side. And test_starttls does pass.
                self._wrap_socket_library_call(
                    lambda: SSL_shutdown(self._ssl.value), ERR_READ_TIMEOUT)
            else:
                raise
        if hasattr(self, "_rsock"):
            # Return wrapped connected server socket (non-listening)
            return _UnwrappedSocket(self._sock, self._rsock, self._udp_demux,
                                    self._ctx,
                                    BIO_dgram_get_peer(self._wbio.value))
        # Return unwrapped client-side socket or unwrapped server-side socket
        # for single-socket servers
        return self._sock
예제 #3
0
    def shutdown(self):
        """Shut down the DTLS connection

        This method attemps to complete a bidirectional shutdown between
        peers. For non-blocking sockets, it should be called repeatedly until
        it no longer raises continuation request exceptions.
        """

        if hasattr(self, "_listening"):
            # Listening server-side sockets cannot be shut down
            return

        try:
            self._wrap_socket_library_call(
                lambda: SSL_shutdown(self._ssl.value), ERR_READ_TIMEOUT)
        except openssl_error() as err:
            if err.result == 0:
                # close-notify alert was just sent; wait for same from peer
                # Note: while it might seem wise to suppress further read-aheads
                # with SSL_set_read_ahead here, doing so causes a shutdown
                # failure (ret: -1, SSL_ERROR_SYSCALL) on the DTLS shutdown
                # initiator side. And test_starttls does pass.
                self._wrap_socket_library_call(
                    lambda: SSL_shutdown(self._ssl.value), ERR_READ_TIMEOUT)
            else:
                raise
        if hasattr(self, "_rsock"):
            # Return wrapped connected server socket (non-listening)
            return _UnwrappedSocket(self._sock, self._rsock, self._udp_demux,
                                    self._ctx,
                                    BIO_dgram_get_peer(self._wbio.value))
        # Return unwrapped client-side socket or unwrapped server-side socket
        # for single-socket servers
        return self._sock
예제 #4
0
    def getpeercertchain(self, binary_form=False):
        try:
            stack, num, certs = SSL_get_peer_cert_chain(self._ssl.value)
        except openssl_error():
            return

        peer_cert_chain = [_Rsrc(cert) for cert in certs]
        ret = []
        if binary_form:
            ret = [i2d_X509(x.value) for x in peer_cert_chain]
        elif len(peer_cert_chain):
            ret = [decode_cert(x) for x in peer_cert_chain]

        return ret
예제 #5
0
    def getpeercertchain(self, binary_form=False):
        try:
            stack, num, certs = SSL_get_peer_cert_chain(self._ssl.value)
        except openssl_error():
            return

        peer_cert_chain = [_Rsrc(cert) for cert in certs]
        ret = []
        if binary_form:
            ret = [i2d_X509(x.value) for x in peer_cert_chain]
        elif len(peer_cert_chain):
            ret = [decode_cert(x) for x in peer_cert_chain]

        return ret
예제 #6
0
파일: openssl.py 프로젝트: aburan28/pydtls
def raise_ssl_error(result, func, args, ssl):
    if not ssl:
        ssl_error = SSL_ERROR_NONE
    else:
        ssl_error = _SSL_get_error(ssl, result)
    errqueue = []
    while True:
        err = _ERR_get_error()
        if not err:
            break
        buf = create_string_buffer(512)
        _ERR_error_string_n(err, buf, sizeof(buf))
        errqueue.append((err, buf.value))
    _logger.debug("SSL error raised: ssl_error: %d, result: %d, " +
                  "errqueue: %s, func_name: %s",
                  ssl_error, result, errqueue, func.func_name)
    raise openssl_error()(ssl_error, errqueue, result, func, args)
예제 #7
0
def raise_ssl_error(result, func, args, ssl):
    if not ssl:
        ssl_error = SSL_ERROR_NONE
    else:
        ssl_error = _SSL_get_error(ssl, result)
    errqueue = []
    while True:
        err = _ERR_get_error()
        if not err:
            break
        buf = create_string_buffer(512)
        _ERR_error_string_n(err, buf, sizeof(buf))
        errqueue.append((err, buf.value))
    _logger.debug("SSL error raised: ssl_error: %d, result: %d, " +
                  "errqueue: %s, func_name: %s",
                  ssl_error, result, errqueue, func.func_name)
    raise openssl_error()(ssl_error, errqueue, result, func, args)
예제 #8
0
 def _config_ssl_ctx(self, verify_mode):
     SSL_CTX_set_verify(self._ctx.value, verify_mode)
     SSL_CTX_set_read_ahead(self._ctx.value, 1)
     # Compression occurs at the stream layer now, leading to datagram
     # corruption when packet loss occurs
     SSL_CTX_set_options(self._ctx.value, SSL_OP_NO_COMPRESSION)
     if self._certfile:
         SSL_CTX_use_certificate_chain_file(self._ctx.value, self._certfile)
     if self._keyfile:
         SSL_CTX_use_PrivateKey_file(self._ctx.value, self._keyfile,
                                     SSL_FILE_TYPE_PEM)
     if self._ca_certs:
         SSL_CTX_load_verify_locations(self._ctx.value, self._ca_certs, None)
     if self._ciphers:
         try:
             SSL_CTX_set_cipher_list(self._ctx.value, self._ciphers)
         except openssl_error() as err:
             raise_ssl_error(ERR_NO_CIPHER, err)
예제 #9
0
    def do_handshake(self):
        """Perform a handshake with the peer

        This method forces an explicit handshake to be performed with either
        the client or server peer.
        """

        _logger.debug("Initiating handshake...")
        try:
            self._wrap_socket_library_call(
                lambda: SSL_do_handshake(self._ssl.value),
                ERR_HANDSHAKE_TIMEOUT)
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
                raise_ssl_error(ERR_PORT_UNREACHABLE, err)
            raise
        self._handshake_done = True
        _logger.debug("...completed handshake")
예제 #10
0
    def do_handshake(self):
        """Perform a handshake with the peer

        This method forces an explicit handshake to be performed with either
        the client or server peer.
        """

        _logger.debug("Initiating handshake...")
        try:
            self._wrap_socket_library_call(
                lambda: SSL_do_handshake(self._ssl.value),
                ERR_HANDSHAKE_TIMEOUT)
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
                raise_ssl_error(ERR_PORT_UNREACHABLE, err)
            raise
        self._handshake_done = True
        _logger.debug("...completed handshake")
예제 #11
0
    def read(self, len=1024, buffer=None):
        """Read data from connection

        Read up to len bytes and return them.
        Arguments:
        len -- maximum number of bytes to read

        Return value:
        string containing read bytes
        """

        try:
            return self._wrap_socket_library_call(
                lambda: SSL_read(self._ssl.value, len, buffer), ERR_READ_TIMEOUT)
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
                raise_ssl_error(ERR_PORT_UNREACHABLE, err)
            raise
예제 #12
0
 def _config_ssl_ctx(self, verify_mode):
     SSL_CTX_set_verify(self._ctx.value, verify_mode)
     SSL_CTX_set_read_ahead(self._ctx.value, 1)
     # Compression occurs at the stream layer now, leading to datagram
     # corruption when packet loss occurs
     SSL_CTX_set_options(self._ctx.value, SSL_OP_NO_COMPRESSION)
     if self._certfile:
         SSL_CTX_use_certificate_chain_file(self._ctx.value, self._certfile)
     if self._keyfile:
         SSL_CTX_use_PrivateKey_file(self._ctx.value, self._keyfile,
                                     SSL_FILE_TYPE_PEM)
     if self._ca_certs:
         SSL_CTX_load_verify_locations(self._ctx.value, self._ca_certs,
                                       None)
     if self._ciphers:
         try:
             SSL_CTX_set_cipher_list(self._ctx.value, self._ciphers)
         except openssl_error() as err:
             raise_ssl_error(ERR_NO_CIPHER, err)
예제 #13
0
 def _wrap_socket_library_call(self, call, timeout_error):
     timeout_sec_start = timeout_sec = self._check_nbio()
     # Pass the call if the socket is blocking or non-blocking
     if not timeout_sec:  # None (blocking) or zero (non-blocking)
         return call()
     start_time = datetime.datetime.now()
     read_sock = self.get_socket(True)
     need_select = False
     while timeout_sec > 0:
         if need_select:
             if not select([read_sock], [], [], timeout_sec)[0]:
                 break
             timeout_sec = timeout_sec_start - \
               (datetime.datetime.now() - start_time).total_seconds()
         try:
             return call()
         except openssl_error() as err:
             if err.ssl_error == SSL_ERROR_WANT_READ:
                 need_select = True
                 continue
             raise
     raise_ssl_error(timeout_error)
예제 #14
0
 def _wrap_socket_library_call(self, call, timeout_error):
     timeout_sec_start = timeout_sec = self._check_nbio()
     # Pass the call if the socket is blocking or non-blocking
     if not timeout_sec:  # None (blocking) or zero (non-blocking)
         return call()
     start_time = datetime.datetime.now()
     read_sock = self.get_socket(True)
     need_select = False
     while timeout_sec > 0:
         if need_select:
             if not select([read_sock], [], [], timeout_sec)[0]:
                 break
             timeout_sec = timeout_sec_start - \
               (datetime.datetime.now() - start_time).total_seconds()
         try:
             return call()
         except openssl_error() as err:
             if err.ssl_error == SSL_ERROR_WANT_READ:
                 need_select = True
                 continue
             raise
     raise_ssl_error(timeout_error)
예제 #15
0
    def write(self, data):
        """Write data to connection

        Write data as string of bytes.

        Arguments:
        data -- buffer containing data to be written

        Return value:
        number of bytes actually transmitted
        """

        try:
            ret = self._wrap_socket_library_call(
                lambda: SSL_write(self._ssl.value, data), ERR_WRITE_TIMEOUT)
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
                raise_ssl_error(ERR_PORT_UNREACHABLE, err)
            raise
        if ret:
            self._handshake_done = True
        return ret
예제 #16
0
    def getpeercert(self, binary_form=False):
        """Retrieve the peer's certificate

        When binary form is requested, the peer's DER-encoded certficate is
        returned if it was transmitted during the handshake.

        When binary form is not requested, and the peer's certificate has been
        validated, then a certificate dictionary is returned. If the certificate
        was not validated, an empty dictionary is returned.

        In all cases, None is returned if no certificate was received from the
        peer.
        """

        try:
            peer_cert = _X509(SSL_get_peer_certificate(self._ssl.value))
        except openssl_error():
            return

        if binary_form:
            return i2d_X509(peer_cert.value)
        if self._cert_reqs == CERT_NONE:
            return {}
        return decode_cert(peer_cert)
예제 #17
0
    def read(self, len=1024, buffer=None):
        """Read data from connection

        Read up to len bytes and return them.
        Arguments:
        len -- maximum number of bytes to read

        Return value:
        string containing read bytes
        """
        #Time test by Johann
        try:
            tInicio = time.time()
            ret = self._wrap_socket_library_call(
                lambda: SSL_read(self._ssl.value, len, buffer),
                ERR_READ_TIMEOUT)
            tFim = time.time()
            if globalvars.handshakeDone:
                globalvars.tSSLRead += tFim - tInicio
            return ret
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
                raise_ssl_error(ERR_PORT_UNREACHABLE, err)
            raise
예제 #18
0
    def getpeercert(self, binary_form=False):
        """Retrieve the peer's certificate

        When binary form is requested, the peer's DER-encoded certficate is
        returned if it was transmitted during the handshake.

        When binary form is not requested, and the peer's certificate has been
        validated, then a certificate dictionary is returned. If the certificate
        was not validated, an empty dictionary is returned.

        In all cases, None is returned if no certificate was received from the
        peer.
        """

        try:
            peer_cert = _X509(SSL_get_peer_certificate(self._ssl.value))
        except openssl_error():
            return

        if binary_form:
            return i2d_X509(peer_cert.value)
        if self._cert_reqs == CERT_NONE:
            return {}
        return decode_cert(peer_cert)
예제 #19
0
    def listen(self):
        """Server-side cookie exchange

        This method reads datagrams from the socket and initiates cookie
        exchange, upon whose successful conclusion one can then proceed to
        the accept method. Alternatively, accept can be called directly, in
        which case it will call this method. In order to prevent denial-of-
        service attacks, only a small, constant set of computing resources
        are used during the listen phase.

        On some platforms, listen must be called so that packets will be
        forwarded to accepted connections. Doing so is therefore recommened
        in all cases for portable code.

        Return value: a peer address if a datagram from a new peer was
        encountered, None if a datagram for a known peer was forwarded
        """

        if not hasattr(self, "_listening"):
            raise InvalidSocketError("listen called on non-listening socket")

        self._pending_peer_address = None
        try:
            peer_address = self._udp_demux.service()
        except socket.timeout:
            peer_address = None
        except socket.error as sock_err:
            if sock_err.errno != errno.EWOULDBLOCK:
                _logger.exception("Unexpected socket error in listen")
                raise
            peer_address = None

        if not peer_address:
            _logger.debug("Listen returning without peer")
            return

        # The demux advises that a datagram from a new peer may have arrived
        if type(peer_address) is tuple:
            # For this type of demux, the write BIO must be pointed at the peer
            BIO_dgram_set_peer(self._wbio.value, peer_address)
            self._udp_demux.forward()
            self._listening_peer_address = peer_address

        self._check_nbio()
        self._listening = True
        try:
            _logger.debug("Invoking DTLSv1_listen for ssl: %d",
                          self._ssl.raw)
            dtls_peer_address = DTLSv1_listen(self._ssl.value)
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_WANT_READ:
                # This method must be called again to forward the next datagram
                _logger.debug("DTLSv1_listen must be resumed")
                return
            elif err.errqueue and err.errqueue[0][0] == ERR_WRONG_VERSION_NUMBER:
                _logger.debug("Wrong version number; aborting handshake")
                raise
            elif err.errqueue and err.errqueue[0][0] == ERR_COOKIE_MISMATCH:
                _logger.debug("Mismatching cookie received; aborting handshake")
                raise
            elif err.errqueue and err.errqueue[0][0] == ERR_NO_SHARED_CIPHER:
                _logger.debug("No shared cipher; aborting handshake")
                raise
            _logger.exception("Unexpected error in DTLSv1_listen")
            raise
        finally:
            self._listening = False
            self._listening_peer_address = None
        if type(peer_address) is tuple:
            _logger.debug("New local peer: %s", dtls_peer_address)
            self._pending_peer_address = peer_address
        else:
            self._pending_peer_address = dtls_peer_address
        _logger.debug("New peer: %s", self._pending_peer_address)
        return self._pending_peer_address
예제 #20
0
    def listen(self):
        """Server-side cookie exchange

        This method reads datagrams from the socket and initiates cookie
        exchange, upon whose successful conclusion one can then proceed to
        the accept method. Alternatively, accept can be called directly, in
        which case it will call this method. In order to prevent denial-of-
        service attacks, only a small, constant set of computing resources
        are used during the listen phase.

        On some platforms, listen must be called so that packets will be
        forwarded to accepted connections. Doing so is therefore recommened
        in all cases for portable code.

        Return value: a peer address if a datagram from a new peer was
        encountered, None if a datagram for a known peer was forwarded
        """

        if not hasattr(self, "_listening"):
            raise InvalidSocketError("listen called on non-listening socket")

        self._pending_peer_address = None
        try:
            peer_address = self._udp_demux.service()
        except socket.timeout:
            peer_address = None
        except socket.error as sock_err:
            if sock_err.errno != errno.EWOULDBLOCK:
                _logger.exception("Unexpected socket error in listen")
                raise
            peer_address = None

        if not peer_address:
            _logger.debug("Listen returning without peer")
            return

        # The demux advises that a datagram from a new peer may have arrived
        if type(peer_address) is tuple:
            # For this type of demux, the write BIO must be pointed at the peer
            BIO_dgram_set_peer(self._wbio.value, peer_address)
            self._udp_demux.forward()
            self._listening_peer_address = peer_address

        self._check_nbio()
        self._listening = True
        try:
            _logger.debug("Invoking DTLSv1_listen for ssl: %d", self._ssl.raw)
            dtls_peer_address = DTLSv1_listen(self._ssl.value)
        except openssl_error() as err:
            if err.ssl_error == SSL_ERROR_WANT_READ:
                # This method must be called again to forward the next datagram
                _logger.debug("DTLSv1_listen must be resumed")
                return
            elif err.errqueue and err.errqueue[0][
                    0] == ERR_WRONG_VERSION_NUMBER:
                _logger.debug("Wrong version number; aborting handshake")
                raise
            elif err.errqueue and err.errqueue[0][0] == ERR_COOKIE_MISMATCH:
                _logger.debug(
                    "Mismatching cookie received; aborting handshake")
                raise
            elif err.errqueue and err.errqueue[0][0] == ERR_NO_SHARED_CIPHER:
                _logger.debug("No shared cipher; aborting handshake")
                raise
            _logger.exception("Unexpected error in DTLSv1_listen")
            raise
        finally:
            self._listening = False
            self._listening_peer_address = None
        if type(peer_address) is tuple:
            _logger.debug("New local peer: %s", dtls_peer_address)
            self._pending_peer_address = peer_address
        else:
            self._pending_peer_address = dtls_peer_address
        _logger.debug("New peer: %s", self._pending_peer_address)
        return self._pending_peer_address