--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/InitialToken.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2000-2006 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.
+ */
+
+package sun.security.jgss.krb5;
+
+import org.ietf.jgss.*;
+import javax.security.auth.kerberos.DelegationPermission;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import sun.security.krb5.*;
+import sun.security.jgss.GSSUtil;
+import sun.security.krb5.internal.Krb5;
+
+abstract class InitialToken extends Krb5Token {
+
+ private static final int CHECKSUM_TYPE = 0x8003;
+
+ private static final int CHECKSUM_LENGTH_SIZE = 4;
+ private static final int CHECKSUM_BINDINGS_SIZE = 16;
+ private static final int CHECKSUM_FLAGS_SIZE = 4;
+ private static final int CHECKSUM_DELEG_OPT_SIZE = 2;
+ private static final int CHECKSUM_DELEG_LGTH_SIZE = 2;
+
+ private static final int CHECKSUM_DELEG_FLAG = 1;
+ private static final int CHECKSUM_MUTUAL_FLAG = 2;
+ private static final int CHECKSUM_REPLAY_FLAG = 4;
+ private static final int CHECKSUM_SEQUENCE_FLAG = 8;
+ private static final int CHECKSUM_CONF_FLAG = 16;
+ private static final int CHECKSUM_INTEG_FLAG = 32;
+
+ private final byte[] CHECKSUM_FIRST_BYTES =
+ {(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00};
+
+ private static final int CHANNEL_BINDING_AF_INET = 2;
+ private static final int CHANNEL_BINDING_AF_INET6 = 24;
+ private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;
+
+ private static final int Inet4_ADDRSZ = 4;
+ private static final int Inet6_ADDRSZ = 16;
+
+ protected class OverloadedChecksum {
+
+ private byte[] checksumBytes = null;
+ private Credentials delegCreds = null;
+ private int flags = 0;
+
+ /**
+ * Called on the initiator side when creating the
+ * InitSecContextToken.
+ */
+ public OverloadedChecksum(Krb5Context context,
+ Credentials tgt,
+ Credentials serviceTicket)
+ throws KrbException, IOException, GSSException {
+
+ byte[] krbCredMessage = null;
+ int pos = 0;
+ int size = CHECKSUM_LENGTH_SIZE + CHECKSUM_BINDINGS_SIZE +
+ CHECKSUM_FLAGS_SIZE;
+
+ if (context.getCredDelegState()) {
+ if (context.getCaller() == GSSUtil.CALLER_HTTP_NEGOTIATE &&
+ !serviceTicket.getFlags()[Krb5.TKT_OPTS_DELEGATE]) {
+ // When the caller is HTTP/SPNEGO and OK-AS-DELEGATE
+ // is not present in the service ticket, delegation
+ // is disabled.
+ context.setCredDelegState(false);
+ } else if (!tgt.isForwardable()) {
+ // XXX log this resetting of delegation state
+ context.setCredDelegState(false);
+ } else {
+ KrbCred krbCred = null;
+ CipherHelper cipherHelper =
+ context.getCipherHelper(serviceTicket.getSessionKey());
+ if (useNullKey(cipherHelper)) {
+ krbCred = new KrbCred(tgt, serviceTicket,
+ EncryptionKey.NULL_KEY);
+ } else {
+ krbCred = new KrbCred(tgt, serviceTicket,
+ serviceTicket.getSessionKey());
+ }
+ krbCredMessage = krbCred.getMessage();
+ size += CHECKSUM_DELEG_OPT_SIZE +
+ CHECKSUM_DELEG_LGTH_SIZE +
+ krbCredMessage.length;
+ }
+ }
+
+ checksumBytes = new byte[size];
+
+ checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[0];
+ checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[1];
+ checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[2];
+ checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[3];
+
+ ChannelBinding localBindings = context.getChannelBinding();
+ if (localBindings != null) {
+ byte[] localBindingsBytes =
+ computeChannelBinding(context.getChannelBinding());
+ System.arraycopy(localBindingsBytes, 0,
+ checksumBytes, pos, localBindingsBytes.length);
+ // System.out.println("ChannelBinding hash: "
+ // + getHexBytes(localBindingsBytes));
+ }
+
+ pos += CHECKSUM_BINDINGS_SIZE;
+
+ if (context.getCredDelegState())
+ flags |= CHECKSUM_DELEG_FLAG;
+ if (context.getMutualAuthState())
+ flags |= CHECKSUM_MUTUAL_FLAG;
+ if (context.getReplayDetState())
+ flags |= CHECKSUM_REPLAY_FLAG;
+ if (context.getSequenceDetState())
+ flags |= CHECKSUM_SEQUENCE_FLAG;
+ if (context.getIntegState())
+ flags |= CHECKSUM_INTEG_FLAG;
+ if (context.getConfState())
+ flags |= CHECKSUM_CONF_FLAG;
+
+ byte[] temp = new byte[4];
+ writeLittleEndian(flags, temp);
+ checksumBytes[pos++] = temp[0];
+ checksumBytes[pos++] = temp[1];
+ checksumBytes[pos++] = temp[2];
+ checksumBytes[pos++] = temp[3];
+
+ if (context.getCredDelegState()) {
+
+ PrincipalName delegateTo =
+ serviceTicket.getServer();
+ // Cannot use '\"' instead of "\"" in constructor because
+ // it is interpreted as suggested length!
+ StringBuffer buf = new StringBuffer("\"");
+ buf.append(delegateTo.getName()).append('\"');
+ String realm = delegateTo.getRealmAsString();
+ buf.append(" \"krbtgt/").append(realm).append('@');
+ buf.append(realm).append('\"');
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ DelegationPermission perm =
+ new DelegationPermission(buf.toString());
+ sm.checkPermission(perm);
+ }
+
+
+ /*
+ * Write 1 in little endian but in two bytes
+ * for DlgOpt
+ */
+
+ checksumBytes[pos++] = (byte)0x01;
+ checksumBytes[pos++] = (byte)0x00;
+
+ /*
+ * Write the length of the delegated credential in little
+ * endian but in two bytes for Dlgth
+ */
+
+ if (krbCredMessage.length > 0x0000ffff)
+ throw new GSSException(GSSException.FAILURE, -1,
+ "Incorrect messsage length");
+
+ writeLittleEndian(krbCredMessage.length, temp);
+ checksumBytes[pos++] = temp[0];
+ checksumBytes[pos++] = temp[1];
+ System.arraycopy(krbCredMessage, 0,
+ checksumBytes, pos, krbCredMessage.length);
+ }
+
+ }
+
+ /**
+ * Called on the acceptor side when reading an InitSecContextToken.
+ */
+ // XXX Passing in Checksum is not required. byte[] can
+ // be passed in if this checksum type denotes a
+ // raw_checksum. In that case, make Checksum class krb5
+ // internal.
+ public OverloadedChecksum(Krb5Context context,
+ Checksum checksum, EncryptionKey key)
+ throws GSSException, KrbException, IOException {
+
+ int pos = 0;
+
+ checksumBytes = checksum.getBytes();
+
+ if ((checksumBytes[0] != CHECKSUM_FIRST_BYTES[0]) ||
+ (checksumBytes[1] != CHECKSUM_FIRST_BYTES[1]) ||
+ (checksumBytes[2] != CHECKSUM_FIRST_BYTES[2]) ||
+ (checksumBytes[3] != CHECKSUM_FIRST_BYTES[3])) {
+ throw new GSSException(GSSException.FAILURE, -1,
+ "Incorrect checksum");
+ }
+
+ byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE];
+ System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0,
+ CHECKSUM_BINDINGS_SIZE);
+
+ byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE];
+ boolean tokenContainsBindings =
+ (!java.util.Arrays.equals(noBindings, remoteBindingBytes));
+
+ ChannelBinding localBindings = context.getChannelBinding();
+
+ if (tokenContainsBindings ||
+ localBindings != null) {
+
+ boolean badBindings = false;
+ String errorMessage = null;
+
+ if (tokenContainsBindings &&
+ localBindings != null) {
+ byte[] localBindingsBytes =
+ computeChannelBinding(localBindings);
+ // System.out.println("ChannelBinding hash: "
+ // + getHexBytes(localBindingsBytes));
+ badBindings =
+ (!java.util.Arrays.equals(localBindingsBytes,
+ remoteBindingBytes));
+ errorMessage = "Bytes mismatch!";
+ } else if (localBindings == null) {
+ errorMessage = "ChannelBinding not provided!";
+ badBindings = true;
+ } else {
+ errorMessage = "Token missing ChannelBinding!";
+ badBindings = true;
+ }
+
+ if (badBindings)
+ throw new GSSException(GSSException.BAD_BINDINGS, -1,
+ errorMessage);
+ }
+
+ flags = readLittleEndian(checksumBytes, 20, 4);
+
+ if ((flags & CHECKSUM_DELEG_FLAG) > 0) {
+
+ /*
+ * XXX
+ * if ((checksumBytes[24] != (byte)0x01) &&
+ * (checksumBytes[25] != (byte)0x00))
+ */
+
+ int credLen = readLittleEndian(checksumBytes, 26, 2);
+ byte[] credBytes = new byte[credLen];
+ System.arraycopy(checksumBytes, 28, credBytes, 0, credLen);
+
+ CipherHelper cipherHelper = context.getCipherHelper(key);
+ if (useNullKey(cipherHelper)) {
+ delegCreds =
+ new KrbCred(credBytes, EncryptionKey.NULL_KEY).
+ getDelegatedCreds()[0];
+ } else {
+ delegCreds =
+ new KrbCred(credBytes, key).
+ getDelegatedCreds()[0];
+ }
+ }
+ }
+
+ // check if KRB-CRED message should use NULL_KEY for encryption
+ private boolean useNullKey(CipherHelper ch) {
+ boolean flag = true;
+ // for "newer" etypes and RC4-HMAC do not use NULL KEY
+ if ((ch.getProto() == 1) || ch.isArcFour()) {
+ flag = false;
+ }
+ return flag;
+ }
+
+ public Checksum getChecksum() throws KrbException {
+ return new Checksum(checksumBytes, CHECKSUM_TYPE);
+ }
+
+ public Credentials getDelegatedCreds() {
+ return delegCreds;
+ }
+
+ public void setContextFlags(Krb5Context context) {
+ // default for cred delegation is false
+ if ((flags & CHECKSUM_DELEG_FLAG) > 0)
+ context.setCredDelegState(true);
+ // default for the following are true
+ if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) {
+ context.setMutualAuthState(false);
+ }
+ if ((flags & CHECKSUM_REPLAY_FLAG) == 0) {
+ context.setReplayDetState(false);
+ }
+ if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) {
+ context.setSequenceDetState(false);
+ }
+ if ((flags & CHECKSUM_CONF_FLAG) == 0) {
+ context.setConfState(false);
+ }
+ if ((flags & CHECKSUM_INTEG_FLAG) == 0) {
+ context.setIntegState(false);
+ }
+ }
+ }
+
+ private int getAddrType(InetAddress addr) {
+ int addressType = CHANNEL_BINDING_AF_NULL_ADDR;
+
+ if (addr instanceof Inet4Address)
+ addressType = CHANNEL_BINDING_AF_INET;
+ else if (addr instanceof Inet6Address)
+ addressType = CHANNEL_BINDING_AF_INET6;
+ return (addressType);
+ }
+
+ private byte[] getAddrBytes(InetAddress addr) throws GSSException {
+ int addressType = getAddrType(addr);
+ byte[] addressBytes = addr.getAddress();
+ if (addressBytes != null) {
+ switch (addressType) {
+ case CHANNEL_BINDING_AF_INET:
+ if (addressBytes.length != Inet4_ADDRSZ) {
+ throw new GSSException(GSSException.FAILURE, -1,
+ "Incorrect AF-INET address length in ChannelBinding.");
+ }
+ return (addressBytes);
+ case CHANNEL_BINDING_AF_INET6:
+ if (addressBytes.length != Inet6_ADDRSZ) {
+ throw new GSSException(GSSException.FAILURE, -1,
+ "Incorrect AF-INET6 address length in ChannelBinding.");
+ }
+ return (addressBytes);
+ default:
+ throw new GSSException(GSSException.FAILURE, -1,
+ "Cannot handle non AF-INET addresses in ChannelBinding.");
+ }
+ }
+ return null;
+ }
+
+ private byte[] computeChannelBinding(ChannelBinding channelBinding)
+ throws GSSException {
+
+ InetAddress initiatorAddress = channelBinding.getInitiatorAddress();
+ InetAddress acceptorAddress = channelBinding.getAcceptorAddress();
+ int size = 5*4;
+
+ int initiatorAddressType = getAddrType(initiatorAddress);
+ int acceptorAddressType = getAddrType(acceptorAddress);
+
+ byte[] initiatorAddressBytes = null;
+ if (initiatorAddress != null) {
+ initiatorAddressBytes = getAddrBytes(initiatorAddress);
+ size += initiatorAddressBytes.length;
+ }
+
+ byte[] acceptorAddressBytes = null;
+ if (acceptorAddress != null) {
+ acceptorAddressBytes = getAddrBytes(acceptorAddress);
+ size += acceptorAddressBytes.length;
+ }
+
+ byte[] appDataBytes = channelBinding.getApplicationData();
+ if (appDataBytes != null) {
+ size += appDataBytes.length;
+ }
+
+ byte[] data = new byte[size];
+
+ int pos = 0;
+
+ writeLittleEndian(initiatorAddressType, data, pos);
+ pos += 4;
+
+ if (initiatorAddressBytes != null) {
+ writeLittleEndian(initiatorAddressBytes.length, data, pos);
+ pos += 4;
+ System.arraycopy(initiatorAddressBytes, 0,
+ data, pos, initiatorAddressBytes.length);
+ pos += initiatorAddressBytes.length;
+ } else {
+ // Write length 0
+ pos += 4;
+ }
+
+ writeLittleEndian(acceptorAddressType, data, pos);
+ pos += 4;
+
+ if (acceptorAddressBytes != null) {
+ writeLittleEndian(acceptorAddressBytes.length, data, pos);
+ pos += 4;
+ System.arraycopy(acceptorAddressBytes, 0,
+ data, pos, acceptorAddressBytes.length);
+ pos += acceptorAddressBytes.length;
+ } else {
+ // Write length 0
+ pos += 4;
+ }
+
+ if (appDataBytes != null) {
+ writeLittleEndian(appDataBytes.length, data, pos);
+ pos += 4;
+ System.arraycopy(appDataBytes, 0, data, pos,
+ appDataBytes.length);
+ pos += appDataBytes.length;
+ } else {
+ // Write 0
+ pos += 4;
+ }
+
+ try {
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ return md5.digest(data);
+ } catch (NoSuchAlgorithmException e) {
+ throw new GSSException(GSSException.FAILURE, -1,
+ "Could not get MD5 Message Digest - "
+ + e.getMessage());
+ }
+ }
+
+ public abstract byte[] encode() throws IOException;
+
+}