141 lines
5.2 KiB
Python
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()
|