connectors/PYRO/PSK_Adapter.py
changeset 3884 34da877021d5
parent 3883 a6e7dd8bac36
child 3885 22a009561502
equal deleted inserted replaced
3883:a6e7dd8bac36 3884:34da877021d5
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 # This file is part of Beremiz, a Integrated Development Environment for
       
     5 # programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
       
     6 #
       
     7 # Copyright (C) 2019: Edouard TISSERANT
       
     8 #
       
     9 # See COPYING file for copyrights details.
       
    10 #
       
    11 # This program is free software; you can redistribute it and/or
       
    12 # modify it under the terms of the GNU General Public License
       
    13 # as published by the Free Software Foundation; either version 2
       
    14 # of the License, or (at your option) any later version.
       
    15 #
       
    16 # This program is distributed in the hope that it will be useful,
       
    17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    19 # GNU General Public License for more details.
       
    20 #
       
    21 # You should have received a copy of the GNU General Public License
       
    22 # along with this program; if not, write to the Free Software
       
    23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
       
    24 
       
    25 
       
    26 """
       
    27 The TLS-PSK adapter that handles SSL connections instead of regular sockets,
       
    28 but using Pre Shared Keys instead of Certificates
       
    29 """
       
    30 
       
    31 
       
    32 
       
    33 
       
    34 import socket
       
    35 import re
       
    36 import ssl
       
    37 import Pyro
       
    38 from Pyro.core import PyroURI
       
    39 from Pyro.protocol import _connect_socket, TCPConnection, PYROAdapter
       
    40 from Pyro.errors import ConnectionDeniedError, ProtocolError
       
    41 from Pyro.util import Log
       
    42 
       
    43 try:
       
    44     import sslpsk
       
    45 except ImportError as e:
       
    46     print(str(e))
       
    47     sslpsk = None
       
    48 
       
    49 
       
    50 class PYROPSKAdapter(PYROAdapter):
       
    51     """
       
    52     This is essentialy the same as in Pyro/protocol.py
       
    53     only raw_sock wrapping into sock through sslpsk.wrap_socket was added
       
    54     Pyro unfortunately doesn't allow cleaner customization
       
    55     """
       
    56 
       
    57     def bindToURI(self, URI):
       
    58         with self.lock:   # only 1 thread at a time can bind the URI
       
    59             try:
       
    60                 self.URI = URI
       
    61 
       
    62                 # This are the statements that differ from Pyro/protocol.py
       
    63                 raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
    64                 _connect_socket(raw_sock, URI.address, URI.port, self.timeout)
       
    65                 sock = sslpsk.wrap_socket(
       
    66                     raw_sock, psk=Pyro.config.PYROPSK, server_side=False,
       
    67                     ciphers="PSK-AES256-CBC-SHA",  # available in openssl 1.0.2
       
    68                     ssl_version=ssl.PROTOCOL_TLSv1)
       
    69                 # all the rest is the same as in Pyro/protocol.py
       
    70 
       
    71                 conn = TCPConnection(sock, sock.getpeername())
       
    72                 # receive the authentication challenge string, and use that to build the actual identification string.
       
    73                 try:
       
    74                     authChallenge = self.recvAuthChallenge(conn)
       
    75                 except ProtocolError as x:
       
    76                     # check if we were denied
       
    77                     if hasattr(x, "partialMsg") and x.partialMsg[:len(self.denyMSG)] == self.denyMSG:
       
    78                         raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(x.partialMsg[-1])])
       
    79                     else:
       
    80                         raise
       
    81                 # reply with our ident token, generated from the ident passphrase and the challenge
       
    82                 msg = self._sendConnect(sock, self.newConnValidator.createAuthToken(self.ident, authChallenge, conn.addr, self.URI, None))
       
    83                 if msg == self.acceptMSG:
       
    84                     self.conn = conn
       
    85                     self.conn.connected = 1
       
    86                     Log.msg('PYROAdapter', 'connected to', str(URI))
       
    87                     if URI.protocol == 'PYROLOCPSK':
       
    88                         self.resolvePYROLOC_URI("PYROPSK")  # updates self.URI
       
    89                 elif msg[:len(self.denyMSG)] == self.denyMSG:
       
    90                     try:
       
    91                         raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(msg[-1])])
       
    92                     except (KeyError, ValueError):
       
    93                         raise ConnectionDeniedError('invalid response')
       
    94             except socket.error:
       
    95                 Log.msg('PYROAdapter', 'connection failed to URI', str(URI))
       
    96                 raise ProtocolError('connection failed')
       
    97 
       
    98 
       
    99 _getProtocolAdapter = Pyro.protocol.getProtocolAdapter
       
   100 
       
   101 
       
   102 def getProtocolAdapter(protocol):
       
   103     if protocol in ('PYROPSK', 'PYROLOCPSK'):
       
   104         return PYROPSKAdapter()
       
   105     return _getProtocolAdapter(protocol)
       
   106 
       
   107 
       
   108 _processStringURI = Pyro.core.processStringURI
       
   109 
       
   110 
       
   111 def processStringURI(URI):
       
   112     x = re.match(r'(?P<protocol>PYROLOCPSK)://(?P<hostname>[^\s:]+):?(?P<port>\d+)?/(?P<name>\S*)', URI)
       
   113     if x:
       
   114         protocol = x.group('protocol')
       
   115         hostname = x.group('hostname')
       
   116         port = x.group('port')
       
   117         if port:
       
   118             port = int(port)
       
   119         else:
       
   120             port = 0
       
   121         name = x.group('name')
       
   122         return PyroURI(hostname, name, port, protocol)
       
   123     return _processStringURI(URI)
       
   124 
       
   125 
       
   126 def setupPSKAdapter():
       
   127     """
       
   128     Add PyroAdapter to the list of available in
       
   129     Pyro adapters and handle new supported protocols
       
   130 
       
   131     This function should be called after
       
   132     reimport of Pyro module to enable PYROS:// again.
       
   133     """
       
   134     if sslpsk is not None:
       
   135         Pyro.protocol.getProtocolAdapter = getProtocolAdapter
       
   136         Pyro.core.processStringURI = processStringURI
       
   137     else:
       
   138         raise Exception("sslpsk python module unavailable")