/*
 * Copyright (c) 2000, 2013, 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.
 */

/*
 *
 *  (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.jgss.krb5.Krb5AcceptCredential;
import java.net.InetAddress;
import sun.security.util.*;
import java.io.IOException;
import java.util.Arrays;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import sun.security.krb5.internal.rcache.AuthTimeWithHash;

This class encapsulates a KRB-AP-REQ that a client sends to a server for authentication.
/** * 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; // Used by acceptor side private static ReplayCache rcache = ReplayCache.getInstance(); private static boolean DEBUG = Krb5.DEBUG; private static final char[] hexConst = "0123456789ABCDEF".toCharArray(); /** * Constructs an AP-REQ message to send to the peer. * @param tgsCred the <code>Credentials</code> 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); } */
Constructs an AP-REQ message to send to the peer.
Params:
  • tgsCred – the Credentials to be used to construct the AP Request protocol message.
  • mutualRequired – Whether mutual authentication is required
  • 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.
  • cksum – checksum of the application data that accompanies the KRB_AP_REQ.
Throws:
  • KrbException – for any Kerberos protocol specific error
  • IOException – for any IO related errors (e.g. socket operations)
/** * Constructs an AP-REQ message to send to the peer. * @param tgsCred the <code>Credentials</code> 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 cksum checksum of 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); }
Constructs an AP-REQ message from the bytes received from the peer.
Params:
  • message – The message received from the peer
  • cred – KrbAcceptCredential containing keys to decrypt the message; key selected will depend on etype used to encrypt data
Throws:
  • KrbException – for any Kerberos protocol specific error
  • IOException – for any IO related errors (e.g. socket operations)
/** * Constructs an AP-REQ message from the bytes received from the * peer. * @param message The message received from the peer * @param cred <code>KrbAcceptCredential</code> containing keys to decrypt * the message; key selected will depend on etype used to encrypt 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, Krb5AcceptCredential cred, InetAddress initiator) throws KrbException, IOException { obuf = message; if (apReqMessg == null) decode(); authenticate(cred, initiator); } /** * Constructs an AP-REQ message from the bytes received from the * peer. * @param value The <code>DerValue</code> that contains the * DER enoded AP-REQ protocol message * @param keys <code>EncrtyptionKey</code>s 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
/** used by KrbTgsReq **/
KrbApReq(APOptions apOptions, Ticket ticket, EncryptionKey key, PrincipalName cname, Checksum cksum, KerberosTime ctime, EncryptionKey subKey, SeqNumber seqNumber, AuthorizationData authorizationData) throws Asn1Exception, IOException, KdcErrException, KrbCryptoException { init(apOptions, ticket, key, 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 = KerberosTime.now(); init(options, tgs_creds.ticket, tgs_creds.key, tgs_creds.client, cksum, ctime, subKey, seqNumber, authorizationData, usage); } private void init(APOptions apOptions, Ticket ticket, EncryptionKey key, PrincipalName cname, Checksum cksum, KerberosTime ctime, EncryptionKey subKey, SeqNumber seqNumber, AuthorizationData authorizationData, int usage) throws Asn1Exception, IOException, KdcErrException, KrbCryptoException { createMessage(apOptions, ticket, key, 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(Krb5AcceptCredential cred, InetAddress initiator) throws KrbException, IOException { int encPartKeyType = apReqMessg.ticket.encPart.getEType(); Integer kvno = apReqMessg.ticket.encPart.getKeyVersionNumber(); EncryptionKey[] keys = cred.getKrb5EncryptionKeys(apReqMessg.ticket.sname); EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, kvno, keys); if (dkey == null) { throw new KrbException(Krb5.API_INVALID_ARG, "Cannot find key of appropriate type to decrypt AP-REQ - " + EType.toString(encPartKeyType)); } byte[] bytes = apReqMessg.ticket.encPart.decrypt(dkey, KeyUsage.KU_TICKET); byte[] temp = apReqMessg.ticket.encPart.reset(bytes); 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); authenticator = new Authenticator(temp2); ctime = authenticator.ctime; cusec = authenticator.cusec; authenticator.ctime = authenticator.ctime.withMicroSeconds(authenticator.cusec); if (!authenticator.cname.equals(enc_ticketPart.cname)) { throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH); } if (!authenticator.ctime.inClockSkew()) throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); String alg = AuthTimeWithHash.DEFAULT_HASH_ALG; byte[] hash; try { hash = MessageDigest.getInstance(AuthTimeWithHash.realAlg(alg)) .digest(apReqMessg.authenticator.cipher); } catch (NoSuchAlgorithmException ex) { throw new AssertionError("Impossible " + alg); } char[] h = new char[hash.length * 2]; for (int i=0; i<hash.length; i++) { h[2*i] = hexConst[(hash[i]&0xff)>>4]; h[2*i+1] = hexConst[hash[i]&0xf]; } AuthTimeWithHash time = new AuthTimeWithHash( authenticator.cname.toString(), apReqMessg.ticket.sname.toString(), authenticator.ctime.getSeconds(), authenticator.cusec, alg, new String(h)); rcache.checkAndStore(KerberosTime.now(), time); if (initiator != null) { // sender host address HostAddress sender = new HostAddress(initiator); if (enc_ticketPart.caddr != null && !enc_ticketPart.caddr.inList(sender)) { if (DEBUG) { System.out.println(">>> KrbApReq: initiator is " + sender.getInetAddress() + ", but caddr is " + Arrays.toString( enc_ticketPart.caddr.getInetAddresses())); } 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 = 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, enc_ticketPart.flags, enc_ticketPart.authtime, enc_ticketPart.starttime, enc_ticketPart.endtime, enc_ticketPart.renewTill, enc_ticketPart.caddr, enc_ticketPart.authorizationData); if (DEBUG) { System.out.println(">>> KrbApReq: authenticate succeed."); } }
Returns the credentials that are contained in the ticket that is part of this AP-REQ.
/** * Returns the credentials that are contained in the ticket that * is part of this AP-REQ. */
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:
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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, 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 = seqNumber.current(); authenticator = new Authenticator(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 (!EType.isSupported(target, etypes)) { throw new KrbException(EType.toString(target) + " encryption type not in permitted_enctypes list"); } } }