1 /* 2 * Copyright (c) 2011, 2013, 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 27 * @compile -XDignore.symbol.file DynamicKeytab.java 28 * @run main/othervm DynamicKeytab 29 * @summary always refresh keytab 30 */ 31 32 import java.io.File; 33 import java.io.FileOutputStream; 34 import java.nio.file.Files; 35 import java.nio.file.Paths; 36 import org.ietf.jgss.GSSException; 37 import sun.security.jgss.GSSUtil; 38 import sun.security.krb5.KrbException; 39 import sun.security.krb5.internal.Krb5; 40 41 public class DynamicKeytab { 42 43 Context c, s; 44 public static void main(String[] args) 45 throws Exception { 46 new DynamicKeytab().go(); 47 } 48 49 void go() throws Exception { 50 OneKDC k = new OneKDC(null); 51 k.writeJAASConf(); 52 53 Files.delete(Paths.get(OneKDC.KTAB)); 54 55 // Starts with no keytab 56 c = Context.fromJAAS("client"); 57 s = Context.fromJAAS("com.sun.security.jgss.krb5.accept"); 58 59 // Test 1: read new key 1 from keytab 60 k.addPrincipal(OneKDC.SERVER, "pass1".toCharArray()); 61 k.writeKtab(OneKDC.KTAB); 62 connect(); 63 64 // Test 2: service key cached, find 1 in keytab (now contains 1 and 2) 65 k.addPrincipal(OneKDC.SERVER, "pass2".toCharArray()); 66 k.appendKtab(OneKDC.KTAB); 67 connect(); 68 69 // Test 3: re-login. Now find 2 in keytab 70 c = Context.fromJAAS("client"); 71 connect(); 72 73 // Test 4: re-login, KDC use 3 this time. 74 c = Context.fromJAAS("client"); 75 // Put 3 and 4 into keytab but keep the real key back to 3. 76 k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); 77 k.appendKtab(OneKDC.KTAB); 78 k.addPrincipal(OneKDC.SERVER, "pass4".toCharArray()); 79 k.appendKtab(OneKDC.KTAB); 80 k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); 81 connect(); 82 83 // Test 5: invalid keytab file, should ignore 84 try (FileOutputStream fos = new FileOutputStream(OneKDC.KTAB)) { 85 fos.write("BADBADBAD".getBytes()); 86 } 87 connect(); 88 89 // Test 6: delete keytab file, identical to revoke all 90 Files.delete(Paths.get(OneKDC.KTAB)); 91 try { 92 connect(); 93 throw new Exception("Should not success"); 94 } catch (GSSException gsse) { 95 System.out.println(gsse); 96 KrbException ke = (KrbException)gsse.getCause(); 97 // KrbApReq.authenticate(*) if (dkey == null)... 98 // This should have been Krb5.KRB_AP_ERR_NOKEY 99 if (ke.returnCode() != Krb5.API_INVALID_ARG) { 100 throw new Exception("Not expected failure code: " + 101 ke.returnCode()); 102 } 103 } 104 105 // Test 7: 3 revoked, should fail (now contains only 5) 106 k.addPrincipal(OneKDC.SERVER, "pass5".toCharArray()); 107 k.writeKtab(OneKDC.KTAB); // overwrite keytab, which means 108 // old key is revoked 109 try { 110 connect(); 111 throw new Exception("Should not success"); 112 } catch (GSSException gsse) { 113 System.out.println(gsse); 114 // Since 7197159, different kvno is accepted, this return code 115 // will never be thrown out again. 116 //KrbException ke = (KrbException)gsse.getCause(); 117 //if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) { 118 // throw new Exception("Not expected failure code: " + 119 // ke.returnCode()); 120 //} 121 } 122 123 // Test 8: an empty KDC means revoke all 124 KDC.create("EMPTY.REALM").writeKtab(OneKDC.KTAB); 125 try { 126 connect(); 127 throw new Exception("Should not success"); 128 } catch (GSSException gsse) { 129 System.out.println(gsse); 130 KrbException ke = (KrbException)gsse.getCause(); 131 // KrbApReq.authenticate(*) if (dkey == null)... 132 // This should have been Krb5.KRB_AP_ERR_NOKEY 133 if (ke.returnCode() != Krb5.API_INVALID_ARG) { 134 throw new Exception("Not expected failure code: " + 135 ke.returnCode()); 136 } 137 } 138 } 139 140 void connect() throws Exception { 141 Thread.sleep(2000); // make sure ktab timestamp is different 142 c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID); 143 s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); 144 Context.handshake(c, s); 145 } 146 }