From 98b967957e0fa2cc46aa61dcad0061fb8ab69e30 Mon Sep 17 00:00:00 2001 From: Marco Mambelli Date: Fri, 30 Jun 2023 01:05:01 -0500 Subject: [PATCH] Fixed encoding in tken files (text files) and token utils Corrected typo in logSupport comment --- frontend/glideinFrontendElement.py | 14 +++---- lib/logSupport.py | 2 +- lib/token_util.py | 62 ++++++++++++------------------ 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/frontend/glideinFrontendElement.py b/frontend/glideinFrontendElement.py index 82a45a09e..80bdd36fb 100755 --- a/frontend/glideinFrontendElement.py +++ b/frontend/glideinFrontendElement.py @@ -1146,12 +1146,13 @@ def refresh_entry_token(self, glidein_el): pwd_file, scope=scope, duration=duration, identity=identity ) # NOTE: Sensitive information. Uncomment only in development machines. - # cmd = "/usr/sbin/frontend_condortoken %s" % glidein_site - # tkn_str = subprocessSupport.iexe_cmd(cmd, useShell=True) - # logSupport.log.debug("tkn_str= %s" % tkn_str) - with tempfile.NamedTemporaryFile(mode="wb", delete=False, dir=tkn_dir) as fd: + # # cmd = "/usr/sbin/frontend_condortoken %s" % glidein_site + # tkn_str = subprocessSupport.iexe_cmd(cmd, useShell=True) + # logSupport.log.debug("tkn_str= %s" % tkn_str) + # The token file is read as text file below. Writing fixed to be consistent + with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=tkn_dir) as fd: os.chmod(fd.name, 0o600) - fd.write(tkn_str) + fd.write(tkn_str.encode()) os.replace(fd.name, tkn_file) logSupport.log.debug("created token %s" % tkn_file) elif os.path.exists(tkn_file): @@ -1160,8 +1161,7 @@ def refresh_entry_token(self, glidein_el): tkn_str += line except Exception as err: logSupport.log.warning("failed to create %s" % tkn_file) - for i in sys.exc_info(): - logSupport.log.warning("%s" % i) + logSupport.log.warning("Error details: %s" % traceback.format_exc()) return tkn_str diff --git a/lib/logSupport.py b/lib/logSupport.py index d2a4312e6..4f367f7cf 100644 --- a/lib/logSupport.py +++ b/lib/logSupport.py @@ -110,7 +110,7 @@ def __init__(self, filename, maxDays=1, minDays=0, maxMBytes=10, backupCount=5, self.compression = compression.lower() except AttributeError: pass - # bz2 compression can be implementes with encoding='bz2-codec' in BaseRotatingHandler + # bz2 compression can be implemented with encoding='bz2-codec' in BaseRotatingHandler mode = "a" BaseRotatingHandler.__init__(self, filename, mode, encoding=None) self.backupCount = backupCount diff --git a/lib/token_util.py b/lib/token_util.py index 235fe9493..805ea66d1 100644 --- a/lib/token_util.py +++ b/lib/token_util.py @@ -22,29 +22,9 @@ from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.hkdf import HKDF -from glideinwms.lib import logSupport +from glideinwms.lib import defaults, logSupport from glideinwms.lib.subprocessSupport import iexe_cmd -# 2/3 compatibility helpers -# Inspired by http://python3porting.com/problems.html#nicer-solutions - -if sys.version_info[0] < 3: - - def byt(x): - return x - -else: - import codecs - - def byt(x): - return codecs.latin_1_encode(x)[0] - - -def un_byt(data): - if not isinstance(data, str): - data = data.decode() - return data.strip() - def token_file_expired(token_file): """ @@ -52,7 +32,7 @@ def token_file_expired(token_file): Do not check signature, audience, or other claims Args: - token_file: (str) a filename containing a jwt + token_file(Path or str): a filename containing a jwt (a text file w/ default encoding is expected) Returns: bool: True if exp in future or absent and nbf in past or absent, @@ -75,7 +55,7 @@ def token_str_expired(token_str): Do not check signature, audience, or other claims Args: - token_str: (str) a string containing a jwt + token_str(str): string containing a jwt Returns: bool: True if exp in future or absent and nbf in past or absent, @@ -93,22 +73,24 @@ def token_str_expired(token_str): logSupport.log.error("Expired token: %s" % e) except Exception as e: logSupport.log.exception("Unknown exception decoding token: %s" % e) + logSupport.log.debug(f"Faulty token: {token_str}") return expired def simple_scramble(data): """Undo the simple scramble of HTCondor simply XOR with 0xdeadbeef + Using defaults.BINARY_ENCODING (latin-1) to have a 1-to-1 characted-byte correspondence Source: https://github.com/CoffeaTeam/jhub/blob/master/charts/coffea-casa-jhub/files/hub/auth.py#L196-L235 Args: - data: binary string to be unscrambled + data(bytearray): binary string to be unscrambled Returns: - str: an HTCondor scrambled binary string + bytearray: an HTCondor scrambled binary string """ - outb = byt("") + outb = "".encode(defaults.BINARY_ENCODING) deadbeef = [0xDE, 0xAD, 0xBE, 0xEF] ldata = len(data) lbeef = len(deadbeef) @@ -119,7 +101,7 @@ def simple_scramble(data): datum = data[i] rslt = datum ^ deadbeef[i % lbeef] b1 = struct.pack("H", rslt)[0] - outb += byt("%c" % b1) + outb += ("%c" % b1).encode(defaults.BINARY_ENCODING) return outb @@ -129,7 +111,7 @@ def derive_master_key(password): Source: https://github.com/CoffeaTeam/jhub/blob/master/charts/coffea-casa-jhub/files/hub/auth.py#L196-L235 Args: - password: (str) an unscrambled HTCondor password + password(bytes): an unscrambled HTCondor password (bytes-like: bytes, bytearray, memoryview) Returns: str: an HTCondor encryption/decryption key @@ -137,21 +119,26 @@ def derive_master_key(password): # Key length, salt, and info fixed as part of protocol hkdf = HKDF( - algorithm=hashes.SHA256(), length=32, salt=byt("htcondor"), info=byt("master jwt"), backend=default_backend() + algorithm=hashes.SHA256(), + length=32, + salt="htcondor".encode(defaults.BINARY_ENCODING), + info="master jwt".encode(defaults.BINARY_ENCODING), + backend=default_backend(), ) - return hkdf.derive(password) + # HKDF.derive() requires bytes and returns bytes + return hkdf.derive(password).decode(defaults.BINARY_ENCODING) def sign_token(identity, issuer, kid, master_key, duration=None, scope=None): """Assemble and sign an idtoken Args: - identity: (str) who the token was generated for - issuer: (str) idtoken issuer, typically HTCondor Collector - kid: (str) Key ID - master_key: (str) encryption key - duration: (int, optional) number of seconds IDTOKEN is valid. Default: infinity - scope: (str, optional) permissions IDTOKEN has. Default: everything + identity(str): who the token was generated for + issuer(str): idtoken issuer, typically HTCondor Collector + kid(str): Key ID + master_key(str): encryption key + duration(int, optional): number of seconds IDTOKEN is valid. Default: infinity + scope(str, optional): permissions IDTOKEN has. Default: everything Returns: str: a signed IDTOKEN @@ -248,4 +235,5 @@ def create_and_sign_token(pwd_file, issuer=None, identity=None, kid=None, durati master_key = derive_master_key(obfusicated) scope = "condor:/READ condor:/WRITE condor:/ADVERTISE_STARTD condor:/ADVERTISE_SCHEDD condor:/ADVERTISE_MASTER" idtoken = sign_token(identity, issuer, kid, master_key, scope=scope) - print(un_byt(idtoken)) + # idtoken is str + print(idtoken)