public static void go0(String... expected) throws Exception { System.setProperty("sun.security.krb5.debug", "true"); // Make sure KDCs' ports starts with 1 and 2 and 3, // useful for checking debug output. int p1 = 10000 + new java.util.Random().nextInt(10000); int p2 = 20000 + new java.util.Random().nextInt(10000); int p3 = 30000 + new java.util.Random().nextInt(10000); FileWriter fw = new FileWriter("alternative-krb5.conf"); fw.write( "[libdefaults]\n" + "default_realm = " + OneKDC.REALM + "\n" + "kdc_timeout = " + toReal(2000) + "\n"); fw.write( "[realms]\n" + OneKDC.REALM + " = {\n" + "kdc = " + OneKDC.KDCHOST + ":" + p1 + "\n" + "kdc = " + OneKDC.KDCHOST + ":" + p2 + "\n" + "kdc = " + OneKDC.KDCHOST + ":" + p3 + "\n" + "}\n"); fw.close(); System.setProperty("java.security.krb5.conf", "alternative-krb5.conf"); Config.refresh(); // Turn on k3 only KDC k3 = on(p3); test(expected[0]); test(expected[1]); Config.refresh(); test(expected[2]); k3.terminate(); // shutdown k3 on(p2); // k2 is on test(expected[3]); on(p1); // k1 and k2 is on test(expected[4]); }
private static KDC on(int p) throws Exception { KDC k = new KDC(OneKDC.REALM, OneKDC.KDCHOST, p, true); k.addPrincipal(OneKDC.USER, OneKDC.PASS); k.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); // Feed a packet to newly started KDC to warm it up System.err.println("-------- IGNORE THIS ERROR MESSAGE --------"); new DatagramSocket() .send(new DatagramPacket("Hello".getBytes(), 5, InetAddress.getByName(OneKDC.KDCHOST), p)); return k; }
/** * Start a KDC server: - create a KDC instance - create Kerberos principals - save Kerberos * configuration - save keys to keytab file - no pre-auth is required */ private static void startKDC(String realm, Map<String, String> principals, String ktab) { try { KDC kdc = KDC.create(realm, HOST, 0, true); kdc.setOption(KDC.Option.PREAUTH_REQUIRED, Boolean.FALSE); if (principals != null) { principals .entrySet() .stream() .forEach( (entry) -> { String name = entry.getKey(); String password = entry.getValue(); if (password == null || password.isEmpty()) { System.out.println( "KDC: add a principal '" + name + "' with a random password"); kdc.addPrincipalRandKey(name); } else { System.out.println( "KDC: add a principal '" + name + "' with '" + password + "' password"); kdc.addPrincipal(name, password.toCharArray()); } }); } KDC.saveConfig(KRB5_CONF_FILENAME, kdc); if (ktab != null) { File ktabFile = new File(ktab); if (ktabFile.exists()) { System.out.println("KDC: append keys to an exising " + "keytab file " + ktab); kdc.appendKtab(ktab); } else { System.out.println("KDC: create a new keytab file " + ktab); kdc.writeKtab(ktab); } } System.out.println( "KDC: started on " + HOST + ":" + kdc.getPort() + " with '" + realm + "' realm"); } catch (Exception e) { throw new RuntimeException("KDC: unexpected exception", e); } }
/** * Creates the KDC and starts it. * * @param etype Encryption type, null if not specified * @throws java.lang.Exception if there's anything wrong */ public OneKDC(String etype) throws Exception { super(REALM, KDCHOST, 0, true); addPrincipal(USER, PASS); addPrincipalRandKey("krbtgt/" + REALM); addPrincipalRandKey(SERVER); addPrincipalRandKey(BACKEND); KDC.saveConfig( KRB5_CONF, this, "forwardable = true", "default_keytab_name = " + KTAB, etype == null ? "" : "default_tkt_enctypes=" + etype + "\ndefault_tgs_enctypes=" + etype); System.setProperty("java.security.krb5.conf", KRB5_CONF); // Whatever krb5.conf had been loaded before, we reload ours now. Config.refresh(); writeKtab(KTAB); new File(KRB5_CONF).deleteOnExit(); new File(KTAB).deleteOnExit(); }
void go() throws Exception { OneKDC k = new OneKDC(null); k.writeJAASConf(); Files.delete(Paths.get(OneKDC.KTAB)); // Starts with no keytab c = Context.fromJAAS("client"); s = Context.fromJAAS("com.sun.security.jgss.krb5.accept"); // Test 1: read new key 1 from keytab k.addPrincipal(OneKDC.SERVER, "pass1".toCharArray()); k.writeKtab(OneKDC.KTAB); connect(); // Test 2: service key cached, find 1 in keytab (now contains 1 and 2) k.addPrincipal(OneKDC.SERVER, "pass2".toCharArray()); k.appendKtab(OneKDC.KTAB); connect(); // Test 3: re-login. Now find 2 in keytab c = Context.fromJAAS("client"); connect(); // Test 4: re-login, KDC use 3 this time. c = Context.fromJAAS("client"); // Put 3 and 4 into keytab but keep the real key back to 3. k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); k.appendKtab(OneKDC.KTAB); k.addPrincipal(OneKDC.SERVER, "pass4".toCharArray()); k.appendKtab(OneKDC.KTAB); k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); connect(); // Test 5: invalid keytab file, should ignore try (FileOutputStream fos = new FileOutputStream(OneKDC.KTAB)) { fos.write("BADBADBAD".getBytes()); } connect(); // Test 6: delete keytab file, identical to revoke all Files.delete(Paths.get(OneKDC.KTAB)); try { connect(); throw new Exception("Should not success"); } catch (GSSException gsse) { System.out.println(gsse); KrbException ke = (KrbException) gsse.getCause(); // KrbApReq.authenticate(*) if (dkey == null)... // This should have been Krb5.KRB_AP_ERR_NOKEY if (ke.returnCode() != Krb5.API_INVALID_ARG) { throw new Exception("Not expected failure code: " + ke.returnCode()); } } // Test 7: 3 revoked, should fail (now contains only 5) k.addPrincipal(OneKDC.SERVER, "pass5".toCharArray()); k.writeKtab(OneKDC.KTAB); // overwrite keytab, which means // old key is revoked try { connect(); throw new Exception("Should not success"); } catch (GSSException gsse) { System.out.println(gsse); // Since 7197159, different kvno is accepted, this return code // will never be thrown out again. // KrbException ke = (KrbException)gsse.getCause(); // if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) { // throw new Exception("Not expected failure code: " + // ke.returnCode()); // } } // Test 8: an empty KDC means revoke all KDC.create("EMPTY.REALM").writeKtab(OneKDC.KTAB); try { connect(); throw new Exception("Should not success"); } catch (GSSException gsse) { System.out.println(gsse); KrbException ke = (KrbException) gsse.getCause(); // KrbApReq.authenticate(*) if (dkey == null)... // This should have been Krb5.KRB_AP_ERR_NOKEY if (ke.returnCode() != Krb5.API_INVALID_ARG) { throw new Exception("Not expected failure code: " + ke.returnCode()); } } }