1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 6894072 8194486 27 * @summary always refresh keytab 28 * @library /test/lib 29 * @compile -XDignore.symbol.file DynamicKeytab.java 30 * @run main jdk.test.lib.FileInstaller TestHosts TestHosts 31 * @run main/othervm -Djdk.net.hosts.file=TestHosts DynamicKeytab 32 */ 33 34 import java.io.File; 35 import java.io.FileOutputStream; 36 import java.nio.file.Files; 37 import java.nio.file.Paths; 38 import org.ietf.jgss.GSSException; 39 import sun.security.jgss.GSSUtil; 40 import sun.security.krb5.KrbException; 41 import sun.security.krb5.internal.Krb5; 42 43 public class DynamicKeytab { 44 45 Context c, s; main(String[] args)46 public static void main(String[] args) 47 throws Exception { 48 new DynamicKeytab().go(); 49 } 50 go()51 void go() throws Exception { 52 OneKDC k = new OneKDC(null); 53 k.writeJAASConf(); 54 55 Files.delete(Paths.get(OneKDC.KTAB)); 56 57 // Starts with no keytab 58 c = Context.fromJAAS("client"); 59 s = Context.fromJAAS("com.sun.security.jgss.krb5.accept"); 60 61 // Test 1: read new key 1 from keytab 62 k.addPrincipal(OneKDC.SERVER, "pass1".toCharArray()); 63 k.writeKtab(OneKDC.KTAB); 64 connect(); 65 66 // Test 2: service key cached, find 1 in keytab (now contains 1 and 2) 67 k.addPrincipal(OneKDC.SERVER, "pass2".toCharArray()); 68 k.appendKtab(OneKDC.KTAB); 69 connect(); 70 71 // Test 3: re-login. Now find 2 in keytab 72 c = Context.fromJAAS("client"); 73 connect(); 74 75 // Test 4: re-login, KDC use 3 this time. 76 c = Context.fromJAAS("client"); 77 // Put 3 and 4 into keytab but keep the real key back to 3. 78 k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); 79 k.appendKtab(OneKDC.KTAB); 80 k.addPrincipal(OneKDC.SERVER, "pass4".toCharArray()); 81 k.appendKtab(OneKDC.KTAB); 82 k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); 83 connect(); 84 85 // Test 5: invalid keytab file, should ignore 86 try (FileOutputStream fos = new FileOutputStream(OneKDC.KTAB)) { 87 fos.write("BADBADBAD".getBytes()); 88 } 89 connect(); 90 91 // Test 6: delete keytab file, identical to revoke all 92 Files.delete(Paths.get(OneKDC.KTAB)); 93 try { 94 connect(); 95 throw new Exception("Should not success"); 96 } catch (GSSException gsse) { 97 System.out.println(gsse); 98 KrbException ke = (KrbException)gsse.getCause(); 99 // KrbApReq.authenticate(*) if (dkey == null)... 100 // This should have been Krb5.KRB_AP_ERR_NOKEY 101 if (ke.returnCode() != Krb5.API_INVALID_ARG) { 102 throw new Exception("Not expected failure code: " + 103 ke.returnCode()); 104 } 105 } 106 107 // Test 7: 3 revoked, should fail (now contains only 5) 108 k.addPrincipal(OneKDC.SERVER, "pass5".toCharArray()); 109 k.writeKtab(OneKDC.KTAB); // overwrite keytab, which means 110 // old key is revoked 111 try { 112 connect(); 113 throw new Exception("Should not success"); 114 } catch (GSSException gsse) { 115 System.out.println(gsse); 116 // Since 7197159, different kvno is accepted, this return code 117 // will never be thrown out again. 118 //KrbException ke = (KrbException)gsse.getCause(); 119 //if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) { 120 // throw new Exception("Not expected failure code: " + 121 // ke.returnCode()); 122 //} 123 } 124 125 // Test 8: an empty KDC means revoke all 126 KDC.create("EMPTY.REALM").writeKtab(OneKDC.KTAB); 127 try { 128 connect(); 129 throw new Exception("Should not success"); 130 } catch (GSSException gsse) { 131 System.out.println(gsse); 132 KrbException ke = (KrbException)gsse.getCause(); 133 // KrbApReq.authenticate(*) if (dkey == null)... 134 // This should have been Krb5.KRB_AP_ERR_NOKEY 135 if (ke.returnCode() != Krb5.API_INVALID_ARG) { 136 throw new Exception("Not expected failure code: " + 137 ke.returnCode()); 138 } 139 } 140 } 141 connect()142 void connect() throws Exception { 143 Thread.sleep(2000); // make sure ktab timestamp is different 144 c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID); 145 s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); 146 Context.handshake(c, s); 147 } 148 } 149