Package sun.security.jgss.krb5

Source Code of sun.security.jgss.krb5.InitialToken

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

            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");
            }

            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;

}
TOP

Related Classes of sun.security.jgss.krb5.InitialToken

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.