460 lines · 17.8 KB · Java
RawDownload
/* * Copyright (c) 2000, 2011, 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.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 java.util.Arrays;import sun.security.krb5.*;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 (!tgt.isForwardable()) {                context.setCredDelegState(false);                context.setDelegPolicyState(false);            } else if (context.getCredDelegState()) {                if (context.getDelegPolicyState()) {                    if (!serviceTicket.checkDelegate()) {                        // delegation not permitted by server policy, mark it                        context.setDelegPolicyState(false);                    }                }            } else if (context.getDelegPolicyState()) {                if (serviceTicket.checkDelegate()) {                    context.setCredDelegState(true);                } else {                    context.setDelegPolicyState(false);                }            }             if (context.getCredDelegState()) {                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, EncryptionKey subKey)            throws GSSException, KrbException, IOException {             int pos = 0;             if (checksum == null) {                GSSException ge = new GSSException(GSSException.FAILURE, -1,                        "No cksum in AP_REQ's authenticator");                ge.initCause(new KrbException(Krb5.KRB_AP_ERR_INAPP_CKSUM));                throw ge;            }            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");            }             ChannelBinding localBindings = context.getChannelBinding();             // Ignore remote channel binding info when not requested at            // local side (RFC 4121 4.1.1.2: the acceptor MAY ignore...).            //            // All major krb5 implementors implement this "MAY",            // and some applications depend on it as a workaround            // for not having a way to negotiate the use of channel            // binding -- the initiator application always uses CB            // and hopes the acceptor will ignore the CB if the            // acceptor doesn't support CB.            if (localBindings != null) {                byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE];                System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0,                                 CHECKSUM_BINDINGS_SIZE);                 byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE];                if (!Arrays.equals(noBindings, remoteBindingBytes)) {                    byte[] localBindingsBytes =                        computeChannelBinding(localBindings);                    if (!Arrays.equals(localBindingsBytes,                                                remoteBindingBytes)) {                        throw new GSSException(GSSException.BAD_BINDINGS, -1,                                               "Bytes mismatch!");                    }                } else {                    throw new GSSException(GSSException.BAD_BINDINGS, -1,                                           "Token missing ChannelBinding!");                }            }             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 {                    KrbCred cred;                    try {                        cred = new KrbCred(credBytes, key);                    } catch (KrbException e) {                        if (subKey != null) {                            cred = new KrbCred(credBytes, subKey);                        } else {                            throw e;                        }                    }                    delegCreds = cred.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;        }         // Only called by acceptor        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; }