提交 d27683e6 编写于 作者: W weijun

6951366: kerberos login failure on win2008 with AD set to win2000 compat mode

Reviewed-by: valeriep, xuelei
上级 cc676ae4
...@@ -356,6 +356,7 @@ public class Credentials { ...@@ -356,6 +356,7 @@ public class Credentials {
* @param princ the client principal. This value cannot be null. * @param princ the client principal. This value cannot be null.
* @param secretKey the secret key of the client principal.This value * @param secretKey the secret key of the client principal.This value
* cannot be null. * cannot be null.
* @param password if null, caller is using a keytab
* @returns the TGT credentials * @returns the TGT credentials
*/ */
public static Credentials acquireTGT(PrincipalName princ, public static Credentials acquireTGT(PrincipalName princ,
...@@ -372,8 +373,18 @@ public class Credentials { ...@@ -372,8 +373,18 @@ public class Credentials {
"Cannot have null secretKey to do AS-Exchange"); "Cannot have null secretKey to do AS-Exchange");
KrbAsRep asRep = null; 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 { try {
asRep = sendASRequest(princ, secretKeys, null); asRep = sendASRequest(princ, secretKeys, eTypes, null);
} catch (KrbException ke) { } catch (KrbException ke) {
if ((ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) || if ((ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) ||
(ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) { (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) {
...@@ -396,7 +407,7 @@ public class Credentials { ...@@ -396,7 +407,7 @@ public class Credentials {
princ.getSalt(), true, princ.getSalt(), true,
error.getEType(), error.getParams()); error.getEType(), error.getParams());
} }
asRep = sendASRequest(princ, secretKeys, ke.getError()); asRep = sendASRequest(princ, secretKeys, eTypes, ke.getError());
} else { } else {
throw ke; throw ke;
} }
...@@ -406,17 +417,18 @@ public class Credentials { ...@@ -406,17 +417,18 @@ public class Credentials {
/** /**
* Sends the AS-REQ * Sends the AS-REQ
* @param eTypes not null if caller using keytab
*/ */
private static KrbAsRep sendASRequest(PrincipalName princ, private static KrbAsRep sendASRequest(PrincipalName princ,
EncryptionKey[] secretKeys, KRBError error) EncryptionKey[] secretKeys, int[] eTypes, KRBError error)
throws KrbException, IOException { throws KrbException, IOException {
// %%% // %%%
KrbAsReq asReq = null; KrbAsReq asReq = null;
if (error == null) { if (error == null) {
asReq = new KrbAsReq(princ, secretKeys); asReq = new KrbAsReq(princ, secretKeys, eTypes);
} else { } else {
asReq = new KrbAsReq(princ, secretKeys, true, asReq = new KrbAsReq(princ, secretKeys, eTypes, true,
error.getEType(), error.getSalt(), error.getParams()); error.getEType(), error.getSalt(), error.getParams());
} }
......
...@@ -76,6 +76,26 @@ public class EncryptionKey ...@@ -76,6 +76,26 @@ public class EncryptionKey
private static final boolean DEBUG = Krb5.DEBUG; 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<len; i++) {
int eType = keys[i].getEType();
for (int j=0; j<count; j++) {
if (result[j] == eType) {
continue loopi;
}
}
result[count++] = eType;
}
if (count != len) {
result = Arrays.copyOf(result, count);
}
return result;
}
public synchronized int getEType() { public synchronized int getEType() {
return keyType; return keyType;
} }
......
...@@ -35,11 +35,9 @@ import sun.security.krb5.internal.*; ...@@ -35,11 +35,9 @@ import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.EType; import sun.security.krb5.internal.crypto.EType;
import sun.security.krb5.internal.crypto.Nonce; import sun.security.krb5.internal.crypto.Nonce;
import sun.security.krb5.internal.crypto.KeyUsage; import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.util.*;
import java.io.IOException; import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.StringTokenizer; import java.util.Arrays;
/** /**
* This class encapsulates the KRB-AS-REQ message that the client * This class encapsulates the KRB-AS-REQ message that the client
...@@ -64,11 +62,13 @@ public class KrbAsReq extends KrbKdcReq { ...@@ -64,11 +62,13 @@ public class KrbAsReq extends KrbKdcReq {
/** /**
* Creates a KRB-AS-REQ to send to the default KDC * Creates a KRB-AS-REQ to send to the default KDC
* @param eTypes not null when using a keytab, this can make sure the etypes
* in AS-REQ contains only those available on client
* @throws KrbException * @throws KrbException
* @throws IOException * @throws IOException
*/ */
// Called by Credentials // Called by Credentials
KrbAsReq(PrincipalName principal, EncryptionKey[] keys) KrbAsReq(PrincipalName principal, EncryptionKey[] keys, int[] eTypes)
throws KrbException, IOException { throws KrbException, IOException {
this(keys, // for pre-authentication this(keys, // for pre-authentication
false, 0, null, null, // pre-auth values false, 0, null, null, // pre-auth values
...@@ -78,7 +78,7 @@ public class KrbAsReq extends KrbKdcReq { ...@@ -78,7 +78,7 @@ public class KrbAsReq extends KrbKdcReq {
null, // KerberosTime from null, // KerberosTime from
null, // KerberosTime till null, // KerberosTime till
null, // KerberosTime rtime null, // KerberosTime rtime
null, // int[] eTypes eTypes, // int[] eTypes
null, // HostAddresses addresses null, // HostAddresses addresses
null); // Ticket[] additionalTickets null); // Ticket[] additionalTickets
} }
...@@ -86,8 +86,10 @@ public class KrbAsReq extends KrbKdcReq { ...@@ -86,8 +86,10 @@ public class KrbAsReq extends KrbKdcReq {
/** /**
* Creates a KRB-AS-REQ to send to the default KDC * Creates a KRB-AS-REQ to send to the default KDC
* with pre-authentication values * with pre-authentication values
* @param eTypes not null when using a keytab, this can make sure the etypes
* in AS-REQ contains only those available on client
*/ */
KrbAsReq(PrincipalName principal, EncryptionKey[] keys, KrbAsReq(PrincipalName principal, EncryptionKey[] keys, int[] eTypes,
boolean pa_exists, int etype, String salt, byte[] s2kparams) boolean pa_exists, int etype, String salt, byte[] s2kparams)
throws KrbException, IOException { throws KrbException, IOException {
this(keys, // for pre-authentication this(keys, // for pre-authentication
...@@ -98,7 +100,7 @@ public class KrbAsReq extends KrbKdcReq { ...@@ -98,7 +100,7 @@ public class KrbAsReq extends KrbKdcReq {
null, // KerberosTime from null, // KerberosTime from
null, // KerberosTime till null, // KerberosTime till
null, // KerberosTime rtime null, // KerberosTime rtime
null, // int[] eTypes eTypes, // int[] eTypes
null, // HostAddresses addresses null, // HostAddresses addresses
null); // Ticket[] additionalTickets null); // Ticket[] additionalTickets
} }
...@@ -343,19 +345,25 @@ public class KrbAsReq extends KrbKdcReq { ...@@ -343,19 +345,25 @@ public class KrbAsReq extends KrbKdcReq {
princName = cname; princName = cname;
EncryptionKey key = null; // keys might contain many etypes, or only one if in preauth mode,
int[] tktETypes = EType.getDefaults("default_tkt_enctypes"); // coz EncryptionKey.acquireSecretKeys() with pa returns only one key.
if (pa_exists && pa_etype != EncryptedData.ETYPE_NULL) {
if (DEBUG) {
System.out.println("Pre-Authenticaton: find key for etype = " + pa_etype);
}
key = EncryptionKey.findKey(pa_etype, keys);
} else {
key = EncryptionKey.findKey(tktETypes[0], keys);
}
PAData[] paData = null; PAData[] paData = null;
if (PA_ENC_TIMESTAMP_REQUIRED) { if (PA_ENC_TIMESTAMP_REQUIRED) {
EncryptionKey key = null;
if (pa_etype != EncryptedData.ETYPE_NULL) {
if (DEBUG) {
System.out.println("Pre-Authenticaton: " +
"find key for etype = " + pa_etype);
}
key = EncryptionKey.findKey(pa_etype, keys);
} else {
int[] availableETypes =
EType.getDefaults("default_tkt_enctypes", keys);
if (availableETypes.length > 0) {
key = EncryptionKey.findKey(availableETypes[0], keys);
}
}
if (DEBUG) { if (DEBUG) {
System.out.println("AS-REQ: Add PA_ENC_TIMESTAMP now"); System.out.println("AS-REQ: Add PA_ENC_TIMESTAMP now");
} }
...@@ -376,7 +384,7 @@ public class KrbAsReq extends KrbKdcReq { ...@@ -376,7 +384,7 @@ public class KrbAsReq extends KrbKdcReq {
} }
if (eTypes == null) { if (eTypes == null) {
eTypes = tktETypes; eTypes = EType.getDefaults("default_tkt_enctypes");
} }
// check to use addresses in tickets // check to use addresses in tickets
......
...@@ -229,7 +229,9 @@ public class Kinit { ...@@ -229,7 +229,9 @@ public class Kinit {
if (useKeytab) { if (useKeytab) {
as_req = new KrbAsReq(skeys, opt, as_req = new KrbAsReq(skeys, opt,
principal, sname, principal, sname,
null, null, null, null, addresses, null); null, null, null,
EncryptionKey.getETypes(skeys),
addresses, null);
} else { } else {
as_req = new KrbAsReq(psswd, opt, as_req = new KrbAsReq(psswd, opt,
principal, sname, principal, sname,
...@@ -257,7 +259,9 @@ public class Kinit { ...@@ -257,7 +259,9 @@ public class Kinit {
if (useKeytab) { if (useKeytab) {
as_req = new KrbAsReq(skeys, true, etype, salt, as_req = new KrbAsReq(skeys, true, etype, salt,
s2kparams, opt, principal, sname, s2kparams, opt, principal, sname,
null, null, null, null, addresses, null); null, null, null,
EncryptionKey.getETypes(skeys),
addresses, null);
} else { } else {
as_req = new KrbAsReq(psswd, true, etype, salt, as_req = new KrbAsReq(psswd, true, etype, salt,
s2kparams, opt, principal, sname, s2kparams, opt, principal, sname,
......
...@@ -42,6 +42,7 @@ import org.ietf.jgss.Oid; ...@@ -42,6 +42,7 @@ import org.ietf.jgss.Oid;
import com.sun.security.jgss.ExtendedGSSContext; import com.sun.security.jgss.ExtendedGSSContext;
import com.sun.security.jgss.InquireType; import com.sun.security.jgss.InquireType;
import com.sun.security.jgss.AuthorizationDataEntry; import com.sun.security.jgss.AuthorizationDataEntry;
import java.io.File;
/** /**
* Context of a JGSS subject, encapsulating Subject and GSSContext. * Context of a JGSS subject, encapsulating Subject and GSSContext.
...@@ -107,7 +108,8 @@ public class Context { ...@@ -107,7 +108,8 @@ public class Context {
* Logins with a username and a password, using Krb5LoginModule directly * Logins with a username and a password, using Krb5LoginModule directly
* @param storeKey true if key should be saved, used on acceptor side * @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(); Context out = new Context();
out.name = user; out.name = user;
out.s = new Subject(); out.s = new Subject();
...@@ -136,6 +138,33 @@ public class Context { ...@@ -136,6 +138,33 @@ public class Context {
return out; return out;
} }
/**
* 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<String, String> map = new HashMap<String, String>();
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 * Starts as a client
* @param target communication peer * @param target communication peer
......
...@@ -35,6 +35,7 @@ import sun.net.spi.nameservice.NameServiceDescriptor; ...@@ -35,6 +35,7 @@ import sun.net.spi.nameservice.NameServiceDescriptor;
import sun.security.krb5.*; import sun.security.krb5.*;
import sun.security.krb5.internal.*; import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.CredentialsCache; 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.crypto.KeyUsage;
import sun.security.krb5.internal.ktab.KeyTab; import sun.security.krb5.internal.ktab.KeyTab;
import sun.security.util.DerInputStream; import sun.security.util.DerInputStream;
...@@ -153,6 +154,10 @@ public class KDC { ...@@ -153,6 +154,10 @@ public class KDC {
* Whether pre-authentication is required. Default Boolean.TRUE * Whether pre-authentication is required. Default Boolean.TRUE
*/ */
PREAUTH_REQUIRED, PREAUTH_REQUIRED,
/**
* Onlyy issue TGT in RC4
*/
ONLY_RC4_TGT,
}; };
static { static {
...@@ -743,13 +748,25 @@ public class KDC { ...@@ -743,13 +748,25 @@ public class KDC {
Field f = KDCReqBody.class.getDeclaredField("eType"); Field f = KDCReqBody.class.getDeclaredField("eType");
f.setAccessible(true); f.setAccessible(true);
eTypes = (int[])f.get(body); eTypes = (int[])f.get(body);
if (eTypes.length < 2) {
throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
}
int eType = eTypes[0]; int eType = eTypes[0];
EncryptionKey ckey = keyForUser(body.cname, eType, false); EncryptionKey ckey = keyForUser(body.cname, eType, false);
EncryptionKey skey = keyForUser(body.sname, eType, true); 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<eTypes.length; i++) {
if (eTypes[i] == tgtEType) {
found = true;
break;
}
}
if (!found) {
throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
}
skey = keyForUser(body.sname, tgtEType, true);
}
if (ckey == null) { if (ckey == null) {
throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP); throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
} }
...@@ -799,7 +816,8 @@ public class KDC { ...@@ -799,7 +816,8 @@ public class KDC {
Constructor<EncryptedData> ctor = EncryptedData.class.getDeclaredConstructor(DerValue.class); Constructor<EncryptedData> ctor = EncryptedData.class.getDeclaredConstructor(DerValue.class);
ctor.setAccessible(true); ctor.setAccessible(true);
EncryptedData data = ctor.newInstance(new DerValue(pas[0].getValue())); 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) { } catch (Exception e) {
throw new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED); throw new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED);
} }
......
/*
* 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();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册