Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/krb5/InitialToken.java
41161 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.jgss.krb5;2627import org.ietf.jgss.*;28import javax.security.auth.kerberos.DelegationPermission;29import java.io.IOException;30import java.net.InetAddress;31import java.net.Inet4Address;32import java.net.Inet6Address;33import java.security.MessageDigest;34import java.security.NoSuchAlgorithmException;35import java.util.Arrays;36import sun.security.krb5.*;37import sun.security.krb5.internal.Krb5;38import sun.security.jgss.krb5.internal.TlsChannelBindingImpl;3940abstract class InitialToken extends Krb5Token {4142private static final int CHECKSUM_TYPE = 0x8003;4344private static final int CHECKSUM_LENGTH_SIZE = 4;45private static final int CHECKSUM_BINDINGS_SIZE = 16;46private static final int CHECKSUM_FLAGS_SIZE = 4;47private static final int CHECKSUM_DELEG_OPT_SIZE = 2;48private static final int CHECKSUM_DELEG_LGTH_SIZE = 2;4950private static final int CHECKSUM_DELEG_FLAG = 1;51private static final int CHECKSUM_MUTUAL_FLAG = 2;52private static final int CHECKSUM_REPLAY_FLAG = 4;53private static final int CHECKSUM_SEQUENCE_FLAG = 8;54private static final int CHECKSUM_CONF_FLAG = 16;55private static final int CHECKSUM_INTEG_FLAG = 32;5657private final byte[] CHECKSUM_FIRST_BYTES =58{(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00};5960private static final int CHANNEL_BINDING_AF_UNSPEC = 0;61private static final int CHANNEL_BINDING_AF_INET = 2;62private static final int CHANNEL_BINDING_AF_INET6 = 24;63private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;6465private static final int Inet4_ADDRSZ = 4;66private static final int Inet6_ADDRSZ = 16;6768protected class OverloadedChecksum {6970private byte[] checksumBytes = null;71private Credentials delegCreds = null;72private int flags = 0;7374/**75* Called on the initiator side when creating the76* InitSecContextToken.77*/78public OverloadedChecksum(Krb5Context context,79Credentials tgt,80Credentials serviceTicket)81throws KrbException, IOException, GSSException {8283byte[] krbCredMessage = null;84int pos = 0;85int size = CHECKSUM_LENGTH_SIZE + CHECKSUM_BINDINGS_SIZE +86CHECKSUM_FLAGS_SIZE;8788if (!tgt.isForwardable()) {89context.setCredDelegState(false);90context.setDelegPolicyState(false);91} else if (context.getCredDelegState()) {92if (context.getDelegPolicyState()) {93if (!serviceTicket.checkDelegate()) {94// delegation not permitted by server policy, mark it95context.setDelegPolicyState(false);96}97}98} else if (context.getDelegPolicyState()) {99if (serviceTicket.checkDelegate()) {100context.setCredDelegState(true);101} else {102context.setDelegPolicyState(false);103}104}105106if (context.getCredDelegState()) {107KrbCred krbCred = null;108CipherHelper cipherHelper =109context.getCipherHelper(serviceTicket.getSessionKey());110if (useNullKey(cipherHelper)) {111krbCred = new KrbCred(tgt, serviceTicket,112EncryptionKey.NULL_KEY);113} else {114krbCred = new KrbCred(tgt, serviceTicket,115serviceTicket.getSessionKey());116}117krbCredMessage = krbCred.getMessage();118size += CHECKSUM_DELEG_OPT_SIZE +119CHECKSUM_DELEG_LGTH_SIZE +120krbCredMessage.length;121}122123checksumBytes = new byte[size];124125checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[0];126checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[1];127checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[2];128checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[3];129130ChannelBinding localBindings = context.getChannelBinding();131if (localBindings != null) {132byte[] localBindingsBytes =133computeChannelBinding(context.getChannelBinding());134System.arraycopy(localBindingsBytes, 0,135checksumBytes, pos, localBindingsBytes.length);136// System.out.println("ChannelBinding hash: "137// + getHexBytes(localBindingsBytes));138}139140pos += CHECKSUM_BINDINGS_SIZE;141142if (context.getCredDelegState())143flags |= CHECKSUM_DELEG_FLAG;144if (context.getMutualAuthState())145flags |= CHECKSUM_MUTUAL_FLAG;146if (context.getReplayDetState())147flags |= CHECKSUM_REPLAY_FLAG;148if (context.getSequenceDetState())149flags |= CHECKSUM_SEQUENCE_FLAG;150if (context.getIntegState())151flags |= CHECKSUM_INTEG_FLAG;152if (context.getConfState())153flags |= CHECKSUM_CONF_FLAG;154155byte[] temp = new byte[4];156writeLittleEndian(flags, temp);157checksumBytes[pos++] = temp[0];158checksumBytes[pos++] = temp[1];159checksumBytes[pos++] = temp[2];160checksumBytes[pos++] = temp[3];161162if (context.getCredDelegState()) {163164PrincipalName delegateTo =165serviceTicket.getServer();166// Cannot use '\"' instead of "\"" in constructor because167// it is interpreted as suggested length!168StringBuilder sb = new StringBuilder("\"");169sb.append(delegateTo.getName()).append('\"');170String realm = delegateTo.getRealmAsString();171sb.append(" \"krbtgt/").append(realm).append('@');172sb.append(realm).append('\"');173@SuppressWarnings("removal")174SecurityManager sm = System.getSecurityManager();175if (sm != null) {176DelegationPermission perm =177new DelegationPermission(sb.toString());178sm.checkPermission(perm);179}180181182/*183* Write 1 in little endian but in two bytes184* for DlgOpt185*/186187checksumBytes[pos++] = (byte)0x01;188checksumBytes[pos++] = (byte)0x00;189190/*191* Write the length of the delegated credential in little192* endian but in two bytes for Dlgth193*/194195if (krbCredMessage.length > 0x0000ffff)196throw new GSSException(GSSException.FAILURE, -1,197"Incorrect message length");198199writeLittleEndian(krbCredMessage.length, temp);200checksumBytes[pos++] = temp[0];201checksumBytes[pos++] = temp[1];202System.arraycopy(krbCredMessage, 0,203checksumBytes, pos, krbCredMessage.length);204}205206}207208/**209* Called on the acceptor side when reading an InitSecContextToken.210*/211// XXX Passing in Checksum is not required. byte[] can212// be passed in if this checksum type denotes a213// raw_checksum. In that case, make Checksum class krb5214// internal.215public OverloadedChecksum(Krb5Context context, Checksum checksum,216EncryptionKey key, EncryptionKey subKey)217throws GSSException, KrbException, IOException {218219int pos = 0;220221if (checksum == null) {222GSSException ge = new GSSException(GSSException.FAILURE, -1,223"No cksum in AP_REQ's authenticator");224ge.initCause(new KrbException(Krb5.KRB_AP_ERR_INAPP_CKSUM));225throw ge;226}227checksumBytes = checksum.getBytes();228229if ((checksumBytes[0] != CHECKSUM_FIRST_BYTES[0]) ||230(checksumBytes[1] != CHECKSUM_FIRST_BYTES[1]) ||231(checksumBytes[2] != CHECKSUM_FIRST_BYTES[2]) ||232(checksumBytes[3] != CHECKSUM_FIRST_BYTES[3])) {233throw new GSSException(GSSException.FAILURE, -1,234"Incorrect checksum");235}236237ChannelBinding localBindings = context.getChannelBinding();238239// Ignore remote channel binding info when not requested at240// local side (RFC 4121 4.1.1.2: the acceptor MAY ignore...).241//242// All major krb5 implementors implement this "MAY",243// and some applications depend on it as a workaround244// for not having a way to negotiate the use of channel245// binding -- the initiator application always uses CB246// and hopes the acceptor will ignore the CB if the247// acceptor doesn't support CB.248if (localBindings != null) {249byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE];250System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0,251CHECKSUM_BINDINGS_SIZE);252253byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE];254if (!Arrays.equals(noBindings, remoteBindingBytes)) {255byte[] localBindingsBytes =256computeChannelBinding(localBindings);257if (!Arrays.equals(localBindingsBytes,258remoteBindingBytes)) {259throw new GSSException(GSSException.BAD_BINDINGS, -1,260"Bytes mismatch!");261}262} else {263throw new GSSException(GSSException.BAD_BINDINGS, -1,264"Token missing ChannelBinding!");265}266}267268flags = readLittleEndian(checksumBytes, 20, 4);269270if ((flags & CHECKSUM_DELEG_FLAG) > 0) {271272/*273* XXX274* if ((checksumBytes[24] != (byte)0x01) &&275* (checksumBytes[25] != (byte)0x00))276*/277278int credLen = readLittleEndian(checksumBytes, 26, 2);279byte[] credBytes = new byte[credLen];280System.arraycopy(checksumBytes, 28, credBytes, 0, credLen);281282KrbCred cred;283try {284cred = new KrbCred(credBytes, key);285} catch (KrbException ke) {286if (subKey != null) {287cred = new KrbCred(credBytes, subKey);288} else {289throw ke;290}291}292delegCreds = cred.getDelegatedCreds()[0];293}294}295296// check if KRB-CRED message should use NULL_KEY for encryption297private boolean useNullKey(CipherHelper ch) {298boolean flag = true;299// for "newer" etypes and RC4-HMAC do not use NULL KEY300if ((ch.getProto() == 1) || ch.isArcFour()) {301flag = false;302}303return flag;304}305306public Checksum getChecksum() throws KrbException {307return new Checksum(checksumBytes, CHECKSUM_TYPE);308}309310public Credentials getDelegatedCreds() {311return delegCreds;312}313314// Only called by acceptor315public void setContextFlags(Krb5Context context) {316// default for cred delegation is false317if ((flags & CHECKSUM_DELEG_FLAG) > 0)318context.setCredDelegState(true);319// default for the following are true320if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) {321context.setMutualAuthState(false);322}323if ((flags & CHECKSUM_REPLAY_FLAG) == 0) {324context.setReplayDetState(false);325}326if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) {327context.setSequenceDetState(false);328}329if ((flags & CHECKSUM_CONF_FLAG) == 0) {330context.setConfState(false);331}332if ((flags & CHECKSUM_INTEG_FLAG) == 0) {333context.setIntegState(false);334}335}336}337338private int getAddrType(InetAddress addr, int defValue) {339int addressType = defValue;340341if (addr instanceof Inet4Address)342addressType = CHANNEL_BINDING_AF_INET;343else if (addr instanceof Inet6Address)344addressType = CHANNEL_BINDING_AF_INET6;345return (addressType);346}347348private byte[] getAddrBytes(InetAddress addr) throws GSSException {349int addressType = getAddrType(addr, CHANNEL_BINDING_AF_NULL_ADDR);350byte[] addressBytes = addr.getAddress();351if (addressBytes != null) {352switch (addressType) {353case CHANNEL_BINDING_AF_INET:354if (addressBytes.length != Inet4_ADDRSZ) {355throw new GSSException(GSSException.FAILURE, -1,356"Incorrect AF-INET address length in ChannelBinding.");357}358return (addressBytes);359case CHANNEL_BINDING_AF_INET6:360if (addressBytes.length != Inet6_ADDRSZ) {361throw new GSSException(GSSException.FAILURE, -1,362"Incorrect AF-INET6 address length in ChannelBinding.");363}364return (addressBytes);365default:366throw new GSSException(GSSException.FAILURE, -1,367"Cannot handle non AF-INET addresses in ChannelBinding.");368}369}370return null;371}372373private byte[] computeChannelBinding(ChannelBinding channelBinding)374throws GSSException {375376InetAddress initiatorAddress = channelBinding.getInitiatorAddress();377InetAddress acceptorAddress = channelBinding.getAcceptorAddress();378int size = 5*4;379380// LDAP TLS Channel Binding requires CHANNEL_BINDING_AF_UNSPEC address type381// for unspecified initiator and acceptor addresses.382// CHANNEL_BINDING_AF_NULL_ADDR value should be used for unspecified address383// in all other cases.384int initiatorAddressType = getAddrType(initiatorAddress,385(channelBinding instanceof TlsChannelBindingImpl) ?386CHANNEL_BINDING_AF_UNSPEC : CHANNEL_BINDING_AF_NULL_ADDR);387int acceptorAddressType = getAddrType(acceptorAddress,388(channelBinding instanceof TlsChannelBindingImpl) ?389CHANNEL_BINDING_AF_UNSPEC : CHANNEL_BINDING_AF_NULL_ADDR);390391byte[] initiatorAddressBytes = null;392if (initiatorAddress != null) {393initiatorAddressBytes = getAddrBytes(initiatorAddress);394size += initiatorAddressBytes.length;395}396397byte[] acceptorAddressBytes = null;398if (acceptorAddress != null) {399acceptorAddressBytes = getAddrBytes(acceptorAddress);400size += acceptorAddressBytes.length;401}402403byte[] appDataBytes = channelBinding.getApplicationData();404if (appDataBytes != null) {405size += appDataBytes.length;406}407408byte[] data = new byte[size];409410int pos = 0;411412writeLittleEndian(initiatorAddressType, data, pos);413pos += 4;414415if (initiatorAddressBytes != null) {416writeLittleEndian(initiatorAddressBytes.length, data, pos);417pos += 4;418System.arraycopy(initiatorAddressBytes, 0,419data, pos, initiatorAddressBytes.length);420pos += initiatorAddressBytes.length;421} else {422// Write length 0423pos += 4;424}425426writeLittleEndian(acceptorAddressType, data, pos);427pos += 4;428429if (acceptorAddressBytes != null) {430writeLittleEndian(acceptorAddressBytes.length, data, pos);431pos += 4;432System.arraycopy(acceptorAddressBytes, 0,433data, pos, acceptorAddressBytes.length);434pos += acceptorAddressBytes.length;435} else {436// Write length 0437pos += 4;438}439440if (appDataBytes != null) {441writeLittleEndian(appDataBytes.length, data, pos);442pos += 4;443System.arraycopy(appDataBytes, 0, data, pos,444appDataBytes.length);445pos += appDataBytes.length;446} else {447// Write 0448pos += 4;449}450451try {452MessageDigest md5 = MessageDigest.getInstance("MD5");453return md5.digest(data);454} catch (NoSuchAlgorithmException e) {455throw new GSSException(GSSException.FAILURE, -1,456"Could not get MD5 Message Digest - "457+ e.getMessage());458}459}460461public abstract byte[] encode() throws IOException;462463}464465466