fredag den 18. oktober 2013

En slags uofficel NemID Java 7u45 post-mortem

Efter en dags tid hvor der havde været snak om at NemID havde været nede efter den seneste Java-opdatering, besluttede jeg mig for at tage en kopi af Applet og dens såkaldte "plugins". Nu hvor NemID er tilbage i topform under Java 7u45, kunne vi så passende se på hvilken ganske alvorlig sygdom NemID fejlede, siden den kunne gå helt ned på grund af en lille bitte opdatering af Oracles JRE.

Disclaimer: jeg er ikke professionel ud i Java, så hvis jeg tager fejl, så retter jeg gerne til

Efter en rituel udpakning af diverse .jar filer, kunne man passende tage et kig på hvor meget der er ændret. Af de godt en håndfuld plugins er ingen af dem ændret. "DanID_Applet.jar" havde en ændret checksum, men en checksum af alt indholdet, viste ikke nogen ændringer. Der har nok blot været tale om et ændret timestamp.

~/nemid/after$ find . -type f -print0 | xargs -0 md5sum > ../after.sum
~/nemid/after$ cd ../before/
~/nemid/before$ md5sum --quiet -c ../after.sum 
./DanID_Applet-boot-prod.jar: FAILED
./DanID_Applet.jar: FAILED
./boot/dk/pbs/applet/bootstrap/BootApplet.class: FAILED
./boot/META-INF/MANIFEST.MF: FAILED
./boot/META-INF/_H17PYF6.RSA: FAILED
./boot/META-INF/_H17PYF6.SF: FAILED

Således kan al mistanke herefter rette sig imod to steder: manifestet for appletten samt BootApplet.class.

META-INF/MANIFEST.MF

På Version 2 havde der været diskussion om det blot var forkert udformning af manifestet, som gav disse problemer. Noget må være ændret, for der er kommet godt 5 nye settings til. Bemærk også build-date, hvilket indikerer at NemID har virket 24-timer før den blev offentliggjort. Men man kunne nok ikke bare ringe til testerne midt i aftensmaden og bede dem komme på arbejde.

~/nemid$ diff before/boot/META-INF/MANIFEST.MF after/boot/META-INF/MANIFEST.MF 
< Build: DEVELOPMENT BUILD 2013-06-18 15.38.16
---
> Build: DEVELOPMENT BUILD 2013-10-17 17.58.27
> Caller-Allowable-Codebase: *
> Application-Library-Allowable-Codebase: *
> Permissions: all-permissions
> Codebase: *
> Application-Name: NemID

dk.pbs.applet.bootstrap.BootApplet.class

Ud over et par ændringer af telefonnumre og nogle konstanter (som i princippet kunne være alt fra build-numre til checksummer), var der kun en ganske beskeden ændring i koden. Efter alt at dømme har 7u45 dræbt noget reflection-funktionalitet, hvilket ikke er helt uden grund, da langt de fleste Java-exploits har udnyttet reflection ganske hæftigt. Fejlen lader til at ligge i en funktion der laver et integritetscheck af de loadede filer. Herunder ses mit forsøg på at genskabe kildekoden ud fra dekompilering. Rettelsen er bestå af 2 linjers kode, markeret med BEGIN/END fix.

// Use reflection on classLoader
Method findResource = cl.getClass().getMethod("findResource", new Class[] { Bootapplet.class }); 
findResource.setAccessible(true);

// Returns null under 7u45
URL manifest = (URL)findResource.invoke(cl, new Object[] { "META-INF/MANIFEST.MF" });

/* BEGIN FIX (diff in decompiled source) */
if (manifest == null){
    manifest = new URL(basePath + "META-INF/MANIFEST.MF");
}
/* END FIX */

Update: 19/10-2013 9:50

Reflection nødvendig?

Nu har jeg kigget lidt på hvorfor NemID har valgt at bruge reflection. Det lader til at metoden ClassLoader.findResource(String name) er protected, således at den ikke kan tilgås fra almindelig kode. Ved at skaffe en reference til metoden, kan den dynamisk ændres med setAccesible(true), således at den alligevel kan kaldes. Således kan man komme ud over Java's indbyggede access-control mekanisme. Kan det være at fordi der har været ændringer til et internt Java API, at dette ikke måske ikke har været med i den beta-udgivelse af 7u45 som NemID har testet imod?