From 252806185bfba9bd73498e89e1c8d0e06bea5050 Mon Sep 17 00:00:00 2001 From: weijun Date: Wed, 7 Sep 2011 08:56:55 +0800 Subject: [PATCH] 7067974: multiple ETYPE-INFO-ENTRY with same etype and different salt Reviewed-by: valeriep --- .../sun/security/krb5/EncryptionKey.java | 25 +++ .../classes/sun/security/krb5/KrbAsRep.java | 10 +- .../sun/security/krb5/KrbAsReqBuilder.java | 76 +++---- .../sun/security/krb5/internal/KRBError.java | 49 +---- .../sun/security/krb5/internal/PAData.java | 194 ++++++++++++++---- test/sun/security/krb5/auto/DupEtypes.java | 63 ++++++ test/sun/security/krb5/auto/KDC.java | 132 ++++++++---- 7 files changed, 380 insertions(+), 169 deletions(-) create mode 100644 test/sun/security/krb5/auto/DupEtypes.java diff --git a/src/share/classes/sun/security/krb5/EncryptionKey.java b/src/share/classes/sun/security/krb5/EncryptionKey.java index 1de032945..ebf794e91 100644 --- a/src/share/classes/sun/security/krb5/EncryptionKey.java +++ b/src/share/classes/sun/security/krb5/EncryptionKey.java @@ -150,12 +150,37 @@ public class EncryptionKey return ktab.readServiceKeys(princ); } + /** + * Obtains a key for a given etype of a principal with possible new salt + * and s2kparams + * @param cname NOT null + * @param password NOT null + * @param etype + * @param snp can be NULL + * @returns never null + */ + public static EncryptionKey acquireSecretKey(PrincipalName cname, + char[] password, int etype, PAData.SaltAndParams snp) + throws KrbException { + String salt; + byte[] s2kparams; + if (snp != null) { + salt = snp.salt != null ? snp.salt : cname.getSalt(); + s2kparams = snp.params; + } else { + salt = cname.getSalt(); + s2kparams = null; + } + return acquireSecretKey(password, salt, etype, s2kparams); + } + /** * Obtains a key for a given etype with salt and optional s2kparams * @param password NOT null * @param salt NOT null * @param etype * @param s2kparams can be NULL + * @returns never null */ public static EncryptionKey acquireSecretKey(char[] password, String salt, int etype, byte[] s2kparams) diff --git a/src/share/classes/sun/security/krb5/KrbAsRep.java b/src/share/classes/sun/security/krb5/KrbAsRep.java index 29a7a5b4c..c2b0df30d 100644 --- a/src/share/classes/sun/security/krb5/KrbAsRep.java +++ b/src/share/classes/sun/security/krb5/KrbAsRep.java @@ -131,13 +131,11 @@ class KrbAsRep extends KrbKdcRep { KrbAsReq asReq, PrincipalName cname) throws KrbException, Asn1Exception, IOException { int encPartKeyType = rep.encPart.getEType(); - PAData.SaltAndParams snp = - PAData.getSaltAndParams(encPartKeyType, rep.pAData); - EncryptionKey dkey = null; - dkey = EncryptionKey.acquireSecretKey(password, - snp.salt == null ? cname.getSalt() : snp.salt, + EncryptionKey dkey = EncryptionKey.acquireSecretKey( + cname, + password, encPartKeyType, - snp.params); + PAData.getSaltAndParams(encPartKeyType, rep.pAData)); decrypt(dkey, asReq); } diff --git a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java index dd9b8dcfb..3426d8b20 100644 --- a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java +++ b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java @@ -169,34 +169,44 @@ public final class KrbAsReqBuilder { * from a keytab on acceptor, but unfortunately (?) Java supports * acceptor using password. In this case, if the service ticket is * encrypted using an etype which we don't have PA-DATA new salt, - * using the default salt is normally wrong (say, case-insensitive + * using the default salt might be wrong (say, case-insensitive * user name). Instead, we would use the new salt of another etype. */ String salt = null; // the saved new salt - for (int i=0; i>>Pre-Authentication Data:"); - System.out.println("\t PA-DATA type = " + pa_type); - } - - switch(pa_type) { - case Krb5.PA_ENC_TIMESTAMP: - if (DEBUG) { - System.out.println("\t PA-ENC-TIMESTAMP"); - } - break; - case Krb5.PA_ETYPE_INFO: - if (pa_value != null) { - DerValue der = new DerValue(pa_value); - while (der.data.available() > 0) { - DerValue value = der.data.getDerValue(); - ETypeInfo info = new ETypeInfo(value); - if (pa_eType == 0) pa_eType = info.getEType(); - if (DEBUG) { - System.out.println("\t PA-ETYPE-INFO etype = " + info.getEType()); - System.out.println("\t PA-ETYPE-INFO salt = " + info.getSalt()); - } - } - } - break; - case Krb5.PA_ETYPE_INFO2: - if (pa_value != null) { - DerValue der = new DerValue(pa_value); - while (der.data.available() > 0) { - DerValue value = der.data.getDerValue(); - ETypeInfo2 info2 = new ETypeInfo2(value); - if (pa_eType == 0) pa_eType = info2.getEType(); - if (DEBUG) { - System.out.println("\t PA-ETYPE-INFO2 etype = " + info2.getEType()); - System.out.println("\t PA-ETYPE-INFO2 salt = " + info2.getSalt()); - } - } - } - break; - default: - // Unknown Pre-auth type - break; + System.out.println(pa_data); } } pa = paList.toArray(new PAData[paList.size()]); @@ -340,10 +297,6 @@ public class KRBError implements java.io.Serializable { return pa; } - public final int getEType() { - return pa_eType; - } - public final String getErrorString() { return eText; } diff --git a/src/share/classes/sun/security/krb5/internal/PAData.java b/src/share/classes/sun/security/krb5/internal/PAData.java index d32bbc47e..573b4f270 100644 --- a/src/share/classes/sun/security/krb5/internal/PAData.java +++ b/src/share/classes/sun/security/krb5/internal/PAData.java @@ -138,10 +138,57 @@ public class PAData { return ((pADataValue == null) ? null : pADataValue.clone()); } + /** + * Gets the preferred etype from the PAData array. + * 1. ETYPE-INFO2-ENTRY with unknown s2kparams ignored + * 2. ETYPE-INFO2 preferred to ETYPE-INFO + * 3. multiple entries for same etype in one PA-DATA, use the first one. + * 4. Multiple PA-DATA with same type, choose the last one + * (This is useful when PA-DATAs from KRB-ERROR and AS-REP are combined). + * @return the etype, or defaultEType if not enough info + * @throws Asn1Exception|IOException if there is an encoding error + */ + public static int getPreferredEType(PAData[] pas, int defaultEType) + throws IOException, Asn1Exception { + + if (pas == null) return defaultEType; + + DerValue d = null, d2 = null; + for (PAData p: pas) { + if (p.getValue() == null) continue; + switch (p.getType()) { + case Krb5.PA_ETYPE_INFO: + d = new DerValue(p.getValue()); + break; + case Krb5.PA_ETYPE_INFO2: + d2 = new DerValue(p.getValue()); + break; + } + } + if (d2 != null) { + while (d2.data.available() > 0) { + DerValue value = d2.data.getDerValue(); + ETypeInfo2 tmp = new ETypeInfo2(value); + if (tmp.getParams() == null) { + // we don't support non-null s2kparams + return tmp.getEType(); + } + } + } + if (d != null) { + while (d.data.available() > 0) { + DerValue value = d.data.getDerValue(); + ETypeInfo tmp = new ETypeInfo(value); + return tmp.getEType(); + } + } + return defaultEType; + } + /** * A place to store a pair of salt and s2kparams. - * An empty salt is changed to null, to be interopable - * with Windows 2000 server. + * An empty salt is changed to null, to be interoperable + * with Windows 2000 server. This is in fact not correct. */ public static class SaltAndParams { public final String salt; @@ -155,57 +202,120 @@ public class PAData { /** * Fetches salt and s2kparams value for eType in a series of PA-DATAs. - * The preference order is PA-ETYPE-INFO2 > PA-ETYPE-INFO > PA-PW-SALT. - * If multiple PA-DATA for the same etype appears, use the last one. + * 1. ETYPE-INFO2-ENTRY with unknown s2kparams ignored + * 2. PA-ETYPE-INFO2 preferred to PA-ETYPE-INFO preferred to PA-PW-SALT. + * 3. multiple entries for same etype in one PA-DATA, use the first one. + * 4. Multiple PA-DATA with same type, choose the last one * (This is useful when PA-DATAs from KRB-ERROR and AS-REP are combined). - * @return salt and s2kparams. never null, its field might be null. + * @return salt and s2kparams. can be null if not found */ public static SaltAndParams getSaltAndParams(int eType, PAData[] pas) - throws Asn1Exception, KrbException { + throws Asn1Exception, IOException { - if (pas == null || pas.length == 0) { - return new SaltAndParams(null, null); - } + if (pas == null) return null; + DerValue d = null, d2 = null; String paPwSalt = null; - ETypeInfo2 info2 = null; - ETypeInfo info = null; for (PAData p: pas) { - if (p.getValue() != null) { - try { - switch (p.getType()) { - case Krb5.PA_PW_SALT: - paPwSalt = new String(p.getValue(), - KerberosString.MSNAME?"UTF8":"8859_1"); - break; - case Krb5.PA_ETYPE_INFO: - DerValue der = new DerValue(p.getValue()); - while (der.data.available() > 0) { - DerValue value = der.data.getDerValue(); - ETypeInfo tmp = new ETypeInfo(value); - if (tmp.getEType() == eType) info = tmp; - } - break; - case Krb5.PA_ETYPE_INFO2: - der = new DerValue(p.getValue()); - while (der.data.available() > 0) { - DerValue value = der.data.getDerValue(); - ETypeInfo2 tmp = new ETypeInfo2(value); - if (tmp.getEType() == eType) info2 = tmp; - } - break; - } - } catch (IOException ioe) { - // Ignored + if (p.getValue() == null) continue; + switch (p.getType()) { + case Krb5.PA_PW_SALT: + paPwSalt = new String(p.getValue(), + KerberosString.MSNAME?"UTF8":"8859_1"); + break; + case Krb5.PA_ETYPE_INFO: + d = new DerValue(p.getValue()); + break; + case Krb5.PA_ETYPE_INFO2: + d2 = new DerValue(p.getValue()); + break; + } + } + if (d2 != null) { + while (d2.data.available() > 0) { + DerValue value = d2.data.getDerValue(); + ETypeInfo2 tmp = new ETypeInfo2(value); + if (tmp.getParams() == null && tmp.getEType() == eType) { + // we don't support non-null s2kparams + return new SaltAndParams(tmp.getSalt(), tmp.getParams()); } } } - if (info2 != null) { - return new SaltAndParams(info2.getSalt(), info2.getParams()); - } else if (info != null) { - return new SaltAndParams(info.getSalt(), null); + if (d != null) { + while (d.data.available() > 0) { + DerValue value = d.data.getDerValue(); + ETypeInfo tmp = new ETypeInfo(value); + if (tmp.getEType() == eType) { + return new SaltAndParams(tmp.getSalt(), null); + } + } + } + if (paPwSalt != null) { + return new SaltAndParams(paPwSalt, null); + } + return null; + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append(">>>Pre-Authentication Data:\n\t PA-DATA type = ") + .append(pADataType).append('\n'); + + switch(pADataType) { + case Krb5.PA_ENC_TIMESTAMP: + sb.append("\t PA-ENC-TIMESTAMP"); + break; + case Krb5.PA_ETYPE_INFO: + if (pADataValue != null) { + try { + DerValue der = new DerValue(pADataValue); + while (der.data.available() > 0) { + DerValue value = der.data.getDerValue(); + ETypeInfo info = new ETypeInfo(value); + sb.append("\t PA-ETYPE-INFO etype = ") + .append(info.getEType()) + .append(", salt = ") + .append(info.getSalt()) + .append('\n'); + } + } catch (IOException|Asn1Exception e) { + sb.append("\t \n"); + } + } + break; + case Krb5.PA_ETYPE_INFO2: + if (pADataValue != null) { + try { + DerValue der = new DerValue(pADataValue); + while (der.data.available() > 0) { + DerValue value = der.data.getDerValue(); + ETypeInfo2 info2 = new ETypeInfo2(value); + sb.append("\t PA-ETYPE-INFO2 etype = ") + .append(info2.getEType()) + .append(", salt = ") + .append(info2.getSalt()) + .append(", s2kparams = "); + byte[] s2kparams = info2.getParams(); + if (s2kparams == null) { + sb.append("null\n"); + } else if (s2kparams.length == 0) { + sb.append("empty\n"); + } else { + sb.append(new sun.misc.HexDumpEncoder() + .encodeBuffer(s2kparams)); + } + } + } catch (IOException|Asn1Exception e) { + sb.append("\t \n"); + } + } + break; + default: + // Unknown Pre-auth type + break; } - return new SaltAndParams(paPwSalt, null); + return sb.toString(); } } diff --git a/test/sun/security/krb5/auto/DupEtypes.java b/test/sun/security/krb5/auto/DupEtypes.java new file mode 100644 index 000000000..92da74bc3 --- /dev/null +++ b/test/sun/security/krb5/auto/DupEtypes.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011, 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 7067974 + * @summary multiple ETYPE-INFO-ENTRY with same etype and different salt + * @compile -XDignore.symbol.file DupEtypes.java + * @run main/othervm DupEtypes 1 + * @run main/othervm DupEtypes 2 + * @run main/othervm/fail DupEtypes 3 + * @run main/othervm DupEtypes 4 + * @run main/othervm DupEtypes 5 + */ + +import sun.security.jgss.GSSUtil; + +public class DupEtypes { + + public static void main(String[] args) throws Exception { + + OneKDC kdc = new OneKDC(null); + kdc.writeJAASConf(); + + // Different test cases, read KDC.processAsReq for details + kdc.setOption(KDC.Option.DUP_ETYPE, Integer.parseInt(args[0])); + + Context c, s; + c = Context.fromJAAS("client"); + s = Context.fromJAAS("server"); + + c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + + Context.handshake(c, s); + + Context.transmit("i say high --", c, s); + Context.transmit(" you say low", s, c); + + s.dispose(); + c.dispose(); + } +} diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java index e4b4a9d8b..9d924f6e8 100644 --- a/test/sun/security/krb5/auto/KDC.java +++ b/test/sun/security/krb5/auto/KDC.java @@ -174,6 +174,10 @@ public class KDC { * Set all name-type to a value in response */ RESP_NT, + /** + * Multiple ETYPE-INFO-ENTRY with same etype but different salt + */ + DUP_ETYPE, }; static { @@ -881,48 +885,104 @@ public class KDC { bFlags[Krb5.TKT_OPTS_INITIAL] = true; // Creating PA-DATA - int[] epas = eTypes; - if (options.containsKey(KDC.Option.RC4_FIRST_PREAUTH)) { - for (int i=1; i