Automatically get PSK and ID when connecting to PYRO[S], so that future connection through PYROS can use that same key. Also fixed case to UPPER for *PSK.
authorEdouard Tisserant
Mon, 29 Oct 2018 11:33:36 +0100
changeset 2324 1cf3768ebf85
parent 2323 33a0dbabccd3
child 2325 71593d3f880b
Automatically get PSK and ID when connecting to PYRO[S], so that future connection through PYROS can use that same key. Also fixed case to UPPER for *PSK.
Beremiz_service.py
connectors/PYRO/__init__.py
runtime/PLCObject.py
runtime/Stunnel.py
--- a/Beremiz_service.py	Tue Oct 23 16:19:20 2018 +0200
+++ b/Beremiz_service.py	Mon Oct 29 11:33:36 2018 +0100
@@ -78,7 +78,7 @@
 interface = ''
 port = 3000
 webport = 8009
-pskpath = None
+PSKpath = None
 wampconf = None
 servicename = None
 autostart = False
@@ -120,7 +120,7 @@
     elif o == "-c":
         wampconf = None if a == "off" else a
     elif o == "-s":
-        pskpath = None if a == "off" else a
+        PSKpath = None if a == "off" else a
     elif o == "-e":
         fnameanddirname = list(os.path.split(os.path.realpath(a)))
         fnameanddirname.reverse()
@@ -492,9 +492,9 @@
 # Service name is used as an ID for stunnel's PSK
 # Some extension may set 'servicename' to a computed ID or Serial Number
 # instead of using commandline '-n'
-if servicename is not None and pskpath is not None:
-    from runtime.Stunnel import ensurepsk
-    ensurepsk(servicename, pskpath)
+if servicename is not None and PSKpath is not None:
+    from runtime.Stunnel import ensurePSK
+    ensurePSK(servicename, PSKpath)
 
 runtime.CreatePLCObjectSingleton(
     WorkingDir, argv, statuschange, evaluator, pyruntimevars)
@@ -517,7 +517,7 @@
     if havewamp:
         try:
             WC.SetServer(pyroserver)
-            WC.RegisterWampClient(wampconf, pskpath)
+            WC.RegisterWampClient(wampconf, PSKpath)
             WC.RegisterWebSettings(NS)
         except Exception:
             LogMessageAndException(_("WAMP client startup failed. "))
--- a/connectors/PYRO/__init__.py	Tue Oct 23 16:19:20 2018 +0200
+++ b/connectors/PYRO/__init__.py	Mon Oct 29 11:33:36 2018 +0100
@@ -36,6 +36,7 @@
 import Pyro.util
 from Pyro.errors import PyroError
 
+
 zeroconf_service_type = '_PYRO._tcp.local.'
 # this module attribute contains a list of DNS-SD (Zeroconf) service types
 # supported by this connector confnode.
@@ -54,14 +55,15 @@
     if servicetype == "PYROS":
         import connectors.PYRO.PSK_Adapter
         schemename = "PYROLOCPSK"
-        url, ID = location.split('#')
+        url, ID = location.split('#') #TODO fix exception when # not found
         # load PSK from project
         secpath = os.path.join(str(confnodesroot.ProjectPath), 'psk', ID+'.secret')
         if not os.path.exists(secpath):
             confnodesroot.logger.write_error(
                 'Error: Pre-Shared-Key Secret in %s is missing!\n' % secpath)
             return None
-        Pyro.config.PYROPSK = (open(secpath).read(), ID)
+        secret = open(secpath).read().partition(':')[2].rstrip('\n\r')
+        Pyro.config.PYROPSK = (secret, ID)
         # strip ID from URL, so that pyro can understand it.
         location = url
     else:
@@ -88,9 +90,9 @@
     # Try to get the proxy object
     try:
         RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject")
-    except Exception:
-        confnodesroot.logger.write_error(_("Connection to '%s' failed.\n") % location)
-        confnodesroot.logger.write_error(traceback.format_exc())
+    except Exception,e:
+        confnodesroot.logger.write_error(_("Connection to '%s' failed with exception '%s'\n") % (location, str(e)))
+        #confnodesroot.logger.write_error(traceback.format_exc())
         return None
 
     def PyroCatcher(func, default=None):
@@ -117,10 +119,17 @@
 
     # Check connection is effective.
     # lambda is for getattr of GetPLCstatus to happen inside catcher
-    if PyroCatcher(RemotePLCObjectProxy.GetPLCstatus)() is None:
-        confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n"))
+    IDPSK = PyroCatcher(RemotePLCObjectProxy.GetPLCID)()
+    if IDPSK is None:
+        confnodesroot.logger.write_error(_("Cannot get PLC ID - connection failed.\n"))
         return None
 
+    if servicetype != "PYROS":
+        ID,PSK = IDPSK
+        secpath = os.path.join(str(confnodesroot.ProjectPath), 'psk', ID+'.secret')
+        with open(secpath, 'w') as f:
+            f.write(ID+":"+PSK)
+
     _special_return_funcs = {
         "StartPLC": False,
         "GetTraceVariables": ("Broken", None),
--- a/runtime/PLCObject.py	Tue Oct 23 16:19:20 2018 +0200
+++ b/runtime/PLCObject.py	Mon Oct 29 11:33:36 2018 +0100
@@ -33,6 +33,7 @@
 
 from runtime.typemapping import TypeTranslator
 from runtime.loglevels import LogLevelsDefault, LogLevelsCount
+from runtime.Stunnel import getPSKID
 from runtime import MainWorker
 
 if os.name in ("nt", "ce"):
@@ -438,6 +439,10 @@
         return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount))
 
     @RunInMain
+    def GetPLCID(self):
+        return getPSKID()
+
+    @RunInMain
     def NewPLC(self, md5sum, data, extrafiles):
         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
             NewFileName = md5sum + lib_ext
--- a/runtime/Stunnel.py	Tue Oct 23 16:19:20 2018 +0200
+++ b/runtime/Stunnel.py	Mon Oct 29 11:33:36 2018 +0100
@@ -4,11 +4,13 @@
 
 restart_stunnel_cmdline = ["/etc/init.d/S50stunnel","restart"]
 
-# stunnel takes no encoding for psk, so we try to lose minimum entropy 
+# stunnel takes no encoding for PSK, so we try to lose minimum entropy 
 # by using all possible chars except '\0\n\r' (checked stunnel parser to be sure)
 translator = ''.join([(lambda c: '#' if c in '\0\n\r' else c)(chr(i)) for i in xrange(256)])
 
-def pskgen(ID, pskpath):
+_PSKpath = None
+
+def PSKgen(ID, PSKpath):
     secret = os.urandom(256) # 2048 bits is still safe nowadays
 
     # following makes 512 length string, rejected by stunnel
@@ -16,14 +18,27 @@
     # secretstring = hexlify(secret)
 
     secretstring = secret.translate(translator)
-    pskstring = ID+":"+secretstring
-    with open(pskpath, 'w') as f:
-        f.write(pskstring)
+    PSKstring = ID+":"+secretstring
+    with open(PSKpath, 'w') as f:
+        f.write(PSKstring)
     call(restart_stunnel_cmdline)
 
-def ensurepsk(ID, pskpath):
+def ensurePSK(ID, PSKpath):
+    global _PSKpath
+    _PSKpath = PSKpath
     # check if already there
-    if not os.path.exists(pskpath):
+    if not os.path.exists(PSKpath):
         # create if needed
-        pskgen(ID, pskpath)
+        PSKgen(ID, PSKpath)
 
+def getPSKID():
+    if _PSKpath is not None :
+        if not os.path.exists(_PSKpath):
+            confnodesroot.logger.write_error(
+                'Error: Pre-Shared-Key Secret in %s is missing!\n' % _PSKpath)
+            return None
+        ID,_sep,PSK = open(_PSKpath).read().partition(':')
+        PSK = PSK.rstrip('\n\r')
+        return (ID,PSK)
+    return None
+