diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/sun/security/krb5/KrbApReq.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/krb5/KrbApReq.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,513 @@ +/* + * Portions Copyright 2000-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import sun.security.krb5.internal.rcache.*; +import java.net.InetAddress; +import sun.security.util.*; +import java.io.IOException; + +/** + * This class encapsulates a KRB-AP-REQ that a client sends to a + * server for authentication. + */ +public class KrbApReq { + + private byte[] obuf; + private KerberosTime ctime; + private int cusec; + private Authenticator authenticator; + private Credentials creds; + private APReq apReqMessg; + + private static CacheTable table = new CacheTable(); + private static boolean DEBUG = Krb5.DEBUG; + + // default is address-less tickets + private boolean KDC_EMPTY_ADDRESSES_ALLOWED = true; + + /** + * Contructs a AP-REQ message to send to the peer. + * @param tgsCred the Credentials to be used to construct the + * AP Request protocol message. + * @param mutualRequired Whether mutual authentication is required + * @param useSubkey Whether the subkey is to be used to protect this + * specific application session. If this is not set then the + * session key from the ticket will be used. + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + /* + // Not Used + public KrbApReq(Credentials tgsCred, + boolean mutualRequired, + boolean useSubKey, + boolean useSeqNumber) throws Asn1Exception, + KrbCryptoException, KrbException, IOException { + + this(tgsCred, mutualRequired, useSubKey, useSeqNumber, null); + } +*/ + + /** + * Contructs a AP-REQ message to send to the peer. + * @param tgsCred the Credentials to be used to construct the + * AP Request protocol message. + * @param mutualRequired Whether mutual authentication is required + * @param useSubkey Whether the subkey is to be used to protect this + * specific application session. If this is not set then the + * session key from the ticket will be used. + * @param checksum checksum of the the application data that accompanies + * the KRB_AP_REQ. + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + // Used in InitSecContextToken + public KrbApReq(Credentials tgsCred, + boolean mutualRequired, + boolean useSubKey, + boolean useSeqNumber, + Checksum cksum) throws Asn1Exception, + KrbCryptoException, KrbException, IOException { + + APOptions apOptions = (mutualRequired? + new APOptions(Krb5.AP_OPTS_MUTUAL_REQUIRED): + new APOptions()); + if (DEBUG) + System.out.println(">>> KrbApReq: APOptions are " + apOptions); + + EncryptionKey subKey = (useSubKey? + new EncryptionKey(tgsCred.getSessionKey()): + null); + + SeqNumber seqNum = new LocalSeqNumber(); + + init(apOptions, + tgsCred, + cksum, + subKey, + seqNum, + null, // AuthorizationData authzData + KeyUsage.KU_AP_REQ_AUTHENTICATOR); + + } + + /** + * Contructs a AP-REQ message from the bytes received from the + * peer. + * @param message The message received from the peer + * @param keys EncrtyptionKeys to decrypt the message; + * key selected will depend on etype used to encrypte data + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + // Used in InitSecContextToken (for AP_REQ and not TGS REQ) + public KrbApReq(byte[] message, + EncryptionKey[] keys, + InetAddress initiator) + throws KrbException, IOException { + obuf = message; + if (apReqMessg == null) + decode(); + authenticate(keys, initiator); + } + + /** + * Contructs a AP-REQ message from the bytes received from the + * peer. + * @param value The DerValue that contains the + * DER enoded AP-REQ protocol message + * @param keys EncrtyptionKeys to decrypt the message; + * + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + /* + public KrbApReq(DerValue value, EncryptionKey[] key, InetAddress initiator) + throws KrbException, IOException { + obuf = value.toByteArray(); + if (apReqMessg == null) + decode(value); + authenticate(keys, initiator); + } + + KrbApReq(APOptions options, + Credentials tgs_creds, + Checksum cksum, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData) + throws KrbException, IOException { + init(options, tgs_creds, cksum, subKey, seqNumber, authorizationData); + } +*/ + + /** used by KrbTgsReq **/ + KrbApReq(APOptions apOptions, + Ticket ticket, + EncryptionKey key, + Realm crealm, + PrincipalName cname, + Checksum cksum, + KerberosTime ctime, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData) + throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + init(apOptions, ticket, key, crealm, cname, + cksum, ctime, subKey, seqNumber, authorizationData, + KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR); + + } + + private void init(APOptions options, + Credentials tgs_creds, + Checksum cksum, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData, + int usage) + throws KrbException, IOException { + + ctime = new KerberosTime(KerberosTime.NOW); + init(options, + tgs_creds.ticket, + tgs_creds.key, + tgs_creds.client.getRealm(), + tgs_creds.client, + cksum, + ctime, + subKey, + seqNumber, + authorizationData, + usage); + } + + private void init(APOptions apOptions, + Ticket ticket, + EncryptionKey key, + Realm crealm, + PrincipalName cname, + Checksum cksum, + KerberosTime ctime, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData, + int usage) + throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + createMessage(apOptions, ticket, key, crealm, cname, + cksum, ctime, subKey, seqNumber, authorizationData, + usage); + obuf = apReqMessg.asn1Encode(); + } + + + void decode() throws KrbException, IOException { + DerValue encoding = new DerValue(obuf); + decode(encoding); + } + + void decode(DerValue encoding) throws KrbException, IOException { + apReqMessg = null; + try { + apReqMessg = new APReq(encoding); + } catch (Asn1Exception e) { + apReqMessg = null; + KRBError err = new KRBError(encoding); + String errStr = err.getErrorString(); + String eText; + if (errStr.charAt(errStr.length() - 1) == 0) + eText = errStr.substring(0, errStr.length() - 1); + else + eText = errStr; + KrbException ke = new KrbException(err.getErrorCode(), eText); + ke.initCause(e); + throw ke; + } + } + + private void authenticate(EncryptionKey[] keys, InetAddress initiator) + throws KrbException, IOException { + int encPartKeyType = apReqMessg.ticket.encPart.getEType(); + EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, keys); + + if (dkey == null) { + throw new KrbException(Krb5.API_INVALID_ARG, + "Cannot find key of appropriate type to decrypt AP REP - " + + EType.toString(encPartKeyType)); + } + + byte[] bytes = apReqMessg.ticket.encPart.decrypt(dkey, + KeyUsage.KU_TICKET); + byte[] temp = apReqMessg.ticket.encPart.reset(bytes, true); + EncTicketPart enc_ticketPart = new EncTicketPart(temp); + + checkPermittedEType(enc_ticketPart.key.getEType()); + + byte[] bytes2 = apReqMessg.authenticator.decrypt(enc_ticketPart.key, + KeyUsage.KU_AP_REQ_AUTHENTICATOR); + byte[] temp2 = apReqMessg.authenticator.reset(bytes2, true); + authenticator = new Authenticator(temp2); + ctime = authenticator.ctime; + cusec = authenticator.cusec; + authenticator.ctime.setMicroSeconds(authenticator.cusec); + authenticator.cname.setRealm(authenticator.crealm); + apReqMessg.ticket.sname.setRealm(apReqMessg.ticket.realm); + enc_ticketPart.cname.setRealm(enc_ticketPart.crealm); + + Config.getInstance().resetDefaultRealm(apReqMessg.ticket.realm.toString()); + + if (!authenticator.cname.equals(enc_ticketPart.cname)) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH); + + KerberosTime currTime = new KerberosTime(KerberosTime.NOW); + if (!authenticator.ctime.inClockSkew(currTime)) + throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); + + // start to check if it is a replay attack. + AuthTime time = + new AuthTime(authenticator.ctime.getTime(), authenticator.cusec); + String client = authenticator.cname.toString(); + if (table.get(time, authenticator.cname.toString()) != null) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + } else { + table.put(client, time, currTime.getTime()); + } + + // check to use addresses in tickets + if (Config.getInstance().useAddresses()) { + KDC_EMPTY_ADDRESSES_ALLOWED = false; + } + + // sender host address + HostAddress sender = null; + if (initiator != null) { + sender = new HostAddress(initiator); + } + + if (sender != null || !KDC_EMPTY_ADDRESSES_ALLOWED) { + if (enc_ticketPart.caddr != null) { + if (sender == null) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR); + if (!enc_ticketPart.caddr.inList(sender)) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR); + } + } + + // XXX check for repeated authenticator + // if found + // throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + // else + // save authenticator to check for later + + KerberosTime now = new KerberosTime(KerberosTime.NOW); + + if ((enc_ticketPart.starttime != null && + enc_ticketPart.starttime.greaterThanWRTClockSkew(now)) || + enc_ticketPart.flags.get(Krb5.TKT_OPTS_INVALID)) + throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_NYV); + + // if the current time is later than end time by more + // than the allowable clock skew, throws ticket expired exception. + if (enc_ticketPart.endtime != null && + now.greaterThanWRTClockSkew(enc_ticketPart.endtime)) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_EXPIRED); + } + + creds = new Credentials( + apReqMessg.ticket, + authenticator.cname, + apReqMessg.ticket.sname, + enc_ticketPart.key, + null, + enc_ticketPart.authtime, + enc_ticketPart.starttime, + enc_ticketPart.endtime, + enc_ticketPart.renewTill, + enc_ticketPart.caddr); + if (DEBUG) { + System.out.println(">>> KrbApReq: authenticate succeed."); + } + } + + /** + * Returns the credentials that are contained in the ticket that + * is part of this this AP-REP. + */ + public Credentials getCreds() { + return creds; + } + + KerberosTime getCtime() { + if (ctime != null) + return ctime; + return authenticator.ctime; + } + + int cusec() { + return cusec; + } + + APOptions getAPOptions() throws KrbException, IOException { + if (apReqMessg == null) + decode(); + if (apReqMessg != null) + return apReqMessg.apOptions; + return null; + } + + /** + * Returns true if mutual authentication is required and hence an + * AP-REP will need to be generated. + * @throws KrbException + * @throws IOException + */ + public boolean getMutualAuthRequired() throws KrbException, IOException { + if (apReqMessg == null) + decode(); + if (apReqMessg != null) + return apReqMessg.apOptions.get(Krb5.AP_OPTS_MUTUAL_REQUIRED); + return false; + } + + boolean useSessionKey() throws KrbException, IOException { + if (apReqMessg == null) + decode(); + if (apReqMessg != null) + return apReqMessg.apOptions.get(Krb5.AP_OPTS_USE_SESSION_KEY); + return false; + } + + /** + * Returns the optional subkey stored in the Authenticator for + * this message. Returns null if none is stored. + */ + public EncryptionKey getSubKey() { + // XXX Can authenticator be null + return authenticator.getSubKey(); + } + + /** + * Returns the optional sequence number stored in the + * Authenticator for this message. Returns null if none is + * stored. + */ + public Integer getSeqNumber() { + // XXX Can authenticator be null + return authenticator.getSeqNumber(); + } + + /** + * Returns the optional Checksum stored in the + * Authenticator for this message. Returns null if none is + * stored. + */ + public Checksum getChecksum() { + return authenticator.getChecksum(); + } + + /** + * Returns the ASN.1 encoding that should be sent to the peer. + */ + public byte[] getMessage() { + return obuf; + } + + /** + * Returns the principal name of the client that generated this + * message. + */ + public PrincipalName getClient() { + return creds.getClient(); + } + + private void createMessage(APOptions apOptions, + Ticket ticket, + EncryptionKey key, + Realm crealm, + PrincipalName cname, + Checksum cksum, + KerberosTime ctime, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData, + int usage) + throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + Integer seqno = null; + + if (seqNumber != null) + seqno = new Integer(seqNumber.current()); + + authenticator = + new Authenticator(crealm, + cname, + cksum, + ctime.getMicroSeconds(), + ctime, + subKey, + seqno, + authorizationData); + + byte[] temp = authenticator.asn1Encode(); + + EncryptedData encAuthenticator = + new EncryptedData(key, temp, usage); + + apReqMessg = + new APReq(apOptions, ticket, encAuthenticator); + } + + // Check that key is one of the permitted types + private static void checkPermittedEType(int target) throws KrbException { + int[] etypes = EType.getDefaults("permitted_enctypes"); + if (etypes == null) { + throw new KrbException( + "No supported encryption types listed in permitted_enctypes"); + } + if (!EType.isSupported(target, etypes)) { + throw new KrbException(EType.toString(target) + + " encryption type not in permitted_enctypes list"); + } + } +}