# HG changeset patch # User Edouard Tisserant # Date 1540809216 -3600 # Node ID 1cf3768ebf85bc647cbbcc42e194bed374b5de91 # Parent 33a0dbabccd3b7c169472abb12fa4ab589072ee7 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. diff -r 33a0dbabccd3 -r 1cf3768ebf85 Beremiz_service.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. ")) diff -r 33a0dbabccd3 -r 1cf3768ebf85 connectors/PYRO/__init__.py --- 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), diff -r 33a0dbabccd3 -r 1cf3768ebf85 runtime/PLCObject.py --- 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 diff -r 33a0dbabccd3 -r 1cf3768ebf85 runtime/Stunnel.py --- 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 +