Files
Jviewer/patch_jviewer.py
T
2026-07-01 14:59:34 +02:00

141 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""
Zwei Funktionen fuer den JViewer-Start mit IcedTea-Web:
1) JNLP-Strukturpatch (Standardaufruf):
patch_jviewer.py <input.jnlp> <output.jnlp>
- <security>/<all-permissions> bleiben UNVERAENDERT: die Jars werden
signiert (s.u.), signierter+vertrauter Code mit dieser Anforderung
bekommt regulaer AllPermission - kein Grund mehr, das zu entfernen.
- <nativelib> wird trotzdem zu <jar> umbenannt: das ist laut Fehlermeldung
"nativelib kann nicht angegeben werden sofern vertrauenswuerdige
Umgebung angefordert wird" mit <security><all-permissions/> inkompatibel,
unabhaengig davon ob signiert oder nicht.
- <codebase> bleibt UNVERAENDERT (zeigt weiter auf die echte BMC). Das ist
wichtig: IcedTea-Web erlaubt unsignierten/nicht vertrauten Apps im
Sandbox-Modus nur Netzwerkzugriff zurueck zu Host+Port der Codebase. Der
KVM-Redirection-Socket (Port 7578) geht an dieselbe IP wie die BMC/
Codebase, aber einen anderen Port - wuerde man die Codebase auf einen
lokalen Proxy umbiegen, wuerde der Zugriff verboten (getestet, "access
denied SocketPermission").
2) Manifest-Patch fuer ein bereits von IcedTea-Web heruntergeladenes,
gecachtes Jar (wird NACH einem ersten, absichtlich fehlschlagenden
javaws-Lauf aufgerufen, der nur zum Befuellen des Caches dient):
patch_jviewer.py --strip-manifest <jarfile> [<jarfile> ...]
Frueherer Ansatz: entfernte "Permissions: all-permissions" aus dem
Manifest, um IcedTea-Webs hartcodierte Forced-Sandbox fuer UNSIGNIERTEN
Code mit dieser Deklaration zu umgehen. Wird seit Einfuehrung der
Selbstsignierung NICHT MEHR aufgerufen (das Attribut wird fuer signierten,
vertrauten Code wieder gebraucht) - Funktion bleibt nur als Fallback im
Code, falls Signierung aus irgendeinem Grund nicht moeglich ist.
"""
import os
import re
import sys
import zipfile
def localname(t):
"""Tag-Namen ohne Namespace-Praefix, egal ob die JNLP ein xmlns deklariert
oder nicht (manche AMI-Firmwarestaende tun es, manche nicht)."""
return t.split("}", 1)[-1] if "}" in t else t
def is_tag(el, name):
return localname(el.tag) == name
def strip_all_permissions_from_manifest(jar_path):
with zipfile.ZipFile(jar_path, "r") as zin:
manifest_name = "META-INF/MANIFEST.MF"
try:
manifest = zin.read(manifest_name).decode("utf-8", "replace")
except KeyError:
print(f"(kein Manifest in {jar_path}, nichts zu tun)")
return
# Manifest-Continuation-Zeilen (beginnen mit einem Leerzeichen) an die
# vorherige Zeile anhaengen, damit "Permissions: all-permissions"
# sicher als eine logische Zeile erkannt wird.
lines = manifest.splitlines()
logical = []
for line in lines:
if line.startswith(" ") and logical:
logical[-1] += line[1:]
else:
logical.append(line)
filtered = [l for l in logical if not re.match(r"(?i)^permissions\s*:", l)]
if filtered == logical:
print(f"(Permissions-Attribut in {jar_path} nicht vorhanden, nichts zu tun)")
return
new_manifest = "\n".join(filtered) + "\n"
entries = {}
for item in zin.infolist():
entries[item] = zin.read(item.filename)
tmp_path = jar_path + ".tmp"
with zipfile.ZipFile(tmp_path, "w", zipfile.ZIP_DEFLATED) as zout:
for item, data in entries.items():
if item.filename == manifest_name:
data = new_manifest.encode("utf-8")
zout.writestr(item, data)
os.replace(tmp_path, jar_path)
print(f"Manifest gepatcht (Permissions: all-permissions entfernt): {jar_path}")
def patch_jnlp(src, dst):
import xml.etree.ElementTree as ET
tree = ET.parse(src)
root = tree.getroot()
# Falls die JNLP ein xmlns deklariert (z.B. xmlns="http://www.w3.org/1999/xhtml"),
# dieselbe Namespace-URI beim Schreiben wieder als Default-Namespace nutzen,
# damit ET keine ns0:-Praefixe einfuegt.
root_ns = localname(root.tag)
if root.tag != root_ns:
ns_uri = root.tag[1:root.tag.index("}")]
ET.register_namespace("", ns_uri)
# <security>/<all-permissions> bleiben unangetastet (s. Docstring oben) -
# nur <nativelib> zu <jar> umbenennen (Namespace-Praefix des Originaltags beibehalten)
for el in root.iter():
if is_tag(el, "nativelib"):
prefix = el.tag[: el.tag.index("}") + 1] if "}" in el.tag else ""
el.tag = f"{prefix}jar"
tree.write(dst, encoding="UTF-8", xml_declaration=True)
print(f"Gepatchte JNLP geschrieben: {dst}")
def main():
args = sys.argv[1:]
if args and args[0] == "--strip-manifest":
jar_paths = args[1:]
if not jar_paths:
print("Benutzung: patch_jviewer.py --strip-manifest <jarfile> [<jarfile> ...]")
sys.exit(1)
for jar_path in jar_paths:
strip_all_permissions_from_manifest(jar_path)
return
if len(args) != 2:
print("Benutzung: patch_jviewer.py <input.jnlp> <output.jnlp>")
print(" oder: patch_jviewer.py --strip-manifest <jarfile> [<jarfile> ...]")
sys.exit(1)
patch_jnlp(args[0], args[1])
if __name__ == "__main__":
main()