0N/A/*
3991N/A * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.jgss.krb5;
0N/A
0N/Aimport org.ietf.jgss.*;
0N/Aimport javax.security.auth.kerberos.DelegationPermission;
0N/Aimport java.io.IOException;
0N/Aimport java.net.InetAddress;
0N/Aimport java.net.Inet4Address;
0N/Aimport java.net.Inet6Address;
0N/Aimport java.security.MessageDigest;
0N/Aimport java.security.NoSuchAlgorithmException;
1304N/Aimport java.util.Arrays;
0N/Aimport sun.security.krb5.*;
0N/Aimport sun.security.krb5.internal.Krb5;
0N/A
0N/Aabstract class InitialToken extends Krb5Token {
0N/A
0N/A private static final int CHECKSUM_TYPE = 0x8003;
0N/A
0N/A private static final int CHECKSUM_LENGTH_SIZE = 4;
0N/A private static final int CHECKSUM_BINDINGS_SIZE = 16;
0N/A private static final int CHECKSUM_FLAGS_SIZE = 4;
0N/A private static final int CHECKSUM_DELEG_OPT_SIZE = 2;
0N/A private static final int CHECKSUM_DELEG_LGTH_SIZE = 2;
0N/A
0N/A private static final int CHECKSUM_DELEG_FLAG = 1;
0N/A private static final int CHECKSUM_MUTUAL_FLAG = 2;
0N/A private static final int CHECKSUM_REPLAY_FLAG = 4;
0N/A private static final int CHECKSUM_SEQUENCE_FLAG = 8;
0N/A private static final int CHECKSUM_CONF_FLAG = 16;
0N/A private static final int CHECKSUM_INTEG_FLAG = 32;
0N/A
0N/A private final byte[] CHECKSUM_FIRST_BYTES =
0N/A {(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00};
0N/A
0N/A private static final int CHANNEL_BINDING_AF_INET = 2;
0N/A private static final int CHANNEL_BINDING_AF_INET6 = 24;
0N/A private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;
0N/A
0N/A private static final int Inet4_ADDRSZ = 4;
0N/A private static final int Inet6_ADDRSZ = 16;
0N/A
0N/A protected class OverloadedChecksum {
0N/A
0N/A private byte[] checksumBytes = null;
0N/A private Credentials delegCreds = null;
0N/A private int flags = 0;
0N/A
0N/A /**
0N/A * Called on the initiator side when creating the
0N/A * InitSecContextToken.
0N/A */
0N/A public OverloadedChecksum(Krb5Context context,
0N/A Credentials tgt,
0N/A Credentials serviceTicket)
0N/A throws KrbException, IOException, GSSException {
0N/A
0N/A byte[] krbCredMessage = null;
0N/A int pos = 0;
0N/A int size = CHECKSUM_LENGTH_SIZE + CHECKSUM_BINDINGS_SIZE +
0N/A CHECKSUM_FLAGS_SIZE;
0N/A
1941N/A if (!tgt.isForwardable()) {
1941N/A context.setCredDelegState(false);
1941N/A context.setDelegPolicyState(false);
1941N/A } else if (context.getCredDelegState()) {
1941N/A if (context.getDelegPolicyState()) {
1941N/A if (!serviceTicket.checkDelegate()) {
1941N/A // delegation not permitted by server policy, mark it
1941N/A context.setDelegPolicyState(false);
1941N/A }
1941N/A }
1941N/A } else if (context.getDelegPolicyState()) {
1941N/A if (serviceTicket.checkDelegate()) {
1941N/A context.setCredDelegState(true);
0N/A } else {
1941N/A context.setDelegPolicyState(false);
0N/A }
0N/A }
0N/A
1941N/A if (context.getCredDelegState()) {
1941N/A KrbCred krbCred = null;
1941N/A CipherHelper cipherHelper =
1941N/A context.getCipherHelper(serviceTicket.getSessionKey());
1941N/A if (useNullKey(cipherHelper)) {
1941N/A krbCred = new KrbCred(tgt, serviceTicket,
1941N/A EncryptionKey.NULL_KEY);
1941N/A } else {
1941N/A krbCred = new KrbCred(tgt, serviceTicket,
1941N/A serviceTicket.getSessionKey());
1941N/A }
1941N/A krbCredMessage = krbCred.getMessage();
1941N/A size += CHECKSUM_DELEG_OPT_SIZE +
1941N/A CHECKSUM_DELEG_LGTH_SIZE +
1941N/A krbCredMessage.length;
1941N/A }
1941N/A
0N/A checksumBytes = new byte[size];
0N/A
0N/A checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[0];
0N/A checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[1];
0N/A checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[2];
0N/A checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[3];
0N/A
0N/A ChannelBinding localBindings = context.getChannelBinding();
0N/A if (localBindings != null) {
0N/A byte[] localBindingsBytes =
0N/A computeChannelBinding(context.getChannelBinding());
0N/A System.arraycopy(localBindingsBytes, 0,
0N/A checksumBytes, pos, localBindingsBytes.length);
0N/A // System.out.println("ChannelBinding hash: "
0N/A // + getHexBytes(localBindingsBytes));
0N/A }
0N/A
0N/A pos += CHECKSUM_BINDINGS_SIZE;
0N/A
0N/A if (context.getCredDelegState())
0N/A flags |= CHECKSUM_DELEG_FLAG;
0N/A if (context.getMutualAuthState())
0N/A flags |= CHECKSUM_MUTUAL_FLAG;
0N/A if (context.getReplayDetState())
0N/A flags |= CHECKSUM_REPLAY_FLAG;
0N/A if (context.getSequenceDetState())
0N/A flags |= CHECKSUM_SEQUENCE_FLAG;
0N/A if (context.getIntegState())
0N/A flags |= CHECKSUM_INTEG_FLAG;
0N/A if (context.getConfState())
0N/A flags |= CHECKSUM_CONF_FLAG;
0N/A
0N/A byte[] temp = new byte[4];
0N/A writeLittleEndian(flags, temp);
0N/A checksumBytes[pos++] = temp[0];
0N/A checksumBytes[pos++] = temp[1];
0N/A checksumBytes[pos++] = temp[2];
0N/A checksumBytes[pos++] = temp[3];
0N/A
0N/A if (context.getCredDelegState()) {
0N/A
0N/A PrincipalName delegateTo =
0N/A serviceTicket.getServer();
0N/A // Cannot use '\"' instead of "\"" in constructor because
0N/A // it is interpreted as suggested length!
0N/A StringBuffer buf = new StringBuffer("\"");
0N/A buf.append(delegateTo.getName()).append('\"');
0N/A String realm = delegateTo.getRealmAsString();
0N/A buf.append(" \"krbtgt/").append(realm).append('@');
0N/A buf.append(realm).append('\"');
0N/A SecurityManager sm = System.getSecurityManager();
0N/A if (sm != null) {
0N/A DelegationPermission perm =
0N/A new DelegationPermission(buf.toString());
0N/A sm.checkPermission(perm);
0N/A }
0N/A
0N/A
0N/A /*
0N/A * Write 1 in little endian but in two bytes
0N/A * for DlgOpt
0N/A */
0N/A
0N/A checksumBytes[pos++] = (byte)0x01;
0N/A checksumBytes[pos++] = (byte)0x00;
0N/A
0N/A /*
0N/A * Write the length of the delegated credential in little
0N/A * endian but in two bytes for Dlgth
0N/A */
0N/A
0N/A if (krbCredMessage.length > 0x0000ffff)
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Incorrect messsage length");
0N/A
0N/A writeLittleEndian(krbCredMessage.length, temp);
0N/A checksumBytes[pos++] = temp[0];
0N/A checksumBytes[pos++] = temp[1];
0N/A System.arraycopy(krbCredMessage, 0,
0N/A checksumBytes, pos, krbCredMessage.length);
0N/A }
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Called on the acceptor side when reading an InitSecContextToken.
0N/A */
0N/A // XXX Passing in Checksum is not required. byte[] can
0N/A // be passed in if this checksum type denotes a
0N/A // raw_checksum. In that case, make Checksum class krb5
0N/A // internal.
3991N/A public OverloadedChecksum(Krb5Context context, Checksum checksum,
3991N/A EncryptionKey key, EncryptionKey subKey)
0N/A throws GSSException, KrbException, IOException {
0N/A
0N/A int pos = 0;
0N/A
2894N/A if (checksum == null) {
2894N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
2894N/A "No cksum in AP_REQ's authenticator");
2894N/A ge.initCause(new KrbException(Krb5.KRB_AP_ERR_INAPP_CKSUM));
2894N/A throw ge;
2894N/A }
0N/A checksumBytes = checksum.getBytes();
0N/A
0N/A if ((checksumBytes[0] != CHECKSUM_FIRST_BYTES[0]) ||
0N/A (checksumBytes[1] != CHECKSUM_FIRST_BYTES[1]) ||
0N/A (checksumBytes[2] != CHECKSUM_FIRST_BYTES[2]) ||
0N/A (checksumBytes[3] != CHECKSUM_FIRST_BYTES[3])) {
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Incorrect checksum");
0N/A }
0N/A
0N/A ChannelBinding localBindings = context.getChannelBinding();
0N/A
1304N/A // Ignore remote channel binding info when not requested at
1304N/A // local side (RFC 4121 4.1.1.2: the acceptor MAY ignore...).
1304N/A //
1304N/A // All major krb5 implementors implement this "MAY",
1304N/A // and some applications depend on it as a workaround
1304N/A // for not having a way to negotiate the use of channel
1304N/A // binding -- the initiator application always uses CB
1304N/A // and hopes the acceptor will ignore the CB if the
1304N/A // acceptor doesn't support CB.
1304N/A if (localBindings != null) {
1304N/A byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE];
1304N/A System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0,
1304N/A CHECKSUM_BINDINGS_SIZE);
0N/A
1304N/A byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE];
1304N/A if (!Arrays.equals(noBindings, remoteBindingBytes)) {
0N/A byte[] localBindingsBytes =
0N/A computeChannelBinding(localBindings);
1304N/A if (!Arrays.equals(localBindingsBytes,
1304N/A remoteBindingBytes)) {
1304N/A throw new GSSException(GSSException.BAD_BINDINGS, -1,
1304N/A "Bytes mismatch!");
1304N/A }
0N/A } else {
1304N/A throw new GSSException(GSSException.BAD_BINDINGS, -1,
1304N/A "Token missing ChannelBinding!");
0N/A }
0N/A }
0N/A
0N/A flags = readLittleEndian(checksumBytes, 20, 4);
0N/A
0N/A if ((flags & CHECKSUM_DELEG_FLAG) > 0) {
0N/A
0N/A /*
0N/A * XXX
0N/A * if ((checksumBytes[24] != (byte)0x01) &&
0N/A * (checksumBytes[25] != (byte)0x00))
0N/A */
0N/A
0N/A int credLen = readLittleEndian(checksumBytes, 26, 2);
0N/A byte[] credBytes = new byte[credLen];
0N/A System.arraycopy(checksumBytes, 28, credBytes, 0, credLen);
0N/A
0N/A CipherHelper cipherHelper = context.getCipherHelper(key);
0N/A if (useNullKey(cipherHelper)) {
0N/A delegCreds =
0N/A new KrbCred(credBytes, EncryptionKey.NULL_KEY).
0N/A getDelegatedCreds()[0];
0N/A } else {
3991N/A KrbCred cred;
3991N/A try {
3991N/A cred = new KrbCred(credBytes, key);
3991N/A } catch (KrbException e) {
3991N/A if (subKey != null) {
3991N/A cred = new KrbCred(credBytes, subKey);
3991N/A } else {
3991N/A throw e;
3991N/A }
3991N/A }
3991N/A delegCreds = cred.getDelegatedCreds()[0];
0N/A }
0N/A }
0N/A }
0N/A
0N/A // check if KRB-CRED message should use NULL_KEY for encryption
0N/A private boolean useNullKey(CipherHelper ch) {
0N/A boolean flag = true;
0N/A // for "newer" etypes and RC4-HMAC do not use NULL KEY
0N/A if ((ch.getProto() == 1) || ch.isArcFour()) {
0N/A flag = false;
0N/A }
0N/A return flag;
0N/A }
0N/A
0N/A public Checksum getChecksum() throws KrbException {
0N/A return new Checksum(checksumBytes, CHECKSUM_TYPE);
0N/A }
0N/A
0N/A public Credentials getDelegatedCreds() {
0N/A return delegCreds;
0N/A }
0N/A
1941N/A // Only called by acceptor
0N/A public void setContextFlags(Krb5Context context) {
0N/A // default for cred delegation is false
0N/A if ((flags & CHECKSUM_DELEG_FLAG) > 0)
0N/A context.setCredDelegState(true);
0N/A // default for the following are true
0N/A if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) {
0N/A context.setMutualAuthState(false);
0N/A }
0N/A if ((flags & CHECKSUM_REPLAY_FLAG) == 0) {
0N/A context.setReplayDetState(false);
0N/A }
0N/A if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) {
0N/A context.setSequenceDetState(false);
0N/A }
0N/A if ((flags & CHECKSUM_CONF_FLAG) == 0) {
0N/A context.setConfState(false);
0N/A }
0N/A if ((flags & CHECKSUM_INTEG_FLAG) == 0) {
0N/A context.setIntegState(false);
0N/A }
0N/A }
0N/A }
0N/A
0N/A private int getAddrType(InetAddress addr) {
0N/A int addressType = CHANNEL_BINDING_AF_NULL_ADDR;
0N/A
0N/A if (addr instanceof Inet4Address)
0N/A addressType = CHANNEL_BINDING_AF_INET;
0N/A else if (addr instanceof Inet6Address)
0N/A addressType = CHANNEL_BINDING_AF_INET6;
0N/A return (addressType);
0N/A }
0N/A
0N/A private byte[] getAddrBytes(InetAddress addr) throws GSSException {
0N/A int addressType = getAddrType(addr);
0N/A byte[] addressBytes = addr.getAddress();
0N/A if (addressBytes != null) {
0N/A switch (addressType) {
0N/A case CHANNEL_BINDING_AF_INET:
0N/A if (addressBytes.length != Inet4_ADDRSZ) {
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Incorrect AF-INET address length in ChannelBinding.");
0N/A }
0N/A return (addressBytes);
0N/A case CHANNEL_BINDING_AF_INET6:
0N/A if (addressBytes.length != Inet6_ADDRSZ) {
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Incorrect AF-INET6 address length in ChannelBinding.");
0N/A }
0N/A return (addressBytes);
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Cannot handle non AF-INET addresses in ChannelBinding.");
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A private byte[] computeChannelBinding(ChannelBinding channelBinding)
0N/A throws GSSException {
0N/A
0N/A InetAddress initiatorAddress = channelBinding.getInitiatorAddress();
0N/A InetAddress acceptorAddress = channelBinding.getAcceptorAddress();
0N/A int size = 5*4;
0N/A
0N/A int initiatorAddressType = getAddrType(initiatorAddress);
0N/A int acceptorAddressType = getAddrType(acceptorAddress);
0N/A
0N/A byte[] initiatorAddressBytes = null;
0N/A if (initiatorAddress != null) {
0N/A initiatorAddressBytes = getAddrBytes(initiatorAddress);
0N/A size += initiatorAddressBytes.length;
0N/A }
0N/A
0N/A byte[] acceptorAddressBytes = null;
0N/A if (acceptorAddress != null) {
0N/A acceptorAddressBytes = getAddrBytes(acceptorAddress);
0N/A size += acceptorAddressBytes.length;
0N/A }
0N/A
0N/A byte[] appDataBytes = channelBinding.getApplicationData();
0N/A if (appDataBytes != null) {
0N/A size += appDataBytes.length;
0N/A }
0N/A
0N/A byte[] data = new byte[size];
0N/A
0N/A int pos = 0;
0N/A
0N/A writeLittleEndian(initiatorAddressType, data, pos);
0N/A pos += 4;
0N/A
0N/A if (initiatorAddressBytes != null) {
0N/A writeLittleEndian(initiatorAddressBytes.length, data, pos);
0N/A pos += 4;
0N/A System.arraycopy(initiatorAddressBytes, 0,
0N/A data, pos, initiatorAddressBytes.length);
0N/A pos += initiatorAddressBytes.length;
0N/A } else {
0N/A // Write length 0
0N/A pos += 4;
0N/A }
0N/A
0N/A writeLittleEndian(acceptorAddressType, data, pos);
0N/A pos += 4;
0N/A
0N/A if (acceptorAddressBytes != null) {
0N/A writeLittleEndian(acceptorAddressBytes.length, data, pos);
0N/A pos += 4;
0N/A System.arraycopy(acceptorAddressBytes, 0,
0N/A data, pos, acceptorAddressBytes.length);
0N/A pos += acceptorAddressBytes.length;
0N/A } else {
0N/A // Write length 0
0N/A pos += 4;
0N/A }
0N/A
0N/A if (appDataBytes != null) {
0N/A writeLittleEndian(appDataBytes.length, data, pos);
0N/A pos += 4;
0N/A System.arraycopy(appDataBytes, 0, data, pos,
0N/A appDataBytes.length);
0N/A pos += appDataBytes.length;
0N/A } else {
0N/A // Write 0
0N/A pos += 4;
0N/A }
0N/A
0N/A try {
0N/A MessageDigest md5 = MessageDigest.getInstance("MD5");
0N/A return md5.digest(data);
0N/A } catch (NoSuchAlgorithmException e) {
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Could not get MD5 Message Digest - "
0N/A + e.getMessage());
0N/A }
0N/A }
0N/A
0N/A public abstract byte[] encode() throws IOException;
0N/A
0N/A}