feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
115
jdkSrc/jdk8/sun/security/jgss/krb5/AcceptSecContextToken.java
Normal file
115
jdkSrc/jdk8/sun/security/jgss/krb5/AcceptSecContextToken.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2013, 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 java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
|
||||
import sun.security.action.GetBooleanAction;
|
||||
import sun.security.krb5.*;
|
||||
|
||||
class AcceptSecContextToken extends InitialToken {
|
||||
|
||||
private KrbApRep apRep = null;
|
||||
|
||||
/**
|
||||
* Creates an AcceptSecContextToken for the context acceptor to send to
|
||||
* the context initiator.
|
||||
*/
|
||||
public AcceptSecContextToken(Krb5Context context,
|
||||
KrbApReq apReq)
|
||||
throws KrbException, IOException, GSSException {
|
||||
|
||||
boolean useSubkey = AccessController.doPrivileged(
|
||||
new GetBooleanAction("sun.security.krb5.acceptor.subkey"));
|
||||
|
||||
boolean useSequenceNumber = true;
|
||||
|
||||
EncryptionKey subKey = null;
|
||||
if (useSubkey) {
|
||||
subKey = new EncryptionKey(apReq.getCreds().getSessionKey());
|
||||
context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey);
|
||||
}
|
||||
apRep = new KrbApRep(apReq, useSequenceNumber, subKey);
|
||||
|
||||
context.resetMySequenceNumber(apRep.getSeqNumber().intValue());
|
||||
|
||||
/*
|
||||
* Note: The acceptor side context key was set when the
|
||||
* InitSecContextToken was received.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AcceptSecContextToken at the context initiator's side
|
||||
* using the bytes received from the acceptor.
|
||||
*/
|
||||
public AcceptSecContextToken(Krb5Context context,
|
||||
Credentials serviceCreds, KrbApReq apReq,
|
||||
InputStream is)
|
||||
throws IOException, GSSException, KrbException {
|
||||
|
||||
int tokenId = ((is.read()<<8) | is.read());
|
||||
|
||||
if (tokenId != Krb5Token.AP_REP_ID)
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
"AP_REP token id does not match!");
|
||||
|
||||
byte[] apRepBytes =
|
||||
new sun.security.util.DerValue(is).toByteArray();
|
||||
|
||||
KrbApRep apRep = new KrbApRep(apRepBytes, serviceCreds, apReq);
|
||||
|
||||
/*
|
||||
* Allow the context acceptor to set a subkey if desired, even
|
||||
* though our context acceptor will not do so.
|
||||
*/
|
||||
EncryptionKey subKey = apRep.getSubKey();
|
||||
if (subKey != null) {
|
||||
context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey);
|
||||
/*
|
||||
System.out.println("\n\nSub-Session key from AP-REP is: " +
|
||||
getHexBytes(subKey.getBytes()) + "\n");
|
||||
*/
|
||||
}
|
||||
|
||||
Integer apRepSeqNumber = apRep.getSeqNumber();
|
||||
int peerSeqNumber = (apRepSeqNumber != null ?
|
||||
apRepSeqNumber.intValue() :
|
||||
0);
|
||||
context.resetPeerSequenceNumber(peerSeqNumber);
|
||||
}
|
||||
|
||||
public final byte[] encode() throws IOException {
|
||||
byte[] apRepBytes = apRep.getMessage();
|
||||
byte[] retVal = new byte[2 + apRepBytes.length];
|
||||
writeInt(Krb5Token.AP_REP_ID, retVal, 0);
|
||||
System.arraycopy(apRepBytes, 0, retVal, 2, apRepBytes.length);
|
||||
return retVal;
|
||||
}
|
||||
}
|
1502
jdkSrc/jdk8/sun/security/jgss/krb5/CipherHelper.java
Normal file
1502
jdkSrc/jdk8/sun/security/jgss/krb5/CipherHelper.java
Normal file
File diff suppressed because it is too large
Load Diff
215
jdkSrc/jdk8/sun/security/jgss/krb5/InitSecContextToken.java
Normal file
215
jdkSrc/jdk8/sun/security/jgss/krb5/InitSecContextToken.java
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2018, 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 com.sun.security.jgss.AuthorizationDataEntry;
|
||||
import org.ietf.jgss.*;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.krb5.*;
|
||||
import java.net.InetAddress;
|
||||
import sun.security.krb5.internal.AuthorizationData;
|
||||
import sun.security.krb5.internal.KerberosTime;
|
||||
|
||||
class InitSecContextToken extends InitialToken {
|
||||
|
||||
// If non-mutual authentication is requested, there is no AP-REP message.
|
||||
// The acceptor thus has no chance to send the seq-number field to the
|
||||
// initiator. In this case, the initiator and acceptor should has an
|
||||
// agreement to derive acceptor's initial seq-number if the acceptor wishes
|
||||
// to send messages to the initiator.
|
||||
|
||||
// If this flag is true, it will the same as the initiator's initial
|
||||
// seq-number (as MIT krb5 and Windows SSPI do). Otherwise, it will be zero
|
||||
// (as Heimdal does). The default value is true.
|
||||
private static final boolean ACCEPTOR_USE_INITIATOR_SEQNUM;
|
||||
|
||||
static {
|
||||
// The ACCEPTOR_USE_INITIATOR_SEQNUM value is determined by the system
|
||||
// property "sun.security.krb5.acceptor.sequence.number.nonmutual",
|
||||
// which can be set to "initiator", "zero" or "0".
|
||||
String propName = "sun.security.krb5.acceptor.sequence.number.nonmutual";
|
||||
String s = GetPropertyAction.privilegedGetProperty(propName, "initiator");
|
||||
if (s.equals("initiator")) {
|
||||
ACCEPTOR_USE_INITIATOR_SEQNUM = true;
|
||||
} else if (s.equals("zero") || s.equals("0")) {
|
||||
ACCEPTOR_USE_INITIATOR_SEQNUM = false;
|
||||
} else {
|
||||
throw new AssertionError("Unrecognized value for " + propName
|
||||
+ ": " + s);
|
||||
}
|
||||
}
|
||||
|
||||
private KrbApReq apReq = null;
|
||||
|
||||
/**
|
||||
* For the context initiator to call. It constructs a new
|
||||
* InitSecContextToken to send over to the peer containing the desired
|
||||
* flags and the AP-REQ. It also updates the context with the local
|
||||
* sequence number and shared context key.
|
||||
* (When mutual auth is enabled the peer has an opportunity to
|
||||
* renegotiate the session key in the followup AcceptSecContextToken
|
||||
* that it sends.)
|
||||
*/
|
||||
InitSecContextToken(Krb5Context context,
|
||||
Credentials tgt,
|
||||
Credentials serviceTicket)
|
||||
throws KrbException, IOException, GSSException {
|
||||
|
||||
boolean mutualRequired = context.getMutualAuthState();
|
||||
boolean useSubkey = true; // MIT Impl will crash if this is not set!
|
||||
boolean useSequenceNumber = true;
|
||||
|
||||
OverloadedChecksum gssChecksum =
|
||||
new OverloadedChecksum(context, tgt, serviceTicket);
|
||||
|
||||
Checksum checksum = gssChecksum.getChecksum();
|
||||
|
||||
context.setTktFlags(serviceTicket.getFlags());
|
||||
context.setAuthTime(
|
||||
new KerberosTime(serviceTicket.getAuthTime()).toString());
|
||||
apReq = new KrbApReq(serviceTicket,
|
||||
mutualRequired,
|
||||
useSubkey,
|
||||
useSequenceNumber,
|
||||
checksum);
|
||||
|
||||
context.resetMySequenceNumber(apReq.getSeqNumber().intValue());
|
||||
|
||||
EncryptionKey subKey = apReq.getSubKey();
|
||||
if (subKey != null)
|
||||
context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey);
|
||||
else
|
||||
context.setKey(Krb5Context.SESSION_KEY, serviceTicket.getSessionKey());
|
||||
|
||||
if (!mutualRequired)
|
||||
context.resetPeerSequenceNumber(
|
||||
ACCEPTOR_USE_INITIATOR_SEQNUM
|
||||
? apReq.getSeqNumber().intValue()
|
||||
: 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the context acceptor to call. It reads the bytes out of an
|
||||
* InputStream and constructs an InitSecContextToken with them.
|
||||
*/
|
||||
InitSecContextToken(Krb5Context context, Krb5AcceptCredential cred,
|
||||
InputStream is)
|
||||
throws IOException, GSSException, KrbException {
|
||||
|
||||
int tokenId = ((is.read()<<8) | is.read());
|
||||
|
||||
if (tokenId != Krb5Token.AP_REQ_ID)
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
"AP_REQ token id does not match!");
|
||||
|
||||
// XXX Modify KrbApReq cons to take an InputStream
|
||||
byte[] apReqBytes =
|
||||
new sun.security.util.DerValue(is).toByteArray();
|
||||
//debug("=====ApReqBytes: [" + getHexBytes(apReqBytes) + "]\n");
|
||||
|
||||
InetAddress addr = null;
|
||||
if (context.getChannelBinding() != null) {
|
||||
addr = context.getChannelBinding().getInitiatorAddress();
|
||||
}
|
||||
apReq = new KrbApReq(apReqBytes, cred, addr);
|
||||
//debug("\nReceived AP-REQ and authenticated it.\n");
|
||||
|
||||
EncryptionKey sessionKey = apReq.getCreds().getSessionKey();
|
||||
|
||||
/*
|
||||
System.out.println("\n\nSession key from service ticket is: " +
|
||||
getHexBytes(sessionKey.getBytes()));
|
||||
*/
|
||||
|
||||
EncryptionKey subKey = apReq.getSubKey();
|
||||
if (subKey != null) {
|
||||
context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey);
|
||||
/*
|
||||
System.out.println("Sub-Session key from authenticator is: " +
|
||||
getHexBytes(subKey.getBytes()) + "\n");
|
||||
*/
|
||||
} else {
|
||||
context.setKey(Krb5Context.SESSION_KEY, sessionKey);
|
||||
//System.out.println("Sub-Session Key Missing in Authenticator.\n");
|
||||
}
|
||||
|
||||
OverloadedChecksum gssChecksum = new OverloadedChecksum(
|
||||
context, apReq.getChecksum(), sessionKey, subKey);
|
||||
gssChecksum.setContextFlags(context);
|
||||
Credentials delegCred = gssChecksum.getDelegatedCreds();
|
||||
if (delegCred != null) {
|
||||
Krb5CredElement credElement =
|
||||
Krb5InitCredential.getInstance(
|
||||
(Krb5NameElement)context.getSrcName(),
|
||||
delegCred);
|
||||
context.setDelegCred(credElement);
|
||||
}
|
||||
|
||||
Integer apReqSeqNumber = apReq.getSeqNumber();
|
||||
int peerSeqNumber = (apReqSeqNumber != null ?
|
||||
apReqSeqNumber.intValue() :
|
||||
0);
|
||||
context.resetPeerSequenceNumber(peerSeqNumber);
|
||||
if (!context.getMutualAuthState()) {
|
||||
context.resetMySequenceNumber(
|
||||
ACCEPTOR_USE_INITIATOR_SEQNUM
|
||||
? peerSeqNumber
|
||||
: 0);
|
||||
}
|
||||
context.setAuthTime(
|
||||
new KerberosTime(apReq.getCreds().getAuthTime()).toString());
|
||||
context.setTktFlags(apReq.getCreds().getFlags());
|
||||
AuthorizationData ad = apReq.getCreds().getAuthzData();
|
||||
if (ad == null) {
|
||||
context.setAuthzData(null);
|
||||
} else {
|
||||
AuthorizationDataEntry[] authzData =
|
||||
new AuthorizationDataEntry[ad.count()];
|
||||
for (int i=0; i<ad.count(); i++) {
|
||||
authzData[i] = new AuthorizationDataEntry(
|
||||
ad.item(i).adType, ad.item(i).adData);
|
||||
}
|
||||
context.setAuthzData(authzData);
|
||||
}
|
||||
}
|
||||
|
||||
public final KrbApReq getKrbApReq() {
|
||||
return apReq;
|
||||
}
|
||||
|
||||
public final byte[] encode() throws IOException {
|
||||
byte[] apReqBytes = apReq.getMessage();
|
||||
byte[] retVal = new byte[2 + apReqBytes.length];
|
||||
writeInt(Krb5Token.AP_REQ_ID, retVal, 0);
|
||||
System.arraycopy(apReqBytes, 0, retVal, 2, apReqBytes.length);
|
||||
// System.out.println("GSS-Token with AP_REQ is:");
|
||||
// System.out.println(getHexBytes(retVal));
|
||||
return retVal;
|
||||
}
|
||||
}
|
453
jdkSrc/jdk8/sun/security/jgss/krb5/InitialToken.java
Normal file
453
jdkSrc/jdk8/sun/security/jgss/krb5/InitialToken.java
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2013, 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 message 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);
|
||||
|
||||
KrbCred cred;
|
||||
try {
|
||||
cred = new KrbCred(credBytes, key);
|
||||
} catch (KrbException ke) {
|
||||
if (subKey != null) {
|
||||
cred = new KrbCred(credBytes, subKey);
|
||||
} else {
|
||||
throw ke;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
197
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5AcceptCredential.java
Normal file
197
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5AcceptCredential.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2013, 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 java.io.IOException;
|
||||
import org.ietf.jgss.*;
|
||||
import sun.security.jgss.GSSCaller;
|
||||
import sun.security.jgss.spi.*;
|
||||
import sun.security.krb5.*;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.AccessController;
|
||||
import java.security.AccessControlContext;
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
|
||||
/**
|
||||
* Implements the krb5 acceptor credential element.
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
* @since 1.4
|
||||
*/
|
||||
public class Krb5AcceptCredential
|
||||
implements Krb5CredElement {
|
||||
|
||||
private final Krb5NameElement name;
|
||||
private final ServiceCreds screds;
|
||||
|
||||
private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) {
|
||||
/*
|
||||
* Initialize this instance with the data from the acquired
|
||||
* KerberosKey. This class needs to be a KerberosKey too
|
||||
* hence we can't just store a reference.
|
||||
*/
|
||||
|
||||
this.name = name;
|
||||
this.screds = creds;
|
||||
}
|
||||
|
||||
static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
|
||||
throws GSSException {
|
||||
|
||||
final String serverPrinc = (name == null? null:
|
||||
name.getKrb5PrincipalName().getName());
|
||||
final AccessControlContext acc = AccessController.getContext();
|
||||
|
||||
ServiceCreds creds = null;
|
||||
try {
|
||||
creds = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<ServiceCreds>() {
|
||||
public ServiceCreds run() throws Exception {
|
||||
return Krb5Util.getServiceCreds(
|
||||
caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
|
||||
serverPrinc, acc);
|
||||
}});
|
||||
} catch (PrivilegedActionException e) {
|
||||
GSSException ge =
|
||||
new GSSException(GSSException.NO_CRED, -1,
|
||||
"Attempt to obtain new ACCEPT credentials failed!");
|
||||
ge.initCause(e.getException());
|
||||
throw ge;
|
||||
}
|
||||
|
||||
if (creds == null)
|
||||
throw new GSSException(GSSException.NO_CRED, -1,
|
||||
"Failed to find any Kerberos credentails");
|
||||
|
||||
if (name == null) {
|
||||
String fullName = creds.getName();
|
||||
if (fullName != null) {
|
||||
name = Krb5NameElement.getInstance(fullName,
|
||||
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
|
||||
}
|
||||
}
|
||||
|
||||
return new Krb5AcceptCredential(name, creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the principal name for this credential. The name
|
||||
* is in mechanism specific format.
|
||||
*
|
||||
* @return GSSNameSpi representing principal name of this credential
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public final GSSNameSpi getName() throws GSSException {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the init lifetime remaining.
|
||||
*
|
||||
* @return the init lifetime remaining in seconds
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public int getInitLifetime() throws GSSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the accept lifetime remaining.
|
||||
*
|
||||
* @return the accept lifetime remaining in seconds
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public int getAcceptLifetime() throws GSSException {
|
||||
return GSSCredential.INDEFINITE_LIFETIME;
|
||||
}
|
||||
|
||||
public boolean isInitiatorCredential() throws GSSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAcceptorCredential() throws GSSException {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the oid representing the underlying credential
|
||||
* mechanism oid.
|
||||
*
|
||||
* @return the Oid for this credential mechanism
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public final Oid getMechanism() {
|
||||
return Krb5MechFactory.GSS_KRB5_MECH_OID;
|
||||
}
|
||||
|
||||
public final java.security.Provider getProvider() {
|
||||
return Krb5MechFactory.PROVIDER;
|
||||
}
|
||||
|
||||
public EncryptionKey[] getKrb5EncryptionKeys(PrincipalName princ) {
|
||||
return screds.getEKeys(princ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to invalidate this credential element.
|
||||
*/
|
||||
public void dispose() throws GSSException {
|
||||
try {
|
||||
destroy();
|
||||
} catch (DestroyFailedException e) {
|
||||
GSSException gssException =
|
||||
new GSSException(GSSException.FAILURE, -1,
|
||||
"Could not destroy credentials - " + e.getMessage());
|
||||
gssException.initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the locally cached EncryptionKey value and then calls
|
||||
* destroy in the base class.
|
||||
*/
|
||||
public void destroy() throws DestroyFailedException {
|
||||
screds.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Impersonation is only available on the initiator side. The
|
||||
* service must starts as an initiator to get an initial TGT to complete
|
||||
* the S4U2self protocol.
|
||||
*/
|
||||
@Override
|
||||
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
|
||||
Credentials cred = screds.getInitCred();
|
||||
if (cred != null) {
|
||||
return Krb5InitCredential.getInstance(this.name, cred)
|
||||
.impersonate(name);
|
||||
} else {
|
||||
throw new GSSException(GSSException.FAILURE, -1,
|
||||
"Only an initiate credentials can impersonate");
|
||||
}
|
||||
}
|
||||
}
|
1454
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5Context.java
Normal file
1454
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5Context.java
Normal file
File diff suppressed because it is too large
Load Diff
41
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5CredElement.java
Normal file
41
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5CredElement.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 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 sun.security.jgss.spi.*;
|
||||
import sun.security.krb5.*;
|
||||
import java.security.Provider;
|
||||
|
||||
/**
|
||||
* Provides type safety for Krb5 credential elements.
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
* @since 1.4
|
||||
*/
|
||||
interface Krb5CredElement
|
||||
extends GSSCredentialSpi {
|
||||
}
|
406
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5InitCredential.java
Normal file
406
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5InitCredential.java
Normal file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2019, 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 sun.security.jgss.GSSCaller;
|
||||
import sun.security.jgss.spi.*;
|
||||
import sun.security.krb5.*;
|
||||
import sun.security.krb5.Config;
|
||||
import javax.security.auth.kerberos.*;
|
||||
import java.net.InetAddress;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.security.AccessController;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
|
||||
/**
|
||||
* Implements the krb5 initiator credential element.
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
* @author Ram Marti
|
||||
* @since 1.4
|
||||
*/
|
||||
|
||||
public class Krb5InitCredential
|
||||
extends KerberosTicket
|
||||
implements Krb5CredElement {
|
||||
|
||||
private static final long serialVersionUID = 7723415700837898232L;
|
||||
|
||||
private Krb5NameElement name;
|
||||
private Credentials krb5Credentials;
|
||||
public KerberosTicket proxyTicket;
|
||||
|
||||
private Krb5InitCredential(Krb5NameElement name,
|
||||
byte[] asn1Encoding,
|
||||
KerberosPrincipal client,
|
||||
KerberosPrincipal clientAlias,
|
||||
KerberosPrincipal server,
|
||||
KerberosPrincipal serverAlias,
|
||||
byte[] sessionKey,
|
||||
int keyType,
|
||||
boolean[] flags,
|
||||
Date authTime,
|
||||
Date startTime,
|
||||
Date endTime,
|
||||
Date renewTill,
|
||||
InetAddress[] clientAddresses)
|
||||
throws GSSException {
|
||||
super(asn1Encoding,
|
||||
client,
|
||||
server,
|
||||
sessionKey,
|
||||
keyType,
|
||||
flags,
|
||||
authTime,
|
||||
startTime,
|
||||
endTime,
|
||||
renewTill,
|
||||
clientAddresses);
|
||||
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketSetClientAlias(this, clientAlias);
|
||||
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketSetServerAlias(this, serverAlias);
|
||||
this.name = name;
|
||||
|
||||
try {
|
||||
// Cache this for later use by the sun.security.krb5 package.
|
||||
krb5Credentials = new Credentials(asn1Encoding,
|
||||
client.getName(),
|
||||
(clientAlias != null ?
|
||||
clientAlias.getName() : null),
|
||||
server.getName(),
|
||||
(serverAlias != null ?
|
||||
serverAlias.getName() : null),
|
||||
sessionKey,
|
||||
keyType,
|
||||
flags,
|
||||
authTime,
|
||||
startTime,
|
||||
endTime,
|
||||
renewTill,
|
||||
clientAddresses);
|
||||
} catch (KrbException e) {
|
||||
throw new GSSException(GSSException.NO_CRED, -1,
|
||||
e.getMessage());
|
||||
} catch (IOException e) {
|
||||
throw new GSSException(GSSException.NO_CRED, -1,
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Krb5InitCredential(Krb5NameElement name,
|
||||
Credentials delegatedCred,
|
||||
byte[] asn1Encoding,
|
||||
KerberosPrincipal client,
|
||||
KerberosPrincipal clientAlias,
|
||||
KerberosPrincipal server,
|
||||
KerberosPrincipal serverAlias,
|
||||
byte[] sessionKey,
|
||||
int keyType,
|
||||
boolean[] flags,
|
||||
Date authTime,
|
||||
Date startTime,
|
||||
Date endTime,
|
||||
Date renewTill,
|
||||
InetAddress[] clientAddresses)
|
||||
throws GSSException {
|
||||
super(asn1Encoding,
|
||||
client,
|
||||
server,
|
||||
sessionKey,
|
||||
keyType,
|
||||
flags,
|
||||
authTime,
|
||||
startTime,
|
||||
endTime,
|
||||
renewTill,
|
||||
clientAddresses);
|
||||
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketSetClientAlias(this, clientAlias);
|
||||
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketSetServerAlias(this, serverAlias);
|
||||
this.name = name;
|
||||
// A delegated cred does not have all fields set. So do not try to
|
||||
// creat new Credentials out of the delegatedCred.
|
||||
this.krb5Credentials = delegatedCred;
|
||||
}
|
||||
|
||||
static Krb5InitCredential getInstance(GSSCaller caller, Krb5NameElement name,
|
||||
int initLifetime)
|
||||
throws GSSException {
|
||||
|
||||
KerberosTicket tgt = getTgt(caller, name, initLifetime);
|
||||
if (tgt == null)
|
||||
throw new GSSException(GSSException.NO_CRED, -1,
|
||||
"Failed to find any Kerberos tgt");
|
||||
|
||||
if (name == null) {
|
||||
String fullName = tgt.getClient().getName();
|
||||
name = Krb5NameElement.getInstance(fullName,
|
||||
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
|
||||
}
|
||||
|
||||
KerberosPrincipal clientAlias = KerberosSecrets
|
||||
.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketGetClientAlias(tgt);
|
||||
KerberosPrincipal serverAlias = KerberosSecrets
|
||||
.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketGetServerAlias(tgt);
|
||||
Krb5InitCredential result = new Krb5InitCredential(name,
|
||||
tgt.getEncoded(),
|
||||
tgt.getClient(),
|
||||
clientAlias,
|
||||
tgt.getServer(),
|
||||
serverAlias,
|
||||
tgt.getSessionKey().getEncoded(),
|
||||
tgt.getSessionKeyType(),
|
||||
tgt.getFlags(),
|
||||
tgt.getAuthTime(),
|
||||
tgt.getStartTime(),
|
||||
tgt.getEndTime(),
|
||||
tgt.getRenewTill(),
|
||||
tgt.getClientAddresses());
|
||||
result.proxyTicket = KerberosSecrets.getJavaxSecurityAuthKerberosAccess().
|
||||
kerberosTicketGetProxy(tgt);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Krb5InitCredential getInstance(Krb5NameElement name,
|
||||
Credentials delegatedCred)
|
||||
throws GSSException {
|
||||
|
||||
EncryptionKey sessionKey = delegatedCred.getSessionKey();
|
||||
|
||||
/*
|
||||
* all of the following data is optional in a KRB-CRED
|
||||
* messages. This check for each field.
|
||||
*/
|
||||
|
||||
PrincipalName cPrinc = delegatedCred.getClient();
|
||||
PrincipalName cAPrinc = delegatedCred.getClientAlias();
|
||||
PrincipalName sPrinc = delegatedCred.getServer();
|
||||
PrincipalName sAPrinc = delegatedCred.getServerAlias();
|
||||
|
||||
KerberosPrincipal client = null;
|
||||
KerberosPrincipal clientAlias = null;
|
||||
KerberosPrincipal server = null;
|
||||
KerberosPrincipal serverAlias = null;
|
||||
|
||||
Krb5NameElement credName = null;
|
||||
|
||||
if (cPrinc != null) {
|
||||
String fullName = cPrinc.getName();
|
||||
credName = Krb5NameElement.getInstance(fullName,
|
||||
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
|
||||
client = new KerberosPrincipal(fullName);
|
||||
}
|
||||
|
||||
if (cAPrinc != null) {
|
||||
clientAlias = new KerberosPrincipal(cAPrinc.getName());
|
||||
}
|
||||
|
||||
// XXX Compare name to credName
|
||||
|
||||
if (sPrinc != null) {
|
||||
server =
|
||||
new KerberosPrincipal(sPrinc.getName(),
|
||||
KerberosPrincipal.KRB_NT_SRV_INST);
|
||||
}
|
||||
|
||||
if (sAPrinc != null) {
|
||||
serverAlias = new KerberosPrincipal(sAPrinc.getName());
|
||||
}
|
||||
|
||||
return new Krb5InitCredential(credName,
|
||||
delegatedCred,
|
||||
delegatedCred.getEncoded(),
|
||||
client,
|
||||
clientAlias,
|
||||
server,
|
||||
serverAlias,
|
||||
sessionKey.getBytes(),
|
||||
sessionKey.getEType(),
|
||||
delegatedCred.getFlags(),
|
||||
delegatedCred.getAuthTime(),
|
||||
delegatedCred.getStartTime(),
|
||||
delegatedCred.getEndTime(),
|
||||
delegatedCred.getRenewTill(),
|
||||
delegatedCred.getClientAddresses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the principal name for this credential. The name
|
||||
* is in mechanism specific format.
|
||||
*
|
||||
* @return GSSNameSpi representing principal name of this credential
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public final GSSNameSpi getName() throws GSSException {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the init lifetime remaining.
|
||||
*
|
||||
* @return the init lifetime remaining in seconds
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public int getInitLifetime() throws GSSException {
|
||||
Date d = getEndTime();
|
||||
if (d == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long retVal = d.getTime() - System.currentTimeMillis();
|
||||
return (int)(retVal/1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the accept lifetime remaining.
|
||||
*
|
||||
* @return the accept lifetime remaining in seconds
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public int getAcceptLifetime() throws GSSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isInitiatorCredential() throws GSSException {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isAcceptorCredential() throws GSSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the oid representing the underlying credential
|
||||
* mechanism oid.
|
||||
*
|
||||
* @return the Oid for this credential mechanism
|
||||
* @exception GSSException may be thrown
|
||||
*/
|
||||
public final Oid getMechanism() {
|
||||
return Krb5MechFactory.GSS_KRB5_MECH_OID;
|
||||
}
|
||||
|
||||
public final java.security.Provider getProvider() {
|
||||
return Krb5MechFactory.PROVIDER;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a sun.security.krb5.Credentials instance so that it maybe
|
||||
* used in that package for th Kerberos protocol.
|
||||
*/
|
||||
Credentials getKrb5Credentials() {
|
||||
return krb5Credentials;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX Call to this.refresh() should refresh the locally cached copy
|
||||
* of krb5Credentials also.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Called to invalidate this credential element.
|
||||
*/
|
||||
public void dispose() throws GSSException {
|
||||
try {
|
||||
destroy();
|
||||
} catch (javax.security.auth.DestroyFailedException e) {
|
||||
GSSException gssException =
|
||||
new GSSException(GSSException.FAILURE, -1,
|
||||
"Could not destroy credentials - " + e.getMessage());
|
||||
gssException.initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX call to this.destroy() should destroy the locally cached copy
|
||||
// of krb5Credentials and then call super.destroy().
|
||||
|
||||
private static KerberosTicket getTgt(GSSCaller caller, Krb5NameElement name,
|
||||
int initLifetime)
|
||||
throws GSSException {
|
||||
|
||||
final String clientPrincipal;
|
||||
|
||||
/*
|
||||
* Find the TGT for the realm that the client is in. If the client
|
||||
* name is not available, then use the default realm.
|
||||
*/
|
||||
if (name != null) {
|
||||
clientPrincipal = (name.getKrb5PrincipalName()).getName();
|
||||
} else {
|
||||
clientPrincipal = null;
|
||||
}
|
||||
|
||||
final AccessControlContext acc = AccessController.getContext();
|
||||
|
||||
try {
|
||||
final GSSCaller realCaller = (caller == GSSCaller.CALLER_UNKNOWN)
|
||||
? GSSCaller.CALLER_INITIATE
|
||||
: caller;
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<KerberosTicket>() {
|
||||
public KerberosTicket run() throws Exception {
|
||||
// It's OK to use null as serverPrincipal. TGT is almost
|
||||
// the first ticket for a principal and we use list.
|
||||
return Krb5Util.getInitialTicket(
|
||||
realCaller,
|
||||
clientPrincipal, acc);
|
||||
}});
|
||||
} catch (PrivilegedActionException e) {
|
||||
GSSException ge =
|
||||
new GSSException(GSSException.NO_CRED, -1,
|
||||
"Attempt to obtain new INITIATE credentials failed!" +
|
||||
" (" + e.getMessage() + ")");
|
||||
ge.initCause(e.getException());
|
||||
throw ge;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
|
||||
try {
|
||||
Krb5NameElement kname = (Krb5NameElement)name;
|
||||
Credentials newCred = Credentials.acquireS4U2selfCreds(
|
||||
kname.getKrb5PrincipalName(), krb5Credentials);
|
||||
return new Krb5ProxyCredential(this, kname, newCred.getTicket());
|
||||
} catch (IOException | KrbException ke) {
|
||||
GSSException ge =
|
||||
new GSSException(GSSException.FAILURE, -1,
|
||||
"Attempt to obtain S4U2self credentials failed!");
|
||||
ge.initCause(ke);
|
||||
throw ge;
|
||||
}
|
||||
}
|
||||
}
|
234
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5MechFactory.java
Normal file
234
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5MechFactory.java
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2019, 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 sun.security.jgss.GSSUtil;
|
||||
import sun.security.jgss.GSSCaller;
|
||||
import sun.security.jgss.spi.*;
|
||||
import javax.security.auth.kerberos.ServicePermission;
|
||||
import java.security.Provider;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Krb5 Mechanism plug in for JGSS
|
||||
* This is the properties object required by the JGSS framework.
|
||||
* All mechanism specific information is defined here.
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
*/
|
||||
|
||||
public final class Krb5MechFactory implements MechanismFactory {
|
||||
|
||||
private static final boolean DEBUG = Krb5Util.DEBUG;
|
||||
|
||||
static final Provider PROVIDER =
|
||||
new sun.security.jgss.SunProvider();
|
||||
|
||||
static final Oid GSS_KRB5_MECH_OID =
|
||||
createOid("1.2.840.113554.1.2.2");
|
||||
|
||||
static final Oid NT_GSS_KRB5_PRINCIPAL =
|
||||
createOid("1.2.840.113554.1.2.2.1");
|
||||
|
||||
private static Oid[] nameTypes =
|
||||
new Oid[] { GSSName.NT_USER_NAME,
|
||||
GSSName.NT_HOSTBASED_SERVICE,
|
||||
GSSName.NT_EXPORT_NAME,
|
||||
NT_GSS_KRB5_PRINCIPAL};
|
||||
|
||||
final private GSSCaller caller;
|
||||
|
||||
private static Krb5CredElement getCredFromSubject(GSSNameSpi name,
|
||||
boolean initiate)
|
||||
throws GSSException {
|
||||
Vector<Krb5CredElement> creds =
|
||||
GSSUtil.searchSubject(name, GSS_KRB5_MECH_OID, initiate,
|
||||
(initiate ?
|
||||
Krb5InitCredential.class :
|
||||
Krb5AcceptCredential.class));
|
||||
|
||||
Krb5CredElement result = ((creds == null || creds.isEmpty()) ?
|
||||
null : creds.firstElement());
|
||||
|
||||
// Force permission check before returning the cred to caller
|
||||
if (result != null) {
|
||||
if (initiate) {
|
||||
checkInitCredPermission((Krb5NameElement) result.getName());
|
||||
} else {
|
||||
checkAcceptCredPermission
|
||||
((Krb5NameElement) result.getName(), name);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Krb5MechFactory(GSSCaller caller) {
|
||||
this.caller = caller;
|
||||
}
|
||||
|
||||
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
|
||||
throws GSSException {
|
||||
return Krb5NameElement.getInstance(nameStr, nameType);
|
||||
}
|
||||
|
||||
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
|
||||
throws GSSException {
|
||||
// At this point, even an exported name is stripped down to safe
|
||||
// bytes only
|
||||
// XXX Use encoding here
|
||||
return Krb5NameElement.getInstance(new String(name), nameType);
|
||||
}
|
||||
|
||||
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
|
||||
int initLifetime, int acceptLifetime,
|
||||
int usage) throws GSSException {
|
||||
|
||||
if (name != null && !(name instanceof Krb5NameElement)) {
|
||||
name = Krb5NameElement.getInstance(name.toString(),
|
||||
name.getStringNameType());
|
||||
}
|
||||
|
||||
Krb5CredElement credElement = getCredFromSubject
|
||||
(name, (usage != GSSCredential.ACCEPT_ONLY));
|
||||
|
||||
if (credElement == null) {
|
||||
if (usage == GSSCredential.INITIATE_ONLY ||
|
||||
usage == GSSCredential.INITIATE_AND_ACCEPT) {
|
||||
credElement = Krb5InitCredential.getInstance
|
||||
(caller, (Krb5NameElement) name, initLifetime);
|
||||
credElement = Krb5ProxyCredential.tryImpersonation(
|
||||
caller, (Krb5InitCredential)credElement);
|
||||
checkInitCredPermission
|
||||
((Krb5NameElement) credElement.getName());
|
||||
} else if (usage == GSSCredential.ACCEPT_ONLY) {
|
||||
credElement =
|
||||
Krb5AcceptCredential.getInstance(caller,
|
||||
(Krb5NameElement) name);
|
||||
checkAcceptCredPermission
|
||||
((Krb5NameElement) credElement.getName(), name);
|
||||
} else
|
||||
throw new GSSException(GSSException.FAILURE, -1,
|
||||
"Unknown usage mode requested");
|
||||
}
|
||||
return credElement;
|
||||
}
|
||||
|
||||
public static void checkInitCredPermission(Krb5NameElement name) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
String realm = (name.getKrb5PrincipalName()).getRealmAsString();
|
||||
String tgsPrincipal =
|
||||
new String("krbtgt/" + realm + '@' + realm);
|
||||
ServicePermission perm =
|
||||
new ServicePermission(tgsPrincipal, "initiate");
|
||||
try {
|
||||
sm.checkPermission(perm);
|
||||
} catch (SecurityException e) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Permission to initiate" +
|
||||
"kerberos init credential" + e.getMessage());
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkAcceptCredPermission(Krb5NameElement name,
|
||||
GSSNameSpi originalName) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null && name != null) {
|
||||
ServicePermission perm = new ServicePermission
|
||||
(name.getKrb5PrincipalName().getName(), "accept");
|
||||
try {
|
||||
sm.checkPermission(perm);
|
||||
} catch (SecurityException e) {
|
||||
if (originalName == null) {
|
||||
// Don't disclose the name of the principal
|
||||
e = new SecurityException("No permission to acquire "
|
||||
+ "Kerberos accept credential");
|
||||
// Don't call e.initCause() with caught exception
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
|
||||
GSSCredentialSpi myInitiatorCred, int lifetime)
|
||||
throws GSSException {
|
||||
if (peer != null && !(peer instanceof Krb5NameElement)) {
|
||||
peer = Krb5NameElement.getInstance(peer.toString(),
|
||||
peer.getStringNameType());
|
||||
}
|
||||
// XXX Convert myInitiatorCred to Krb5CredElement
|
||||
if (myInitiatorCred == null) {
|
||||
myInitiatorCred = getCredentialElement(null, lifetime, 0,
|
||||
GSSCredential.INITIATE_ONLY);
|
||||
}
|
||||
return new Krb5Context(caller, (Krb5NameElement)peer,
|
||||
(Krb5CredElement)myInitiatorCred, lifetime);
|
||||
}
|
||||
|
||||
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
|
||||
throws GSSException {
|
||||
// XXX Convert myAcceptorCred to Krb5CredElement
|
||||
if (myAcceptorCred == null) {
|
||||
myAcceptorCred = getCredentialElement(null, 0,
|
||||
GSSCredential.INDEFINITE_LIFETIME, GSSCredential.ACCEPT_ONLY);
|
||||
}
|
||||
return new Krb5Context(caller, (Krb5CredElement)myAcceptorCred);
|
||||
}
|
||||
|
||||
public GSSContextSpi getMechanismContext(byte[] exportedContext)
|
||||
throws GSSException {
|
||||
return new Krb5Context(caller, exportedContext);
|
||||
}
|
||||
|
||||
|
||||
public final Oid getMechanismOid() {
|
||||
return GSS_KRB5_MECH_OID;
|
||||
}
|
||||
|
||||
public Provider getProvider() {
|
||||
return PROVIDER;
|
||||
}
|
||||
|
||||
public Oid[] getNameTypes() {
|
||||
// nameTypes is cloned in GSSManager.getNamesForMech
|
||||
return nameTypes;
|
||||
}
|
||||
|
||||
private static Oid createOid(String oidStr) {
|
||||
Oid retVal = null;
|
||||
try {
|
||||
retVal = new Oid(oidStr);
|
||||
} catch (GSSException e) {
|
||||
// Should not happen!
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
348
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5NameElement.java
Normal file
348
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5NameElement.java
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2019, 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 sun.security.jgss.spi.*;
|
||||
import sun.security.krb5.PrincipalName;
|
||||
import sun.security.krb5.Realm;
|
||||
import sun.security.krb5.KrbException;
|
||||
|
||||
import javax.security.auth.kerberos.ServicePermission;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.Provider;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Implements the GSSNameSpi for the krb5 mechanism.
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
*/
|
||||
public class Krb5NameElement
|
||||
implements GSSNameSpi {
|
||||
|
||||
private PrincipalName krb5PrincipalName;
|
||||
|
||||
private String gssNameStr = null;
|
||||
private Oid gssNameType = null;
|
||||
|
||||
// XXX Move this concept into PrincipalName's asn1Encode() sometime
|
||||
private static String CHAR_ENCODING = "UTF-8";
|
||||
|
||||
private Krb5NameElement(PrincipalName principalName,
|
||||
String gssNameStr,
|
||||
Oid gssNameType) {
|
||||
this.krb5PrincipalName = principalName;
|
||||
this.gssNameStr = gssNameStr;
|
||||
this.gssNameType = gssNameType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Krb5NameElement object. Internally it stores the
|
||||
* information provided by the input parameters so that they may later
|
||||
* be used for output when a printable representaion of this name is
|
||||
* needed in GSS-API format rather than in Kerberos format.
|
||||
*
|
||||
*/
|
||||
static Krb5NameElement getInstance(String gssNameStr, Oid gssNameType)
|
||||
throws GSSException {
|
||||
|
||||
/*
|
||||
* A null gssNameType implies that the mechanism default
|
||||
* Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL be used.
|
||||
*/
|
||||
if (gssNameType == null)
|
||||
gssNameType = Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL;
|
||||
else
|
||||
if (!gssNameType.equals(GSSName.NT_USER_NAME) &&
|
||||
!gssNameType.equals(GSSName.NT_HOSTBASED_SERVICE) &&
|
||||
!gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL) &&
|
||||
!gssNameType.equals(GSSName.NT_EXPORT_NAME))
|
||||
throw new GSSException(GSSException.BAD_NAMETYPE, -1,
|
||||
gssNameType.toString()
|
||||
+" is an unsupported nametype");
|
||||
|
||||
PrincipalName principalName;
|
||||
try {
|
||||
|
||||
if (gssNameType.equals(GSSName.NT_EXPORT_NAME) ||
|
||||
gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)) {
|
||||
principalName = new PrincipalName(gssNameStr,
|
||||
PrincipalName.KRB_NT_PRINCIPAL);
|
||||
} else {
|
||||
|
||||
String[] components = getComponents(gssNameStr);
|
||||
|
||||
/*
|
||||
* We have forms of GSS name strings that can come in:
|
||||
*
|
||||
* 1. names of the form "foo" with just one
|
||||
* component. (This might include a "@" but only in escaped
|
||||
* form like "\@")
|
||||
* 2. names of the form "foo@bar" with two components
|
||||
*
|
||||
* The nametypes that are accepted are NT_USER_NAME, and
|
||||
* NT_HOSTBASED_SERVICE.
|
||||
*/
|
||||
|
||||
if (gssNameType.equals(GSSName.NT_USER_NAME))
|
||||
principalName = new PrincipalName(gssNameStr,
|
||||
PrincipalName.KRB_NT_PRINCIPAL);
|
||||
else {
|
||||
String hostName = null;
|
||||
String service = components[0];
|
||||
if (components.length >= 2)
|
||||
hostName = components[1];
|
||||
|
||||
String principal = getHostBasedInstance(service, hostName);
|
||||
principalName = new PrincipalName(principal,
|
||||
PrincipalName.KRB_NT_SRV_HST);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (KrbException e) {
|
||||
throw new GSSException(GSSException.BAD_NAME, -1, e.getMessage());
|
||||
}
|
||||
|
||||
if (principalName.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
try {
|
||||
sm.checkPermission(new ServicePermission(
|
||||
"@" + principalName.getRealmAsString(), "-"));
|
||||
} catch (SecurityException se) {
|
||||
// Do not chain the actual exception to hide info
|
||||
throw new GSSException(GSSException.FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Krb5NameElement(principalName, gssNameStr, gssNameType);
|
||||
}
|
||||
|
||||
public static Krb5NameElement getInstance(PrincipalName principalName) {
|
||||
return new Krb5NameElement(principalName,
|
||||
principalName.getName(),
|
||||
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
|
||||
}
|
||||
|
||||
private static String[] getComponents(String gssNameStr)
|
||||
throws GSSException {
|
||||
|
||||
String[] retVal;
|
||||
|
||||
// XXX Perhaps provide this parsing code in PrincipalName
|
||||
|
||||
// Look for @ as in service@host
|
||||
// Assumes host name will not have an escaped '@'
|
||||
int separatorPos = gssNameStr.lastIndexOf('@', gssNameStr.length());
|
||||
|
||||
// Not really a separator if it is escaped. Then this is just part
|
||||
// of the principal name or service name
|
||||
if ((separatorPos > 0) &&
|
||||
(gssNameStr.charAt(separatorPos-1) == '\\')) {
|
||||
// Is the `\` character escaped itself?
|
||||
if ((separatorPos - 2 < 0) ||
|
||||
(gssNameStr.charAt(separatorPos-2) != '\\'))
|
||||
separatorPos = -1;
|
||||
}
|
||||
|
||||
if (separatorPos > 0) {
|
||||
String serviceName = gssNameStr.substring(0, separatorPos);
|
||||
String hostName = gssNameStr.substring(separatorPos+1);
|
||||
retVal = new String[] { serviceName, hostName};
|
||||
} else {
|
||||
retVal = new String[] {gssNameStr};
|
||||
}
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
private static String getHostBasedInstance(String serviceName,
|
||||
String hostName)
|
||||
throws GSSException {
|
||||
StringBuffer temp = new StringBuffer(serviceName);
|
||||
|
||||
try {
|
||||
// A lack of "@" defaults to the service being on the local
|
||||
// host as per RFC 2743
|
||||
// XXX Move this part into JGSS framework
|
||||
if (hostName == null)
|
||||
hostName = InetAddress.getLocalHost().getHostName();
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
// use hostname as it is
|
||||
}
|
||||
hostName = hostName.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
temp = temp.append('/').append(hostName);
|
||||
return temp.toString();
|
||||
}
|
||||
|
||||
public final PrincipalName getKrb5PrincipalName() {
|
||||
return krb5PrincipalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equal method for the GSSNameSpi objects.
|
||||
* If either name denotes an anonymous principal, the call should
|
||||
* return false.
|
||||
*
|
||||
* @param other to be compared with
|
||||
* @return true if they both refer to the same entity, else false
|
||||
* @exception GSSException with major codes of BAD_NAMETYPE,
|
||||
* BAD_NAME, FAILURE
|
||||
*/
|
||||
public boolean equals(GSSNameSpi other) throws GSSException {
|
||||
|
||||
if (other == this)
|
||||
return true;
|
||||
|
||||
if (other instanceof Krb5NameElement) {
|
||||
Krb5NameElement that = (Krb5NameElement) other;
|
||||
return (this.krb5PrincipalName.getName().equals(
|
||||
that.krb5PrincipalName.getName()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this <code>GSSNameSpi</code> object to another Object
|
||||
* that might be a <code>GSSNameSpi</code>. The behaviour is exactly
|
||||
* the same as in {@link #equals(GSSNameSpi) equals} except that
|
||||
* no GSSException is thrown; instead, false will be returned in the
|
||||
* situation where an error occurs.
|
||||
*
|
||||
* @param another the object to be compared to
|
||||
* @return true if they both refer to the same entity, else false
|
||||
* @see #equals(GSSNameSpi)
|
||||
*/
|
||||
public boolean equals(Object another) {
|
||||
if (this == another) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (another instanceof Krb5NameElement)
|
||||
return equals((Krb5NameElement) another);
|
||||
} catch (GSSException e) {
|
||||
// ignore exception
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hashcode value for this GSSNameSpi.
|
||||
*
|
||||
* @return a hashCode value
|
||||
*/
|
||||
public int hashCode() {
|
||||
return 37 * 17 + krb5PrincipalName.getName().hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the principal name in the form user@REALM or
|
||||
* host/service@REALM but with the following constraints that are
|
||||
* imposed by RFC 1964:
|
||||
* <pre>
|
||||
* (1) all occurrences of the characters `@`, `/`, and `\` within
|
||||
* principal components or realm names shall be quoted with an
|
||||
* immediately-preceding `\`.
|
||||
*
|
||||
* (2) all occurrences of the null, backspace, tab, or newline
|
||||
* characters within principal components or realm names will be
|
||||
* represented, respectively, with `\0`, `\b`, `\t`, or `\n`.
|
||||
*
|
||||
* (3) the `\` quoting character shall not be emitted within an
|
||||
* exported name except to accommodate cases (1) and (2).
|
||||
* </pre>
|
||||
*/
|
||||
public byte[] export() throws GSSException {
|
||||
// XXX Apply the above constraints.
|
||||
byte[] retVal = null;
|
||||
try {
|
||||
retVal = krb5PrincipalName.getName().getBytes(CHAR_ENCODING);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Can't happen
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mechanism type that this NameElement corresponds to.
|
||||
*
|
||||
* @return the Oid of the mechanism type
|
||||
*/
|
||||
public Oid getMechanism() {
|
||||
return (Krb5MechFactory.GSS_KRB5_MECH_OID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation for this name. The printed
|
||||
* name type can be obtained by calling getStringNameType().
|
||||
*
|
||||
* @return string form of this name
|
||||
* @see #getStringNameType()
|
||||
* @overrides Object#toString
|
||||
*/
|
||||
public String toString() {
|
||||
return (gssNameStr);
|
||||
// For testing: return (super.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name type oid.
|
||||
*/
|
||||
public Oid getGSSNameType() {
|
||||
return (gssNameType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the oid describing the format of the printable name.
|
||||
*
|
||||
* @return the Oid for the format of the printed name
|
||||
*/
|
||||
public Oid getStringNameType() {
|
||||
// XXX For NT_EXPORT_NAME return a different name type. Infact,
|
||||
// don't even store NT_EXPORT_NAME in the cons.
|
||||
return (gssNameType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this name object represents an Anonymous name.
|
||||
*/
|
||||
public boolean isAnonymousName() {
|
||||
return (gssNameType.equals(GSSName.NT_ANONYMOUS));
|
||||
}
|
||||
|
||||
public Provider getProvider() {
|
||||
return Krb5MechFactory.PROVIDER;
|
||||
}
|
||||
|
||||
}
|
142
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5ProxyCredential.java
Normal file
142
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5ProxyCredential.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2019, 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 sun.security.jgss.GSSCaller;
|
||||
import sun.security.jgss.spi.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.krb5.KrbException;
|
||||
import sun.security.krb5.internal.Ticket;
|
||||
|
||||
import javax.security.auth.kerberos.KerberosTicket;
|
||||
|
||||
/**
|
||||
* Implements the krb5 proxy credential element used in constrained
|
||||
* delegation. It is used in both impersonation (where there is no Kerberos 5
|
||||
* communication between the middle server and the client) and normal
|
||||
* constrained delegation (where there is, but client has not called
|
||||
* requestCredDeleg(true)).
|
||||
* @since 1.8
|
||||
*/
|
||||
|
||||
public class Krb5ProxyCredential
|
||||
implements Krb5CredElement {
|
||||
|
||||
public final Krb5InitCredential self; // the middle server
|
||||
private final Krb5NameElement client; // the client
|
||||
|
||||
// The ticket with cname=client and sname=self. This can be a normal
|
||||
// service ticket or an S4U2self ticket.
|
||||
public final Ticket tkt;
|
||||
|
||||
Krb5ProxyCredential(Krb5InitCredential self, Krb5NameElement client,
|
||||
Ticket tkt) {
|
||||
this.self = self;
|
||||
this.tkt = tkt;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
// The client name behind the proxy
|
||||
@Override
|
||||
public final Krb5NameElement getName() throws GSSException {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitLifetime() throws GSSException {
|
||||
// endTime of tkt is not used by KDC, and it's also not
|
||||
// available in the case of kerberos constr deleg
|
||||
return self.getInitLifetime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAcceptLifetime() throws GSSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitiatorCredential() throws GSSException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAcceptorCredential() throws GSSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Oid getMechanism() {
|
||||
return Krb5MechFactory.GSS_KRB5_MECH_OID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final java.security.Provider getProvider() {
|
||||
return Krb5MechFactory.PROVIDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() throws GSSException {
|
||||
try {
|
||||
self.destroy();
|
||||
} catch (javax.security.auth.DestroyFailedException e) {
|
||||
GSSException gssException =
|
||||
new GSSException(GSSException.FAILURE, -1,
|
||||
"Could not destroy credentials - " + e.getMessage());
|
||||
gssException.initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
|
||||
// Cannot impersonate multiple levels without the impersonatee's TGT.
|
||||
throw new GSSException(GSSException.FAILURE, -1,
|
||||
"Only an initiate credentials can impersonate");
|
||||
}
|
||||
|
||||
// Try to see if a default credential should act as an impersonator.
|
||||
static Krb5CredElement tryImpersonation(GSSCaller caller,
|
||||
Krb5InitCredential initiator) throws GSSException {
|
||||
|
||||
try {
|
||||
KerberosTicket proxy = initiator.proxyTicket;
|
||||
if (proxy != null) {
|
||||
Credentials proxyCreds = Krb5Util.ticketToCreds(proxy);
|
||||
return new Krb5ProxyCredential(initiator,
|
||||
Krb5NameElement.getInstance(proxyCreds.getClient()),
|
||||
proxyCreds.getTicket());
|
||||
} else {
|
||||
return initiator;
|
||||
}
|
||||
} catch (KrbException | IOException e) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_CREDENTIAL, -1,
|
||||
"Cannot create proxy credential");
|
||||
}
|
||||
}
|
||||
}
|
118
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5Token.java
Normal file
118
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5Token.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 java.io.IOException;
|
||||
import sun.security.util.*;
|
||||
import sun.security.jgss.*;
|
||||
|
||||
/**
|
||||
* This class represents a base class for all Kerberos v5 GSS-API
|
||||
* tokens. It contains commonly used definitions and utilities.
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
*/
|
||||
|
||||
abstract class Krb5Token extends GSSToken {
|
||||
|
||||
/**
|
||||
* The token id defined for the token emitted by the initSecContext call
|
||||
* carrying the AP_REQ .
|
||||
*/
|
||||
public static final int AP_REQ_ID = 0x0100;
|
||||
|
||||
/**
|
||||
* The token id defined for the token emitted by the acceptSecContext call
|
||||
* carrying the AP_REP .
|
||||
*/
|
||||
public static final int AP_REP_ID = 0x0200;
|
||||
|
||||
/**
|
||||
* The token id defined for any token carrying a KRB-ERR message.
|
||||
*/
|
||||
public static final int ERR_ID = 0x0300;
|
||||
|
||||
/**
|
||||
* The token id defined for the token emitted by the getMIC call.
|
||||
*/
|
||||
public static final int MIC_ID = 0x0101;
|
||||
|
||||
/**
|
||||
* The token id defined for the token emitted by the wrap call.
|
||||
*/
|
||||
public static final int WRAP_ID = 0x0201;
|
||||
|
||||
// new token ID draft-ietf-krb-wg-gssapi-cfx-07.txt
|
||||
public static final int MIC_ID_v2 = 0x0404;
|
||||
public static final int WRAP_ID_v2 = 0x0504;
|
||||
|
||||
/**
|
||||
* The object identifier corresponding to the Kerberos v5 GSS-API
|
||||
* mechanism.
|
||||
*/
|
||||
public static ObjectIdentifier OID;
|
||||
|
||||
static {
|
||||
try {
|
||||
OID = new ObjectIdentifier(Krb5MechFactory.
|
||||
GSS_KRB5_MECH_OID.toString());
|
||||
} catch (IOException ioe) {
|
||||
// should not happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a strign representing the token type.
|
||||
*
|
||||
* @param tokenId the token id for which a string name is desired
|
||||
* @return the String name of this token type
|
||||
*/
|
||||
public static String getTokenName(int tokenId) {
|
||||
String retVal = null;
|
||||
switch (tokenId) {
|
||||
case AP_REQ_ID:
|
||||
case AP_REP_ID:
|
||||
retVal = "Context Establishment Token";
|
||||
break;
|
||||
case MIC_ID:
|
||||
retVal = "MIC Token";
|
||||
break;
|
||||
case MIC_ID_v2:
|
||||
retVal = "MIC Token (new format)";
|
||||
break;
|
||||
case WRAP_ID:
|
||||
retVal = "Wrap Token";
|
||||
break;
|
||||
case WRAP_ID_v2:
|
||||
retVal = "Wrap Token (new format)";
|
||||
break;
|
||||
default:
|
||||
retVal = "Kerberos GSS-API Mechanism Token";
|
||||
break;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
319
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5Util.java
Normal file
319
jdkSrc/jdk8/sun/security/jgss/krb5/Krb5Util.java
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, 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 javax.security.auth.kerberos.KerberosTicket;
|
||||
import javax.security.auth.kerberos.KerberosKey;
|
||||
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||
import javax.security.auth.kerberos.KeyTab;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.security.AccessControlContext;
|
||||
import sun.security.jgss.GSSUtil;
|
||||
import sun.security.jgss.GSSCaller;
|
||||
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.krb5.EncryptionKey;
|
||||
import sun.security.krb5.KrbException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import sun.security.krb5.KerberosSecrets;
|
||||
import sun.security.krb5.PrincipalName;
|
||||
/**
|
||||
* Utilities for obtaining and converting Kerberos tickets.
|
||||
*
|
||||
*/
|
||||
public class Krb5Util {
|
||||
|
||||
static final boolean DEBUG =
|
||||
java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetBooleanAction
|
||||
("sun.security.krb5.debug")).booleanValue();
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
private Krb5Util() { // Cannot create one of these
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the service ticket for serverPrincipal from caller's Subject
|
||||
* or from Subject obtained by logging in, or if not found, via the
|
||||
* Ticket Granting Service using the TGT obtained from the Subject.
|
||||
*
|
||||
* Caller must have permission to:
|
||||
* - access and update Subject's private credentials
|
||||
* - create LoginContext
|
||||
* - read the auth.login.defaultCallbackHandler security property
|
||||
*
|
||||
* NOTE: This method is used by JSSE Kerberos Cipher Suites
|
||||
*/
|
||||
public static KerberosTicket getTicketFromSubjectAndTgs(GSSCaller caller,
|
||||
String clientPrincipal, String serverPrincipal, String tgsPrincipal,
|
||||
AccessControlContext acc)
|
||||
throws LoginException, KrbException, IOException {
|
||||
|
||||
// 1. Try to find service ticket in acc subject
|
||||
Subject accSubj = Subject.getSubject(acc);
|
||||
KerberosTicket ticket = SubjectComber.find(accSubj,
|
||||
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
||||
|
||||
if (ticket != null) {
|
||||
return ticket; // found it
|
||||
}
|
||||
|
||||
Subject loginSubj = null;
|
||||
if (!GSSUtil.useSubjectCredsOnly(caller)) {
|
||||
// 2. Try to get ticket from login
|
||||
try {
|
||||
loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
||||
ticket = SubjectComber.find(loginSubj,
|
||||
serverPrincipal, clientPrincipal, KerberosTicket.class);
|
||||
if (ticket != null) {
|
||||
return ticket; // found it
|
||||
}
|
||||
} catch (LoginException e) {
|
||||
// No login entry to use
|
||||
// ignore and continue
|
||||
}
|
||||
}
|
||||
|
||||
// Service ticket not found in subject or login
|
||||
// Try to get TGT to acquire service ticket
|
||||
|
||||
// 3. Try to get TGT from acc subject
|
||||
KerberosTicket tgt = SubjectComber.find(accSubj,
|
||||
tgsPrincipal, clientPrincipal, KerberosTicket.class);
|
||||
|
||||
boolean fromAcc;
|
||||
if (tgt == null && loginSubj != null) {
|
||||
// 4. Try to get TGT from login subject
|
||||
tgt = SubjectComber.find(loginSubj,
|
||||
tgsPrincipal, clientPrincipal, KerberosTicket.class);
|
||||
fromAcc = false;
|
||||
} else {
|
||||
fromAcc = true;
|
||||
}
|
||||
|
||||
// 5. Try to get service ticket using TGT
|
||||
if (tgt != null) {
|
||||
Credentials tgtCreds = ticketToCreds(tgt);
|
||||
Credentials serviceCreds = Credentials.acquireServiceCreds(
|
||||
serverPrincipal, tgtCreds);
|
||||
if (serviceCreds != null) {
|
||||
ticket = credsToTicket(serviceCreds);
|
||||
|
||||
// Store service ticket in acc's Subject
|
||||
if (fromAcc && accSubj != null && !accSubj.isReadOnly()) {
|
||||
accSubj.getPrivateCredentials().add(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ticket corresponding to the client/server principal
|
||||
* pair from the Subject in the specified AccessControlContext.
|
||||
*/
|
||||
static KerberosTicket getServiceTicket(GSSCaller caller,
|
||||
String clientPrincipal, String serverPrincipal,
|
||||
AccessControlContext acc) throws LoginException {
|
||||
|
||||
// Try to get ticket from acc's Subject
|
||||
Subject accSubj = Subject.getSubject(acc);
|
||||
KerberosTicket ticket =
|
||||
SubjectComber.find(accSubj, serverPrincipal, clientPrincipal,
|
||||
KerberosTicket.class);
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the initial TGT corresponding to the client principal
|
||||
* from the Subject in the specified AccessControlContext.
|
||||
* If the ticket can not be found in the Subject, and if
|
||||
* useSubjectCredsOnly is false, then obtain ticket from
|
||||
* a LoginContext.
|
||||
*/
|
||||
static KerberosTicket getInitialTicket(GSSCaller caller,
|
||||
String clientPrincipal,
|
||||
AccessControlContext acc) throws LoginException {
|
||||
|
||||
// Try to get ticket from acc's Subject
|
||||
Subject accSubj = Subject.getSubject(acc);
|
||||
KerberosTicket ticket =
|
||||
SubjectComber.find(accSubj, null, clientPrincipal,
|
||||
KerberosTicket.class);
|
||||
|
||||
// Try to get ticket from Subject obtained from GSSUtil
|
||||
if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) {
|
||||
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
||||
ticket = SubjectComber.find(subject,
|
||||
null, clientPrincipal, KerberosTicket.class);
|
||||
}
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the caller's Subject, or Subject obtained by logging in
|
||||
* via the specified caller.
|
||||
*
|
||||
* Caller must have permission to:
|
||||
* - access the Subject
|
||||
* - create LoginContext
|
||||
* - read the auth.login.defaultCallbackHandler security property
|
||||
*
|
||||
* NOTE: This method is used by JSSE Kerberos Cipher Suites
|
||||
*/
|
||||
public static Subject getSubject(GSSCaller caller,
|
||||
AccessControlContext acc) throws LoginException {
|
||||
|
||||
// Try to get the Subject from acc
|
||||
Subject subject = Subject.getSubject(acc);
|
||||
|
||||
// Try to get Subject obtained from GSSUtil
|
||||
if (subject == null && !GSSUtil.useSubjectCredsOnly(caller)) {
|
||||
subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
||||
}
|
||||
return subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ServiceCreds for the specified server principal from
|
||||
* the Subject in the specified AccessControlContext. If not found, and if
|
||||
* useSubjectCredsOnly is false, then obtain from a LoginContext.
|
||||
*
|
||||
* NOTE: This method is also used by JSSE Kerberos Cipher Suites
|
||||
*/
|
||||
public static ServiceCreds getServiceCreds(GSSCaller caller,
|
||||
String serverPrincipal, AccessControlContext acc)
|
||||
throws LoginException {
|
||||
|
||||
Subject accSubj = Subject.getSubject(acc);
|
||||
ServiceCreds sc = null;
|
||||
if (accSubj != null) {
|
||||
sc = ServiceCreds.getInstance(accSubj, serverPrincipal);
|
||||
}
|
||||
if (sc == null && !GSSUtil.useSubjectCredsOnly(caller)) {
|
||||
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
|
||||
sc = ServiceCreds.getInstance(subject, serverPrincipal);
|
||||
}
|
||||
return sc;
|
||||
}
|
||||
|
||||
public static KerberosTicket credsToTicket(Credentials serviceCreds) {
|
||||
EncryptionKey sessionKey = serviceCreds.getSessionKey();
|
||||
KerberosTicket kt = new KerberosTicket(
|
||||
serviceCreds.getEncoded(),
|
||||
new KerberosPrincipal(serviceCreds.getClient().getName()),
|
||||
new KerberosPrincipal(serviceCreds.getServer().getName(),
|
||||
KerberosPrincipal.KRB_NT_SRV_INST),
|
||||
sessionKey.getBytes(),
|
||||
sessionKey.getEType(),
|
||||
serviceCreds.getFlags(),
|
||||
serviceCreds.getAuthTime(),
|
||||
serviceCreds.getStartTime(),
|
||||
serviceCreds.getEndTime(),
|
||||
serviceCreds.getRenewTill(),
|
||||
serviceCreds.getClientAddresses());
|
||||
PrincipalName clientAlias = serviceCreds.getClientAlias();
|
||||
PrincipalName serverAlias = serviceCreds.getServerAlias();
|
||||
if (clientAlias != null) {
|
||||
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketSetClientAlias(kt, new KerberosPrincipal(
|
||||
clientAlias.getName(), clientAlias.getNameType()));
|
||||
}
|
||||
if (serverAlias != null) {
|
||||
KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketSetServerAlias(kt, new KerberosPrincipal(
|
||||
serverAlias.getName(), serverAlias.getNameType()));
|
||||
}
|
||||
return kt;
|
||||
};
|
||||
|
||||
public static Credentials ticketToCreds(KerberosTicket kerbTicket)
|
||||
throws KrbException, IOException {
|
||||
KerberosPrincipal clientAlias = KerberosSecrets
|
||||
.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketGetClientAlias(kerbTicket);
|
||||
KerberosPrincipal serverAlias = KerberosSecrets
|
||||
.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketGetServerAlias(kerbTicket);
|
||||
return new Credentials(
|
||||
kerbTicket.getEncoded(),
|
||||
kerbTicket.getClient().getName(),
|
||||
(clientAlias != null ? clientAlias.getName() : null),
|
||||
kerbTicket.getServer().getName(),
|
||||
(serverAlias != null ? serverAlias.getName() : null),
|
||||
kerbTicket.getSessionKey().getEncoded(),
|
||||
kerbTicket.getSessionKeyType(),
|
||||
kerbTicket.getFlags(),
|
||||
kerbTicket.getAuthTime(),
|
||||
kerbTicket.getStartTime(),
|
||||
kerbTicket.getEndTime(),
|
||||
kerbTicket.getRenewTill(),
|
||||
kerbTicket.getClientAddresses());
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to get a sun..KeyTab from a javax..KeyTab
|
||||
* @param ktab the javax..KeyTab object
|
||||
* @return the sun..KeyTab object
|
||||
*/
|
||||
public static sun.security.krb5.internal.ktab.KeyTab
|
||||
snapshotFromJavaxKeyTab(KeyTab ktab) {
|
||||
return KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
|
||||
.keyTabTakeSnapshot(ktab);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to get EncryptionKeys from a javax..KeyTab
|
||||
* @param ktab the javax..KeyTab object
|
||||
* @param cname the PrincipalName
|
||||
* @return the EKeys, never null, might be empty
|
||||
*/
|
||||
public static EncryptionKey[] keysFromJavaxKeyTab(
|
||||
KeyTab ktab, PrincipalName cname) {
|
||||
return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname);
|
||||
}
|
||||
|
||||
public static String keyInfo(byte[] data) {
|
||||
if (data == null) {
|
||||
return "null key";
|
||||
} else if (data.length == 0) {
|
||||
return "empty key";
|
||||
} else {
|
||||
for (byte b : data) {
|
||||
if (b != 0) {
|
||||
return data.length + "-byte key";
|
||||
}
|
||||
}
|
||||
return data.length + "-byte zero key";
|
||||
}
|
||||
}
|
||||
}
|
721
jdkSrc/jdk8/sun/security/jgss/krb5/MessageToken.java
Normal file
721
jdkSrc/jdk8/sun/security/jgss/krb5/MessageToken.java
Normal file
@@ -0,0 +1,721 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2013, 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 sun.security.jgss.*;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* This class is a base class for other token definitions that pertain to
|
||||
* per-message GSS-API calls. Conceptually GSS-API has two types of
|
||||
* per-message tokens: WrapToken and MicToken. They differ in the respect
|
||||
* that a WrapToken carries additional plaintext or ciphertext application
|
||||
* data besides just the sequence number and checksum. This class
|
||||
* encapsulates the commonality in the structure of the WrapToken and the
|
||||
* MicToken. This structure can be represented as:
|
||||
* <p>
|
||||
* <pre>
|
||||
* 0..1 TOK_ID Identification field.
|
||||
* 01 01 - Mic token
|
||||
* 02 01 - Wrap token
|
||||
* 2..3 SGN_ALG Checksum algorithm indicator.
|
||||
* 00 00 - DES MAC MD5
|
||||
* 01 00 - MD2.5
|
||||
* 02 00 - DES MAC
|
||||
* 04 00 - HMAC SHA1 DES3-KD
|
||||
* 11 00 - RC4-HMAC
|
||||
* 4..5 SEAL_ALG ff ff - none
|
||||
* 00 00 - DES
|
||||
* 02 00 - DES3-KD
|
||||
* 10 00 - RC4-HMAC
|
||||
* 6..7 Filler Contains ff ff
|
||||
* 8..15 SND_SEQ Encrypted sequence number field.
|
||||
* 16..s+15 SGN_CKSUM Checksum of plaintext padded data,
|
||||
* calculated according to algorithm
|
||||
* specified in SGN_ALG field.
|
||||
* s+16..last Data encrypted or plaintext padded data
|
||||
* </pre>
|
||||
* Where "s" indicates the size of the checksum.
|
||||
* <p>
|
||||
* As always, this is preceeded by a GSSHeader.
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
* @author Ram Marti
|
||||
* @see sun.security.jgss.GSSHeader
|
||||
*/
|
||||
|
||||
abstract class MessageToken extends Krb5Token {
|
||||
/* Fields in header minus checksum size */
|
||||
private static final int TOKEN_NO_CKSUM_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Filler data as defined in the specification of the Kerberos v5 GSS-API
|
||||
* Mechanism.
|
||||
*/
|
||||
private static final int FILLER = 0xffff;
|
||||
|
||||
// Signing algorithm values (for the SNG_ALG field)
|
||||
|
||||
// From RFC 1964
|
||||
/* Use a DES MAC MD5 checksum */
|
||||
static final int SGN_ALG_DES_MAC_MD5 = 0x0000;
|
||||
|
||||
/* Use DES MAC checksum. */
|
||||
static final int SGN_ALG_DES_MAC = 0x0200;
|
||||
|
||||
// From draft-raeburn-cat-gssapi-krb5-3des-00
|
||||
/* Use a HMAC SHA1 DES3 -KD checksum */
|
||||
static final int SGN_ALG_HMAC_SHA1_DES3_KD = 0x0400;
|
||||
|
||||
// Sealing algorithm values (for the SEAL_ALG field)
|
||||
|
||||
// RFC 1964
|
||||
/**
|
||||
* A value for the SEAL_ALG field that indicates that no encryption was
|
||||
* used.
|
||||
*/
|
||||
static final int SEAL_ALG_NONE = 0xffff;
|
||||
/* Use DES CBC encryption algorithm. */
|
||||
static final int SEAL_ALG_DES = 0x0000;
|
||||
|
||||
// From draft-raeburn-cat-gssapi-krb5-3des-00
|
||||
/**
|
||||
* Use DES3-KD sealing algorithm. (draft-raeburn-cat-gssapi-krb5-3des-00)
|
||||
* This algorithm uses triple-DES with key derivation, with a usage
|
||||
* value KG_USAGE_SEAL. Padding is still to 8-byte multiples, and the
|
||||
* IV for encrypting application data is zero.
|
||||
*/
|
||||
static final int SEAL_ALG_DES3_KD = 0x0200;
|
||||
|
||||
// draft draft-brezak-win2k-krb-rc4-hmac-04.txt
|
||||
static final int SEAL_ALG_ARCFOUR_HMAC = 0x1000;
|
||||
static final int SGN_ALG_HMAC_MD5_ARCFOUR = 0x1100;
|
||||
|
||||
private static final int TOKEN_ID_POS = 0;
|
||||
private static final int SIGN_ALG_POS = 2;
|
||||
private static final int SEAL_ALG_POS = 4;
|
||||
|
||||
private int seqNumber;
|
||||
|
||||
private boolean confState = true;
|
||||
private boolean initiator = true;
|
||||
|
||||
private int tokenId = 0;
|
||||
private GSSHeader gssHeader = null;
|
||||
private MessageTokenHeader tokenHeader = null;
|
||||
private byte[] checksum = null;
|
||||
private byte[] encSeqNumber = null;
|
||||
private byte[] seqNumberData = null;
|
||||
|
||||
/* cipher instance used by the corresponding GSSContext */
|
||||
CipherHelper cipherHelper = null;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a MessageToken from a byte array. If there are more bytes
|
||||
* in the array than needed, the extra bytes are simply ignroed.
|
||||
*
|
||||
* @param tokenId the token id that should be contained in this token as
|
||||
* it is read.
|
||||
* @param context the Kerberos context associated with this token
|
||||
* @param tokenBytes the byte array containing the token
|
||||
* @param tokenOffset the offset where the token begins
|
||||
* @param tokenLen the length of the token
|
||||
* @param prop the MessageProp structure in which the properties of the
|
||||
* token should be stored.
|
||||
* @throws GSSException if there is a problem parsing the token
|
||||
*/
|
||||
MessageToken(int tokenId, Krb5Context context,
|
||||
byte[] tokenBytes, int tokenOffset, int tokenLen,
|
||||
MessageProp prop) throws GSSException {
|
||||
this(tokenId, context,
|
||||
new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen),
|
||||
prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MessageToken from an InputStream. Bytes will be read on
|
||||
* demand and the thread might block if there are not enough bytes to
|
||||
* complete the token.
|
||||
*
|
||||
* @param tokenId the token id that should be contained in this token as
|
||||
* it is read.
|
||||
* @param context the Kerberos context associated with this token
|
||||
* @param is the InputStream from which to read
|
||||
* @param prop the MessageProp structure in which the properties of the
|
||||
* token should be stored.
|
||||
* @throws GSSException if there is a problem reading from the
|
||||
* InputStream or parsing the token
|
||||
*/
|
||||
MessageToken(int tokenId, Krb5Context context, InputStream is,
|
||||
MessageProp prop) throws GSSException {
|
||||
init(tokenId, context);
|
||||
|
||||
try {
|
||||
gssHeader = new GSSHeader(is);
|
||||
|
||||
if (!gssHeader.getOid().equals((Object)OID)) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(tokenId));
|
||||
}
|
||||
if (!confState) {
|
||||
prop.setPrivacy(false);
|
||||
}
|
||||
|
||||
tokenHeader = new MessageTokenHeader(is, prop);
|
||||
|
||||
encSeqNumber = new byte[8];
|
||||
readFully(is, encSeqNumber);
|
||||
|
||||
// debug("\n\tRead EncSeq#=" +
|
||||
// getHexBytes(encSeqNumber, encSeqNumber.length));
|
||||
|
||||
checksum = new byte[cipherHelper.getChecksumLength()];
|
||||
readFully(is, checksum);
|
||||
|
||||
// debug("\n\tRead checksum=" +
|
||||
// getHexBytes(checksum, checksum.length));
|
||||
// debug("\nLeaving MessageToken.Cons\n");
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(tokenId) + ":" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to obtain the GSSHeader that was at the start of this
|
||||
* token.
|
||||
*/
|
||||
public final GSSHeader getGSSHeader() {
|
||||
return gssHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to obtain the token id that was contained in this token.
|
||||
* @return the token id in the token
|
||||
*/
|
||||
public final int getTokenId() {
|
||||
return tokenId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to obtain the encrypted sequence number in this token.
|
||||
* @return the encrypted sequence number in the token
|
||||
*/
|
||||
public final byte[] getEncSeqNumber() {
|
||||
return encSeqNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to obtain the checksum that was contained in this token.
|
||||
* @return the checksum in the token
|
||||
*/
|
||||
public final byte[] getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if this token contains any encrypted data.
|
||||
* @return true if it contains any encrypted data, false if there is only
|
||||
* plaintext data or if there is no data.
|
||||
*/
|
||||
public final boolean getConfState() {
|
||||
return confState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the checksum field and the encrypted sequence number
|
||||
* field. The encrypted sequence number uses the 8 bytes of the checksum
|
||||
* as an initial vector in a fixed DesCbc algorithm.
|
||||
*
|
||||
* @param prop the MessageProp structure that determines what sort of
|
||||
* checksum and sealing algorithm should be used. The lower byte
|
||||
* of qop determines the checksum algorithm while the upper byte
|
||||
* determines the signing algorithm.
|
||||
* Checksum values are:
|
||||
* 0 - default (DES_MAC)
|
||||
* 1 - MD5
|
||||
* 2 - DES_MD5
|
||||
* 3 - DES_MAC
|
||||
* 4 - HMAC_SHA1
|
||||
* Sealing values are:
|
||||
* 0 - default (DES)
|
||||
* 1 - DES
|
||||
* 2 - DES3-KD
|
||||
*
|
||||
* @param optionalHeader an optional header that will be processed first
|
||||
* during checksum calculation
|
||||
*
|
||||
* @param data the application data to checksum
|
||||
* @param offset the offset where the data starts
|
||||
* @param len the length of the data
|
||||
*
|
||||
* @param optionalTrailer an optional trailer that will be processed
|
||||
* last during checksum calculation. e.g., padding that should be
|
||||
* appended to the application data
|
||||
*
|
||||
* @throws GSSException if an error occurs in the checksum calculation or
|
||||
* encryption sequence number calculation.
|
||||
*/
|
||||
public void genSignAndSeqNumber(MessageProp prop,
|
||||
byte[] optionalHeader,
|
||||
byte[] data, int offset, int len,
|
||||
byte[] optionalTrailer)
|
||||
throws GSSException {
|
||||
|
||||
// debug("Inside MessageToken.genSignAndSeqNumber:\n");
|
||||
|
||||
int qop = prop.getQOP();
|
||||
if (qop != 0) {
|
||||
qop = 0;
|
||||
prop.setQOP(qop);
|
||||
}
|
||||
|
||||
if (!confState) {
|
||||
prop.setPrivacy(false);
|
||||
}
|
||||
|
||||
// Create a token header with the correct sign and seal algorithm
|
||||
// values.
|
||||
tokenHeader =
|
||||
new MessageTokenHeader(tokenId, prop.getPrivacy(), qop);
|
||||
|
||||
// Calculate SGN_CKSUM
|
||||
|
||||
checksum =
|
||||
getChecksum(optionalHeader, data, offset, len, optionalTrailer);
|
||||
|
||||
// debug("\n\tCalc checksum=" +
|
||||
// getHexBytes(checksum, checksum.length));
|
||||
|
||||
// Calculate SND_SEQ
|
||||
|
||||
seqNumberData = new byte[8];
|
||||
|
||||
// When using this RC4 based encryption type, the sequence number is
|
||||
// always sent in big-endian rather than little-endian order.
|
||||
if (cipherHelper.isArcFour()) {
|
||||
writeBigEndian(seqNumber, seqNumberData);
|
||||
} else {
|
||||
// for all other etypes
|
||||
writeLittleEndian(seqNumber, seqNumberData);
|
||||
}
|
||||
if (!initiator) {
|
||||
seqNumberData[4] = (byte)0xff;
|
||||
seqNumberData[5] = (byte)0xff;
|
||||
seqNumberData[6] = (byte)0xff;
|
||||
seqNumberData[7] = (byte)0xff;
|
||||
}
|
||||
|
||||
encSeqNumber = cipherHelper.encryptSeq(checksum, seqNumberData, 0, 8);
|
||||
|
||||
// debug("\n\tCalc seqNum=" +
|
||||
// getHexBytes(seqNumberData, seqNumberData.length));
|
||||
// debug("\n\tCalc encSeqNum=" +
|
||||
// getHexBytes(encSeqNumber, encSeqNumber.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the checksum field and sequence number direction bytes
|
||||
* are valid and consistent with the application data.
|
||||
*
|
||||
* @param optionalHeader an optional header that will be processed first
|
||||
* during checksum calculation.
|
||||
*
|
||||
* @param data the application data
|
||||
* @param offset the offset where the data begins
|
||||
* @param len the length of the application data
|
||||
*
|
||||
* @param optionalTrailer an optional trailer that will be processed last
|
||||
* during checksum calculation. e.g., padding that should be appended to
|
||||
* the application data
|
||||
*
|
||||
* @throws GSSException if an error occurs in the checksum calculation or
|
||||
* encryption sequence number calculation.
|
||||
*/
|
||||
public final boolean verifySignAndSeqNumber(byte[] optionalHeader,
|
||||
byte[] data, int offset, int len,
|
||||
byte[] optionalTrailer)
|
||||
throws GSSException {
|
||||
// debug("\tIn verifySign:\n");
|
||||
|
||||
// debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n");
|
||||
|
||||
byte[] myChecksum =
|
||||
getChecksum(optionalHeader, data, offset, len, optionalTrailer);
|
||||
|
||||
// debug("\t\tmychecksum: [" + getHexBytes(myChecksum) +"]\n");
|
||||
// debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n");
|
||||
|
||||
if (MessageDigest.isEqual(checksum, myChecksum)) {
|
||||
|
||||
seqNumberData = cipherHelper.decryptSeq(
|
||||
checksum, encSeqNumber, 0, 8);
|
||||
|
||||
// debug("\t\tencSeqNumber: [" + getHexBytes(encSeqNumber)
|
||||
// + "]\n");
|
||||
// debug("\t\tseqNumberData: [" + getHexBytes(seqNumberData)
|
||||
// + "]\n");
|
||||
|
||||
/*
|
||||
* The token from the initiator has direction bytes 0x00 and
|
||||
* the token from the acceptor has direction bytes 0xff.
|
||||
*/
|
||||
byte directionByte = 0;
|
||||
if (initiator)
|
||||
directionByte = (byte) 0xff; // Received token from acceptor
|
||||
|
||||
if ((seqNumberData[4] == directionByte) &&
|
||||
(seqNumberData[5] == directionByte) &&
|
||||
(seqNumberData[6] == directionByte) &&
|
||||
(seqNumberData[7] == directionByte))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public final int getSequenceNumber() {
|
||||
int sequenceNum = 0;
|
||||
if (cipherHelper.isArcFour()) {
|
||||
sequenceNum = readBigEndian(seqNumberData, 0, 4);
|
||||
} else {
|
||||
sequenceNum = readLittleEndian(seqNumberData, 0, 4);
|
||||
}
|
||||
return sequenceNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the checksum based on the algorithm stored in the
|
||||
* tokenHeader.
|
||||
*
|
||||
* @param optionalHeader an optional header that will be processed first
|
||||
* during checksum calculation.
|
||||
*
|
||||
* @param data the application data
|
||||
* @param offset the offset where the data begins
|
||||
* @param len the length of the application data
|
||||
*
|
||||
* @param optionalTrailer an optional trailer that will be processed last
|
||||
* during checksum calculation. e.g., padding that should be appended to
|
||||
* the application data
|
||||
*
|
||||
* @throws GSSException if an error occurs in the checksum calculation.
|
||||
*/
|
||||
private byte[] getChecksum(byte[] optionalHeader,
|
||||
byte[] data, int offset, int len,
|
||||
byte[] optionalTrailer)
|
||||
throws GSSException {
|
||||
|
||||
// debug("Will do getChecksum:\n");
|
||||
|
||||
/*
|
||||
* For checksum calculation the token header bytes i.e., the first 8
|
||||
* bytes following the GSSHeader, are logically prepended to the
|
||||
* application data to bind the data to this particular token.
|
||||
*
|
||||
* Note: There is no such requirement wrt adding padding to the
|
||||
* application data for checksumming, although the cryptographic
|
||||
* algorithm used might itself apply some padding.
|
||||
*/
|
||||
|
||||
byte[] tokenHeaderBytes = tokenHeader.getBytes();
|
||||
byte[] existingHeader = optionalHeader;
|
||||
byte[] checksumDataHeader = tokenHeaderBytes;
|
||||
|
||||
if (existingHeader != null) {
|
||||
checksumDataHeader = new byte[tokenHeaderBytes.length +
|
||||
existingHeader.length];
|
||||
System.arraycopy(tokenHeaderBytes, 0,
|
||||
checksumDataHeader, 0, tokenHeaderBytes.length);
|
||||
System.arraycopy(existingHeader, 0,
|
||||
checksumDataHeader, tokenHeaderBytes.length,
|
||||
existingHeader.length);
|
||||
}
|
||||
|
||||
return cipherHelper.calculateChecksum(tokenHeader.getSignAlg(),
|
||||
checksumDataHeader, optionalTrailer, data, offset, len, tokenId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an empty MessageToken for the local context to send to
|
||||
* the peer. It also increments the local sequence number in the
|
||||
* Krb5Context instance it uses after obtaining the object lock for
|
||||
* it.
|
||||
*
|
||||
* @param tokenId the token id that should be contained in this token
|
||||
* @param context the Kerberos context associated with this token
|
||||
*/
|
||||
MessageToken(int tokenId, Krb5Context context) throws GSSException {
|
||||
/*
|
||||
debug("\n============================");
|
||||
debug("\nMySessionKey=" +
|
||||
getHexBytes(context.getMySessionKey().getBytes()));
|
||||
debug("\nPeerSessionKey=" +
|
||||
getHexBytes(context.getPeerSessionKey().getBytes()));
|
||||
debug("\n============================\n");
|
||||
*/
|
||||
init(tokenId, context);
|
||||
this.seqNumber = context.incrementMySequenceNumber();
|
||||
}
|
||||
|
||||
private void init(int tokenId, Krb5Context context) throws GSSException {
|
||||
this.tokenId = tokenId;
|
||||
// Just for consistency check in Wrap
|
||||
this.confState = context.getConfState();
|
||||
|
||||
this.initiator = context.isInitiator();
|
||||
|
||||
this.cipherHelper = context.getCipherHelper(null);
|
||||
// debug("In MessageToken.Cons");
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a GSSHeader and this token onto an OutputStream.
|
||||
*
|
||||
* @param os the OutputStream to which this should be written
|
||||
* @throws GSSException if an error occurs while writing to the OutputStream
|
||||
*/
|
||||
public void encode(OutputStream os) throws IOException, GSSException {
|
||||
gssHeader = new GSSHeader(OID, getKrb5TokenSize());
|
||||
gssHeader.encode(os);
|
||||
tokenHeader.encode(os);
|
||||
// debug("Writing seqNumber: " + getHexBytes(encSeqNumber));
|
||||
os.write(encSeqNumber);
|
||||
// debug("Writing checksum: " + getHexBytes(checksum));
|
||||
os.write(checksum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the size of this token. Note that this excludes the size of
|
||||
* the GSSHeader.
|
||||
* @return token size
|
||||
*/
|
||||
protected int getKrb5TokenSize() throws GSSException {
|
||||
return getTokenSize();
|
||||
}
|
||||
|
||||
protected final int getTokenSize() throws GSSException {
|
||||
return TOKEN_NO_CKSUM_SIZE + cipherHelper.getChecksumLength();
|
||||
}
|
||||
|
||||
protected static final int getTokenSize(CipherHelper ch)
|
||||
throws GSSException {
|
||||
return TOKEN_NO_CKSUM_SIZE + ch.getChecksumLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the conext key that is associated with this token.
|
||||
* @return the context key
|
||||
*/
|
||||
/*
|
||||
public final byte[] getContextKey() {
|
||||
return contextKey;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the encryption algorithm that should be used in this token
|
||||
* given the state of confidentiality the application requested.
|
||||
* Requested qop must be consistent with negotiated session key.
|
||||
* @param confRequested true if the application desired confidentiality
|
||||
* on this token, false otherwise
|
||||
* @param qop the qop requested by the application
|
||||
* @throws GSSException if qop is incompatible with the negotiated
|
||||
* session key
|
||||
*/
|
||||
protected abstract int getSealAlg(boolean confRequested, int qop)
|
||||
throws GSSException;
|
||||
|
||||
// ******************************************* //
|
||||
// I N N E R C L A S S E S F O L L O W
|
||||
// ******************************************* //
|
||||
|
||||
/**
|
||||
* This inner class represents the initial portion of the message token
|
||||
* and contains information about the checksum and encryption algorithms
|
||||
* that are in use. It constitutes the first 8 bytes of the
|
||||
* message token:
|
||||
* <pre>
|
||||
* 0..1 TOK_ID Identification field.
|
||||
* 01 01 - Mic token
|
||||
* 02 01 - Wrap token
|
||||
* 2..3 SGN_ALG Checksum algorithm indicator.
|
||||
* 00 00 - DES MAC MD5
|
||||
* 01 00 - MD2.5
|
||||
* 02 00 - DES MAC
|
||||
* 04 00 - HMAC SHA1 DES3-KD
|
||||
* 11 00 - RC4-HMAC
|
||||
* 4..5 SEAL_ALG ff ff - none
|
||||
* 00 00 - DES
|
||||
* 02 00 - DES3-KD
|
||||
* 10 00 - RC4-HMAC
|
||||
* 6..7 Filler Contains ff ff
|
||||
* </pre>
|
||||
*/
|
||||
class MessageTokenHeader {
|
||||
|
||||
private int tokenId;
|
||||
private int signAlg;
|
||||
private int sealAlg;
|
||||
|
||||
private byte[] bytes = new byte[8];
|
||||
|
||||
/**
|
||||
* Constructs a MessageTokenHeader for the specified token type with
|
||||
* appropriate checksum and encryption algorithms fields.
|
||||
*
|
||||
* @param tokenId the token id for this message token
|
||||
* @param conf true if confidentiality will be resuested with this
|
||||
* message token, false otherwise.
|
||||
* @param qop the value of the quality of protection that will be
|
||||
* desired.
|
||||
*/
|
||||
public MessageTokenHeader(int tokenId, boolean conf, int qop)
|
||||
throws GSSException {
|
||||
|
||||
this.tokenId = tokenId;
|
||||
|
||||
signAlg = MessageToken.this.getSgnAlg(qop);
|
||||
|
||||
sealAlg = MessageToken.this.getSealAlg(conf, qop);
|
||||
|
||||
bytes[0] = (byte) (tokenId >>> 8);
|
||||
bytes[1] = (byte) (tokenId);
|
||||
|
||||
bytes[2] = (byte) (signAlg >>> 8);
|
||||
bytes[3] = (byte) (signAlg);
|
||||
|
||||
bytes[4] = (byte) (sealAlg >>> 8);
|
||||
bytes[5] = (byte) (sealAlg);
|
||||
|
||||
bytes[6] = (byte) (MessageToken.FILLER >>> 8);
|
||||
bytes[7] = (byte) (MessageToken.FILLER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MessageTokenHeader by reading it from an InputStream
|
||||
* and sets the appropriate confidentiality and quality of protection
|
||||
* values in a MessageProp structure.
|
||||
*
|
||||
* @param is the InputStream to read from
|
||||
* @param prop the MessageProp to populate
|
||||
* @throws IOException is an error occurs while reading from the
|
||||
* InputStream
|
||||
*/
|
||||
public MessageTokenHeader(InputStream is, MessageProp prop)
|
||||
throws IOException {
|
||||
readFully(is, bytes);
|
||||
tokenId = readInt(bytes, TOKEN_ID_POS);
|
||||
signAlg = readInt(bytes, SIGN_ALG_POS);
|
||||
sealAlg = readInt(bytes, SEAL_ALG_POS);
|
||||
// debug("\nMessageTokenHeader read tokenId=" +
|
||||
// getHexBytes(bytes) + "\n");
|
||||
// XXX compare to FILLER
|
||||
int temp = readInt(bytes, SEAL_ALG_POS + 2);
|
||||
|
||||
// debug("SIGN_ALG=" + signAlg);
|
||||
|
||||
switch (sealAlg) {
|
||||
case SEAL_ALG_DES:
|
||||
case SEAL_ALG_DES3_KD:
|
||||
case SEAL_ALG_ARCFOUR_HMAC:
|
||||
prop.setPrivacy(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
prop.setPrivacy(false);
|
||||
}
|
||||
|
||||
prop.setQOP(0); // default
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes this MessageTokenHeader onto an OutputStream
|
||||
* @param os the OutputStream to write to
|
||||
* @throws IOException is an error occurs while writing
|
||||
*/
|
||||
public final void encode(OutputStream os) throws IOException {
|
||||
os.write(bytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the token id for the message token.
|
||||
* @return the token id
|
||||
* @see sun.security.jgss.krb5.Krb5Token#MIC_ID
|
||||
* @see sun.security.jgss.krb5.Krb5Token#WRAP_ID
|
||||
*/
|
||||
public final int getTokenId() {
|
||||
return tokenId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sign algorithm for the message token.
|
||||
* @return the sign algorithm
|
||||
* @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC
|
||||
* @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC_MD5
|
||||
*/
|
||||
public final int getSignAlg() {
|
||||
return signAlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the seal algorithm for the message token.
|
||||
* @return the seal algorithm
|
||||
* @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_DES
|
||||
* @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_NONE
|
||||
*/
|
||||
public final int getSealAlg() {
|
||||
return sealAlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes of this header.
|
||||
* @return 8 bytes that form this header
|
||||
*/
|
||||
public final byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
} // end of class MessageTokenHeader
|
||||
|
||||
|
||||
/**
|
||||
* Determine signing algorithm based on QOP.
|
||||
*/
|
||||
protected int getSgnAlg(int qop) throws GSSException {
|
||||
// QOP ignored
|
||||
return cipherHelper.getSgnAlg();
|
||||
}
|
||||
}
|
643
jdkSrc/jdk8/sun/security/jgss/krb5/MessageToken_v2.java
Normal file
643
jdkSrc/jdk8/sun/security/jgss/krb5/MessageToken_v2.java
Normal file
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2018, 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 java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class is a base class for new GSS token definitions, as defined
|
||||
* in RFC 4121, that pertain to per-message GSS-API calls. Conceptually
|
||||
* GSS-API has two types of per-message tokens: WrapToken and MicToken.
|
||||
* They differ in the respect that a WrapToken carries additional plaintext
|
||||
* or ciphertext application data besides just the sequence number and
|
||||
* checksum. This class encapsulates the commonality in the structure of
|
||||
* the WrapToken and the MicToken. This structure can be represented as:
|
||||
* <p>
|
||||
* <pre>
|
||||
* Wrap Tokens
|
||||
*
|
||||
* Octet no Name Description
|
||||
* ---------------------------------------------------------------
|
||||
* 0..1 TOK_ID Identification field. Tokens emitted by
|
||||
* GSS_Wrap() contain the hex value 05 04
|
||||
* expressed in big-endian order in this field.
|
||||
* 2 Flags Attributes field, as described in section
|
||||
* 4.2.2.
|
||||
* 3 Filler Contains the hex value FF.
|
||||
* 4..5 EC Contains the "extra count" field, in big-
|
||||
* endian order as described in section 4.2.3.
|
||||
* 6..7 RRC Contains the "right rotation count" in big
|
||||
* endian order, as described in section 4.2.5.
|
||||
* 8..15 SND_SEQ Sequence number field in clear text,
|
||||
* expressed in big-endian order.
|
||||
* 16..last Data Encrypted data for Wrap tokens with
|
||||
* confidentiality, or plaintext data followed
|
||||
* by the checksum for Wrap tokens without
|
||||
* confidentiality, as described in section
|
||||
* 4.2.4.
|
||||
* MIC Tokens
|
||||
*
|
||||
* Octet no Name Description
|
||||
* -----------------------------------------------------------------
|
||||
* 0..1 TOK_ID Identification field. Tokens emitted by
|
||||
* GSS_GetMIC() contain the hex value 04 04
|
||||
* expressed in big-endian order in this field.
|
||||
* 2 Flags Attributes field, as described in section
|
||||
* 4.2.2.
|
||||
* 3..7 Filler Contains five octets of hex value FF.
|
||||
* 8..15 SND_SEQ Sequence number field in clear text,
|
||||
* expressed in big-endian order.
|
||||
* 16..last SGN_CKSUM Checksum of the "to-be-signed" data and
|
||||
* octet 0..15, as described in section 4.2.4.
|
||||
*
|
||||
* </pre>
|
||||
* <p>
|
||||
* This class is the super class of WrapToken_v2 and MicToken_v2. The token's
|
||||
* header (bytes[0..15]) and data (byte[16..]) are saved in tokenHeader and
|
||||
* tokenData fields. Since there is no easy way to find out the exact length
|
||||
* of a WrapToken_v2 token from any header info, in the case of reading from
|
||||
* stream, we read all available() bytes into the token.
|
||||
* <p>
|
||||
* All read actions are performed in this super class. On the write part, the
|
||||
* super class only write the tokenHeader, and the content writing is inside
|
||||
* child classes.
|
||||
*
|
||||
* @author Seema Malkani
|
||||
*/
|
||||
|
||||
abstract class MessageToken_v2 extends Krb5Token {
|
||||
|
||||
protected static final int TOKEN_HEADER_SIZE = 16;
|
||||
private static final int TOKEN_ID_POS = 0;
|
||||
private static final int TOKEN_FLAG_POS = 2;
|
||||
private static final int TOKEN_EC_POS = 4;
|
||||
private static final int TOKEN_RRC_POS = 6;
|
||||
|
||||
/**
|
||||
* The size of the random confounder used in a WrapToken.
|
||||
*/
|
||||
protected static final int CONFOUNDER_SIZE = 16;
|
||||
|
||||
// RFC 4121, key usage values
|
||||
static final int KG_USAGE_ACCEPTOR_SEAL = 22;
|
||||
static final int KG_USAGE_ACCEPTOR_SIGN = 23;
|
||||
static final int KG_USAGE_INITIATOR_SEAL = 24;
|
||||
static final int KG_USAGE_INITIATOR_SIGN = 25;
|
||||
|
||||
// RFC 4121, Flags Field
|
||||
private static final int FLAG_SENDER_IS_ACCEPTOR = 1;
|
||||
private static final int FLAG_WRAP_CONFIDENTIAL = 2;
|
||||
private static final int FLAG_ACCEPTOR_SUBKEY = 4;
|
||||
private static final int FILLER = 0xff;
|
||||
|
||||
private MessageTokenHeader tokenHeader = null;
|
||||
|
||||
// Common field
|
||||
private int tokenId = 0;
|
||||
private int seqNumber;
|
||||
protected byte[] tokenData; // content of token, without the header
|
||||
protected int tokenDataLen;
|
||||
|
||||
// Key usage number for crypto action
|
||||
private int key_usage = 0;
|
||||
|
||||
// EC and RRC fields, WrapToken only
|
||||
private int ec = 0;
|
||||
private int rrc = 0;
|
||||
|
||||
// Checksum. Always in MicToken, might be in WrapToken
|
||||
byte[] checksum = null;
|
||||
|
||||
// Context properties
|
||||
private boolean confState = true;
|
||||
private boolean initiator = true;
|
||||
private boolean have_acceptor_subkey = false;
|
||||
|
||||
/* cipher instance used by the corresponding GSSContext */
|
||||
CipherHelper cipherHelper = null;
|
||||
|
||||
/**
|
||||
* Constructs a MessageToken from a byte array.
|
||||
*
|
||||
* @param tokenId the token id that should be contained in this token as
|
||||
* it is read.
|
||||
* @param context the Kerberos context associated with this token
|
||||
* @param tokenBytes the byte array containing the token
|
||||
* @param tokenOffset the offset where the token begins
|
||||
* @param tokenLen the length of the token
|
||||
* @param prop the MessageProp structure in which the properties of the
|
||||
* token should be stored.
|
||||
* @throws GSSException if there is a problem parsing the token
|
||||
*/
|
||||
MessageToken_v2(int tokenId, Krb5Context context,
|
||||
byte[] tokenBytes, int tokenOffset, int tokenLen,
|
||||
MessageProp prop) throws GSSException {
|
||||
this(tokenId, context,
|
||||
new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen),
|
||||
prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MessageToken from an InputStream. Bytes will be read on
|
||||
* demand and the thread might block if there are not enough bytes to
|
||||
* complete the token. Please note there is no accurate way to find out
|
||||
* the size of a token, but we try our best to make sure there is
|
||||
* enough bytes to construct one.
|
||||
*
|
||||
* @param tokenId the token id that should be contained in this token as
|
||||
* it is read.
|
||||
* @param context the Kerberos context associated with this token
|
||||
* @param is the InputStream from which to read
|
||||
* @param prop the MessageProp structure in which the properties of the
|
||||
* token should be stored.
|
||||
* @throws GSSException if there is a problem reading from the
|
||||
* InputStream or parsing the token
|
||||
*/
|
||||
MessageToken_v2(int tokenId, Krb5Context context, InputStream is,
|
||||
MessageProp prop) throws GSSException {
|
||||
init(tokenId, context);
|
||||
|
||||
try {
|
||||
if (!confState) {
|
||||
prop.setPrivacy(false);
|
||||
}
|
||||
tokenHeader = new MessageTokenHeader(is, prop, tokenId);
|
||||
|
||||
// set key_usage
|
||||
if (tokenId == Krb5Token.WRAP_ID_v2) {
|
||||
key_usage = (!initiator ? KG_USAGE_INITIATOR_SEAL
|
||||
: KG_USAGE_ACCEPTOR_SEAL);
|
||||
} else if (tokenId == Krb5Token.MIC_ID_v2) {
|
||||
key_usage = (!initiator ? KG_USAGE_INITIATOR_SIGN
|
||||
: KG_USAGE_ACCEPTOR_SIGN);
|
||||
}
|
||||
|
||||
int minSize = 0; // minimal size for token data
|
||||
if (tokenId == Krb5Token.WRAP_ID_v2 && prop.getPrivacy()) {
|
||||
minSize = CONFOUNDER_SIZE +
|
||||
TOKEN_HEADER_SIZE + cipherHelper.getChecksumLength();
|
||||
} else {
|
||||
minSize = cipherHelper.getChecksumLength();
|
||||
}
|
||||
|
||||
// Read token data
|
||||
if (tokenId == Krb5Token.MIC_ID_v2) {
|
||||
// The only case we can precisely predict the token data length
|
||||
tokenDataLen = minSize;
|
||||
tokenData = new byte[minSize];
|
||||
readFully(is, tokenData);
|
||||
} else {
|
||||
tokenDataLen = is.available();
|
||||
if (tokenDataLen >= minSize) { // read in one shot
|
||||
tokenData = new byte[tokenDataLen];
|
||||
readFully(is, tokenData);
|
||||
} else {
|
||||
byte[] tmp = new byte[minSize];
|
||||
readFully(is, tmp);
|
||||
// Hope while blocked in the read above, more data would
|
||||
// come and is.available() below contains the whole token.
|
||||
int more = is.available();
|
||||
tokenDataLen = minSize + more;
|
||||
tokenData = Arrays.copyOf(tmp, tokenDataLen);
|
||||
readFully(is, tokenData, minSize, more);
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenId == Krb5Token.WRAP_ID_v2) {
|
||||
rotate();
|
||||
}
|
||||
|
||||
if (tokenId == Krb5Token.MIC_ID_v2 ||
|
||||
(tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy())) {
|
||||
// Read checksum
|
||||
int chkLen = cipherHelper.getChecksumLength();
|
||||
checksum = new byte[chkLen];
|
||||
System.arraycopy(tokenData, tokenDataLen-chkLen,
|
||||
checksum, 0, chkLen);
|
||||
|
||||
// validate EC for Wrap tokens without confidentiality
|
||||
if (tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy()) {
|
||||
if (chkLen != ec) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(tokenId) + ":" + "EC incorrect!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(tokenId) + ":" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to obtain the token id that was contained in this token.
|
||||
* @return the token id in the token
|
||||
*/
|
||||
public final int getTokenId() {
|
||||
return tokenId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to obtain the key_usage type for this token.
|
||||
* @return the key_usage for the token
|
||||
*/
|
||||
public final int getKeyUsage() {
|
||||
return key_usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if this token contains any encrypted data.
|
||||
* @return true if it contains any encrypted data, false if there is only
|
||||
* plaintext data or if there is no data.
|
||||
*/
|
||||
public final boolean getConfState() {
|
||||
return confState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the checksum field and the sequence number field.
|
||||
*
|
||||
* @param prop the MessageProp structure
|
||||
* @param data the application data to checksum
|
||||
* @param offset the offset where the data starts
|
||||
* @param len the length of the data
|
||||
*
|
||||
* @throws GSSException if an error occurs in the checksum calculation or
|
||||
* sequence number calculation.
|
||||
*/
|
||||
public void genSignAndSeqNumber(MessageProp prop,
|
||||
byte[] data, int offset, int len)
|
||||
throws GSSException {
|
||||
|
||||
// debug("Inside MessageToken.genSignAndSeqNumber:\n");
|
||||
|
||||
int qop = prop.getQOP();
|
||||
if (qop != 0) {
|
||||
qop = 0;
|
||||
prop.setQOP(qop);
|
||||
}
|
||||
|
||||
if (!confState) {
|
||||
prop.setPrivacy(false);
|
||||
}
|
||||
|
||||
// Create a new gss token header as defined in RFC 4121
|
||||
tokenHeader = new MessageTokenHeader(tokenId, prop.getPrivacy());
|
||||
// debug("\n\t Message Header = " +
|
||||
// getHexBytes(tokenHeader.getBytes(), tokenHeader.getBytes().length));
|
||||
|
||||
// set key_usage
|
||||
if (tokenId == Krb5Token.WRAP_ID_v2) {
|
||||
key_usage = (initiator ? KG_USAGE_INITIATOR_SEAL
|
||||
: KG_USAGE_ACCEPTOR_SEAL);
|
||||
} else if (tokenId == Krb5Token.MIC_ID_v2) {
|
||||
key_usage = (initiator ? KG_USAGE_INITIATOR_SIGN
|
||||
: KG_USAGE_ACCEPTOR_SIGN);
|
||||
}
|
||||
|
||||
// Calculate SGN_CKSUM
|
||||
if ((tokenId == MIC_ID_v2) ||
|
||||
(!prop.getPrivacy() && (tokenId == WRAP_ID_v2))) {
|
||||
checksum = getChecksum(data, offset, len);
|
||||
// debug("\n\tCalc checksum=" +
|
||||
// getHexBytes(checksum, checksum.length));
|
||||
}
|
||||
|
||||
// In Wrap tokens without confidentiality, the EC field SHALL be used
|
||||
// to encode the number of octets in the trailing checksum
|
||||
if (!prop.getPrivacy() && (tokenId == WRAP_ID_v2)) {
|
||||
byte[] tok_header = tokenHeader.getBytes();
|
||||
tok_header[4] = (byte) (checksum.length >>> 8);
|
||||
tok_header[5] = (byte) (checksum.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the validity of checksum field
|
||||
*
|
||||
* @param data the application data
|
||||
* @param offset the offset where the data begins
|
||||
* @param len the length of the application data
|
||||
*
|
||||
* @throws GSSException if an error occurs in the checksum calculation
|
||||
*/
|
||||
public final boolean verifySign(byte[] data, int offset, int len)
|
||||
throws GSSException {
|
||||
|
||||
// debug("\t====In verifySign:====\n");
|
||||
// debug("\t\t checksum: [" + getHexBytes(checksum) + "]\n");
|
||||
// debug("\t\t data = [" + getHexBytes(data) + "]\n");
|
||||
|
||||
byte[] myChecksum = getChecksum(data, offset, len);
|
||||
// debug("\t\t mychecksum: [" + getHexBytes(myChecksum) +"]\n");
|
||||
|
||||
if (MessageDigest.isEqual(checksum, myChecksum)) {
|
||||
// debug("\t\t====Checksum PASS:====\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate bytes as per the "RRC" (Right Rotation Count) received.
|
||||
* Our implementation does not do any rotates when sending, only
|
||||
* when receiving, we rotate left as per the RRC count, to revert it.
|
||||
*/
|
||||
private void rotate() {
|
||||
if (rrc % tokenDataLen != 0) {
|
||||
rrc = rrc % tokenDataLen;
|
||||
byte[] newBytes = new byte[tokenDataLen];
|
||||
|
||||
System.arraycopy(tokenData, rrc, newBytes, 0, tokenDataLen-rrc);
|
||||
System.arraycopy(tokenData, 0, newBytes, tokenDataLen-rrc, rrc);
|
||||
|
||||
tokenData = newBytes;
|
||||
}
|
||||
}
|
||||
|
||||
public final int getSequenceNumber() {
|
||||
return seqNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the checksum based on the algorithm stored in the
|
||||
* tokenHeader.
|
||||
*
|
||||
* @param data the application data
|
||||
* @param offset the offset where the data begins
|
||||
* @param len the length of the application data
|
||||
*
|
||||
* @throws GSSException if an error occurs in the checksum calculation.
|
||||
*/
|
||||
byte[] getChecksum(byte[] data, int offset, int len)
|
||||
throws GSSException {
|
||||
|
||||
// debug("Will do getChecksum:\n");
|
||||
|
||||
/*
|
||||
* For checksum calculation the token header bytes i.e., the first 16
|
||||
* bytes following the GSSHeader, are logically prepended to the
|
||||
* application data to bind the data to this particular token.
|
||||
*
|
||||
* Note: There is no such requirement wrt adding padding to the
|
||||
* application data for checksumming, although the cryptographic
|
||||
* algorithm used might itself apply some padding.
|
||||
*/
|
||||
|
||||
byte[] tokenHeaderBytes = tokenHeader.getBytes();
|
||||
|
||||
// check confidentiality
|
||||
int conf_flag = tokenHeaderBytes[TOKEN_FLAG_POS] &
|
||||
FLAG_WRAP_CONFIDENTIAL;
|
||||
|
||||
// clear EC and RRC in token header for checksum calculation
|
||||
if ((conf_flag == 0) && (tokenId == WRAP_ID_v2)) {
|
||||
tokenHeaderBytes[4] = 0;
|
||||
tokenHeaderBytes[5] = 0;
|
||||
tokenHeaderBytes[6] = 0;
|
||||
tokenHeaderBytes[7] = 0;
|
||||
}
|
||||
return cipherHelper.calculateChecksum(tokenHeaderBytes, data,
|
||||
offset, len, key_usage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an empty MessageToken for the local context to send to
|
||||
* the peer. It also increments the local sequence number in the
|
||||
* Krb5Context instance it uses after obtaining the object lock for
|
||||
* it.
|
||||
*
|
||||
* @param tokenId the token id that should be contained in this token
|
||||
* @param context the Kerberos context associated with this token
|
||||
*/
|
||||
MessageToken_v2(int tokenId, Krb5Context context) throws GSSException {
|
||||
/*
|
||||
debug("\n============================");
|
||||
debug("\nMySessionKey=" +
|
||||
getHexBytes(context.getMySessionKey().getBytes()));
|
||||
debug("\nPeerSessionKey=" +
|
||||
getHexBytes(context.getPeerSessionKey().getBytes()));
|
||||
debug("\n============================\n");
|
||||
*/
|
||||
init(tokenId, context);
|
||||
this.seqNumber = context.incrementMySequenceNumber();
|
||||
}
|
||||
|
||||
private void init(int tokenId, Krb5Context context) throws GSSException {
|
||||
this.tokenId = tokenId;
|
||||
// Just for consistency check in Wrap
|
||||
this.confState = context.getConfState();
|
||||
|
||||
this.initiator = context.isInitiator();
|
||||
|
||||
this.have_acceptor_subkey = context.getKeySrc() == Krb5Context.ACCEPTOR_SUBKEY;
|
||||
|
||||
this.cipherHelper = context.getCipherHelper(null);
|
||||
// debug("In MessageToken.Cons");
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a MessageTokenHeader onto an OutputStream.
|
||||
*
|
||||
* @param os the OutputStream to which this should be written
|
||||
* @throws IOException is an error occurs while writing to the OutputStream
|
||||
*/
|
||||
protected void encodeHeader(OutputStream os) throws IOException {
|
||||
tokenHeader.encode(os);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a MessageToken_v2 onto an OutputStream.
|
||||
*
|
||||
* @param os the OutputStream to which this should be written
|
||||
* @throws IOException is an error occurs while encoding the token
|
||||
*/
|
||||
public abstract void encode(OutputStream os) throws IOException;
|
||||
|
||||
protected final byte[] getTokenHeader() {
|
||||
return (tokenHeader.getBytes());
|
||||
}
|
||||
|
||||
// ******************************************* //
|
||||
// I N N E R C L A S S E S F O L L O W
|
||||
// ******************************************* //
|
||||
|
||||
/**
|
||||
* This inner class represents the initial portion of the message token.
|
||||
* It constitutes the first 16 bytes of the message token.
|
||||
*/
|
||||
class MessageTokenHeader {
|
||||
|
||||
private int tokenId;
|
||||
private byte[] bytes = new byte[TOKEN_HEADER_SIZE];
|
||||
|
||||
// Writes a new token header
|
||||
public MessageTokenHeader(int tokenId, boolean conf) throws GSSException {
|
||||
|
||||
this.tokenId = tokenId;
|
||||
|
||||
bytes[0] = (byte) (tokenId >>> 8);
|
||||
bytes[1] = (byte) (tokenId);
|
||||
|
||||
// Flags (Note: MIT impl requires subkey)
|
||||
int flags = 0;
|
||||
flags = (initiator ? 0 : FLAG_SENDER_IS_ACCEPTOR) |
|
||||
((conf && tokenId != MIC_ID_v2) ?
|
||||
FLAG_WRAP_CONFIDENTIAL : 0) |
|
||||
(have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0);
|
||||
bytes[2] = (byte) flags;
|
||||
|
||||
// filler
|
||||
bytes[3] = (byte) FILLER;
|
||||
|
||||
if (tokenId == WRAP_ID_v2) {
|
||||
// EC field
|
||||
bytes[4] = (byte) 0;
|
||||
bytes[5] = (byte) 0;
|
||||
// RRC field
|
||||
bytes[6] = (byte) 0;
|
||||
bytes[7] = (byte) 0;
|
||||
} else if (tokenId == MIC_ID_v2) {
|
||||
// more filler for MicToken
|
||||
for (int i = 4; i < 8; i++) {
|
||||
bytes[i] = (byte) FILLER;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate SND_SEQ, only write 4 bytes from the 12th position
|
||||
writeBigEndian(seqNumber, bytes, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a MessageTokenHeader from an InputStream and sets the
|
||||
* appropriate confidentiality and quality of protection
|
||||
* values in a MessageProp structure.
|
||||
*
|
||||
* @param is the InputStream to read from
|
||||
* @param prop the MessageProp to populate
|
||||
* @throws IOException is an error occurs while reading from the
|
||||
* InputStream
|
||||
*/
|
||||
public MessageTokenHeader(InputStream is, MessageProp prop, int tokId)
|
||||
throws IOException, GSSException {
|
||||
|
||||
readFully(is, bytes, 0, TOKEN_HEADER_SIZE);
|
||||
tokenId = readInt(bytes, TOKEN_ID_POS);
|
||||
|
||||
// validate Token ID
|
||||
if (tokenId != tokId) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(tokenId) + ":" + "Defective Token ID!");
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate new GSS TokenHeader
|
||||
*/
|
||||
|
||||
// valid acceptor_flag
|
||||
// If I am initiator, the received token should have ACCEPTOR on
|
||||
int acceptor_flag = (initiator ? FLAG_SENDER_IS_ACCEPTOR : 0);
|
||||
int flag = bytes[TOKEN_FLAG_POS] & FLAG_SENDER_IS_ACCEPTOR;
|
||||
if (flag != acceptor_flag) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(tokenId) + ":" + "Acceptor Flag Error!");
|
||||
}
|
||||
|
||||
// check for confidentiality
|
||||
int conf_flag = bytes[TOKEN_FLAG_POS] & FLAG_WRAP_CONFIDENTIAL;
|
||||
if ((conf_flag == FLAG_WRAP_CONFIDENTIAL) &&
|
||||
(tokenId == WRAP_ID_v2)) {
|
||||
prop.setPrivacy(true);
|
||||
} else {
|
||||
prop.setPrivacy(false);
|
||||
}
|
||||
|
||||
if (tokenId == WRAP_ID_v2) {
|
||||
// validate filler
|
||||
if ((bytes[3] & 0xff) != FILLER) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(tokenId) + ":" + "Defective Token Filler!");
|
||||
}
|
||||
|
||||
// read EC field
|
||||
ec = readBigEndian(bytes, TOKEN_EC_POS, 2);
|
||||
|
||||
// read RRC field
|
||||
rrc = readBigEndian(bytes, TOKEN_RRC_POS, 2);
|
||||
} else if (tokenId == MIC_ID_v2) {
|
||||
for (int i = 3; i < 8; i++) {
|
||||
if ((bytes[i] & 0xff) != FILLER) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN,
|
||||
-1, getTokenName(tokenId) + ":" +
|
||||
"Defective Token Filler!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set default QOP
|
||||
prop.setQOP(0);
|
||||
|
||||
// sequence number
|
||||
seqNumber = readBigEndian(bytes, 12, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes this MessageTokenHeader onto an OutputStream
|
||||
* @param os the OutputStream to write to
|
||||
* @throws IOException is an error occurs while writing
|
||||
*/
|
||||
public final void encode(OutputStream os) throws IOException {
|
||||
os.write(bytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the token id for the message token.
|
||||
* @return the token id
|
||||
* @see sun.security.jgss.krb5.Krb5Token#MIC_ID_v2
|
||||
* @see sun.security.jgss.krb5.Krb5Token#WRAP_ID_v2
|
||||
*/
|
||||
public final int getTokenId() {
|
||||
return tokenId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes of this header.
|
||||
* @return 8 bytes that form this header
|
||||
*/
|
||||
public final byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
} // end of class MessageTokenHeader
|
||||
}
|
114
jdkSrc/jdk8/sun/security/jgss/krb5/MicToken.java
Normal file
114
jdkSrc/jdk8/sun/security/jgss/krb5/MicToken.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
class MicToken extends MessageToken {
|
||||
|
||||
public MicToken(Krb5Context context,
|
||||
byte[] tokenBytes, int tokenOffset, int tokenLen,
|
||||
MessageProp prop) throws GSSException {
|
||||
super(Krb5Token.MIC_ID, context,
|
||||
tokenBytes, tokenOffset, tokenLen, prop);
|
||||
}
|
||||
|
||||
public MicToken(Krb5Context context,
|
||||
InputStream is, MessageProp prop)
|
||||
throws GSSException {
|
||||
super(Krb5Token.MIC_ID, context, is, prop);
|
||||
}
|
||||
|
||||
public void verify(byte[] data, int offset, int len) throws GSSException {
|
||||
if (!verifySignAndSeqNumber(null, data, offset, len, null))
|
||||
throw new GSSException(GSSException.BAD_MIC, -1,
|
||||
"Corrupt checksum or sequence number in MIC token");
|
||||
}
|
||||
|
||||
public void verify(InputStream data) throws GSSException {
|
||||
byte[] dataBytes = null;
|
||||
try {
|
||||
dataBytes = new byte[data.available()];
|
||||
data.read(dataBytes);
|
||||
} catch (IOException e) {
|
||||
// Error reading application data
|
||||
throw new GSSException(GSSException.BAD_MIC, -1,
|
||||
"Corrupt checksum or sequence number in MIC token");
|
||||
}
|
||||
verify(dataBytes, 0, dataBytes.length);
|
||||
}
|
||||
|
||||
public MicToken(Krb5Context context, MessageProp prop,
|
||||
byte[] data, int pos, int len)
|
||||
throws GSSException {
|
||||
super(Krb5Token.MIC_ID, context);
|
||||
|
||||
// debug("Application data to MicToken verify is [" +
|
||||
// getHexBytes(data, pos, len) + "]\n");
|
||||
if (prop == null) prop = new MessageProp(0, false);
|
||||
genSignAndSeqNumber(prop, null, data, pos, len, null);
|
||||
}
|
||||
|
||||
public MicToken(Krb5Context context, MessageProp prop,
|
||||
InputStream data)
|
||||
throws GSSException, IOException {
|
||||
super(Krb5Token.MIC_ID, context);
|
||||
byte[] dataBytes = new byte[data.available()];
|
||||
data.read(dataBytes);
|
||||
|
||||
//debug("Application data to MicToken cons is [" +
|
||||
// getHexBytes(dataBytes) + "]\n");
|
||||
if (prop == null) prop = new MessageProp(0, false);
|
||||
genSignAndSeqNumber(prop, null, dataBytes, 0, dataBytes.length, null);
|
||||
}
|
||||
|
||||
protected int getSealAlg(boolean confRequested, int qop) {
|
||||
return (SEAL_ALG_NONE);
|
||||
}
|
||||
|
||||
public int encode(byte[] outToken, int offset)
|
||||
throws IOException, GSSException {
|
||||
// Token is small
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
super.encode(bos);
|
||||
byte[] token = bos.toByteArray();
|
||||
System.arraycopy(token, 0, outToken, offset, token.length);
|
||||
return token.length;
|
||||
}
|
||||
|
||||
public byte[] encode() throws IOException, GSSException{
|
||||
// XXX Fine tune this initial size
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(50);
|
||||
encode(bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
}
|
118
jdkSrc/jdk8/sun/security/jgss/krb5/MicToken_v2.java
Normal file
118
jdkSrc/jdk8/sun/security/jgss/krb5/MicToken_v2.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2010, 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 java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* This class represents the new format of GSS MIC tokens, as specified
|
||||
* in RFC 4121
|
||||
*
|
||||
* MIC tokens = { 16-byte token-header | HMAC }
|
||||
* where HMAC is on { plaintext | 16-byte token-header }
|
||||
*
|
||||
* @author Seema Malkani
|
||||
*/
|
||||
|
||||
class MicToken_v2 extends MessageToken_v2 {
|
||||
|
||||
public MicToken_v2(Krb5Context context,
|
||||
byte[] tokenBytes, int tokenOffset, int tokenLen,
|
||||
MessageProp prop) throws GSSException {
|
||||
super(Krb5Token.MIC_ID_v2, context,
|
||||
tokenBytes, tokenOffset, tokenLen, prop);
|
||||
}
|
||||
|
||||
public MicToken_v2(Krb5Context context, InputStream is, MessageProp prop)
|
||||
throws GSSException {
|
||||
super(Krb5Token.MIC_ID_v2, context, is, prop);
|
||||
}
|
||||
|
||||
public void verify(byte[] data, int offset, int len) throws GSSException {
|
||||
if (!verifySign(data, offset, len))
|
||||
throw new GSSException(GSSException.BAD_MIC, -1,
|
||||
"Corrupt checksum or sequence number in MIC token");
|
||||
}
|
||||
|
||||
public void verify(InputStream data) throws GSSException {
|
||||
byte[] dataBytes = null;
|
||||
try {
|
||||
dataBytes = new byte[data.available()];
|
||||
data.read(dataBytes);
|
||||
} catch (IOException e) {
|
||||
// Error reading application data
|
||||
throw new GSSException(GSSException.BAD_MIC, -1,
|
||||
"Corrupt checksum or sequence number in MIC token");
|
||||
}
|
||||
verify(dataBytes, 0, dataBytes.length);
|
||||
}
|
||||
|
||||
public MicToken_v2(Krb5Context context, MessageProp prop,
|
||||
byte[] data, int pos, int len)
|
||||
throws GSSException {
|
||||
super(Krb5Token.MIC_ID_v2, context);
|
||||
|
||||
// debug("Application data to MicToken verify is [" +
|
||||
// getHexBytes(data, pos, len) + "]\n");
|
||||
if (prop == null) prop = new MessageProp(0, false);
|
||||
genSignAndSeqNumber(prop, data, pos, len);
|
||||
}
|
||||
|
||||
public MicToken_v2(Krb5Context context, MessageProp prop, InputStream data)
|
||||
throws GSSException, IOException {
|
||||
|
||||
super(Krb5Token.MIC_ID_v2, context);
|
||||
byte[] dataBytes = new byte[data.available()];
|
||||
data.read(dataBytes);
|
||||
|
||||
// debug("Application data to MicToken cons is [" +
|
||||
// getHexBytes(dataBytes) + "]\n");
|
||||
if (prop == null) prop = new MessageProp(0, false);
|
||||
genSignAndSeqNumber(prop, dataBytes, 0, dataBytes.length);
|
||||
}
|
||||
|
||||
public byte[] encode() throws IOException {
|
||||
// XXX Fine tune this initial size
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(50);
|
||||
encode(bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
public int encode(byte[] outToken, int offset) throws IOException {
|
||||
byte[] token = encode();
|
||||
System.arraycopy(token, 0, outToken, offset, token.length);
|
||||
return token.length;
|
||||
}
|
||||
|
||||
public void encode(OutputStream os) throws IOException {
|
||||
encodeHeader(os);
|
||||
os.write(checksum);
|
||||
}
|
||||
}
|
262
jdkSrc/jdk8/sun/security/jgss/krb5/ServiceCreds.java
Normal file
262
jdkSrc/jdk8/sun/security/jgss/krb5/ServiceCreds.java
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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 javax.security.auth.kerberos.KerberosTicket;
|
||||
import javax.security.auth.kerberos.KerberosKey;
|
||||
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||
import javax.security.auth.kerberos.KeyTab;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.krb5.EncryptionKey;
|
||||
import sun.security.krb5.KrbException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import sun.security.krb5.*;
|
||||
import sun.security.krb5.internal.Krb5;
|
||||
|
||||
/**
|
||||
* Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is
|
||||
* the principal. It can be specified as the serverPrincipal argument
|
||||
* in the getInstance() method, or uses only KerberosPrincipal in the subject.
|
||||
* Otherwise, the creds object is unbound and kp is null.
|
||||
*
|
||||
* The class also encapsulates various secrets, which can be:
|
||||
*
|
||||
* 1. Some KerberosKeys (generated from password)
|
||||
* 2. Some KeyTabs (for a typical service based on keytabs)
|
||||
* 3. A TGT (for S4U2proxy extension or user2user)
|
||||
*
|
||||
* Note that some secrets can coexist. For example, a user2user service
|
||||
* can use its keytab (or keys) if the client can successfully obtain a
|
||||
* normal service ticket, or it can use the TGT (actually, the session key
|
||||
* of the TGT) if the client can only acquire a service ticket
|
||||
* of ENC-TKT-IN-SKEY style.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public final class ServiceCreds {
|
||||
// The principal, or null if unbound
|
||||
private KerberosPrincipal kp;
|
||||
|
||||
// All principals in the subject's princ set
|
||||
private Set<KerberosPrincipal> allPrincs;
|
||||
|
||||
// All private credentials that can be used
|
||||
private List<KeyTab> ktabs;
|
||||
private List<KerberosKey> kk;
|
||||
private KerberosTicket tgt;
|
||||
|
||||
private boolean destroyed;
|
||||
|
||||
private ServiceCreds() {
|
||||
// Make sure this class cannot be instantiated externally.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ServiceCreds object based on info in a Subject for
|
||||
* a given principal name (if specified).
|
||||
* @return the object, or null if there is no private creds for it
|
||||
*/
|
||||
public static ServiceCreds getInstance(
|
||||
Subject subj, String serverPrincipal) {
|
||||
|
||||
ServiceCreds sc = new ServiceCreds();
|
||||
|
||||
sc.allPrincs =
|
||||
subj.getPrincipals(KerberosPrincipal.class);
|
||||
|
||||
// Compatibility. A key implies its own principal
|
||||
for (KerberosKey key: SubjectComber.findMany(
|
||||
subj, serverPrincipal, null, KerberosKey.class)) {
|
||||
sc.allPrincs.add(key.getPrincipal());
|
||||
}
|
||||
|
||||
if (serverPrincipal != null) { // A named principal
|
||||
sc.kp = new KerberosPrincipal(serverPrincipal);
|
||||
} else {
|
||||
// For compatibility reason, we set the name of default principal
|
||||
// to the "only possible" name it can take, which means there is
|
||||
// only one KerberosPrincipal and there is no unbound keytabs
|
||||
if (sc.allPrincs.size() == 1) {
|
||||
boolean hasUnbound = false;
|
||||
for (KeyTab ktab: SubjectComber.findMany(
|
||||
subj, null, null, KeyTab.class)) {
|
||||
if (!ktab.isBound()) {
|
||||
hasUnbound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasUnbound) {
|
||||
sc.kp = sc.allPrincs.iterator().next();
|
||||
serverPrincipal = sc.kp.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sc.ktabs = SubjectComber.findMany(
|
||||
subj, serverPrincipal, null, KeyTab.class);
|
||||
sc.kk = SubjectComber.findMany(
|
||||
subj, serverPrincipal, null, KerberosKey.class);
|
||||
sc.tgt = SubjectComber.find(
|
||||
subj, null, serverPrincipal, KerberosTicket.class);
|
||||
if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
sc.destroyed = false;
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
// can be null
|
||||
public String getName() {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("This object is destroyed");
|
||||
}
|
||||
return kp == null ? null : kp.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets keys for "someone". Used in 2 cases:
|
||||
* 1. By TLS because it needs to get keys before client comes in.
|
||||
* 2. As a fallback in getEKeys() below.
|
||||
* This method can still return an empty array.
|
||||
*/
|
||||
public KerberosKey[] getKKeys() {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("This object is destroyed");
|
||||
}
|
||||
KerberosPrincipal one = kp; // named principal
|
||||
if (one == null && !allPrincs.isEmpty()) { // or, a known principal
|
||||
one = allPrincs.iterator().next();
|
||||
}
|
||||
if (one == null) { // Or, some random one
|
||||
for (KeyTab ktab: ktabs) {
|
||||
// Must be unbound keytab, otherwise, allPrincs is not empty
|
||||
PrincipalName pn =
|
||||
Krb5Util.snapshotFromJavaxKeyTab(ktab).getOneName();
|
||||
if (pn != null) {
|
||||
one = new KerberosPrincipal(pn.getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (one != null) {
|
||||
return getKKeys(one);
|
||||
} else {
|
||||
return new KerberosKey[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get kkeys for a principal,
|
||||
* @param princ the target name initiator requests. Not null.
|
||||
* @return keys for the princ, never null, might be empty
|
||||
*/
|
||||
public KerberosKey[] getKKeys(KerberosPrincipal princ) {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("This object is destroyed");
|
||||
}
|
||||
ArrayList<KerberosKey> keys = new ArrayList<>();
|
||||
if (kp != null && !princ.equals(kp)) { // named principal
|
||||
return new KerberosKey[0];
|
||||
}
|
||||
for (KerberosKey k: kk) {
|
||||
if (k.getPrincipal().equals(princ)) {
|
||||
keys.add(k);
|
||||
}
|
||||
}
|
||||
for (KeyTab ktab: ktabs) {
|
||||
if (ktab.getPrincipal() == null && ktab.isBound()) {
|
||||
// legacy bound keytab. although we don't know who
|
||||
// the bound principal is, it must be in allPrincs
|
||||
if (!allPrincs.contains(princ)) {
|
||||
continue; // skip this legacy bound keytab
|
||||
}
|
||||
}
|
||||
for (KerberosKey k: ktab.getKeys(princ)) {
|
||||
keys.add(k);
|
||||
}
|
||||
}
|
||||
return keys.toArray(new KerberosKey[keys.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets EKeys for a principal.
|
||||
* @param princ the target name initiator requests. Not null.
|
||||
* @return keys for the princ, never null, might be empty
|
||||
*/
|
||||
public EncryptionKey[] getEKeys(PrincipalName princ) {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("This object is destroyed");
|
||||
}
|
||||
KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName()));
|
||||
if (kkeys.length == 0) {
|
||||
// Fallback: old JDK does not perform real name checking. If the
|
||||
// acceptor has host.sun.com but initiator requests for host,
|
||||
// as long as their keys match (i.e. keys for one can decrypt
|
||||
// the other's service ticket), the authentication is OK.
|
||||
// There are real customers depending on this to use different
|
||||
// names for a single service.
|
||||
kkeys = getKKeys();
|
||||
}
|
||||
EncryptionKey[] ekeys = new EncryptionKey[kkeys.length];
|
||||
for (int i=0; i<ekeys.length; i++) {
|
||||
ekeys[i] = new EncryptionKey(
|
||||
kkeys[i].getEncoded(), kkeys[i].getKeyType(),
|
||||
new Integer(kkeys[i].getVersionNumber()));
|
||||
}
|
||||
return ekeys;
|
||||
}
|
||||
|
||||
public Credentials getInitCred() {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("This object is destroyed");
|
||||
}
|
||||
if (tgt == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Krb5Util.ticketToCreds(tgt);
|
||||
} catch (KrbException | IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
// Do not wipe out real keys because they are references to the
|
||||
// priv creds in subject. Just make it useless.
|
||||
destroyed = true;
|
||||
kp = null;
|
||||
ktabs.clear();
|
||||
kk.clear();
|
||||
tgt = null;
|
||||
}
|
||||
}
|
251
jdkSrc/jdk8/sun/security/jgss/krb5/SubjectComber.java
Normal file
251
jdkSrc/jdk8/sun/security/jgss/krb5/SubjectComber.java
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2019, 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 sun.security.krb5.JavaxSecurityAuthKerberosAccess;
|
||||
import sun.security.krb5.KerberosSecrets;
|
||||
|
||||
import javax.security.auth.kerberos.KerberosTicket;
|
||||
import javax.security.auth.kerberos.KerberosKey;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
import java.util.Iterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||
import javax.security.auth.kerberos.KeyTab;
|
||||
|
||||
/**
|
||||
* This utility looks through the current Subject and retrieves private
|
||||
* credentials for the desired client/server principals.
|
||||
*
|
||||
* @author Ram Marti
|
||||
* @since 1.4.2
|
||||
*/
|
||||
|
||||
class SubjectComber {
|
||||
|
||||
private static final boolean DEBUG = Krb5Util.DEBUG;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
private SubjectComber() { // Cannot create one of these
|
||||
}
|
||||
|
||||
static <T> T find(Subject subject, String serverPrincipal,
|
||||
String clientPrincipal, Class<T> credClass) {
|
||||
|
||||
// findAux returns T if oneOnly.
|
||||
return credClass.cast(findAux(subject, serverPrincipal,
|
||||
clientPrincipal, credClass, true));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // findAux returns List<T> if !oneOnly.
|
||||
static <T> List<T> findMany(Subject subject, String serverPrincipal,
|
||||
String clientPrincipal, Class<T> credClass) {
|
||||
|
||||
return (List<T>)findAux(subject, serverPrincipal, clientPrincipal,
|
||||
credClass, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find private credentials for the specified client/server principals
|
||||
* in the subject. Returns null if the subject is null.
|
||||
*
|
||||
* @return the private credentials
|
||||
*/
|
||||
// Returns T if oneOnly and List<T> if !oneOnly.
|
||||
private static <T> Object findAux(Subject subject, String serverPrincipal,
|
||||
String clientPrincipal, Class<T> credClass, boolean oneOnly) {
|
||||
|
||||
if (subject == null) {
|
||||
return null;
|
||||
} else {
|
||||
List<T> answer = (oneOnly ? null : new ArrayList<T>());
|
||||
|
||||
if (credClass == KeyTab.class) {
|
||||
Iterator<KeyTab> iterator =
|
||||
subject.getPrivateCredentials(KeyTab.class).iterator();
|
||||
while (iterator.hasNext()) {
|
||||
KeyTab t = iterator.next();
|
||||
if (serverPrincipal != null && t.isBound()) {
|
||||
KerberosPrincipal name = t.getPrincipal();
|
||||
if (name != null) {
|
||||
if (!serverPrincipal.equals(name.getName())) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// legacy bound keytab. although we don't know who
|
||||
// the bound principal is, it must be in allPrincs
|
||||
boolean found = false;
|
||||
for (KerberosPrincipal princ:
|
||||
subject.getPrincipals(KerberosPrincipal.class)) {
|
||||
if (princ.getName().equals(serverPrincipal)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) continue;
|
||||
}
|
||||
}
|
||||
// Check passed, we can add now
|
||||
if (DEBUG) {
|
||||
System.out.println("Found " + credClass.getSimpleName()
|
||||
+ " " + t);
|
||||
}
|
||||
if (oneOnly) {
|
||||
return t;
|
||||
} else {
|
||||
answer.add(credClass.cast(t));
|
||||
}
|
||||
}
|
||||
} else if (credClass == KerberosKey.class) {
|
||||
// We are looking for credentials for the serverPrincipal
|
||||
Iterator<KerberosKey> iterator =
|
||||
subject.getPrivateCredentials(KerberosKey.class).iterator();
|
||||
while (iterator.hasNext()) {
|
||||
KerberosKey t = iterator.next();
|
||||
String name = t.getPrincipal().getName();
|
||||
if (serverPrincipal == null || serverPrincipal.equals(name)) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Found " +
|
||||
credClass.getSimpleName() + " for " + name);
|
||||
}
|
||||
if (oneOnly) {
|
||||
return t;
|
||||
} else {
|
||||
answer.add(credClass.cast(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (credClass == KerberosTicket.class) {
|
||||
// we are looking for a KerberosTicket credentials
|
||||
// for client-service principal pair
|
||||
Set<Object> pcs = subject.getPrivateCredentials();
|
||||
synchronized (pcs) {
|
||||
Iterator<Object> iterator = pcs.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Object obj = iterator.next();
|
||||
if (!(obj instanceof KerberosTicket)) {
|
||||
continue;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
KerberosTicket ticket = (KerberosTicket)obj;
|
||||
if (DEBUG) {
|
||||
System.out.println("Found ticket for "
|
||||
+ ticket.getClient()
|
||||
+ " to go to "
|
||||
+ ticket.getServer()
|
||||
+ " expiring on "
|
||||
+ ticket.getEndTime());
|
||||
}
|
||||
if (!ticket.isCurrent()) {
|
||||
// let us remove the ticket from the Subject
|
||||
// Note that both TGT and service ticket will be
|
||||
// removed upon expiration
|
||||
if (!subject.isReadOnly()) {
|
||||
iterator.remove();
|
||||
try {
|
||||
ticket.destroy();
|
||||
if (DEBUG) {
|
||||
System.out.println("Removed and destroyed "
|
||||
+ "the expired Ticket \n"
|
||||
+ ticket);
|
||||
|
||||
}
|
||||
} catch (DestroyFailedException dfe) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Expired ticket not" +
|
||||
" detroyed successfully. " + dfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String serverMatch = findServerMatch(serverPrincipal, ticket);
|
||||
if (serverMatch != null) {
|
||||
String clientMatch = findClientMatch(clientPrincipal, ticket);
|
||||
if (clientMatch != null) {
|
||||
if (oneOnly) {
|
||||
return ticket;
|
||||
} else {
|
||||
// Record names so that tickets will
|
||||
// all belong to same principals
|
||||
if (clientPrincipal == null) {
|
||||
clientPrincipal = clientMatch;
|
||||
}
|
||||
if (serverPrincipal == null) {
|
||||
serverPrincipal = serverMatch;
|
||||
}
|
||||
answer.add(credClass.cast(ticket));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
|
||||
private static String findServerMatch(String input, KerberosTicket ticket) {
|
||||
KerberosPrincipal serverAlias = KerberosSecrets
|
||||
.getJavaxSecurityAuthKerberosAccess()
|
||||
.kerberosTicketGetServerAlias(ticket);
|
||||
if (input != null) {
|
||||
return ((serverAlias != null && input.equals(serverAlias.getName())) ||
|
||||
input.equals(ticket.getServer().getName()))
|
||||
? input : null;
|
||||
} else {
|
||||
return serverAlias != null
|
||||
? serverAlias.getName()
|
||||
: ticket.getServer().getName();
|
||||
}
|
||||
}
|
||||
|
||||
private static String findClientMatch(String input, KerberosTicket ticket) {
|
||||
JavaxSecurityAuthKerberosAccess access = KerberosSecrets
|
||||
.getJavaxSecurityAuthKerberosAccess();
|
||||
KerberosPrincipal clientAlias = access.kerberosTicketGetClientAlias(ticket);
|
||||
KerberosTicket proxy = access.kerberosTicketGetProxy(ticket);
|
||||
if (input != null) {
|
||||
return ((clientAlias != null && input.equals(clientAlias.getName())) ||
|
||||
(proxy != null && input.equals(proxy.getClient().getName())) ||
|
||||
(proxy == null && input.equals(ticket.getClient().getName())))
|
||||
? input : null;
|
||||
} else {
|
||||
if (clientAlias != null) {
|
||||
return clientAlias.getName();
|
||||
} else if (proxy != null) {
|
||||
return proxy.getClient().getName();
|
||||
} else {
|
||||
return ticket.getClient().getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
532
jdkSrc/jdk8/sun/security/jgss/krb5/WrapToken.java
Normal file
532
jdkSrc/jdk8/sun/security/jgss/krb5/WrapToken.java
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2010, 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 sun.security.jgss.*;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import sun.security.krb5.Confounder;
|
||||
|
||||
/**
|
||||
* This class represents a token emitted by the GSSContext.wrap()
|
||||
* call. It is a MessageToken except that it also contains plaintext
|
||||
* or encrypted data at the end. A wrapToken has certain other rules
|
||||
* that are peculiar to it and different from a MICToken, which is
|
||||
* another type of MessageToken. All data in a WrapToken is prepended
|
||||
* by a random counfounder of 8 bytes. All data in a WrapToken is
|
||||
* also padded with one to eight bytes where all bytes are equal in
|
||||
* value to the number of bytes being padded. Thus, all application
|
||||
* data is replaced by (confounder || data || padding).
|
||||
*
|
||||
* @author Mayank Upadhyay
|
||||
*/
|
||||
class WrapToken extends MessageToken {
|
||||
/**
|
||||
* The size of the random confounder used in a WrapToken.
|
||||
*/
|
||||
static final int CONFOUNDER_SIZE = 8;
|
||||
|
||||
/*
|
||||
* The padding used with a WrapToken. All data is padded to the
|
||||
* next multiple of 8 bytes, even if its length is already
|
||||
* multiple of 8.
|
||||
* Use this table as a quick way to obtain padding bytes by
|
||||
* indexing it with the number of padding bytes required.
|
||||
*/
|
||||
static final byte[][] pads = {
|
||||
null, // No, no one escapes padding
|
||||
{0x01},
|
||||
{0x02, 0x02},
|
||||
{0x03, 0x03, 0x03},
|
||||
{0x04, 0x04, 0x04, 0x04},
|
||||
{0x05, 0x05, 0x05, 0x05, 0x05},
|
||||
{0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
|
||||
{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07},
|
||||
{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}
|
||||
};
|
||||
|
||||
/*
|
||||
* A token may come in either in an InputStream or as a
|
||||
* byte[]. Store a reference to it in either case and process
|
||||
* it's data only later when getData() is called and
|
||||
* decryption/copying is needed to be done. Note that JCE can
|
||||
* decrypt both from a byte[] and from an InputStream.
|
||||
*/
|
||||
private boolean readTokenFromInputStream = true;
|
||||
private InputStream is = null;
|
||||
private byte[] tokenBytes = null;
|
||||
private int tokenOffset = 0;
|
||||
private int tokenLen = 0;
|
||||
|
||||
/*
|
||||
* Application data may come from an InputStream or from a
|
||||
* byte[]. However, it will always be stored and processed as a
|
||||
* byte[] since
|
||||
* (a) the MessageDigest class only accepts a byte[] as input and
|
||||
* (b) It allows writing to an OuputStream via a CipherOutputStream.
|
||||
*/
|
||||
private byte[] dataBytes = null;
|
||||
private int dataOffset = 0;
|
||||
private int dataLen = 0;
|
||||
|
||||
// the len of the token data: (confounder || data || padding)
|
||||
private int dataSize = 0;
|
||||
|
||||
// Accessed by CipherHelper
|
||||
byte[] confounder = null;
|
||||
byte[] padding = null;
|
||||
|
||||
private boolean privacy = false;
|
||||
|
||||
/**
|
||||
* Constructs a WrapToken from token bytes obtained from the
|
||||
* peer.
|
||||
* @param context the mechanism context associated with this
|
||||
* token
|
||||
* @param tokenBytes the bytes of the token
|
||||
* @param tokenOffset the offset of the token
|
||||
* @param tokenLen the length of the token
|
||||
* @param prop the MessageProp into which characteristics of the
|
||||
* parsed token will be stored.
|
||||
* @throws GSSException if the token is defective
|
||||
*/
|
||||
public WrapToken(Krb5Context context,
|
||||
byte[] tokenBytes, int tokenOffset, int tokenLen,
|
||||
MessageProp prop) throws GSSException {
|
||||
|
||||
// Just parse the MessageToken part first
|
||||
super(Krb5Token.WRAP_ID, context,
|
||||
tokenBytes, tokenOffset, tokenLen, prop);
|
||||
|
||||
this.readTokenFromInputStream = false;
|
||||
|
||||
// Will need the token bytes again when extracting data
|
||||
this.tokenBytes = tokenBytes;
|
||||
this.tokenOffset = tokenOffset;
|
||||
this.tokenLen = tokenLen;
|
||||
this.privacy = prop.getPrivacy();
|
||||
dataSize =
|
||||
getGSSHeader().getMechTokenLength() - getKrb5TokenSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a WrapToken from token bytes read on the fly from
|
||||
* an InputStream.
|
||||
* @param context the mechanism context associated with this
|
||||
* token
|
||||
* @param is the InputStream containing the token bytes
|
||||
* @param prop the MessageProp into which characteristics of the
|
||||
* parsed token will be stored.
|
||||
* @throws GSSException if the token is defective or if there is
|
||||
* a problem reading from the InputStream
|
||||
*/
|
||||
public WrapToken(Krb5Context context,
|
||||
InputStream is, MessageProp prop)
|
||||
throws GSSException {
|
||||
|
||||
// Just parse the MessageToken part first
|
||||
super(Krb5Token.WRAP_ID, context, is, prop);
|
||||
|
||||
// Will need the token bytes again when extracting data
|
||||
this.is = is;
|
||||
this.privacy = prop.getPrivacy();
|
||||
/*
|
||||
debug("WrapToken Cons: gssHeader.getMechTokenLength=" +
|
||||
getGSSHeader().getMechTokenLength());
|
||||
debug("\n token size="
|
||||
+ getTokenSize());
|
||||
*/
|
||||
|
||||
dataSize =
|
||||
getGSSHeader().getMechTokenLength() - getTokenSize();
|
||||
// debug("\n dataSize=" + dataSize);
|
||||
// debug("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the application data that was transmitted in this
|
||||
* WrapToken.
|
||||
* @return a byte array containing the application data
|
||||
* @throws GSSException if an error occurs while decrypting any
|
||||
* cipher text and checking for validity
|
||||
*/
|
||||
public byte[] getData() throws GSSException {
|
||||
|
||||
byte[] temp = new byte[dataSize];
|
||||
getData(temp, 0);
|
||||
|
||||
// Remove the confounder and the padding
|
||||
byte[] retVal = new byte[dataSize - confounder.length -
|
||||
padding.length];
|
||||
System.arraycopy(temp, 0, retVal, 0, retVal.length);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the application data that was transmitted in this
|
||||
* WrapToken, writing it into an application provided output
|
||||
* array.
|
||||
* @param dataBuf the output buffer into which the data must be
|
||||
* written
|
||||
* @param dataBufOffset the offset at which to write the data
|
||||
* @return the size of the data written
|
||||
* @throws GSSException if an error occurs while decrypting any
|
||||
* cipher text and checking for validity
|
||||
*/
|
||||
public int getData(byte[] dataBuf, int dataBufOffset)
|
||||
throws GSSException {
|
||||
|
||||
if (readTokenFromInputStream)
|
||||
getDataFromStream(dataBuf, dataBufOffset);
|
||||
else
|
||||
getDataFromBuffer(dataBuf, dataBufOffset);
|
||||
|
||||
return (dataSize - confounder.length - padding.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper routine to obtain the application data transmitted in
|
||||
* this WrapToken. It is called if the WrapToken was constructed
|
||||
* with a byte array as input.
|
||||
* @param dataBuf the output buffer into which the data must be
|
||||
* written
|
||||
* @param dataBufOffset the offset at which to write the data
|
||||
* @throws GSSException if an error occurs while decrypting any
|
||||
* cipher text and checking for validity
|
||||
*/
|
||||
private void getDataFromBuffer(byte[] dataBuf, int dataBufOffset)
|
||||
throws GSSException {
|
||||
|
||||
GSSHeader gssHeader = getGSSHeader();
|
||||
int dataPos = tokenOffset +
|
||||
gssHeader.getLength() + getTokenSize();
|
||||
|
||||
if (dataPos + dataSize > tokenOffset + tokenLen)
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
"Insufficient data in "
|
||||
+ getTokenName(getTokenId()));
|
||||
|
||||
// debug("WrapToken cons: data is token is [" +
|
||||
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
|
||||
|
||||
confounder = new byte[CONFOUNDER_SIZE];
|
||||
|
||||
// Do decryption if this token was privacy protected.
|
||||
|
||||
if (privacy) {
|
||||
cipherHelper.decryptData(this,
|
||||
tokenBytes, dataPos, dataSize, dataBuf, dataBufOffset);
|
||||
/*
|
||||
debug("\t\tDecrypted data is [" +
|
||||
getHexBytes(confounder) + " " +
|
||||
getHexBytes(dataBuf, dataBufOffset,
|
||||
dataSize - CONFOUNDER_SIZE - padding.length) +
|
||||
getHexBytes(padding) +
|
||||
"]\n");
|
||||
*/
|
||||
|
||||
} else {
|
||||
|
||||
// Token data is in cleartext
|
||||
// debug("\t\tNo encryption was performed by peer.\n");
|
||||
System.arraycopy(tokenBytes, dataPos,
|
||||
confounder, 0, CONFOUNDER_SIZE);
|
||||
int padSize = tokenBytes[dataPos + dataSize - 1];
|
||||
if (padSize < 0)
|
||||
padSize = 0;
|
||||
if (padSize > 8)
|
||||
padSize %= 8;
|
||||
|
||||
padding = pads[padSize];
|
||||
// debug("\t\tPadding applied was: " + padSize + "\n");
|
||||
|
||||
System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
|
||||
dataBuf, dataBufOffset, dataSize -
|
||||
CONFOUNDER_SIZE - padSize);
|
||||
|
||||
// byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize];
|
||||
// System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
|
||||
// debugbuf, 0, debugbuf.length);
|
||||
// debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length));
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure sign and sequence number are not corrupt
|
||||
*/
|
||||
|
||||
if (!verifySignAndSeqNumber(confounder,
|
||||
dataBuf, dataBufOffset,
|
||||
dataSize - CONFOUNDER_SIZE
|
||||
- padding.length,
|
||||
padding))
|
||||
throw new GSSException(GSSException.BAD_MIC, -1,
|
||||
"Corrupt checksum or sequence number in Wrap token");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper routine to obtain the application data transmitted in
|
||||
* this WrapToken. It is called if the WrapToken was constructed
|
||||
* with an Inputstream.
|
||||
* @param dataBuf the output buffer into which the data must be
|
||||
* written
|
||||
* @param dataBufOffset the offset at which to write the data
|
||||
* @throws GSSException if an error occurs while decrypting any
|
||||
* cipher text and checking for validity
|
||||
*/
|
||||
private void getDataFromStream(byte[] dataBuf, int dataBufOffset)
|
||||
throws GSSException {
|
||||
|
||||
GSSHeader gssHeader = getGSSHeader();
|
||||
|
||||
// Don't check the token length. Data will be read on demand from
|
||||
// the InputStream.
|
||||
|
||||
// debug("WrapToken cons: data will be read from InputStream.\n");
|
||||
|
||||
confounder = new byte[CONFOUNDER_SIZE];
|
||||
|
||||
try {
|
||||
|
||||
// Do decryption if this token was privacy protected.
|
||||
|
||||
if (privacy) {
|
||||
cipherHelper.decryptData(this, is, dataSize,
|
||||
dataBuf, dataBufOffset);
|
||||
|
||||
// debug("\t\tDecrypted data is [" +
|
||||
// getHexBytes(confounder) + " " +
|
||||
// getHexBytes(dataBuf, dataBufOffset,
|
||||
// dataSize - CONFOUNDER_SIZE - padding.length) +
|
||||
// getHexBytes(padding) +
|
||||
// "]\n");
|
||||
|
||||
} else {
|
||||
|
||||
// Token data is in cleartext
|
||||
// debug("\t\tNo encryption was performed by peer.\n");
|
||||
readFully(is, confounder);
|
||||
|
||||
if (cipherHelper.isArcFour()) {
|
||||
padding = pads[1];
|
||||
readFully(is, dataBuf, dataBufOffset, dataSize-CONFOUNDER_SIZE-1);
|
||||
} else {
|
||||
// Data is always a multiple of 8 with this GSS Mech
|
||||
// Copy all but last block as they are
|
||||
int numBlocks = (dataSize - CONFOUNDER_SIZE)/8 - 1;
|
||||
int offset = dataBufOffset;
|
||||
for (int i = 0; i < numBlocks; i++) {
|
||||
readFully(is, dataBuf, offset, 8);
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
byte[] finalBlock = new byte[8];
|
||||
readFully(is, finalBlock);
|
||||
|
||||
int padSize = finalBlock[7];
|
||||
padding = pads[padSize];
|
||||
|
||||
// debug("\t\tPadding applied was: " + padSize + "\n");
|
||||
System.arraycopy(finalBlock, 0, dataBuf, offset,
|
||||
finalBlock.length - padSize);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
|
||||
getTokenName(getTokenId())
|
||||
+ ": " + e.getMessage());
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure sign and sequence number are not corrupt
|
||||
*/
|
||||
|
||||
if (!verifySignAndSeqNumber(confounder,
|
||||
dataBuf, dataBufOffset,
|
||||
dataSize - CONFOUNDER_SIZE
|
||||
- padding.length,
|
||||
padding))
|
||||
throw new GSSException(GSSException.BAD_MIC, -1,
|
||||
"Corrupt checksum or sequence number in Wrap token");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper routine to pick the right padding for a certain length
|
||||
* of application data. Every application message has some
|
||||
* padding between 1 and 8 bytes.
|
||||
* @param len the length of the application data
|
||||
* @return the padding to be applied
|
||||
*/
|
||||
private byte[] getPadding(int len) {
|
||||
int padSize = 0;
|
||||
// For RC4-HMAC, all padding is rounded up to 1 byte.
|
||||
// One byte is needed to say that there is 1 byte of padding.
|
||||
if (cipherHelper.isArcFour()) {
|
||||
padSize = 1;
|
||||
} else {
|
||||
padSize = len % 8;
|
||||
padSize = 8 - padSize;
|
||||
}
|
||||
return pads[padSize];
|
||||
}
|
||||
|
||||
public WrapToken(Krb5Context context, MessageProp prop,
|
||||
byte[] dataBytes, int dataOffset, int dataLen)
|
||||
throws GSSException {
|
||||
|
||||
super(Krb5Token.WRAP_ID, context);
|
||||
|
||||
confounder = Confounder.bytes(CONFOUNDER_SIZE);
|
||||
|
||||
padding = getPadding(dataLen);
|
||||
dataSize = confounder.length + dataLen + padding.length;
|
||||
this.dataBytes = dataBytes;
|
||||
this.dataOffset = dataOffset;
|
||||
this.dataLen = dataLen;
|
||||
|
||||
/*
|
||||
debug("\nWrapToken cons: data to wrap is [" +
|
||||
getHexBytes(confounder) + " " +
|
||||
getHexBytes(dataBytes, dataOffset, dataLen) + " " +
|
||||
// padding is never null for Wrap
|
||||
getHexBytes(padding) + "]\n");
|
||||
*/
|
||||
|
||||
genSignAndSeqNumber(prop,
|
||||
confounder,
|
||||
dataBytes, dataOffset, dataLen,
|
||||
padding);
|
||||
|
||||
/*
|
||||
* If the application decides to ask for privacy when the context
|
||||
* did not negotiate for it, do not provide it. The peer might not
|
||||
* have support for it. The app will realize this with a call to
|
||||
* pop.getPrivacy() after wrap().
|
||||
*/
|
||||
if (!context.getConfState())
|
||||
prop.setPrivacy(false);
|
||||
|
||||
privacy = prop.getPrivacy();
|
||||
}
|
||||
|
||||
public void encode(OutputStream os) throws IOException, GSSException {
|
||||
|
||||
super.encode(os);
|
||||
|
||||
// debug("Writing data: [");
|
||||
if (!privacy) {
|
||||
|
||||
// debug(getHexBytes(confounder, confounder.length));
|
||||
os.write(confounder);
|
||||
|
||||
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
|
||||
os.write(dataBytes, dataOffset, dataLen);
|
||||
|
||||
// debug(" " + getHexBytes(padding, padding.length));
|
||||
os.write(padding);
|
||||
|
||||
} else {
|
||||
|
||||
cipherHelper.encryptData(this, confounder,
|
||||
dataBytes, dataOffset, dataLen, padding, os);
|
||||
}
|
||||
// debug("]\n");
|
||||
}
|
||||
|
||||
public byte[] encode() throws IOException, GSSException {
|
||||
// XXX Fine tune this initial size
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(dataSize + 50);
|
||||
encode(bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
public int encode(byte[] outToken, int offset)
|
||||
throws IOException, GSSException {
|
||||
|
||||
// Token header is small
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
super.encode(bos);
|
||||
byte[] header = bos.toByteArray();
|
||||
System.arraycopy(header, 0, outToken, offset, header.length);
|
||||
offset += header.length;
|
||||
|
||||
// debug("WrapToken.encode: Writing data: [");
|
||||
if (!privacy) {
|
||||
|
||||
// debug(getHexBytes(confounder, confounder.length));
|
||||
System.arraycopy(confounder, 0, outToken, offset,
|
||||
confounder.length);
|
||||
offset += confounder.length;
|
||||
|
||||
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
|
||||
System.arraycopy(dataBytes, dataOffset, outToken, offset,
|
||||
dataLen);
|
||||
offset += dataLen;
|
||||
|
||||
// debug(" " + getHexBytes(padding, padding.length));
|
||||
System.arraycopy(padding, 0, outToken, offset, padding.length);
|
||||
|
||||
} else {
|
||||
|
||||
cipherHelper.encryptData(this, confounder, dataBytes,
|
||||
dataOffset, dataLen, padding, outToken, offset);
|
||||
|
||||
// debug(getHexBytes(outToken, offset, dataSize));
|
||||
}
|
||||
|
||||
// debug("]\n");
|
||||
|
||||
// %%% assume that plaintext length == ciphertext len
|
||||
return (header.length + confounder.length + dataLen + padding.length);
|
||||
|
||||
}
|
||||
|
||||
protected int getKrb5TokenSize() throws GSSException {
|
||||
return (getTokenSize() + dataSize);
|
||||
}
|
||||
|
||||
protected int getSealAlg(boolean conf, int qop) throws GSSException {
|
||||
if (!conf) {
|
||||
return SEAL_ALG_NONE;
|
||||
}
|
||||
|
||||
// ignore QOP
|
||||
return cipherHelper.getSealAlg();
|
||||
}
|
||||
|
||||
// This implementation is way too conservative. And it certainly
|
||||
// doesn't return the maximum limit.
|
||||
static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,
|
||||
CipherHelper ch) throws GSSException {
|
||||
return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -
|
||||
(getTokenSize(ch) + CONFOUNDER_SIZE) - 8); /* safety */
|
||||
}
|
||||
|
||||
}
|
231
jdkSrc/jdk8/sun/security/jgss/krb5/WrapToken_v2.java
Normal file
231
jdkSrc/jdk8/sun/security/jgss/krb5/WrapToken_v2.java
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2010, 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 sun.security.jgss.*;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
import sun.security.krb5.Confounder;
|
||||
|
||||
/**
|
||||
* This class represents the new format of GSS tokens, as specified in RFC
|
||||
* 4121, emitted by the GSSContext.wrap() call. It is a MessageToken except
|
||||
* that it also contains plaintext or encrypted data at the end. A WrapToken
|
||||
* has certain other rules that are peculiar to it and different from a
|
||||
* MICToken, which is another type of MessageToken. All data in a WrapToken is
|
||||
* prepended by a random confounder of 16 bytes. Thus, all application data
|
||||
* is replaced by (confounder || data || tokenHeader || checksum).
|
||||
*
|
||||
* @author Seema Malkani
|
||||
*/
|
||||
class WrapToken_v2 extends MessageToken_v2 {
|
||||
|
||||
// Accessed by CipherHelper
|
||||
byte[] confounder = null;
|
||||
|
||||
private final boolean privacy;
|
||||
|
||||
/**
|
||||
* Constructs a WrapToken from token bytes obtained from the
|
||||
* peer.
|
||||
* @param context the mechanism context associated with this
|
||||
* token
|
||||
* @param tokenBytes the bytes of the token
|
||||
* @param tokenOffset the offset of the token
|
||||
* @param tokenLen the length of the token
|
||||
* @param prop the MessageProp into which characteristics of the
|
||||
* parsed token will be stored.
|
||||
* @throws GSSException if the token is defective
|
||||
*/
|
||||
public WrapToken_v2(Krb5Context context,
|
||||
byte[] tokenBytes, int tokenOffset, int tokenLen,
|
||||
MessageProp prop) throws GSSException {
|
||||
|
||||
super(Krb5Token.WRAP_ID_v2, context,
|
||||
tokenBytes, tokenOffset, tokenLen, prop);
|
||||
this.privacy = prop.getPrivacy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a WrapToken from token bytes read on the fly from
|
||||
* an InputStream.
|
||||
* @param context the mechanism context associated with this
|
||||
* token
|
||||
* @param is the InputStream containing the token bytes
|
||||
* @param prop the MessageProp into which characteristics of the
|
||||
* parsed token will be stored.
|
||||
* @throws GSSException if the token is defective or if there is
|
||||
* a problem reading from the InputStream
|
||||
*/
|
||||
public WrapToken_v2(Krb5Context context,
|
||||
InputStream is, MessageProp prop)
|
||||
throws GSSException {
|
||||
|
||||
super(Krb5Token.WRAP_ID_v2, context, is, prop);
|
||||
this.privacy = prop.getPrivacy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the application data that was transmitted in this
|
||||
* WrapToken.
|
||||
* @return a byte array containing the application data
|
||||
* @throws GSSException if an error occurs while decrypting any
|
||||
* cipher text and checking for validity
|
||||
*/
|
||||
public byte[] getData() throws GSSException {
|
||||
|
||||
byte[] temp = new byte[tokenDataLen];
|
||||
int len = getData(temp, 0);
|
||||
return Arrays.copyOf(temp, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the application data that was transmitted in this
|
||||
* WrapToken, writing it into an application provided output
|
||||
* array.
|
||||
* @param dataBuf the output buffer into which the data must be
|
||||
* written
|
||||
* @param dataBufOffset the offset at which to write the data
|
||||
* @return the size of the data written
|
||||
* @throws GSSException if an error occurs while decrypting any
|
||||
* cipher text and checking for validity
|
||||
*/
|
||||
public int getData(byte[] dataBuf, int dataBufOffset)
|
||||
throws GSSException {
|
||||
|
||||
// debug("WrapToken cons: data is token is [" +
|
||||
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
|
||||
|
||||
// Do decryption if this token was privacy protected.
|
||||
if (privacy) {
|
||||
|
||||
// decrypt data
|
||||
cipherHelper.decryptData(this, tokenData, 0, tokenDataLen,
|
||||
dataBuf, dataBufOffset, getKeyUsage());
|
||||
|
||||
return tokenDataLen - CONFOUNDER_SIZE -
|
||||
TOKEN_HEADER_SIZE - cipherHelper.getChecksumLength();
|
||||
} else {
|
||||
|
||||
// Token data is in cleartext
|
||||
// debug("\t\tNo encryption was performed by peer.\n");
|
||||
|
||||
// data
|
||||
int data_length = tokenDataLen - cipherHelper.getChecksumLength();
|
||||
System.arraycopy(tokenData, 0,
|
||||
dataBuf, dataBufOffset,
|
||||
data_length);
|
||||
// debug("\t\tData is: " + getHexBytes(dataBuf, data_length));
|
||||
|
||||
/*
|
||||
* Make sure checksum is not corrupt
|
||||
*/
|
||||
if (!verifySign(dataBuf, dataBufOffset, data_length)) {
|
||||
throw new GSSException(GSSException.BAD_MIC, -1,
|
||||
"Corrupt checksum in Wrap token");
|
||||
}
|
||||
return data_length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a WrapToken_v2 object
|
||||
*/
|
||||
public WrapToken_v2(Krb5Context context, MessageProp prop,
|
||||
byte[] dataBytes, int dataOffset, int dataLen)
|
||||
throws GSSException {
|
||||
|
||||
super(Krb5Token.WRAP_ID_v2, context);
|
||||
|
||||
confounder = Confounder.bytes(CONFOUNDER_SIZE);
|
||||
|
||||
// debug("\nWrapToken cons: data to wrap is [" +
|
||||
// getHexBytes(confounder) + " " +
|
||||
// getHexBytes(dataBytes, dataOffset, dataLen) + "]\n");
|
||||
|
||||
genSignAndSeqNumber(prop, dataBytes, dataOffset, dataLen);
|
||||
|
||||
/*
|
||||
* If the application decides to ask for privacy when the context
|
||||
* did not negotiate for it, do not provide it. The peer might not
|
||||
* have support for it. The app will realize this with a call to
|
||||
* pop.getPrivacy() after wrap().
|
||||
*/
|
||||
if (!context.getConfState())
|
||||
prop.setPrivacy(false);
|
||||
|
||||
privacy = prop.getPrivacy();
|
||||
|
||||
if (!privacy) {
|
||||
// Wrap Tokens (without confidentiality) =
|
||||
// { 16 byte token_header | plaintext | 12-byte HMAC }
|
||||
// where HMAC is on { plaintext | token_header }
|
||||
|
||||
tokenData = new byte[dataLen + checksum.length];
|
||||
System.arraycopy(dataBytes, dataOffset, tokenData, 0, dataLen);
|
||||
System.arraycopy(checksum, 0, tokenData, dataLen, checksum.length);
|
||||
} else {
|
||||
// Wrap Tokens (with confidentiality) =
|
||||
// { 16 byte token_header |
|
||||
// Encrypt(16-byte confounder | plaintext | token_header) |
|
||||
// 12-byte HMAC }
|
||||
|
||||
tokenData = cipherHelper.encryptData(this, confounder, getTokenHeader(),
|
||||
dataBytes, dataOffset, dataLen, getKeyUsage());
|
||||
}
|
||||
}
|
||||
|
||||
public void encode(OutputStream os) throws IOException {
|
||||
encodeHeader(os);
|
||||
os.write(tokenData);
|
||||
}
|
||||
|
||||
public byte[] encode() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(
|
||||
MessageToken_v2.TOKEN_HEADER_SIZE + tokenData.length);
|
||||
encode(bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
public int encode(byte[] outToken, int offset) throws IOException {
|
||||
byte[] token = encode();
|
||||
System.arraycopy(token, 0, outToken, offset, token.length);
|
||||
return token.length;
|
||||
}
|
||||
|
||||
// This implementation is way to conservative. And it certainly
|
||||
// doesn't return the maximum limit.
|
||||
static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,
|
||||
CipherHelper ch) throws GSSException {
|
||||
return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -
|
||||
(TOKEN_HEADER_SIZE + ch.getChecksumLength() + CONFOUNDER_SIZE)
|
||||
- 8 /* safety */);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user