package org.kde.kjas.server; import java.security.*; import java.security.cert.*; import java.net.*; import java.util.*; public class KJASSecurityManager extends SecurityManager { static Hashtable confirmRequests = new Hashtable(); static int confirmId = 0; Hashtable grantedPermissions = new Hashtable(); HashSet grantAllPermissions = new HashSet(); HashSet rejectAllPermissions = new HashSet(); private static final char [] base64table = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; static String encode64( byte [] data) { StringBuffer buf = new StringBuffer( 4*((data.length + 2)/3) ); int i = 0, b1, b2, b3; while (i < data.length - 2) { b1 = data[i++]; b2 = data[i++]; b3 = data[i++]; buf.append( base64table[(b1 >>> 2) & 0x3F] ); buf.append( base64table[((b1 << 4) & 0x30) | ((b2 >>> 4) & 0xF)] ); buf.append( base64table[((b2 << 2) & 0x3C) | ((b3 >>> 6) & 0x03)] ); buf.append( base64table[b3 & 0x3F] ); } if ( i < data.length ) { b1 = data[i++]; buf.append( base64table[(b1 >>> 2) & 0x3F] ); if ( i < data.length ) { b2 = data[i++]; buf.append( base64table[((b1 << 4) & 0x30) | ((b2 >>> 4) & 0xF)] ); buf.append( base64table[(b2 << 2) & 0x3C] ); } else { buf.append( base64table[(b1 << 4) & 0x30] ); buf.append( "=" ); } buf.append( '=' ); } return buf.toString(); } public KJASSecurityManager() { } /** * checks for an applets permission to access certain resources * currently, only a check for SocketPermission is done, that the * applet cannot connect to any other but the host, where it comes from. * Anything else seems to be handled automagically */ public void checkPermission(Permission perm) throws SecurityException, NullPointerException { // ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { super.checkPermission(perm); } catch (SecurityException se) { // Don't annoy users with these if (/*perm instanceof java.lang.RuntimePermission || */ perm instanceof java.awt.AWTPermission) throw se; // Collect certificates HashSet signers = new HashSet(); Class [] cls = getClassContext(); for (int i = 1; i < cls.length; i++) { Object[] objs = cls[i].getSigners(); if (objs != null && objs.length > 0) { for (int j = 0; j < objs.length; j++) if (objs[j] instanceof X509Certificate) signers.add( ((X509Certificate) objs[j]) ); } } Main.debug("Certificates " + signers.size() + " for " + perm); // Check granted/denied permission if ( grantAllPermissions.contains(signers) ) return; if ( rejectAllPermissions.contains(signers) ) throw se; Permissions permissions = (Permissions) grantedPermissions.get(signers); if (permissions != null && permissions.implies(perm)) return; // Ok, ask user what to do String [] certs = new String[signers.size()]; int certsnr = 0; for (Iterator i = signers.iterator(); i.hasNext(); ) { try { certs[certsnr] = encode64( ((X509Certificate) i.next()).getEncoded() ); certsnr++; } catch (CertificateEncodingException cee) {} } if (certsnr == 0) throw se; String id = "" + confirmId++; confirmRequests.put(id, Thread.currentThread()); Main.protocol.sendSecurityConfirm(certs, certsnr, perm.toString(), id); boolean granted = false; try { Thread.sleep(300000); } catch (InterruptedException ie) { if (((String) confirmRequests.get(id)).equals("yes")) { granted = true; permissions = (Permissions) grantedPermissions.get(signers); if (permissions == null) { permissions = new Permissions(); grantedPermissions.put(signers, permissions); } permissions.add(perm); } else if (((String) confirmRequests.get(id)).equals("grant")) { grantAllPermissions.add( signers ); granted = true; } else if (((String) confirmRequests.get(id)).equals("reject")) { rejectAllPermissions.add( signers ); } // else "no", "nossl" or "invalid" } finally { confirmRequests.remove(id); } if (!granted) { Main.debug("Permission denied" + perm); throw se; } } } // keytool -genkey -keystore mystore -alias myalias // keytool -export -keystore mystore -alias myalias -file mycert // keytool -printcert -file mycert // keytool -import -keystore myotherstore -alias myalias -file mycert // jarsigner -keystore mystore myjar.jar myalias // jarsigner -verify -keystore myotherstore myjar.jar // // policy file (use policytool and check java.security): // keystore "file:myotherstore", "JKS" // grant signedBy "myalias" // { // permission java.io.FilePermission "<>", "read" // } // // java code: // KeyStore store = KeyStore.getInstance("JKS", "SUN"); public void disabled___checkPermission(Permission perm) throws SecurityException, NullPointerException { // does not seem to work as expected, Problems with proxy - and it seems that the default // implementation already does all that well, what I wanted to do here. // It is likely that this method will hence disappear soon again. Object context = getSecurityContext(); Thread thread = Thread.currentThread(); if (perm instanceof SocketPermission) { // check if this is a connection back to the originating host // if not, fall through and call super.checkPermission // this gives normally access denied Main.debug("*** checkPermission " + perm + " in context=" + context + " Thread=" + thread); // use the context class loader to determine if this is one // of our applets ClassLoader contextClassLoader = thread.getContextClassLoader(); Main.debug("* ClassLoader=" + contextClassLoader); try { // try to cast ... KJASAppletClassLoader loader = (KJASAppletClassLoader)contextClassLoader; // ok. cast succeeded. Now get the codebase of the loader // because it contains the host name URL codebase = loader.getCodeBase(); URL docbase = loader.getDocBase(); Main.debug("* Class Loader docbase=" + docbase + " codebase=" + codebase); String hostname = perm.getName(); // extract the hostname from the permission name // which is something like "some.host.domain:XX" // with XX as the port number int colonIdx = hostname.indexOf(':'); if (colonIdx > 0) { // strip of the port hostname = hostname.substring(0, colonIdx); } // Main.info("Checking " + hostname + "<->" + codebase.getHost()); if (hostsAreEqual(hostname, codebase.getHost())) { // ok, host matches String actions = perm.getActions(); // just check if listen is specified which we do not want // to allow if (actions != null && actions.indexOf("listen") >= 0) { Main.debug("* Listen is not allowed."); } else { // ok, just return and throw _no_ exception Main.debug("* Hostname equals. Permission granted."); return; } } else { Main.info("Host mismatch: " + perm + " != " + codebase.getHost()); } } catch (ClassCastException e) { Main.debug("* ClassLoader is not a KJASAppletClassLoader"); } Main.debug("* Fall through to super.checkPermission()"); } super.checkPermission(perm); } private static final boolean hostsAreEqual(String host1, String host2) { if (host1 == null || host2 == null) { return false; } if (host1.length() == 0 || host2.length() == 0) { return false; } if (host1.equalsIgnoreCase(host2)) { return true; } if ( Main.proxyHost != null && Main.proxyPort != 0) { // if we use a proxy, we certainly cannot use DNS return false; } InetAddress inet1=null, inet2=null; try { inet1 = InetAddress.getByName(host1); } catch (UnknownHostException e) { Main.kjas_err("Unknown host:" + host1, e); return false; } try { inet2 = InetAddress.getByName(host2); } catch (UnknownHostException e) { Main.kjas_err("Unknown host:" + host2, e); return false; } if (inet1.equals(inet2)) { return true; } return false; } }