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 {