提交 cce44f8f 编写于 作者: W weijun

6960894: Better AS-REQ creation and processing

Reviewed-by: valeriep
上级 be79dd59
......@@ -27,7 +27,6 @@
package com.sun.security.auth.module;
import java.io.*;
import java.net.*;
import java.text.MessageFormat;
import java.util.*;
......@@ -38,9 +37,6 @@ import javax.security.auth.login.*;
import javax.security.auth.spi.*;
import sun.security.krb5.*;
import sun.security.krb5.Config;
import sun.security.krb5.RealmException;
import sun.security.util.AuthResources;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.Credentials;
import sun.misc.HexDumpEncoder;
......@@ -685,32 +681,27 @@ public class Krb5LoginModule implements LoginModule {
}
}
KrbAsReqBuilder builder;
// We can't get the key from the keytab so prompt
if (encKeys == null) {
promptForPass(getPasswdFromSharedState);
encKeys = EncryptionKey.acquireSecretKeys(
password, principal.getSalt());
builder = new KrbAsReqBuilder(principal, password);
if (isInitiator) {
if (debug)
System.out.println("Acquire TGT using AS Exchange");
cred = Credentials.acquireTGT(principal,
encKeys, password);
// update keys after pre-auth
encKeys = EncryptionKey.acquireSecretKeys(password,
principal.getSalt());
// XXX Even if isInitiator=false, it might be
// better to do an AS-REQ so that keys can be
// updated with PA info
cred = builder.action().getCreds();
}
encKeys = builder.getKeys();
} else {
builder = new KrbAsReqBuilder(principal, encKeys);
if (isInitiator) {
if (debug)
System.out.println("Acquire TGT using AS Exchange");
cred = Credentials.acquireTGT(principal,
encKeys, password);
cred = builder.action().getCreds();
}
}
builder.destroy();
// Get the TGT using AS Exchange
if (debug) {
System.out.println("principal is " + principal);
HexDumpEncoder hd = new HexDumpEncoder();
......
......@@ -111,7 +111,7 @@ public class Config {
public static synchronized void refresh() throws KrbException {
singleton = new Config();
KeyTab.refresh();
KrbKdcReq.initStatic();
KdcComm.initStatic();
}
......
......@@ -347,94 +347,6 @@ public class Credentials {
}
}
/**
* Returns a TGT for the given client principal via an AS-Exchange.
* This method causes pre-authentication data to be sent in the
* AS-REQ.
*
* @param princ the client principal. This value cannot be null.
* @param secretKey the secret key of the client principal.This value
* cannot be null.
* @returns the TGT credentials
*/
public static Credentials acquireTGT(PrincipalName princ,
EncryptionKey[] secretKeys,
char[] password)
throws KrbException, IOException {
if (princ == null)
throw new IllegalArgumentException(
"Cannot have null principal to do AS-Exchange");
if (secretKeys == null)
throw new IllegalArgumentException(
"Cannot have null secretKey to do AS-Exchange");
KrbAsRep asRep = null;
try {
asRep = sendASRequest(princ, secretKeys, null);
} catch (KrbException ke) {
if ((ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) ||
(ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) {
// process pre-auth info
if (DEBUG) {
System.out.println("AcquireTGT: PREAUTH FAILED/REQUIRED," +
" re-send AS-REQ");
}
KRBError error = ke.getError();
// update salt in PrincipalName
String newSalt = error.getSalt();
if (newSalt != null && newSalt.length() > 0) {
princ.setSalt(newSalt);
}
// refresh keys
if (password != null) {
secretKeys = EncryptionKey.acquireSecretKeys(password,
princ.getSalt(), true,
error.getEType(), error.getParams());
}
asRep = sendASRequest(princ, secretKeys, ke.getError());
} else {
throw ke;
}
}
return asRep.getCreds();
}
/**
* Sends the AS-REQ
*/
private static KrbAsRep sendASRequest(PrincipalName princ,
EncryptionKey[] secretKeys, KRBError error)
throws KrbException, IOException {
// %%%
KrbAsReq asReq = null;
if (error == null) {
asReq = new KrbAsReq(princ, secretKeys);
} else {
asReq = new KrbAsReq(princ, secretKeys, true,
error.getEType(), error.getSalt(), error.getParams());
}
String kdc = null;
KrbAsRep asRep = null;
try {
kdc = asReq.send();
asRep = asReq.getReply(secretKeys);
} catch (KrbException ke) {
if (ke.returnCode() == Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
asReq.send(princ.getRealmString(), kdc, true);
asRep = asReq.getReply(secretKeys);
} else {
throw ke;
}
}
return asRep;
}
/**
* Acquires default credentials.
* <br>The possible locations for default credentials cache is searched in
......@@ -529,29 +441,6 @@ public class Credentials {
return CredentialsUtil.acquireServiceCreds(service, ccreds);
}
/*
* This method does the real job to request the service credential.
*/
private static Credentials serviceCreds(ServiceName service,
Credentials ccreds)
throws KrbException, IOException {
return new KrbTgsReq(
new KDCOptions(),
ccreds,
service,
null, // KerberosTime from
null, // KerberosTime till
null, // KerberosTime rtime
null, // int[] eTypes
null, // HostAddresses addresses
null, // AuthorizationData
null, // Ticket[] additionalTickets
null // EncryptionKey subSessionKey
).sendAndGetCreds();
}
public CredentialsCache getCache() {
return cache;
}
......
......@@ -156,6 +156,22 @@ public class EncryptionKey
return ktab.readServiceKeys(princ);
}
/**
* 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
*/
public static EncryptionKey acquireSecretKey(char[] password,
String salt, int etype, byte[] s2kparams)
throws KrbException {
return new EncryptionKey(
stringToKey(password, salt, s2kparams, etype),
etype, null);
}
/**
* Generate a list of keys using the given principal and password.
* Construct a key for each configured etype.
......@@ -169,19 +185,8 @@ public class EncryptionKey
* as the default in that case. If default_tkt_enctypes was set in
* the libdefaults of krb5.conf, then use that sequence.
*/
// Used in Krb5LoginModule
public static EncryptionKey[] acquireSecretKeys(char[] password,
String salt) throws KrbException {
return (acquireSecretKeys(password, salt, false, 0, null));
}
/**
* Generates a list of keys using the given principal, password,
* and the pre-authentication values.
*/
public static EncryptionKey[] acquireSecretKeys(char[] password,
String salt, boolean pa_exists, int pa_etype, byte[] pa_s2kparams)
throws KrbException {
String salt) throws KrbException {
int[] etypes = EType.getDefaults("default_tkt_enctypes");
if (etypes == null) {
......@@ -191,10 +196,8 @@ public class EncryptionKey
EncryptionKey[] encKeys = new EncryptionKey[etypes.length];
for (int i = 0; i < etypes.length; i++) {
if (EType.isSupported(etypes[i])) {
byte[] s2kparams = (pa_exists && etypes[i] == pa_etype)
? pa_s2kparams : null;
encKeys[i] = new EncryptionKey(
stringToKey(password, salt, s2kparams, etypes[i]),
stringToKey(password, salt, null, etypes[i]),
etypes[i], null);
} else {
if (DEBUG) {
......
......@@ -31,7 +31,6 @@
package sun.security.krb5;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Security;
import java.util.Locale;
......@@ -47,8 +46,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import sun.security.krb5.internal.KRBError;
public abstract class KrbKdcReq {
/**
* KDC-REQ/KDC-REP communication. No more base class for KrbAsReq and
* KrbTgsReq. This class is now communication only.
*/
public final class KdcComm {
// The following settings can be configured in [libdefaults]
// section of krb5.conf, which are global for all realms. Each of
......@@ -160,20 +164,23 @@ public abstract class KrbKdcReq {
KdcAccessibility.reset();
}
protected byte[] obuf;
protected byte[] ibuf;
/**
* Sends the provided data to the KDC of the specified realm.
* Returns the response from the KDC.
* Default realm/KDC is used if realm is null.
* @param realm the realm of the KDC where data is to be sent.
* @returns the kdc to which the AS request was sent to
* @exception InterruptedIOException if timeout expires
* @exception KrbException
* The instance fields
*/
private String realm;
public KdcComm(String realm) throws KrbException {
if (realm == null) {
realm = Config.getInstance().getDefaultRealm();
if (realm == null) {
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Cannot find default realm");
}
}
this.realm = realm;
}
public String send(String realm)
public byte[] send(byte[] obuf)
throws IOException, KrbException {
int udpPrefLimit = getRealmSpecificValue(
realm, "udp_preference_limit", defaultUdpPrefLimit);
......@@ -181,10 +188,10 @@ public abstract class KrbKdcReq {
boolean useTCP = (udpPrefLimit > 0 &&
(obuf != null && obuf.length > udpPrefLimit));
return (send(realm, useTCP));
return send(obuf, useTCP);
}
public String send(String realm, boolean useTCP)
private byte[] send(byte[] obuf, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
......@@ -205,10 +212,21 @@ public abstract class KrbKdcReq {
throw new KrbException("Cannot get kdc for realm " + realm);
}
String tempKdc = null; // may include the port number also
byte[] ibuf = null;
for (String tmp: KdcAccessibility.list(kdcList)) {
tempKdc = tmp;
try {
send(realm,tempKdc,useTCP);
ibuf = send(obuf,tempKdc,useTCP);
KRBError ke = null;
try {
ke = new KRBError(ibuf);
} catch (Exception e) {
// OK
}
if (ke != null && ke.getErrorCode() ==
Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
ibuf = send(obuf, tempKdc, true);
}
KdcAccessibility.removeBad(tempKdc);
break;
} catch (Exception e) {
......@@ -228,16 +246,16 @@ public abstract class KrbKdcReq {
throw (KrbException) savedException;
}
}
return tempKdc;
return ibuf;
}
// send the AS Request to the specified KDC
public void send(String realm, String tempKdc, boolean useTCP)
private byte[] send(byte[] obuf, String tempKdc, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
return;
return null;
int port = Krb5.KDC_INET_DEFAULT_PORT;
int retries = getRealmSpecificValue(
......@@ -302,11 +320,12 @@ public abstract class KrbKdcReq {
KdcCommunication kdcCommunication =
new KdcCommunication(kdc, port, useTCP, timeout, retries, obuf);
try {
ibuf = AccessController.doPrivileged(kdcCommunication);
byte[] ibuf = AccessController.doPrivileged(kdcCommunication);
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: #bytes read="
+ (ibuf != null ? ibuf.length : 0));
}
return ibuf;
} catch (PrivilegedActionException e) {
Exception wrappedException = e.getException();
if (wrappedException instanceof IOException) {
......@@ -315,10 +334,6 @@ public abstract class KrbKdcReq {
throw (KrbException) wrappedException;
}
}
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: #bytes read="
+ (ibuf != null ? ibuf.length : 0));
}
}
private static class KdcCommunication
......
......@@ -36,25 +36,24 @@ import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.krb5.internal.crypto.EType;
import sun.security.util.*;
import java.io.IOException;
import java.util.Objects;
/**
* This class encapsulates a AS-REP message that the KDC sends to the
* client.
*/
public class KrbAsRep extends KrbKdcRep {
class KrbAsRep extends KrbKdcRep {
private ASRep rep;
private Credentials creds;
private ASRep rep; // The AS-REP message
private Credentials creds; // The Credentials provide by the AS-REP
// message, created by initiator after calling
// the decrypt() method
private boolean DEBUG = Krb5.DEBUG;
KrbAsRep(byte[] ibuf, EncryptionKey[] keys, KrbAsReq asReq) throws
KrbException, Asn1Exception, IOException {
if (keys == null)
throw new KrbException(Krb5.API_INVALID_ARG);
KrbAsRep(byte[] ibuf) throws
KrbException, Asn1Exception, IOException {
DerValue encoding = new DerValue(ibuf);
ASReq req = asReq.getMessage();
ASRep rep = null;
try {
rep = new ASRep(encoding);
} catch (Asn1Exception e) {
......@@ -83,25 +82,77 @@ public class KrbAsRep extends KrbKdcRep {
ke.initCause(e);
throw ke;
}
}
int encPartKeyType = rep.encPart.getEType();
EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, keys);
// KrbAsReqBuilder need to read back the PA for key generation
PAData[] getPA() {
return rep.pAData;
}
/**
* Called by KrbAsReqBuilder to resolve a AS-REP message using keys.
* @param keys user provided keys, not null
* @param asReq the original AS-REQ sent, used to validate AS-REP
*/
void decryptUsingKeys(EncryptionKey[] keys, KrbAsReq asReq)
throws KrbException, Asn1Exception, IOException {
EncryptionKey dkey = null;
int encPartKeyType = rep.encPart.getEType();
Integer encPartKvno = rep.encPart.kvno;
try {
dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno, keys);
} catch (KrbException ke) {
if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) {
// Fallback to no kvno. In some cases, keytab is generated
// not by sysadmin but Java's ktab command
dkey = EncryptionKey.findKey(encPartKeyType, keys);
}
}
if (dkey == null) {
throw new KrbException(Krb5.API_INVALID_ARG,
"Cannot find key of appropriate type to decrypt AS REP - " +
EType.toString(encPartKeyType));
"Cannot find key for type/kvno to decrypt AS REP - " +
EType.toString(encPartKeyType) + "/" + encPartKvno);
}
decrypt(dkey, asReq);
}
/**
* Called by KrbAsReqBuilder to resolve a AS-REP message using a password.
* @param password user provided password. not null
* @param asReq the original AS-REQ sent, used to validate AS-REP
* @param cname the user principal name, used to provide salt
*/
void decryptUsingPassword(char[] password,
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,
encPartKeyType,
snp.params);
decrypt(dkey, asReq);
}
/**
* Decrypts encrypted content inside AS-REP. Called by initiator.
* @param dkey the decryption key to use
* @param asReq the original AS-REQ sent, used to validate AS-REP
*/
private void decrypt(EncryptionKey dkey, KrbAsReq asReq)
throws KrbException, Asn1Exception, IOException {
byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey,
KeyUsage.KU_ENC_AS_REP_PART);
byte[] enc_as_rep_part = rep.encPart.reset(enc_as_rep_bytes);
encoding = new DerValue(enc_as_rep_part);
DerValue encoding = new DerValue(enc_as_rep_part);
EncASRepPart enc_part = new EncASRepPart(encoding);
rep.ticket.sname.setRealm(rep.ticket.realm);
rep.encKDCRepPart = enc_part;
ASReq req = asReq.getMessage();
check(req, rep);
creds = new Credentials(
......@@ -119,17 +170,13 @@ public class KrbAsRep extends KrbKdcRep {
System.out.println(">>> KrbAsRep cons in KrbAsReq.getReply " +
req.reqBody.cname.getNameString());
}
this.rep = rep;
this.creds = creds;
}
public Credentials getCreds() {
return creds;
Credentials getCreds() {
return Objects.nonNull(creds, "Creds not available yet.");
}
// made public for Kinit
public sun.security.krb5.internal.ccache.Credentials setCredentials() {
sun.security.krb5.internal.ccache.Credentials getCCreds() {
return new sun.security.krb5.internal.ccache.Credentials(rep);
}
}
......@@ -32,291 +32,39 @@
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.EType;
import sun.security.krb5.internal.crypto.Nonce;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.util.*;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
/**
* This class encapsulates the KRB-AS-REQ message that the client
* sends to the KDC.
*/
public class KrbAsReq extends KrbKdcReq {
private PrincipalName princName;
public class KrbAsReq {
private ASReq asReqMessg;
private boolean DEBUG = Krb5.DEBUG;
private static KDCOptions defaultKDCOptions = new KDCOptions();
// pre-auth info
private boolean PA_ENC_TIMESTAMP_REQUIRED = false;
private boolean pa_exists = false;
private int pa_etype = 0;
private String pa_salt = null;
private byte[] pa_s2kparams = null;
// default is address-less tickets
private boolean KDC_EMPTY_ADDRESSES_ALLOWED = true;
/**
* Creates a KRB-AS-REQ to send to the default KDC
* @throws KrbException
* @throws IOException
* Constructs an AS-REQ message.
*/
// Called by Credentials
KrbAsReq(PrincipalName principal, EncryptionKey[] keys)
throws KrbException, IOException {
this(keys, // for pre-authentication
false, 0, null, null, // pre-auth values
defaultKDCOptions,
principal,
null, // PrincipalName sname
null, // KerberosTime from
null, // KerberosTime till
null, // KerberosTime rtime
null, // int[] eTypes
null, // HostAddresses addresses
null); // Ticket[] additionalTickets
}
/**
* Creates a KRB-AS-REQ to send to the default KDC
* with pre-authentication values
*/
KrbAsReq(PrincipalName principal, EncryptionKey[] keys,
boolean pa_exists, int etype, String salt, byte[] s2kparams)
throws KrbException, IOException {
this(keys, // for pre-authentication
pa_exists, etype, salt, s2kparams, // pre-auth values
defaultKDCOptions,
principal,
null, // PrincipalName sname
null, // KerberosTime from
null, // KerberosTime till
null, // KerberosTime rtime
null, // int[] eTypes
null, // HostAddresses addresses
null); // Ticket[] additionalTickets
}
private static int[] getETypesFromKeys(EncryptionKey[] keys) {
int[] types = new int[keys.length];
for (int i = 0; i < keys.length; i++) {
types[i] = keys[i].getEType();
}
return types;
}
// update with pre-auth info
public void updatePA(int etype, String salt, byte[] params, PrincipalName name) {
// set the pre-auth values
pa_exists = true;
pa_etype = etype;
pa_salt = salt;
pa_s2kparams = params;
// update salt in PrincipalName
if (salt != null && salt.length() > 0) {
name.setSalt(salt);
if (DEBUG) {
System.out.println("Updated salt from pre-auth = " + name.getSalt());
}
}
PA_ENC_TIMESTAMP_REQUIRED = true;
}
// Used by Kinit
public KrbAsReq(
char[] password,
KDCOptions options,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
Ticket[] additionalTickets)
throws KrbException, IOException {
this(password,
false, 0, null, null, // pre-auth values
options,
cname,
sname, // PrincipalName sname
from, // KerberosTime from
till, // KerberosTime till
rtime, // KerberosTime rtime
eTypes, // int[] eTypes
addresses, // HostAddresses addresses
additionalTickets); // Ticket[] additionalTickets
}
// Used by Kinit
public KrbAsReq(
char[] password,
boolean pa_exists,
int etype,
String salt,
byte[] s2kparams,
KDCOptions options,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
Ticket[] additionalTickets)
throws KrbException, IOException {
EncryptionKey[] keys = null;
// update with preauth info
if (pa_exists) {
updatePA(etype, salt, s2kparams, cname);
// Can be null? has default?
public KrbAsReq(EncryptionKey pakey, // ok
KDCOptions options, // ok, new KDCOptions()
PrincipalName cname, // NO and must have realm
PrincipalName sname, // ok, krgtgt@CREALM
KerberosTime from, // ok
KerberosTime till, // ok, will use
KerberosTime rtime, // ok
int[] eTypes, // NO
HostAddresses addresses // ok
)
throws KrbException, IOException {
if (options == null) {
options = new KDCOptions();
}
if (password != null) {
keys = EncryptionKey.acquireSecretKeys(password, cname.getSalt(), pa_exists,
pa_etype, pa_s2kparams);
}
if (DEBUG) {
System.out.println(">>>KrbAsReq salt is " + cname.getSalt());
}
try {
init(
keys,
options,
cname,
sname,
from,
till,
rtime,
eTypes,
addresses,
additionalTickets);
}
finally {
/*
* Its ok to destroy the key here because we created it and are
* now done with it.
*/
if (keys != null) {
for (int i = 0; i < keys.length; i++) {
keys[i].destroy();
}
}
}
}
// Used in Kinit
public KrbAsReq(
EncryptionKey[] keys,
KDCOptions options,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
Ticket[] additionalTickets)
throws KrbException, IOException {
this(keys,
false, 0, null, null, // pre-auth values
options,
cname,
sname, // PrincipalName sname
from, // KerberosTime from
till, // KerberosTime till
rtime, // KerberosTime rtime
eTypes, // int[] eTypes
addresses, // HostAddresses addresses
additionalTickets); // Ticket[] additionalTickets
}
// Used by Kinit
public KrbAsReq(
EncryptionKey[] keys,
boolean pa_exists,
int etype,
String salt,
byte[] s2kparams,
KDCOptions options,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
Ticket[] additionalTickets)
throws KrbException, IOException {
// update with preauth info
if (pa_exists) {
// update pre-auth info
updatePA(etype, salt, s2kparams, cname);
if (DEBUG) {
System.out.println(">>>KrbAsReq salt is " + cname.getSalt());
}
}
init(
keys,
options,
cname,
sname,
from,
till,
rtime,
eTypes,
addresses,
additionalTickets);
}
/*
private KrbAsReq(KDCOptions options,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
Ticket[] additionalTickets)
throws KrbException, IOException {
init(null,
options,
cname,
sname,
from,
till,
rtime,
eTypes,
addresses,
additionalTickets);
}
*/
private void init(EncryptionKey[] keys,
KDCOptions options,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
Ticket[] additionalTickets )
throws KrbException, IOException {
// check if they are valid arguments. The optional fields should be
// consistent with settings in KDCOptions. Mar 17 2000
if (options.get(KDCOptions.FORWARDED) ||
......@@ -341,189 +89,66 @@ public class KrbAsReq extends KrbKdcReq {
if (rtime != null) rtime = null;
}
princName = cname;
int[] tktETypes = EType.getDefaults("default_tkt_enctypes", keys);
PAData[] paData = null;
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 {
if (tktETypes.length > 0) {
key = EncryptionKey.findKey(tktETypes[0], keys);
}
}
if (DEBUG) {
System.out.println("AS-REQ: Add PA_ENC_TIMESTAMP now");
}
if (pakey != null) {
PAEncTSEnc ts = new PAEncTSEnc();
byte[] temp = ts.asn1Encode();
if (key != null) {
// Use first key in list
EncryptedData encTs = new EncryptedData(key, temp,
KeyUsage.KU_PA_ENC_TS);
paData = new PAData[1];
paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
encTs.asn1Encode());
}
EncryptedData encTs = new EncryptedData(pakey, temp,
KeyUsage.KU_PA_ENC_TS);
paData = new PAData[1];
paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
encTs.asn1Encode());
}
if (DEBUG) {
System.out.println(">>> KrbAsReq calling createMessage");
if (cname.getRealm() == null) {
throw new RealmException(Krb5.REALM_NULL,
"default realm not specified ");
}
if (eTypes == null) {
eTypes = tktETypes;
if (DEBUG) {
System.out.println(">>> KrbAsReq creating message");
}
// check to use addresses in tickets
if (Config.getInstance().useAddresses()) {
KDC_EMPTY_ADDRESSES_ALLOWED = false;
}
// get the local InetAddress if required
if (addresses == null && !KDC_EMPTY_ADDRESSES_ALLOWED) {
if (addresses == null && Config.getInstance().useAddresses()) {
addresses = HostAddresses.getLocalAddresses();
}
asReqMessg = createMessage(
paData,
options,
cname,
cname.getRealm(),
sname,
from,
till,
rtime,
eTypes,
addresses,
additionalTickets);
obuf = asReqMessg.asn1Encode();
}
/**
* Returns an AS-REP message corresponding to the AS-REQ that
* was sent.
* @param password The password that will be used to derive the
* secret key that will decrypt the AS-REP from the KDC.
* @exception KrbException if an error occurs while reading the data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public KrbAsRep getReply(char[] password)
throws KrbException, IOException {
if (password == null)
throw new KrbException(Krb5.API_INVALID_ARG);
KrbAsRep temp = null;
EncryptionKey[] keys = null;
try {
keys = EncryptionKey.acquireSecretKeys(password,
princName.getSalt(), pa_exists, pa_etype, pa_s2kparams);
temp = getReply(keys);
} finally {
/*
* Its ok to destroy the key here because we created it and are
* now done with it.
*/
if (keys != null) {
for (int i = 0; i < keys.length; i++) {
keys[i].destroy();
}
}
}
return temp;
}
/**
* Sends an AS request to the realm of the client.
* returns the KDC hostname that the request was sent to
*/
public String send()
throws IOException, KrbException
{
String realmStr = null;
if (princName != null)
realmStr = princName.getRealmString();
return (send(realmStr));
}
/**
* Returns an AS-REP message corresponding to the AS-REQ that
* was sent.
* @param keys The secret keys that will decrypt the AS-REP from
* the KDC; key selected depends on etype used to encrypt data.
* @exception KrbException if an error occurs while reading the data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*/
public KrbAsRep getReply(EncryptionKey[] keys)
throws KrbException,IOException {
return new KrbAsRep(ibuf, keys, this);
}
private ASReq createMessage(
PAData[] paData,
KDCOptions kdc_options,
PrincipalName cname,
Realm crealm,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
Ticket[] additionalTickets
) throws Asn1Exception, KrbApErrException,
RealmException, UnknownHostException, IOException {
if (DEBUG) {
System.out.println(">>> KrbAsReq in createMessage");
}
PrincipalName req_sname = null;
if (sname == null) {
if (crealm == null) {
throw new RealmException(Krb5.REALM_NULL,
"default realm not specified ");
}
req_sname = new PrincipalName(
"krbtgt" +
PrincipalName.NAME_COMPONENT_SEPARATOR +
crealm.toString(),
PrincipalName.KRB_NT_SRV_INST);
} else
req_sname = sname;
sname = new PrincipalName("krbtgt" +
PrincipalName.NAME_COMPONENT_SEPARATOR +
cname.getRealmAsString(),
PrincipalName.KRB_NT_SRV_INST);
}
KerberosTime req_till = null;
if (till == null) {
req_till = new KerberosTime();
} else {
req_till = till;
till = new KerberosTime(0); // Choose KDC maximum allowed
}
KDCReqBody kdc_req_body = new KDCReqBody(kdc_options,
// enc-authorization-data and additional-tickets never in AS-REQ
KDCReqBody kdc_req_body = new KDCReqBody(options,
cname,
crealm,
req_sname,
cname.getRealm(),
sname,
from,
req_till,
till,
rtime,
Nonce.value(),
eTypes,
addresses,
null,
additionalTickets);
null);
return new ASReq(
asReqMessg = new ASReq(
paData,
kdc_req_body);
}
byte[] encoding() throws IOException, Asn1Exception {
return asReqMessg.asn1Encode();
}
// Used by KrbAsRep to validate AS-REP
ASReq getMessage() {
return asReqMessg;
}
......
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package sun.security.krb5;
import java.io.IOException;
import java.util.Arrays;
import sun.security.krb5.internal.HostAddresses;
import sun.security.krb5.internal.KDCOptions;
import sun.security.krb5.internal.KRBError;
import sun.security.krb5.internal.KerberosTime;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.PAData;
import sun.security.krb5.internal.crypto.EType;
/**
* A manager class for AS-REQ communications.
*
* This class does:
* 1. Gather information to create AS-REQ
* 2. Create and send AS-REQ
* 3. Receive AS-REP and KRB-ERROR (-KRB_ERR_RESPONSE_TOO_BIG) and parse them
* 4. Emit credentials and secret keys (for JAAS storeKey=true)
*
* This class does not:
* 1. Deal with real communications (KdcComm does it, and TGS-REQ)
* a. Name of KDCs for a realm
* b. Server availability, timeout, UDP or TCP
* d. KRB_ERR_RESPONSE_TOO_BIG
*
* With this class:
* 1. KrbAsReq has only one constructor
* 2. Krb5LoginModule and Kinit call a single builder
* 3. Better handling of sensitive info
*
* @since 1.7
*/
public final class KrbAsReqBuilder {
// Common data for AS-REQ fields
private KDCOptions options;
private PrincipalName cname;
private PrincipalName sname;
private KerberosTime from;
private KerberosTime till;
private KerberosTime rtime;
private HostAddresses addresses;
// Secret source: can't be changed once assigned, only one (of the two
// sources) can be set and should be non-null
private EncryptionKey[] keys;
private char[] password;
// Used to create a ENC-TIMESTAMP in the 2nd AS-REQ
private EncryptionKey pakey;
private PAData[] paList; // PA-DATA from both KRB-ERROR and AS-REP.
// Used by getKeys() only.
// Only AS-REP should be enough per RFC,
// combined in case etypes are different.
// The generated and received:
int[] eTypes;
private KrbAsReq req;
private KrbAsRep rep;
private static enum State {
INIT, // Initialized, can still add more initialization info
REQ_OK, // AS-REQ performed
DESTROYED, // Destroyed, not usable anymore
}
private State state;
// Called by other constructors
private KrbAsReqBuilder(PrincipalName cname)
throws KrbException {
if (cname.getRealm() == null) {
cname.setRealm(Config.getInstance().getDefaultRealm());
}
this.cname = cname;
state = State.INIT;
}
/**
* Creates a builder to be used by {@code cname} with existing keys.
*
* @param cname the client of the AS-REQ. Must not be null. Might have no
* realm, where default realm will be used. This realm will be the target
* realm for AS-REQ. I believe a client should only get initial TGT from
* its own realm.
* @param keys must not be null. if empty, might be quite useless.
* This argument will neither be modified nor stored by the method.
* @throws KrbException
*/
public KrbAsReqBuilder(PrincipalName cname, EncryptionKey[] keys)
throws KrbException {
this(cname);
this.keys = new EncryptionKey[keys.length];
for (int i=0; i<keys.length; i++) {
this.keys[i] = (EncryptionKey)keys[i].clone();
}
eTypes = EType.getDefaults("default_tkt_enctypes", keys);
}
/**
* Creates a builder to be used by {@code cname} with a known password.
*
* @param cname the client of the AS-REQ. Must not be null. Might have no
* realm, where default realm will be used. This realm will be the target
* realm for AS-REQ. I believe a client should only get initial TGT from
* its own realm.
* @param pass must not be null. This argument will neither be modified
* nor stored by the method.
* @throws KrbException
*/
public KrbAsReqBuilder(PrincipalName cname, char[] pass)
throws KrbException {
this(cname);
this.password = pass.clone();
eTypes = EType.getDefaults("default_tkt_enctypes");
}
/**
* Retrieves an array of secret keys for the client. This is useful if
* the client supplies password but need keys to act as an acceptor
* (in JAAS words, isInitiator=true and storeKey=true)
* @return original keys if initiated with keys, or generated keys if
* password. In latter case, PA-DATA from server might be used to
* generate keys. All "default_tkt_enctypes" keys will be generated,
* Never null.
* @throws KrbException
*/
public EncryptionKey[] getKeys() throws KrbException {
checkState(State.REQ_OK, "Cannot get keys");
if (keys != null) {
EncryptionKey[] result = new EncryptionKey[keys.length];
for (int i=0; i<keys.length; i++) {
result[i] = (EncryptionKey)keys[i].clone();
}
return result;
} else {
EncryptionKey[] result = new EncryptionKey[eTypes.length];
/*
* Returns an array of keys. Before KrbAsReqBuilder, all etypes
* use the same salt which is either the default one or a new salt
* coming from PA-DATA. After KrbAsReqBuilder, each etype uses its
* own new salt from PA-DATA. For an etype with no PA-DATA new salt
* at all, what salt should it use?
*
* Commonly, the stored keys are only to be used by an acceptor to
* decrypt service ticket in AP-REQ. Most impls only allow keys
* 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
* user name). Instead, we would use the new salt of another etype.
*/
String salt = null; // the saved new salt
for (int i=0; i<eTypes.length; i++) {
PAData.SaltAndParams snp =
PAData.getSaltAndParams(eTypes[i], paList);
// First round, only calculate those with new salt
if (snp.salt != null) {
salt = snp.salt;
result[i] = EncryptionKey.acquireSecretKey(password,
snp.salt,
eTypes[i],
snp.params);
}
}
if (salt == null) salt = cname.getSalt();
for (int i=0; i<eTypes.length; i++) {
// Second round, calculate those with no new salt
if (result[i] == null) {
PAData.SaltAndParams snp =
PAData.getSaltAndParams(eTypes[i], paList);
result[i] = EncryptionKey.acquireSecretKey(password,
salt,
eTypes[i],
snp.params);
}
}
return result;
}
}
/**
* Sets or clears options. If cleared, default options will be used
* at creation time.
* @param options
*/
public void setOptions(KDCOptions options) {
checkState(State.INIT, "Cannot specify options");
this.options = options;
}
/**
* Sets or clears target. If cleared, KrbAsReq might choose krbtgt
* for cname realm
* @param sname
*/
public void setTarget(PrincipalName sname) {
checkState(State.INIT, "Cannot specify target");
this.sname = sname;
}
/**
* Adds or clears addresses. KrbAsReq might add some if empty
* field not allowed
* @param addresses
*/
public void setAddresses(HostAddresses addresses) {
checkState(State.INIT, "Cannot specify addresses");
this.addresses = addresses;
}
/**
* Build a KrbAsReq object from all info fed above. Normally this method
* will be called twice: initial AS-REQ and second with pakey
* @return the KrbAsReq object
* @throws KrbException
* @throws IOException
*/
private KrbAsReq build() throws KrbException, IOException {
return new KrbAsReq(pakey,
options,
cname,
sname,
from,
till,
rtime,
eTypes,
addresses);
}
/**
* Parses AS-REP, decrypts enc-part, retrieves ticket and session key
* @throws KrbException
* @throws Asn1Exception
* @throws IOException
*/
private KrbAsReqBuilder resolve() throws KrbException, Asn1Exception, IOException {
if (keys != null) {
rep.decryptUsingKeys(keys, req);
} else {
rep.decryptUsingPassword(password, req, cname);
}
if (rep.getPA() != null) {
if (paList == null || paList.length == 0) {
paList = rep.getPA();
} else {
int extraLen = rep.getPA().length;
if (extraLen > 0) {
int oldLen = paList.length;
paList = Arrays.copyOf(paList, paList.length + extraLen);
System.arraycopy(rep.getPA(), 0, paList, oldLen, extraLen);
}
}
}
return this;
}
/**
* Communication until AS-REP or non preauth-related KRB-ERROR received
* @throws KrbException
* @throws IOException
*/
private KrbAsReqBuilder send() throws KrbException, IOException {
boolean preAuthFailedOnce = false;
KdcComm comm = new KdcComm(cname.getRealmAsString());
while (true) {
try {
req = build();
rep = new KrbAsRep(comm.send(req.encoding()));
return this;
} catch (KrbException ke) {
if (!preAuthFailedOnce && (
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED ||
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) {
if (Krb5.DEBUG) {
System.out.println("KrbAsReqBuilder: " +
"PREAUTH FAILED/REQ, re-send AS-REQ");
}
preAuthFailedOnce = true;
KRBError kerr = ke.getError();
if (password == null) {
pakey = EncryptionKey.findKey(kerr.getEType(), keys);
} else {
PAData.SaltAndParams snp = PAData.getSaltAndParams(
kerr.getEType(), kerr.getPA());
if (kerr.getEType() == 0) {
// Possible if PA-PW-SALT is in KRB-ERROR. RFC
// does not recommend this
pakey = EncryptionKey.acquireSecretKey(password,
snp.salt == null ? cname.getSalt() : snp.salt,
eTypes[0],
null);
} else {
pakey = EncryptionKey.acquireSecretKey(password,
snp.salt == null ? cname.getSalt() : snp.salt,
kerr.getEType(),
snp.params);
}
}
paList = kerr.getPA(); // Update current paList
} else {
throw ke;
}
}
}
}
/**
* Performs AS-REQ send and AS-REP receive.
* Maybe a state is needed here, to divide prepare process and getCreds.
* @throws KrbException
* @throws Asn1Exception
* @throws IOException
*/
public KrbAsReqBuilder action()
throws KrbException, Asn1Exception, IOException {
checkState(State.INIT, "Cannot call action");
state = State.REQ_OK;
return send().resolve();
}
/**
* Gets Credentials object after action
*/
public Credentials getCreds() {
checkState(State.REQ_OK, "Cannot retrieve creds");
return rep.getCreds();
}
/**
* Gets another type of Credentials after action
*/
public sun.security.krb5.internal.ccache.Credentials getCCreds() {
checkState(State.REQ_OK, "Cannot retrieve CCreds");
return rep.getCCreds();
}
/**
* Destroys the object and clears keys and password info.
*/
public void destroy() {
state = State.DESTROYED;
if (keys != null) {
for (EncryptionKey k: keys) {
k.destroy();
}
keys = null;
}
if (password != null) {
Arrays.fill(password, (char)0);
password = null;
}
}
/**
* Checks if the current state is the specified one.
* @param st the expected state
* @param msg error message if state is not correct
* @throws IllegalStateException if state is not correct
*/
private void checkState(State st, String msg) {
if (state != st) {
throw new IllegalStateException(msg + " at " + st + " state");
}
}
}
/*
* Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
......@@ -31,20 +31,16 @@
package sun.security.krb5;
import sun.security.util.*;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
import java.io.InterruptedIOException;
/**
* This class encapsulates a Kerberos TGS-REQ that is sent from the
* client to the KDC.
*/
public class KrbTgsReq extends KrbKdcReq {
public class KrbTgsReq {
private PrincipalName princName;
private PrincipalName servName;
......@@ -56,7 +52,8 @@ public class KrbTgsReq extends KrbKdcReq {
private static final boolean DEBUG = Krb5.DEBUG;
private int defaultTimeout = 30*1000; // 30 seconds
private byte[] obuf;
private byte[] ibuf;
// Used in CredentialsUtil
public KrbTgsReq(Credentials asCreds,
......@@ -182,11 +179,12 @@ public class KrbTgsReq extends KrbKdcReq {
* @throws KrbException
* @throws IOException
*/
public String send() throws IOException, KrbException {
public void send() throws IOException, KrbException {
String realmStr = null;
if (servName != null)
realmStr = servName.getRealmString();
return (send(realmStr));
KdcComm comm = new KdcComm(realmStr);
ibuf = comm.send(obuf);
}
public KrbTgsRep getReply()
......@@ -201,18 +199,8 @@ public class KrbTgsReq extends KrbKdcReq {
public Credentials sendAndGetCreds() throws IOException, KrbException {
KrbTgsRep tgs_rep = null;
String kdc = null;
try {
kdc = send();
tgs_rep = getReply();
} catch (KrbException ke) {
if (ke.returnCode() == Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
// set useTCP and retry
send(servName.getRealmString(), kdc, true);
tgs_rep = getReply();
} else {
throw ke;
}
}
send();
tgs_rep = getReply();
return tgs_rep.getCreds();
}
......@@ -240,7 +228,7 @@ public class KrbTgsReq extends KrbKdcReq {
UnknownHostException, KrbCryptoException {
KerberosTime req_till = null;
if (till == null) {
req_till = new KerberosTime();
req_till = new KerberosTime(0);
} else {
req_till = till;
}
......
......@@ -511,10 +511,6 @@ public class PrincipalName
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String toString() {
StringBuffer str = new StringBuffer();
for (int i = 0; i < nameStrings.length; i++) {
......
......@@ -32,7 +32,6 @@ package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.util.*;
import java.util.Vector;
import java.io.IOException;
import java.math.BigInteger;
......@@ -69,7 +68,7 @@ public class KDCRep {
public EncKDCRepPart encKDCRepPart; //not part of ASN.1 encoding
private int pvno;
private int msgType;
private PAData[] pAData = null; //optional
public PAData[] pAData = null; //optional
private boolean DEBUG = Krb5.DEBUG;
public KDCRep(
......
/*
* Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
......@@ -41,7 +41,9 @@ import sun.security.util.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import sun.security.krb5.internal.util.KerberosString;
/**
* Implements the ASN.1 KRBError type.
......@@ -96,10 +98,8 @@ public class KRBError implements java.io.Serializable {
private byte[] eData; //optional
private Checksum eCksum; //optional
// pre-auth info
private int etype = 0;
private String salt = null;
private byte[] s2kparams = null;
private PAData[] pa; // PA-DATA in eData
private int pa_eType; // The 1st etype appeared in salt-related PAData
private static boolean DEBUG = Krb5.DEBUG;
......@@ -260,10 +260,12 @@ public class KRBError implements java.io.Serializable {
private void parsePAData(byte[] data)
throws IOException, Asn1Exception {
DerValue derPA = new DerValue(data);
List<PAData> paList = new ArrayList<PAData>();
while (derPA.data.available() > 0) {
// read the PA-DATA
DerValue tmp = derPA.data.getDerValue();
PAData pa_data = new PAData(tmp);
paList.add(pa_data);
int pa_type = pa_data.getType();
byte[] pa_value = pa_data.getValue();
if (DEBUG) {
......@@ -280,24 +282,13 @@ public class KRBError implements java.io.Serializable {
case Krb5.PA_ETYPE_INFO:
if (pa_value != null) {
DerValue der = new DerValue(pa_value);
DerValue value = der.data.getDerValue();
ETypeInfo info = new ETypeInfo(value);
etype = info.getEType();
salt = info.getSalt();
if (DEBUG) {
System.out.println("\t PA-ETYPE-INFO etype = " + etype);
System.out.println("\t PA-ETYPE-INFO salt = " + salt);
}
while (der.data.available() > 0) {
value = der.data.getDerValue();
info = new ETypeInfo(value);
DerValue value = der.data.getDerValue();
ETypeInfo info = new ETypeInfo(value);
if (pa_eType == 0) pa_eType = info.getEType();
if (DEBUG) {
etype = info.getEType();
System.out.println("\t salt for " + etype
+ " is " + info.getSalt());
}
if (salt == null || salt.isEmpty()) {
salt = info.getSalt();
System.out.println("\t PA-ETYPE-INFO etype = " + info.getEType());
System.out.println("\t PA-ETYPE-INFO salt = " + info.getSalt());
}
}
}
......@@ -305,25 +296,13 @@ public class KRBError implements java.io.Serializable {
case Krb5.PA_ETYPE_INFO2:
if (pa_value != null) {
DerValue der = new DerValue(pa_value);
DerValue value = der.data.getDerValue();
ETypeInfo2 info2 = new ETypeInfo2(value);
etype = info2.getEType();
salt = info2.getSalt();
s2kparams = info2.getParams();
if (DEBUG) {
System.out.println("\t PA-ETYPE-INFO2 etype = " + etype);
System.out.println("\t PA-ETYPE-INFO salt = " + salt);
}
while (der.data.available() > 0) {
value = der.data.getDerValue();
info2 = new ETypeInfo2(value);
DerValue value = der.data.getDerValue();
ETypeInfo2 info2 = new ETypeInfo2(value);
if (pa_eType == 0) pa_eType = info2.getEType();
if (DEBUG) {
etype = info2.getEType();
System.out.println("\t salt for " + etype
+ " is " + info2.getSalt());
}
if (salt == null || salt.isEmpty()) {
salt = info2.getSalt();
System.out.println("\t PA-ETYPE-INFO2 etype = " + info2.getEType());
System.out.println("\t PA-ETYPE-INFO2 salt = " + info2.getSalt());
}
}
}
......@@ -333,6 +312,7 @@ public class KRBError implements java.io.Serializable {
break;
}
}
pa = paList.toArray(new PAData[paList.size()]);
}
public final KerberosTime getServerTime() {
......@@ -356,18 +336,12 @@ public class KRBError implements java.io.Serializable {
}
// access pre-auth info
public final int getEType() {
return etype;
}
// access pre-auth info
public final String getSalt() {
return salt;
public final PAData[] getPA() {
return pa;
}
// access pre-auth info
public final byte[] getParams() {
return ((s2kparams == null) ? null : s2kparams.clone());
public final int getEType() {
return pa_eType;
}
public final String getErrorString() {
......
......@@ -77,11 +77,6 @@ public class KerberosTime implements Cloneable {
public static final boolean NOW = true;
public static final boolean UNADJUSTED_NOW = false;
//defaults to zero instead of now; use setNow() for current time
public KerberosTime() {
kerberosTime = 0;
}
public KerberosTime(long time) {
kerberosTime = time;
}
......
......@@ -30,9 +30,11 @@
package sun.security.krb5.internal;
import sun.security.krb5.KrbException;
import sun.security.util.*;
import sun.security.krb5.Asn1Exception;
import java.io.IOException;
import sun.security.krb5.internal.util.KerberosString;
/**
* Implements the ASN.1 PA-DATA type.
......@@ -135,4 +137,75 @@ public class PAData {
public byte[] getValue() {
return ((pADataValue == null) ? null : pADataValue.clone());
}
/**
* A place to store a pair of salt and s2kparams.
* An empty salt is changed to null, to be interopable
* with Windows 2000 server.
*/
public static class SaltAndParams {
public final String salt;
public final byte[] params;
public SaltAndParams(String s, byte[] p) {
if (s != null && s.isEmpty()) s = null;
this.salt = s;
this.params = p;
}
}
/**
* 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.
* (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.
*/
public static SaltAndParams getSaltAndParams(int eType, PAData[] pas)
throws Asn1Exception, KrbException {
if (pas == null || pas.length == 0) {
return new SaltAndParams(null, 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 (info2 != null) {
return new SaltAndParams(info2.getSalt(), info2.getParams());
} else if (info != null) {
return new SaltAndParams(info.getSalt(), null);
}
return new SaltAndParams(paPwSalt, null);
}
}
/*
* Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
......@@ -33,11 +33,7 @@ package sun.security.krb5.internal.tools;
import sun.security.krb5.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;
import java.io.File;
import java.util.Arrays;
import sun.security.util.Password;
......@@ -152,6 +148,7 @@ public class Kinit {
if (principal != null) {
princName = principal.toString();
}
KrbAsReqBuilder builder;
if (DEBUG) {
System.out.println("Principal is " + principal);
}
......@@ -172,6 +169,7 @@ public class Kinit {
new String(psswd));
}
}
builder = new KrbAsReqBuilder(principal, psswd);
} else {
if (DEBUG) {
System.out.println(">>> Kinit using keytab");
......@@ -198,11 +196,13 @@ public class Kinit {
}
throw new KrbException(msg);
}
builder = new KrbAsReqBuilder(principal, skeys);
}
KDCOptions opt = new KDCOptions();
setOptions(KDCOptions.FORWARDABLE, options.forwardable, opt);
setOptions(KDCOptions.PROXIABLE, options.proxiable, opt);
builder.setOptions(opt);
String realm = options.getKDCRealm();
if (realm == null) {
realm = Config.getInstance().getDefaultRealm();
......@@ -215,62 +215,21 @@ public class Kinit {
PrincipalName sname = new PrincipalName("krbtgt" + "/" + realm,
PrincipalName.KRB_NT_SRV_INST);
sname.setRealm(realm);
builder.setTarget(sname);
if (DEBUG) {
System.out.println(">>> Creating KrbAsReq");
}
KrbAsReq as_req = null;
HostAddresses addresses = null;
try {
if (options.getAddressOption())
addresses = HostAddresses.getLocalAddresses();
if (options.getAddressOption())
builder.setAddresses(HostAddresses.getLocalAddresses());
if (useKeytab) {
as_req = new KrbAsReq(skeys, opt,
principal, sname,
null, null, null, null, addresses, null);
} else {
as_req = new KrbAsReq(psswd, opt,
principal, sname,
null, null, null, null, addresses, null);
}
} catch (KrbException exc) {
throw exc;
} catch (Exception exc) {
throw new KrbException(exc.toString());
}
KrbAsRep as_rep = null;
try {
as_rep = sendASRequest(as_req, useKeytab, realm, psswd, skeys);
} catch (KrbException ke) {
if ((ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) ||
(ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) {
if (DEBUG) {
System.out.println("Kinit: PREAUTH FAILED/REQ, re-send AS-REQ");
}
KRBError error = ke.getError();
int etype = error.getEType();
String salt = error.getSalt();
byte[] s2kparams = error.getParams();
if (useKeytab) {
as_req = new KrbAsReq(skeys, true, etype, salt,
s2kparams, opt, principal, sname,
null, null, null, null, addresses, null);
} else {
as_req = new KrbAsReq(psswd, true, etype, salt,
s2kparams, opt, principal, sname,
null, null, null, null, addresses, null);
}
as_rep = sendASRequest(as_req, useKeytab, realm, psswd, skeys);
} else {
throw ke;
}
}
builder.action();
sun.security.krb5.internal.ccache.Credentials credentials =
as_rep.setCredentials();
builder.getCCreds();
builder.destroy();
// we always create a new cache and store the ticket we get
CredentialsCache cache =
CredentialsCache.create(principal, options.cachename);
......@@ -296,41 +255,6 @@ public class Kinit {
options = null; // release reference to options
}
private static KrbAsRep sendASRequest(KrbAsReq as_req, boolean useKeytab,
String realm, char[] passwd, EncryptionKey[] skeys)
throws IOException, RealmException, KrbException {
if (DEBUG) {
System.out.println(">>> Kinit: sending as_req to realm " + realm);
}
String kdc = as_req.send(realm);
if (DEBUG) {
System.out.println(">>> reading response from kdc");
}
KrbAsRep as_rep = null;
try {
if (useKeytab) {
as_rep = as_req.getReply(skeys);
} else {
as_rep = as_req.getReply(passwd);
}
} catch (KrbException ke) {
if (ke.returnCode() == Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
as_req.send(realm, kdc, true); // useTCP is set
if (useKeytab) {
as_rep = as_req.getReply(skeys);
} else {
as_rep = as_req.getReply(passwd);
}
} else {
throw ke;
}
}
return as_rep;
}
private static void setOptions(int flag, int option, KDCOptions opt) {
switch (option) {
case 0:
......
......@@ -35,7 +35,6 @@ import sun.net.spi.nameservice.NameServiceDescriptor;
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;
......@@ -129,8 +128,13 @@ public class KDC {
// The random generator to generate random keys (including session keys)
private static SecureRandom secureRandom = new SecureRandom();
// Principal db. principal -> pass
private Map<String,char[]> passwords = new HashMap<String,char[]>();
// Principal db. principal -> pass. A case-insensitive TreeMap is used
// so that even if the client provides a name with different case, the KDC
// can still locate the principal and give back correct salt.
private TreeMap<String,char[]> passwords = new TreeMap<String,char[]>
(String.CASE_INSENSITIVE_ORDER);
// Realm name
private String realm;
// KDC
......@@ -159,9 +163,13 @@ public class KDC {
*/
ONLY_RC4_TGT,
/**
* Only use RC4 in preauth, enc-type still using eTypes[0]
* Use RC4 as the first in preauth
*/
ONLY_RC4_PREAUTH,
RC4_FIRST_PREAUTH,
/**
* Use only one preauth, so that some keys are not easy to generate
*/
ONLY_ONE_PREAUTH,
};
static {
......@@ -191,6 +199,12 @@ public class KDC {
return create(realm, "kdc." + realm.toLowerCase(), 0, true);
}
public static KDC existing(String realm, String kdc, int port) {
KDC k = new KDC(realm, kdc);
k.port = port;
return k;
}
/**
* Creates and starts a KDC server.
* @param realm the realm name
......@@ -471,7 +485,18 @@ public class KDC {
* @return the salt
*/
private String getSalt(PrincipalName p) {
String[] ns = p.getNameStrings();
String pn = p.toString();
if (p.getRealmString() == null) {
pn = pn + "@" + getRealm();
}
if (passwords.containsKey(pn)) {
try {
// Find the principal name with correct case.
p = new PrincipalName(passwords.ceilingEntry(pn).getKey());
} catch (RealmException re) {
// Won't happen
}
}
String s = p.getRealmString();
if (s == null) s = getRealm();
for (String n: p.getNameStrings()) {
......@@ -493,8 +518,6 @@ public class KDC {
try {
// Do not call EncryptionKey.acquireSecretKeys(), otherwise
// the krb5.conf config file would be loaded.
Method stringToKey = EncryptionKey.class.getDeclaredMethod("stringToKey", char[].class, String.class, byte[].class, Integer.TYPE);
stringToKey.setAccessible(true);
Integer kvno = null;
// For service whose password ending with a number, use it as kvno.
// Kvno must be postive.
......@@ -504,12 +527,9 @@ public class KDC {
kvno = pass[pass.length-1] - '0';
}
}
return new EncryptionKey((byte[]) stringToKey.invoke(
null, getPassword(p, server), getSalt(p), null, etype),
return new EncryptionKey(EncryptionKeyDotStringToKey(
getPassword(p, server), getSalt(p), null, etype),
etype, kvno);
} catch (InvocationTargetException ex) {
KrbException ke = (KrbException)ex.getCause();
throw ke;
} catch (KrbException ke) {
throw ke;
} catch (Exception e) {
......@@ -590,12 +610,11 @@ public class KDC {
" sends TGS-REQ for " +
tgsReq.reqBody.sname);
KDCReqBody body = tgsReq.reqBody;
int etype = 0;
int[] eTypes = KDCReqBodyDotEType(body);
int e2 = eTypes[0]; // etype for outgoing session key
int e3 = eTypes[0]; // etype for outgoing ticket
// Reflection: PAData[] pas = tgsReq.pAData;
Field f = KDCReq.class.getDeclaredField("pAData");
f.setAccessible(true);
PAData[] pas = (PAData[])f.get(tgsReq);
PAData[] pas = kDCReqDotPAData(tgsReq);
Ticket tkt = null;
EncTicketPart etp = null;
......@@ -607,9 +626,9 @@ public class KDC {
APReq apReq = new APReq(pa.getValue());
EncryptedData ed = apReq.authenticator;
tkt = apReq.ticket;
etype = tkt.encPart.getEType();
int te = tkt.encPart.getEType();
tkt.sname.setRealm(tkt.realm);
EncryptionKey kkey = keyForUser(tkt.sname, etype, true);
EncryptionKey kkey = keyForUser(tkt.sname, te, true);
byte[] bb = tkt.encPart.decrypt(kkey, KeyUsage.KU_TICKET);
DerInputStream derIn = new DerInputStream(bb);
DerValue der = derIn.getDerValue();
......@@ -620,16 +639,12 @@ public class KDC {
throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
}
}
EncryptionKey skey = keyForUser(body.sname, etype, true);
if (skey == null) {
throw new KrbException(Krb5.KDC_ERR_SUMTYPE_NOSUPP); // TODO
}
// Session key for original ticket, TGT
EncryptionKey ckey = etp.key;
// Session key for session with the service
EncryptionKey key = generateRandomKey(etype);
EncryptionKey key = generateRandomKey(e2);
// Check time, TODO
KerberosTime till = body.till;
......@@ -678,6 +693,10 @@ public class KDC {
till, body.rtime,
body.addresses,
null);
EncryptionKey skey = keyForUser(body.sname, e3, true);
if (skey == null) {
throw new KrbException(Krb5.KDC_ERR_SUMTYPE_NOSUPP); // TODO
}
Ticket t = new Ticket(
body.crealm,
body.sname,
......@@ -741,17 +760,17 @@ public class KDC {
private byte[] processAsReq(byte[] in) throws Exception {
ASReq asReq = new ASReq(in);
int[] eTypes = null;
List<PAData> outPAs = new ArrayList<PAData>();
try {
System.out.println(realm + "> " + asReq.reqBody.cname +
" sends AS-REQ for " +
asReq.reqBody.sname);
KDCReqBody body = asReq.reqBody;
body.cname.setRealm(getRealm());
// Reflection: int[] eType = body.eType;
Field f = KDCReqBody.class.getDeclaredField("eType");
f.setAccessible(true);
eTypes = (int[])f.get(body);
eTypes = KDCReqBodyDotEType(body);
int eType = eTypes[0];
EncryptionKey ckey = keyForUser(body.cname, eType, false);
......@@ -807,19 +826,63 @@ public class KDC {
}
bFlags[Krb5.TKT_OPTS_INITIAL] = true;
f = KDCReq.class.getDeclaredField("pAData");
f.setAccessible(true);
PAData[] pas = (PAData[])f.get(asReq);
if (pas == null || pas.length == 0) {
// Creating PA-DATA
int[] epas = eTypes;
if (options.containsKey(KDC.Option.RC4_FIRST_PREAUTH)) {
for (int i=1; i<epas.length; i++) {
if (epas[i] == EncryptedData.ETYPE_ARCFOUR_HMAC) {
epas[i] = epas[0];
epas[0] = EncryptedData.ETYPE_ARCFOUR_HMAC;
break;
}
};
} else if (options.containsKey(KDC.Option.ONLY_ONE_PREAUTH)) {
epas = new int[] { eTypes[0] };
}
DerValue[] pas = new DerValue[epas.length];
for (int i=0; i<epas.length; i++) {
pas[i] = new DerValue(new ETypeInfo2(
epas[i],
epas[i] == EncryptedData.ETYPE_ARCFOUR_HMAC ?
null : getSalt(body.cname),
null).asn1Encode());
}
DerOutputStream eid = new DerOutputStream();
eid.putSequence(pas);
outPAs.add(new PAData(Krb5.PA_ETYPE_INFO2, eid.toByteArray()));
boolean allOld = true;
for (int i: eTypes) {
if (i == EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96 ||
i == EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96) {
allOld = false;
break;
}
}
if (allOld) {
for (int i=0; i<epas.length; i++) {
pas[i] = new DerValue(new ETypeInfo(
epas[i],
epas[i] == EncryptedData.ETYPE_ARCFOUR_HMAC ?
null : getSalt(body.cname)
).asn1Encode());
}
eid = new DerOutputStream();
eid.putSequence(pas);
outPAs.add(new PAData(Krb5.PA_ETYPE_INFO, eid.toByteArray()));
}
PAData[] inPAs = kDCReqDotPAData(asReq);
if (inPAs == null || inPAs.length == 0) {
Object preauth = options.get(Option.PREAUTH_REQUIRED);
if (preauth == null || preauth.equals(Boolean.TRUE)) {
throw new KrbException(Krb5.KDC_ERR_PREAUTH_REQUIRED);
}
} else {
try {
Constructor<EncryptedData> ctor = EncryptedData.class.getDeclaredConstructor(DerValue.class);
ctor.setAccessible(true);
EncryptedData data = ctor.newInstance(new DerValue(pas[0].getValue()));
EncryptedData data = newEncryptedData(new DerValue(inPAs[0].getValue()));
EncryptionKey pakey = keyForUser(body.cname, data.getEType(), false);
data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS);
} catch (Exception e) {
......@@ -862,7 +925,8 @@ public class KDC {
body.addresses
);
EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_AS_REP_PART);
ASRep asRep = new ASRep(null,
ASRep asRep = new ASRep(
outPAs.toArray(new PAData[outPAs.size()]),
body.crealm,
body.cname,
t,
......@@ -907,36 +971,10 @@ public class KDC {
if (kerr == null) {
if (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED ||
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) {
PAData pa;
int epa = eTypes[0];
if (options.containsKey(KDC.Option.ONLY_RC4_PREAUTH)) {
epa = EncryptedData.ETYPE_ARCFOUR_HMAC;
}
ETypeInfo2 ei2 = new ETypeInfo2(epa, null, null);
DerOutputStream eid = new DerOutputStream();
eid.write(DerValue.tag_Sequence, ei2.asn1Encode());
pa = new PAData(Krb5.PA_ETYPE_INFO2, eid.toByteArray());
DerOutputStream bytes = new DerOutputStream();
bytes.write(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]).asn1Encode());
bytes.write(pa.asn1Encode());
boolean allOld = true;
for (int i: eTypes) {
if (i == EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96 ||
i == EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96) {
allOld = false;
break;
}
}
if (allOld) {
ETypeInfo ei = new ETypeInfo(epa, null);
eid = new DerOutputStream();
eid.write(DerValue.tag_Sequence, ei.asn1Encode());
pa = new PAData(Krb5.PA_ETYPE_INFO, eid.toByteArray());
bytes.write(pa.asn1Encode());
for (PAData p: outPAs) {
bytes.write(p.asn1Encode());
}
DerOutputStream temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
......@@ -1146,4 +1184,61 @@ public class KDC {
return "ns";
}
}
// Calling private methods thru reflections
private static final Field getPADataField;
private static final Field getEType;
private static final Constructor<EncryptedData> ctorEncryptedData;
private static final Method stringToKey;
static {
try {
ctorEncryptedData = EncryptedData.class.getDeclaredConstructor(DerValue.class);
ctorEncryptedData.setAccessible(true);
getPADataField = KDCReq.class.getDeclaredField("pAData");
getPADataField.setAccessible(true);
getEType = KDCReqBody.class.getDeclaredField("eType");
getEType.setAccessible(true);
stringToKey = EncryptionKey.class.getDeclaredMethod(
"stringToKey",
char[].class, String.class, byte[].class, Integer.TYPE);
stringToKey.setAccessible(true);
} catch (NoSuchFieldException nsfe) {
throw new AssertionError(nsfe);
} catch (NoSuchMethodException nsme) {
throw new AssertionError(nsme);
}
}
private EncryptedData newEncryptedData(DerValue der) {
try {
return ctorEncryptedData.newInstance(der);
} catch (Exception e) {
throw new AssertionError(e);
}
}
private static PAData[] kDCReqDotPAData(KDCReq req) {
try {
return (PAData[])getPADataField.get(req);
} catch (Exception e) {
throw new AssertionError(e);
}
}
private static int[] KDCReqBodyDotEType(KDCReqBody body) {
try {
return (int[]) getEType.get(body);
} catch (Exception e) {
throw new AssertionError(e);
}
}
private static byte[] EncryptionKeyDotStringToKey(char[] password, String salt,
byte[] s2kparams, int keyType) throws KrbCryptoException {
try {
return (byte[])stringToKey.invoke(
null, password, salt, s2kparams, keyType);
} catch (InvocationTargetException ex) {
throw (KrbCryptoException)ex.getCause();
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
/*
* 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 6960894
* @summary Better AS-REQ creation and processing
* @run main NewSalt
* @run main/othervm -Dnopreauth NewSalt
* @run main/othervm -Donlyonepreauth NewSalt
*/
import sun.security.jgss.GSSUtil;
import sun.security.krb5.Config;
public class NewSalt {
public static void main(String[] args)
throws Exception {
// Create and start the KDC
KDC kdc = new OneKDC(null);
if (System.getProperty("onlyonepreauth") != null) {
KDC.saveConfig(OneKDC.KRB5_CONF, kdc,
"default_tgs_enctypes=des3-cbc-sha1");
Config.refresh();
kdc.setOption(KDC.Option.ONLY_ONE_PREAUTH, true);
}
if (System.getProperty("nopreauth") != null) {
kdc.setOption(KDC.Option.PREAUTH_REQUIRED, false);
}
// Use a different case of name. KDC will return correct salt
Context c1 = Context.fromUserPass(OneKDC.USER.toUpperCase(),
OneKDC.PASS, true);
Context c2 = Context.fromUserPass(OneKDC.USER2.toUpperCase(),
OneKDC.PASS2, true);
c1.startAsClient(OneKDC.USER2, GSSUtil.GSS_KRB5_MECH_OID);
c2.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
Context.handshake(c1, c2);
}
}
/*
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 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
......@@ -46,6 +46,8 @@ public class OneKDC extends KDC {
public static final String USER = "dummy";
public static final char[] PASS = "bogus".toCharArray();
public static final String USER2 = "foo";
public static final char[] PASS2 = "bar".toCharArray();
public static final String KRB5_CONF = "localkdc-krb5.conf";
public static final String KTAB = "localkdc.ktab";
public static final String JAAS_CONF = "localkdc-jaas.conf";
......@@ -61,6 +63,7 @@ public class OneKDC extends KDC {
public OneKDC(String etype) throws Exception {
super(REALM, KDCHOST, 0, true);
addPrincipal(USER, PASS);
addPrincipal(USER2, PASS2);
addPrincipalRandKey("krbtgt/" + REALM);
addPrincipalRandKey(SERVER);
addPrincipalRandKey(BACKEND);
......
......@@ -26,6 +26,8 @@
* @bug 6932525 6951366 6959292
* @summary kerberos login failure on win2008 with AD set to win2000 compat mode
* and cannot login if session key and preauth does not use the same etype
* @run main/othervm -D6932525 W83
* @run main/othervm -D6959292 W83
*/
import com.sun.security.auth.module.Krb5LoginModule;
import java.io.File;
......@@ -61,14 +63,16 @@ public class W83 {
}
ktab.save();
// For 6932525 and 6951366, make sure the etypes sent in 2nd AS-REQ
// is not restricted to that of preauth
kdc.setOption(KDC.Option.ONLY_RC4_TGT, true);
x.go();
// For 6959292, make sure that when etype for enc-part in 2nd AS-REQ
// is different from that of preauth, client can still decrypt it
kdc.setOption(KDC.Option.ONLY_RC4_PREAUTH, true);
if (System.getProperty("6932525") != null) {
// For 6932525 and 6951366, make sure the etypes sent in 2nd AS-REQ
// is not restricted to that of preauth
kdc.setOption(KDC.Option.ONLY_RC4_TGT, true);
}
if (System.getProperty("6959292") != null) {
// For 6959292, make sure that when etype for enc-part in 2nd AS-REQ
// is different from that of preauth, client can still decrypt it
kdc.setOption(KDC.Option.RC4_FIRST_PREAUTH, true);
}
x.go();
}
......@@ -78,11 +82,13 @@ public class W83 {
try {
Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
} catch (Exception e) {
e.printStackTrace();
error.append("Krb5LoginModule password login error\n");
}
try {
Context.fromUserKtab(OneKDC.USER, OneKDC.KTAB, false);
} catch (Exception e) {
e.printStackTrace();
error.append("Krb5LoginModule keytab login error\n");
}
try {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册