# HG changeset patch # User weijun # Date 1275650933 -28800 # Node ID 4b9857e483c10bcbdbd1de31d47a8eccfddc75f4 # Parent f66c0faf01842f3c914cee4f527efd575ada08a8 6951366: kerberos login failure on win2008 with AD set to win2000 compat mode Reviewed-by: valeriep, xuelei diff -r f66c0faf0184 -r 4b9857e483c1 jdk/src/share/classes/sun/security/krb5/Credentials.java --- a/jdk/src/share/classes/sun/security/krb5/Credentials.java Wed Jun 02 17:53:54 2010 -0700 +++ b/jdk/src/share/classes/sun/security/krb5/Credentials.java Fri Jun 04 19:28:53 2010 +0800 @@ -356,6 +356,7 @@ * @param princ the client principal. This value cannot be null. * @param secretKey the secret key of the client principal.This value * cannot be null. + * @param password if null, caller is using a keytab * @returns the TGT credentials */ public static Credentials acquireTGT(PrincipalName princ, @@ -372,8 +373,18 @@ "Cannot have null secretKey to do AS-Exchange"); KrbAsRep asRep = null; + + // The etype field to be placed in AS-REQ. If caller is using keytab, + // it must be limited to etypes in keytab. Otherwise, leave it null, + // and KrbAsReq will populate it with all supported etypes. + + int[] eTypes = null; + if (password == null) { + eTypes = EncryptionKey.getETypes(secretKeys); + } + try { - asRep = sendASRequest(princ, secretKeys, null); + asRep = sendASRequest(princ, secretKeys, eTypes, null); } catch (KrbException ke) { if ((ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) || (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) { @@ -396,7 +407,7 @@ princ.getSalt(), true, error.getEType(), error.getParams()); } - asRep = sendASRequest(princ, secretKeys, ke.getError()); + asRep = sendASRequest(princ, secretKeys, eTypes, ke.getError()); } else { throw ke; } @@ -406,17 +417,18 @@ /** * Sends the AS-REQ + * @param eTypes not null if caller using keytab */ private static KrbAsRep sendASRequest(PrincipalName princ, - EncryptionKey[] secretKeys, KRBError error) + EncryptionKey[] secretKeys, int[] eTypes, KRBError error) throws KrbException, IOException { // %%% KrbAsReq asReq = null; if (error == null) { - asReq = new KrbAsReq(princ, secretKeys); + asReq = new KrbAsReq(princ, secretKeys, eTypes); } else { - asReq = new KrbAsReq(princ, secretKeys, true, + asReq = new KrbAsReq(princ, secretKeys, eTypes, true, error.getEType(), error.getSalt(), error.getParams()); } diff -r f66c0faf0184 -r 4b9857e483c1 jdk/src/share/classes/sun/security/krb5/EncryptionKey.java --- a/jdk/src/share/classes/sun/security/krb5/EncryptionKey.java Wed Jun 02 17:53:54 2010 -0700 +++ b/jdk/src/share/classes/sun/security/krb5/EncryptionKey.java Fri Jun 04 19:28:53 2010 +0800 @@ -76,6 +76,26 @@ private static final boolean DEBUG = Krb5.DEBUG; + public static int[] getETypes(EncryptionKey[] keys) { + int len = keys.length; + int[] result = new int[len]; + int count = 0; // Number of elements in result. Might be less than + // len if there are keys having the same etype + loopi: for (int i=0; i 0) { + key = EncryptionKey.findKey(availableETypes[0], keys); + } + } if (DEBUG) { System.out.println("AS-REQ: Add PA_ENC_TIMESTAMP now"); } @@ -376,7 +384,7 @@ } if (eTypes == null) { - eTypes = tktETypes; + eTypes = EType.getDefaults("default_tkt_enctypes"); } // check to use addresses in tickets diff -r f66c0faf0184 -r 4b9857e483c1 jdk/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java --- a/jdk/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java Wed Jun 02 17:53:54 2010 -0700 +++ b/jdk/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java Fri Jun 04 19:28:53 2010 +0800 @@ -229,7 +229,9 @@ if (useKeytab) { as_req = new KrbAsReq(skeys, opt, principal, sname, - null, null, null, null, addresses, null); + null, null, null, + EncryptionKey.getETypes(skeys), + addresses, null); } else { as_req = new KrbAsReq(psswd, opt, principal, sname, @@ -257,7 +259,9 @@ if (useKeytab) { as_req = new KrbAsReq(skeys, true, etype, salt, s2kparams, opt, principal, sname, - null, null, null, null, addresses, null); + null, null, null, + EncryptionKey.getETypes(skeys), + addresses, null); } else { as_req = new KrbAsReq(psswd, true, etype, salt, s2kparams, opt, principal, sname, diff -r f66c0faf0184 -r 4b9857e483c1 jdk/test/sun/security/krb5/auto/Context.java --- a/jdk/test/sun/security/krb5/auto/Context.java Wed Jun 02 17:53:54 2010 -0700 +++ b/jdk/test/sun/security/krb5/auto/Context.java Fri Jun 04 19:28:53 2010 +0800 @@ -42,6 +42,7 @@ import com.sun.security.jgss.ExtendedGSSContext; import com.sun.security.jgss.InquireType; import com.sun.security.jgss.AuthorizationDataEntry; +import java.io.File; /** * Context of a JGSS subject, encapsulating Subject and GSSContext. @@ -107,7 +108,8 @@ * Logins with a username and a password, using Krb5LoginModule directly * @param storeKey true if key should be saved, used on acceptor side */ - public static Context fromUserPass(String user, char[] pass, boolean storeKey) throws Exception { + public static Context fromUserPass(String user, char[] pass, boolean storeKey) + throws Exception { Context out = new Context(); out.name = user; out.s = new Subject(); @@ -137,6 +139,33 @@ } /** + * Logins with a username and a keytab, using Krb5LoginModule directly + * @param storeKey true if key should be saved, used on acceptor side + */ + public static Context fromUserKtab(String user, String ktab, boolean storeKey) + throws Exception { + Context out = new Context(); + out.name = user; + out.s = new Subject(); + Krb5LoginModule krb5 = new Krb5LoginModule(); + Map map = new HashMap(); + + map.put("doNotPrompt", "true"); + map.put("useTicketCache", "false"); + map.put("useKeyTab", "true"); + map.put("keyTab", ktab); + map.put("principal", user); + if (storeKey) { + map.put("storeKey", "true"); + } + + krb5.initialize(out.s, null, null, map); + krb5.login(); + krb5.commit(); + return out; + } + + /** * Starts as a client * @param target communication peer * @param mech GSS mech diff -r f66c0faf0184 -r 4b9857e483c1 jdk/test/sun/security/krb5/auto/KDC.java --- a/jdk/test/sun/security/krb5/auto/KDC.java Wed Jun 02 17:53:54 2010 -0700 +++ b/jdk/test/sun/security/krb5/auto/KDC.java Fri Jun 04 19:28:53 2010 +0800 @@ -35,6 +35,7 @@ import sun.security.krb5.*; import sun.security.krb5.internal.*; import sun.security.krb5.internal.ccache.CredentialsCache; +import sun.security.krb5.internal.crypto.EType; import sun.security.krb5.internal.crypto.KeyUsage; import sun.security.krb5.internal.ktab.KeyTab; import sun.security.util.DerInputStream; @@ -153,6 +154,10 @@ * Whether pre-authentication is required. Default Boolean.TRUE */ PREAUTH_REQUIRED, + /** + * Onlyy issue TGT in RC4 + */ + ONLY_RC4_TGT, }; static { @@ -743,13 +748,25 @@ Field f = KDCReqBody.class.getDeclaredField("eType"); f.setAccessible(true); eTypes = (int[])f.get(body); - if (eTypes.length < 2) { - throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP); - } int eType = eTypes[0]; EncryptionKey ckey = keyForUser(body.cname, eType, false); EncryptionKey skey = keyForUser(body.sname, eType, true); + + if (options.containsKey(KDC.Option.ONLY_RC4_TGT)) { + int tgtEType = EncryptedData.ETYPE_ARCFOUR_HMAC; + boolean found = false; + for (int i=0; i ctor = EncryptedData.class.getDeclaredConstructor(DerValue.class); ctor.setAccessible(true); EncryptedData data = ctor.newInstance(new DerValue(pas[0].getValue())); - data.decrypt(ckey, KeyUsage.KU_PA_ENC_TS); + EncryptionKey pakey = keyForUser(body.cname, data.getEType(), false); + data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS); } catch (Exception e) { throw new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED); } diff -r f66c0faf0184 -r 4b9857e483c1 jdk/test/sun/security/krb5/auto/W83.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/W83.java Fri Jun 04 19:28:53 2010 +0800 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6951366 + * @summary kerberos login failure on win2008 with AD set to win2000 compat mode + */ +import com.sun.security.auth.module.Krb5LoginModule; +import java.io.File; +import sun.security.krb5.Config; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.PrincipalName; +import sun.security.krb5.internal.crypto.EType; +import sun.security.krb5.internal.ktab.KeyTab; + +public class W83 { + public static void main(String[] args) throws Exception { + + W83 x = new W83(); + + // Cannot use OneKDC. kinit command cannot resolve + // hostname kdc.rabbit.hole + KDC kdc = new KDC(OneKDC.REALM, "127.0.0.1", 0, true); + kdc.addPrincipal(OneKDC.USER, OneKDC.PASS); + kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); + KDC.saveConfig(OneKDC.KRB5_CONF, kdc); + System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF); + Config.refresh(); + + kdc.writeKtab(OneKDC.KTAB); + new File(OneKDC.KRB5_CONF).deleteOnExit(); + new File(OneKDC.KTAB).deleteOnExit(); + + kdc.setOption(KDC.Option.ONLY_RC4_TGT, true); + + KeyTab ktab = KeyTab.getInstance(OneKDC.KTAB); + for (int etype: EType.getBuiltInDefaults()) { + if (etype != EncryptedData.ETYPE_ARCFOUR_HMAC) { + ktab.deleteEntry(new PrincipalName(OneKDC.USER), etype); + } + } + ktab.save(); + x.go(); + } + + void go() throws Exception { + Krb5LoginModule krb5 = new Krb5LoginModule(); + StringBuffer error = new StringBuffer(); + try { + Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + } catch (Exception e) { + error.append("Krb5LoginModule password login error\n"); + } + try { + Context.fromUserKtab(OneKDC.USER, OneKDC.KTAB, false); + } catch (Exception e) { + error.append("Krb5LoginModule keytab login error\n"); + } + try { + Class.forName("sun.security.krb5.internal.tools.Kinit"); + String cmd = System.getProperty("java.home") + + System.getProperty("file.separator") + + "bin" + + System.getProperty("file.separator") + + "kinit"; + + int p = execute( + cmd, + "-J-Djava.security.krb5.conf=" + OneKDC.KRB5_CONF, + "-c", "cache1", + OneKDC.USER, + new String(OneKDC.PASS)); + if (p != 0) { + error.append("kinit password login error\n"); + } + p = execute( + cmd, + "-J-Djava.security.krb5.conf=" + OneKDC.KRB5_CONF, + "-c", "cache2", + "-k", "-t", OneKDC.KTAB, + OneKDC.USER); + if (p != 0) { + error.append("kinit keytab login error\n"); + } + } catch (ClassNotFoundException cnfe) { + System.out.println("No kinit, test ignored."); + // Ignore, not on windows + } + if (error.length() != 0) { + throw new Exception(error.toString()); + } + } + + private static int execute(String... args) throws Exception { + for (String arg: args) { + System.out.printf("%s ", arg); + } + System.out.println(); + Process p = Runtime.getRuntime().exec(args); + return p.waitFor(); + } +}