diff --git a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
index 5c1e5c23a9fdd9415fd7a16e6efb2b168bead9d0..c9499f1d1a0f528ed61cebc8ba95c1e2f60ce427 100644
--- a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
+++ b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java
@@ -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();
diff --git a/src/share/classes/sun/security/krb5/Config.java b/src/share/classes/sun/security/krb5/Config.java
index 21fb4cea7f5b001bc2248b38b1cf497f8c762338..272c3b1e6ca66f626ea91935a2980594ec92f3f6 100644
--- a/src/share/classes/sun/security/krb5/Config.java
+++ b/src/share/classes/sun/security/krb5/Config.java
@@ -111,7 +111,7 @@ public class Config {
public static synchronized void refresh() throws KrbException {
singleton = new Config();
KeyTab.refresh();
- KrbKdcReq.initStatic();
+ KdcComm.initStatic();
}
diff --git a/src/share/classes/sun/security/krb5/Credentials.java b/src/share/classes/sun/security/krb5/Credentials.java
index 6896aa18e95fdc49a17cb625ffee61399dd61c8a..86dc3a01668a02151abb6091ebb8702ac266db60 100644
--- a/src/share/classes/sun/security/krb5/Credentials.java
+++ b/src/share/classes/sun/security/krb5/Credentials.java
@@ -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.
*
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;
}
diff --git a/src/share/classes/sun/security/krb5/EncryptionKey.java b/src/share/classes/sun/security/krb5/EncryptionKey.java
index ff920af8e9927d8012f1d059beb520a0d8f91f97..8ce51c0c659f7fbffc63443d5b680226adb81a74 100644
--- a/src/share/classes/sun/security/krb5/EncryptionKey.java
+++ b/src/share/classes/sun/security/krb5/EncryptionKey.java
@@ -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) {
diff --git a/src/share/classes/sun/security/krb5/KrbKdcReq.java b/src/share/classes/sun/security/krb5/KdcComm.java
similarity index 92%
rename from src/share/classes/sun/security/krb5/KrbKdcReq.java
rename to src/share/classes/sun/security/krb5/KdcComm.java
index 1919b70fc912e4282d8525ce74f363db5e600690..22e54afe5e86aeaf30200df1aed85f11ee31ded0 100644
--- a/src/share/classes/sun/security/krb5/KrbKdcReq.java
+++ b/src/share/classes/sun/security/krb5/KdcComm.java
@@ -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
diff --git a/src/share/classes/sun/security/krb5/KrbAsRep.java b/src/share/classes/sun/security/krb5/KrbAsRep.java
index 709e4410a0acda90233e1a44c01c57cbf94a8129..c05d8bd08bfbde4cf9d2b3caaadbf3519fc8af9b 100644
--- a/src/share/classes/sun/security/krb5/KrbAsRep.java
+++ b/src/share/classes/sun/security/krb5/KrbAsRep.java
@@ -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);
}
}
diff --git a/src/share/classes/sun/security/krb5/KrbAsReq.java b/src/share/classes/sun/security/krb5/KrbAsReq.java
index 77f3798332fb5312d735ac7f629140466fdffdcd..1c2dfdf94db51a92cd74086507d68f2213e5764a 100644
--- a/src/share/classes/sun/security/krb5/KrbAsReq.java
+++ b/src/share/classes/sun/security/krb5/KrbAsReq.java
@@ -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;
}
diff --git a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..940d31107e500438442ca67fec7adacca20a694f
--- /dev/null
+++ b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java
@@ -0,0 +1,395 @@
+/*
+ * 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 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");
+ }
+ }
+}
diff --git a/src/share/classes/sun/security/krb5/KrbTgsReq.java b/src/share/classes/sun/security/krb5/KrbTgsReq.java
index 286018ce3284ee72147bd0d1edee2913e3d5c359..1021a7b10bde099b35109e2a06930ab0808db26f 100644
--- a/src/share/classes/sun/security/krb5/KrbTgsReq.java
+++ b/src/share/classes/sun/security/krb5/KrbTgsReq.java
@@ -1,5 +1,5 @@
/*
- * 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;
}
diff --git a/src/share/classes/sun/security/krb5/PrincipalName.java b/src/share/classes/sun/security/krb5/PrincipalName.java
index 6e41b93d604065f6e844b053563d291c015dbf61..647f7199b3fd6b5f8d84ad1e58329a9285e3e2ce 100644
--- a/src/share/classes/sun/security/krb5/PrincipalName.java
+++ b/src/share/classes/sun/security/krb5/PrincipalName.java
@@ -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++) {
diff --git a/src/share/classes/sun/security/krb5/internal/KDCRep.java b/src/share/classes/sun/security/krb5/internal/KDCRep.java
index fad96c8c4149468e86f3245d027581682b5aaf53..5ff8da2e3aae697e05ea9b1cac4170eb1cdc8d5a 100644
--- a/src/share/classes/sun/security/krb5/internal/KDCRep.java
+++ b/src/share/classes/sun/security/krb5/internal/KDCRep.java
@@ -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(
diff --git a/src/share/classes/sun/security/krb5/internal/KRBError.java b/src/share/classes/sun/security/krb5/internal/KRBError.java
index 4bf1de79e94bfac9abbf9df4a0d46314e25345b5..ac8667f0686b3f48ae168de65e499e5e0887caff 100644
--- a/src/share/classes/sun/security/krb5/internal/KRBError.java
+++ b/src/share/classes/sun/security/krb5/internal/KRBError.java
@@ -1,5 +1,5 @@
/*
- * 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 paList = new ArrayList();
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() {
diff --git a/src/share/classes/sun/security/krb5/internal/KerberosTime.java b/src/share/classes/sun/security/krb5/internal/KerberosTime.java
index 1e0cbeccb2deb8b931a26cc410abd6999c1a88ab..eae48e173ed6b295f07b32903341490719f19b97 100644
--- a/src/share/classes/sun/security/krb5/internal/KerberosTime.java
+++ b/src/share/classes/sun/security/krb5/internal/KerberosTime.java
@@ -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;
}
diff --git a/src/share/classes/sun/security/krb5/internal/PAData.java b/src/share/classes/sun/security/krb5/internal/PAData.java
index b5f95f1ceeaf745a17b9df7c69bc10eca795c426..d32bbc47ee5b834f894ecce4cd37064dc9976971 100644
--- a/src/share/classes/sun/security/krb5/internal/PAData.java
+++ b/src/share/classes/sun/security/krb5/internal/PAData.java
@@ -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);
+ }
}
diff --git a/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java b/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java
index 7a3ba93c60b766361e045b6b222af4ca6bcdb4ba..f6c53f81f1ebe2fb538604a116ec556fb98fe4d6 100644
--- a/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java
+++ b/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java
@@ -1,5 +1,5 @@
/*
- * 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:
diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java
index 8b56ed0f341be37e9ad26daaac67b3617a5fbbba..796415bb03e3f63493bb10e45865c8c0aa127bdc 100644
--- a/test/sun/security/krb5/auto/KDC.java
+++ b/test/sun/security/krb5/auto/KDC.java
@@ -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 passwords = new HashMap();
+
+ // 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 passwords = new TreeMap
+ (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 outPAs = new ArrayList();
+
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 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 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);
+ }
+ }
}
diff --git a/test/sun/security/krb5/auto/NewSalt.java b/test/sun/security/krb5/auto/NewSalt.java
new file mode 100644
index 0000000000000000000000000000000000000000..54cc9562f37f77b8e2588e8078b355496fa11f4d
--- /dev/null
+++ b/test/sun/security/krb5/auto/NewSalt.java
@@ -0,0 +1,64 @@
+/*
+ * 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);
+ }
+}
diff --git a/test/sun/security/krb5/auto/OneKDC.java b/test/sun/security/krb5/auto/OneKDC.java
index 538ab3f32c8f32ece5af390667d822f37c34a98d..79a883017e3fbd3ac32d62dba67b36916a791fe9 100644
--- a/test/sun/security/krb5/auto/OneKDC.java
+++ b/test/sun/security/krb5/auto/OneKDC.java
@@ -1,5 +1,5 @@
/*
- * 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);
diff --git a/test/sun/security/krb5/auto/W83.java b/test/sun/security/krb5/auto/W83.java
index 298353c3e4526a7d0a7c47113507969ee12a4221..5c5e9af5cb3975c7baa5b50a3393340d8189a9b1 100644
--- a/test/sun/security/krb5/auto/W83.java
+++ b/test/sun/security/krb5/auto/W83.java
@@ -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 {