8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2005, 2016, 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.net.www.protocol.http.spnego;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.util.Arrays;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import sun.net.www.protocol.http.HttpCallerInfo;
/**
* @since 1.6
* Special callback handler used in JGSS for the HttpCaller.
*/
public class NegotiateCallbackHandler implements CallbackHandler {
private String username;
private char[] password;
/**
* Authenticator asks for username and password in a single prompt,
* but CallbackHandler checks one by one. So, no matter which callback
* gets handled first, make sure Authenticator is only called once.
*/
private boolean answered;
private final HttpCallerInfo hci;
public NegotiateCallbackHandler(HttpCallerInfo hci) {
this.hci = hci;
}
private void getAnswer() {
if (!answered) {
answered = true;
PasswordAuthentication passAuth =
Authenticator.requestPasswordAuthentication(
hci.authenticator,
hci.host, hci.addr, hci.port, hci.protocol,
hci.prompt, hci.scheme, hci.url, hci.authType);
/**
* To be compatible with existing callback handler implementations,
* when the underlying Authenticator is canceled, username and
* password are assigned null. No exception is thrown.
*/
if (passAuth != null) {
username = passAuth.getUserName();
password = passAuth.getPassword();
}
}
}
public void handle(Callback[] callbacks) throws
UnsupportedCallbackException, IOException {
for (int i=0; i<callbacks.length; i++) {
Callback callBack = callbacks[i];
if (callBack instanceof NameCallback) {
getAnswer();
((NameCallback)callBack).setName(username);
} else if (callBack instanceof PasswordCallback) {
getAnswer();
((PasswordCallback)callBack).setPassword(password);
if (password != null) Arrays.fill(password, ' ');
} else {
throw new UnsupportedCallbackException(callBack,
"Call back not supported");
}
}
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2005, 2009, 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.net.www.protocol.http.spnego;
import java.io.IOException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import sun.net.www.protocol.http.HttpCallerInfo;
import sun.net.www.protocol.http.Negotiator;
import sun.security.jgss.GSSManagerImpl;
import sun.security.jgss.GSSContextImpl;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.HttpCaller;
/**
* This class encapsulates all JAAS and JGSS API calls in a separate class
* outside NegotiateAuthentication.java so that J2SE build can go smoothly
* without the presence of it.
*
* @author weijun.wang@sun.com
* @since 1.6
*/
public class NegotiatorImpl extends Negotiator {
private static final boolean DEBUG =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction("sun.security.krb5.debug"));
private GSSContext context;
private byte[] oneToken;
/**
* Initialize the object, which includes:<ul>
* <li>Find out what GSS mechanism to use from the system property
* <code>http.negotiate.mechanism.oid</code>, defaults SPNEGO
* <li>Creating the GSSName for the target host, "HTTP/"+hostname
* <li>Creating GSSContext
* <li>A first call to initSecContext</ul>
*/
private void init(HttpCallerInfo hci) throws GSSException {
final Oid oid;
if (hci.scheme.equalsIgnoreCase("Kerberos")) {
// we can only use Kerberos mech when the scheme is kerberos
oid = GSSUtil.GSS_KRB5_MECH_OID;
} else {
String pref = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<String>() {
public String run() {
return System.getProperty(
"http.auth.preference",
"spnego");
}
});
if (pref.equalsIgnoreCase("kerberos")) {
oid = GSSUtil.GSS_KRB5_MECH_OID;
} else {
// currently there is no 3rd mech we can use
oid = GSSUtil.GSS_SPNEGO_MECH_OID;
}
}
GSSManagerImpl manager = new GSSManagerImpl(
new HttpCaller(hci));
// RFC 4559 4.1 uses uppercase service name "HTTP".
// RFC 4120 6.2.1 demands the host be lowercase
String peerName = "HTTP@" + hci.host.toLowerCase();
GSSName serverName = manager.createName(peerName,
GSSName.NT_HOSTBASED_SERVICE);
context = manager.createContext(serverName,
oid,
null,
GSSContext.DEFAULT_LIFETIME);
// Always respect delegation policy in HTTP/SPNEGO.
if (context instanceof GSSContextImpl) {
((GSSContextImpl)context).requestDelegPolicy(true);
}
oneToken = context.initSecContext(new byte[0], 0, 0);
}
/**
* Constructor
* @throws java.io.IOException If negotiator cannot be constructed
*/
public NegotiatorImpl(HttpCallerInfo hci) throws IOException {
try {
init(hci);
} catch (GSSException e) {
if (DEBUG) {
System.out.println("Negotiate support not initiated, will " +
"fallback to other scheme if allowed. Reason:");
e.printStackTrace();
}
IOException ioe = new IOException("Negotiate support not initiated");
ioe.initCause(e);
throw ioe;
}
}
/**
* Return the first token of GSS, in SPNEGO, it's called NegTokenInit
* @return the first token
*/
@Override
public byte[] firstToken() {
return oneToken;
}
/**
* Return the rest tokens of GSS, in SPNEGO, it's called NegTokenTarg
* @param token the token received from server
* @return the next token
* @throws java.io.IOException if the token cannot be created successfully
*/
@Override
public byte[] nextToken(byte[] token) throws IOException {
try {
return context.initSecContext(token, 0, token.length);
} catch (GSSException e) {
if (DEBUG) {
System.out.println("Negotiate support cannot continue. Reason:");
e.printStackTrace();
}
IOException ioe = new IOException("Negotiate support cannot continue");
ioe.initCause(e);
throw ioe;
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2009, 2012, 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;
/**
* Denotes what client is calling the JGSS-API. The object can be sent deep
* into the mechanism level so that special actions can be performed for
* different callers.
*/
public class GSSCaller {
public static final GSSCaller CALLER_UNKNOWN = new GSSCaller("UNKNOWN");
public static final GSSCaller CALLER_INITIATE = new GSSCaller("INITIATE");
public static final GSSCaller CALLER_ACCEPT = new GSSCaller("ACCEPT");
public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller("SSL_CLIENT");
public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller("SSL_SERVER");
private String name;
GSSCaller(String s) {
name = s;
}
@Override
public String toString() {
return "GSSCaller{" + name + '}';
}
}

View file

@ -0,0 +1,671 @@
/*
* Copyright (c) 2000, 2015, 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;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.util.ObjectIdentifier;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* This class represents the JGSS security context and its associated
* operations. JGSS security contexts are established between
* peers using locally established credentials. Multiple contexts
* may exist simultaneously between a pair of peers, using the same
* or different set of credentials. The JGSS is independent of
* the underlying transport protocols and depends on its callers to
* transport the tokens between peers.
* <p>
* The context object can be thought of as having 3 implicit states:
* before it is established, during its context establishment, and
* after a fully established context exists.
* <p>
* Before the context establishment phase is initiated, the context
* initiator may request specific characteristics desired of the
* established context. These can be set using the set methods. After the
* context is established, the caller can check the actual characteristic
* and services offered by the context using the query methods.
* <p>
* The context establishment phase begins with the first call to the
* initSecContext method by the context initiator. During this phase the
* initSecContext and acceptSecContext methods will produce GSS-API
* authentication tokens which the calling application needs to send to its
* peer. The initSecContext and acceptSecContext methods may
* return a CONTINUE_NEEDED code which indicates that a token is needed
* from its peer in order to continue the context establishment phase. A
* return code of COMPLETE signals that the local end of the context is
* established. This may still require that a token be sent to the peer,
* depending if one is produced by GSS-API. The isEstablished method can
* also be used to determine if the local end of the context has been
* fully established. During the context establishment phase, the
* isProtReady method may be called to determine if the context can be
* used for the per-message operations. This allows implementation to
* use per-message operations on contexts which aren't fully established.
* <p>
* After the context has been established or the isProtReady method
* returns "true", the query routines can be invoked to determine the actual
* characteristics and services of the established context. The
* application can also start using the per-message methods of wrap and
* getMIC to obtain cryptographic operations on application supplied data.
* <p>
* When the context is no longer needed, the application should call
* dispose to release any system resources the context may be using.
* <DL><DT><B>RFC 2078</b>
* <DD>This class corresponds to the context level calls together with
* the per message calls of RFC 2078. The gss_init_sec_context and
* gss_accept_sec_context calls have been made simpler by only taking
* required parameters. The context can have its properties set before
* the first call to initSecContext. The supplementary status codes for the
* per-message operations are returned in an instance of the MessageProp
* class, which is used as an argument in these calls.</dl>
*/
public class GSSContextImpl implements GSSContext {
private GSSManagerImpl gssManager;
private boolean initiator;
// private flags for the context state
private static final int PRE_INIT = 1;
private static final int IN_PROGRESS = 2;
private static final int READY = 3;
private static final int DELETED = 4;
// instance variables
private int currentState = PRE_INIT;
private GSSContextSpi mechCtxt = null;
private Oid mechOid = null;
private ObjectIdentifier objId = null;
private GSSCredentialImpl myCred = null;
private GSSNameImpl srcName = null;
private GSSNameImpl targName = null;
private int reqLifetime = INDEFINITE_LIFETIME;
private ChannelBinding channelBindings = null;
private boolean reqConfState = true;
private boolean reqIntegState = true;
private boolean reqMutualAuthState = true;
private boolean reqReplayDetState = true;
private boolean reqSequenceDetState = true;
private boolean reqCredDelegState = false;
private boolean reqAnonState = false;
private boolean reqDelegPolicyState = false;
public GSSContextImpl() {
// Useless
}
// Used by new ExtendedGSSContext.ExtendedGSSContextImpl(ctxt)
protected GSSContextImpl(GSSContextImpl src) {
for (Field f: GSSContextImpl.class.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) {
try {
f.set(this, f.get(src));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
/**
* Creates a GSSContextImp on the context initiator's side.
*/
public GSSContextImpl(GSSManagerImpl gssManager, GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException {
if ((peer == null) || !(peer instanceof GSSNameImpl)) {
throw new GSSException(GSSException.BAD_NAME);
}
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
this.gssManager = gssManager;
this.myCred = (GSSCredentialImpl) myCred; // XXX Check first
reqLifetime = lifetime;
targName = (GSSNameImpl)peer;
this.mechOid = mech;
initiator = true;
}
/**
* Creates a GSSContextImpl on the context acceptor's side.
*/
public GSSContextImpl(GSSManagerImpl gssManager, GSSCredential myCred)
throws GSSException {
this.gssManager = gssManager;
this.myCred = (GSSCredentialImpl) myCred; // XXX Check first
initiator = false;
}
/**
* Creates a GSSContextImpl out of a previously exported
* GSSContext.
*
* @see #isTransferable
*/
public GSSContextImpl(GSSManagerImpl gssManager, byte[] interProcessToken)
throws GSSException {
this.gssManager = gssManager;
mechCtxt = gssManager.getMechanismContext(interProcessToken);
initiator = mechCtxt.isInitiator();
this.mechOid = mechCtxt.getMech();
}
public byte[] initSecContext(byte inputBuf[], int offset, int len)
throws GSSException {
/*
* Size of ByteArrayOutputStream will double each time that extra
* bytes are to be written. Usually, without delegation, a GSS
* initial token containing the Kerberos AP-REQ is between 400 and
* 600 bytes.
*/
ByteArrayOutputStream bos = new ByteArrayOutputStream(600);
ByteArrayInputStream bin =
new ByteArrayInputStream(inputBuf, offset, len);
int size = initSecContext(bin, bos);
return (size == 0? null : bos.toByteArray());
}
public int initSecContext(InputStream inStream,
OutputStream outStream) throws GSSException {
if (mechCtxt != null && currentState != IN_PROGRESS) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Illegal call to initSecContext");
}
GSSHeader gssHeader = null;
int inTokenLen = -1;
GSSCredentialSpi credElement = null;
boolean firstToken = false;
try {
if (mechCtxt == null) {
if (myCred != null) {
try {
credElement = myCred.getElement(mechOid, true);
} catch (GSSException ge) {
if (GSSUtil.isSpNegoMech(mechOid) &&
ge.getMajor() == GSSException.NO_CRED) {
credElement = myCred.getElement
(myCred.getMechs()[0], true);
} else {
throw ge;
}
}
}
GSSNameSpi nameElement = targName.getElement(mechOid);
mechCtxt = gssManager.getMechanismContext(nameElement,
credElement,
reqLifetime,
mechOid);
mechCtxt.requestConf(reqConfState);
mechCtxt.requestInteg(reqIntegState);
mechCtxt.requestCredDeleg(reqCredDelegState);
mechCtxt.requestMutualAuth(reqMutualAuthState);
mechCtxt.requestReplayDet(reqReplayDetState);
mechCtxt.requestSequenceDet(reqSequenceDetState);
mechCtxt.requestAnonymity(reqAnonState);
mechCtxt.setChannelBinding(channelBindings);
mechCtxt.requestDelegPolicy(reqDelegPolicyState);
objId = new ObjectIdentifier(mechOid.toString());
currentState = IN_PROGRESS;
firstToken = true;
} else {
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
GSSUtil.isSpNegoMech(mechOid)) {
// do not parse GSS header for native provider or SPNEGO
// mech
} else {
// parse GSS header
gssHeader = new GSSHeader(inStream);
if (!gssHeader.getOid().equals(objId))
throw new GSSExceptionImpl
(GSSException.DEFECTIVE_TOKEN,
"Mechanism not equal to " +
mechOid.toString() +
" in initSecContext token");
inTokenLen = gssHeader.getMechTokenLength();
}
}
byte[] obuf = mechCtxt.initSecContext(inStream, inTokenLen);
int retVal = 0;
if (obuf != null) {
retVal = obuf.length;
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(!firstToken && GSSUtil.isSpNegoMech(mechOid))) {
// do not add GSS header for native provider or SPNEGO
// except for the first SPNEGO token
} else {
// add GSS header
gssHeader = new GSSHeader(objId, obuf.length);
retVal += gssHeader.encode(outStream);
}
outStream.write(obuf);
}
if (mechCtxt.isEstablished())
currentState = READY;
return retVal;
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN,
e.getMessage());
}
}
public byte[] acceptSecContext(byte inTok[], int offset, int len)
throws GSSException {
/*
* Usually initial GSS token containing a Kerberos AP-REP is less
* than 100 bytes.
*/
ByteArrayOutputStream bos = new ByteArrayOutputStream(100);
acceptSecContext(new ByteArrayInputStream(inTok, offset, len),
bos);
byte[] out = bos.toByteArray();
return (out.length == 0) ? null : out;
}
public void acceptSecContext(InputStream inStream,
OutputStream outStream) throws GSSException {
if (mechCtxt != null && currentState != IN_PROGRESS) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Illegal call to acceptSecContext");
}
GSSHeader gssHeader = null;
int inTokenLen = -1;
GSSCredentialSpi credElement = null;
try {
if (mechCtxt == null) {
// mechOid will be null for an acceptor's context
gssHeader = new GSSHeader(inStream);
inTokenLen = gssHeader.getMechTokenLength();
/*
* Convert ObjectIdentifier to Oid
*/
objId = gssHeader.getOid();
mechOid = new Oid(objId.toString());
// System.out.println("Entered GSSContextImpl.acceptSecContext"
// + " with mechanism = " + mechOid);
if (myCred != null) {
credElement = myCred.getElement(mechOid, false);
}
mechCtxt = gssManager.getMechanismContext(credElement,
mechOid);
mechCtxt.setChannelBinding(channelBindings);
currentState = IN_PROGRESS;
} else {
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(GSSUtil.isSpNegoMech(mechOid))) {
// do not parse GSS header for native provider and SPNEGO
} else {
// parse GSS Header
gssHeader = new GSSHeader(inStream);
if (!gssHeader.getOid().equals(objId))
throw new GSSExceptionImpl
(GSSException.DEFECTIVE_TOKEN,
"Mechanism not equal to " +
mechOid.toString() +
" in acceptSecContext token");
inTokenLen = gssHeader.getMechTokenLength();
}
}
byte[] obuf = mechCtxt.acceptSecContext(inStream, inTokenLen);
if (obuf != null) {
int retVal = obuf.length;
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(GSSUtil.isSpNegoMech(mechOid))) {
// do not add GSS header for native provider and SPNEGO
} else {
// add GSS header
gssHeader = new GSSHeader(objId, obuf.length);
retVal += gssHeader.encode(outStream);
}
outStream.write(obuf);
}
if (mechCtxt.isEstablished()) {
currentState = READY;
}
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN,
e.getMessage());
}
}
public boolean isEstablished() {
if (mechCtxt == null)
return false;
else
return (currentState == READY);
}
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize) throws GSSException {
if (mechCtxt != null)
return mechCtxt.getWrapSizeLimit(qop, confReq, maxTokenSize);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] wrap(byte inBuf[], int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.wrap(inBuf, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.wrap(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte [] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.unwrap(inBuf, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.unwrap(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] getMIC(byte []inMsg, int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.getMIC(inMsg, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.getMIC(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.verifyMIC(inTok, tokOffset, tokLen,
inMsg, msgOffset, msgLen, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.verifyMIC(tokStream, msgStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] export() throws GSSException {
// Defaults to null to match old behavior
byte[] result = null;
// Only allow context export from native provider since JGSS
// still has not defined its own interprocess token format
if (mechCtxt.isTransferable() &&
mechCtxt.getProvider().getName().equals("SunNativeGSS")) {
result = mechCtxt.export();
}
return result;
}
public void requestMutualAuth(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqMutualAuthState = state;
}
public void requestReplayDet(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqReplayDetState = state;
}
public void requestSequenceDet(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqSequenceDetState = state;
}
public void requestCredDeleg(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqCredDelegState = state;
}
public void requestAnonymity(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqAnonState = state;
}
public void requestConf(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqConfState = state;
}
public void requestInteg(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqIntegState = state;
}
public void requestLifetime(int lifetime) throws GSSException {
if (mechCtxt == null && initiator)
reqLifetime = lifetime;
}
public void setChannelBinding(ChannelBinding channelBindings)
throws GSSException {
if (mechCtxt == null)
this.channelBindings = channelBindings;
}
public boolean getCredDelegState() {
if (mechCtxt != null)
return mechCtxt.getCredDelegState();
else
return reqCredDelegState;
}
public boolean getMutualAuthState() {
if (mechCtxt != null)
return mechCtxt.getMutualAuthState();
else
return reqMutualAuthState;
}
public boolean getReplayDetState() {
if (mechCtxt != null)
return mechCtxt.getReplayDetState();
else
return reqReplayDetState;
}
public boolean getSequenceDetState() {
if (mechCtxt != null)
return mechCtxt.getSequenceDetState();
else
return reqSequenceDetState;
}
public boolean getAnonymityState() {
if (mechCtxt != null)
return mechCtxt.getAnonymityState();
else
return reqAnonState;
}
public boolean isTransferable() throws GSSException {
if (mechCtxt != null)
return mechCtxt.isTransferable();
else
return false;
}
public boolean isProtReady() {
if (mechCtxt != null)
return mechCtxt.isProtReady();
else
return false;
}
public boolean getConfState() {
if (mechCtxt != null)
return mechCtxt.getConfState();
else
return reqConfState;
}
public boolean getIntegState() {
if (mechCtxt != null)
return mechCtxt.getIntegState();
else
return reqIntegState;
}
public int getLifetime() {
if (mechCtxt != null)
return mechCtxt.getLifetime();
else
return reqLifetime;
}
public GSSName getSrcName() throws GSSException {
if (srcName == null) {
srcName = GSSNameImpl.wrapElement
(gssManager, mechCtxt.getSrcName());
}
return srcName;
}
public GSSName getTargName() throws GSSException {
if (targName == null) {
targName = GSSNameImpl.wrapElement
(gssManager, mechCtxt.getTargName());
}
return targName;
}
public Oid getMech() throws GSSException {
if (mechCtxt != null) {
return mechCtxt.getMech();
}
return mechOid;
}
public GSSCredential getDelegCred() throws GSSException {
if (mechCtxt == null)
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
GSSCredentialSpi delCredElement = mechCtxt.getDelegCred();
return (delCredElement == null ?
null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, delCredElement)));
}
public boolean isInitiator() throws GSSException {
return initiator;
}
public void dispose() throws GSSException {
currentState = DELETED;
if (mechCtxt != null) {
mechCtxt.dispose();
mechCtxt = null;
}
myCred = null;
srcName = null;
targName = null;
}
// ExtendedGSSContext methods:
public Object inquireSecContext(String type) throws GSSException {
if (mechCtxt == null) {
throw new GSSException(GSSException.NO_CONTEXT);
}
return mechCtxt.inquireSecContext(type);
}
public void requestDelegPolicy(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqDelegPolicyState = state;
}
public boolean getDelegPolicyState() {
if (mechCtxt != null)
return mechCtxt.getDelegPolicyState();
else
return reqDelegPolicyState;
}
}

View file

@ -0,0 +1,702 @@
/*
* 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;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.*;
import sun.security.jgss.spnego.SpNegoCredElement;
public class GSSCredentialImpl implements GSSCredential {
private GSSManagerImpl gssManager = null;
private boolean destroyed = false;
/*
* We store all elements in a hashtable, using <oid, usage> as the
* key. This makes it easy to locate the specific kind of credential we
* need. The implementation needs to be optimized for the case where
* there is just one element (tempCred).
*/
private Hashtable<SearchKey, GSSCredentialSpi> hashtable = null;
// XXX Optimization for single mech usage
private GSSCredentialSpi tempCred = null;
public GSSCredentialImpl() {
// Useless
}
// Used by new ExtendedGSSCredential.ExtendedGSSCredentialImpl(cred)
protected GSSCredentialImpl(GSSCredentialImpl src) {
this.gssManager = src.gssManager;
this.destroyed = src.destroyed;
this.hashtable = src.hashtable;
this.tempCred = src.tempCred;
}
GSSCredentialImpl(GSSManagerImpl gssManager, int usage)
throws GSSException {
this(gssManager, null, GSSCredential.DEFAULT_LIFETIME,
(Oid[]) null, usage);
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
int lifetime, Oid mech, int usage)
throws GSSException {
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
init(gssManager);
add(name, lifetime, lifetime, mech, usage);
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
int lifetime, Oid[] mechs, int usage)
throws GSSException {
init(gssManager);
boolean defaultList = false;
if (mechs == null) {
mechs = gssManager.getMechs();
defaultList = true;
}
for (int i = 0; i < mechs.length; i++) {
try {
add(name, lifetime, lifetime, mechs[i], usage);
} catch (GSSException e) {
if (defaultList) {
// Try the next mechanism
GSSUtil.debug("Ignore " + e + " while acquring cred for "
+ mechs[i]);
//e.printStackTrace();
} else throw e; // else try the next mechanism
}
}
if ((hashtable.size() == 0) || (usage != getUsage()))
throw new GSSException(GSSException.NO_CRED);
}
// Wrap a mech cred into a GSS cred
public GSSCredentialImpl(GSSManagerImpl gssManager,
GSSCredentialSpi mechElement) throws GSSException {
init(gssManager);
int usage = GSSCredential.ACCEPT_ONLY;
if (mechElement.isInitiatorCredential()) {
if (mechElement.isAcceptorCredential()) {
usage = GSSCredential.INITIATE_AND_ACCEPT;
} else {
usage = GSSCredential.INITIATE_ONLY;
}
}
SearchKey key = new SearchKey(mechElement.getMechanism(),
usage);
tempCred = mechElement;
hashtable.put(key, tempCred);
// More mechs that can use this cred, say, SPNEGO
if (!GSSUtil.isSpNegoMech(mechElement.getMechanism())) {
key = new SearchKey(GSSUtil.GSS_SPNEGO_MECH_OID, usage);
hashtable.put(key, new SpNegoCredElement(mechElement));
}
}
void init(GSSManagerImpl gssManager) {
this.gssManager = gssManager;
hashtable = new Hashtable<SearchKey, GSSCredentialSpi>(
gssManager.getMechs().length);
}
public void dispose() throws GSSException {
if (!destroyed) {
GSSCredentialSpi element;
Enumeration<GSSCredentialSpi> values = hashtable.elements();
while (values.hasMoreElements()) {
element = values.nextElement();
element.dispose();
}
destroyed = true;
}
}
public GSSCredential impersonate(GSSName name) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Oid mech = tempCred.getMechanism();
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
GSSCredentialSpi cred = tempCred.impersonate(nameElement);
return (cred == null ?
null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, cred)));
}
public GSSName getName() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
return GSSNameImpl.wrapElement(gssManager, tempCred.getName());
}
public GSSName getName(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey key = null;
GSSCredentialSpi element = null;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
}
if (element == null) {
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
}
if (element == null) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return GSSNameImpl.wrapElement(gssManager, element.getName());
}
/**
* Returns the remaining lifetime of this credential. The remaining
* lifetime is defined as the minimum lifetime, either for initiate or
* for accept, across all elements contained in it. Not terribly
* useful, but required by GSS-API.
*/
public int getRemainingLifetime() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey tempKey;
GSSCredentialSpi tempCred;
int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0;
int min = INDEFINITE_LIFETIME;
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
tempKey = e.nextElement();
tempCred = hashtable.get(tempKey);
if (tempKey.getUsage() == INITIATE_ONLY)
tempLife = tempCred.getInitLifetime();
else if (tempKey.getUsage() == ACCEPT_ONLY)
tempLife = tempCred.getAcceptLifetime();
else {
tempInitLife = tempCred.getInitLifetime();
tempAcceptLife = tempCred.getAcceptLifetime();
tempLife = (tempInitLife < tempAcceptLife ?
tempInitLife:
tempAcceptLife);
}
if (min > tempLife)
min = tempLife;
}
return min;
}
public int getRemainingInitLifetime(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean found = false;
int max = 0;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getInitLifetime())
max = element.getInitLifetime();
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getInitLifetime())
max = element.getInitLifetime();
}
if (!found) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return max;
}
public int getRemainingAcceptLifetime(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean found = false;
int max = 0;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getAcceptLifetime())
max = element.getAcceptLifetime();
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getAcceptLifetime())
max = element.getAcceptLifetime();
}
if (!found) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return max;
}
/**
* Returns the usage mode for this credential. Returns
* INITIATE_AND_ACCEPT if any one element contained in it supports
* INITIATE_AND_ACCEPT or if two different elements exist where one
* support INITIATE_ONLY and the other supports ACCEPT_ONLY.
*/
public int getUsage() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey tempKey;
boolean initiate = false;
boolean accept = false;
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
tempKey = e.nextElement();
if (tempKey.getUsage() == INITIATE_ONLY)
initiate = true;
else if (tempKey.getUsage() == ACCEPT_ONLY)
accept = true;
else
return INITIATE_AND_ACCEPT;
}
if (initiate) {
if (accept)
return INITIATE_AND_ACCEPT;
else
return INITIATE_ONLY;
} else
return ACCEPT_ONLY;
}
public int getUsage(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean initiate = false;
boolean accept = false;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element != null) {
initiate = true;
}
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
if (element != null) {
accept = true;
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
initiate = true;
accept = true;
}
if (initiate && accept)
return GSSCredential.INITIATE_AND_ACCEPT;
else if (initiate)
return GSSCredential.INITIATE_ONLY;
else if (accept)
return GSSCredential.ACCEPT_ONLY;
else {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
}
public Oid[] getMechs() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Vector<Oid> result = new Vector<Oid>(hashtable.size());
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
SearchKey tempKey = e.nextElement();
result.addElement(tempKey.getMech());
}
return result.toArray(new Oid[0]);
}
public void add(GSSName name, int initLifetime, int acceptLifetime,
Oid mech, int usage) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
SearchKey key = new SearchKey(mech, usage);
if (hashtable.containsKey(key)) {
throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT,
"Duplicate element found: " +
getElementStr(mech, usage));
}
// XXX If not instance of GSSNameImpl then throw exception
// Application mixing GSS implementations
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
tempCred = gssManager.getCredentialElement(nameElement,
initLifetime,
acceptLifetime,
mech,
usage);
/*
* Not all mechanisms support the concept of one credential element
* that can be used for both initiating and accepting a context. In
* the event that an application requests usage INITIATE_AND_ACCEPT
* for a credential from such a mechanism, the GSS framework will
* need to obtain two different credential elements from the
* mechanism, one that will have usage INITIATE_ONLY and another
* that will have usage ACCEPT_ONLY. The mechanism will help the
* GSS-API realize this by returning a credential element with
* usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another
* call to getCredentialElement, this time with the other usage
* mode.
*/
if (tempCred != null) {
if (usage == GSSCredential.INITIATE_AND_ACCEPT &&
(!tempCred.isAcceptorCredential() ||
!tempCred.isInitiatorCredential())) {
int currentUsage;
int desiredUsage;
if (!tempCred.isInitiatorCredential()) {
currentUsage = GSSCredential.ACCEPT_ONLY;
desiredUsage = GSSCredential.INITIATE_ONLY;
} else {
currentUsage = GSSCredential.INITIATE_ONLY;
desiredUsage = GSSCredential.ACCEPT_ONLY;
}
key = new SearchKey(mech, currentUsage);
hashtable.put(key, tempCred);
tempCred = gssManager.getCredentialElement(nameElement,
initLifetime,
acceptLifetime,
mech,
desiredUsage);
key = new SearchKey(mech, desiredUsage);
hashtable.put(key, tempCred);
} else {
hashtable.put(key, tempCred);
}
}
}
public boolean equals(Object another) {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
if (this == another) {
return true;
}
if (!(another instanceof GSSCredentialImpl)) {
return false;
}
// NOTE: The specification does not define the criteria to compare
// credentials.
/*
* XXX
* The RFC says: "Tests if this GSSCredential refers to the same
* entity as the supplied object. The two credentials must be
* acquired over the same mechanisms and must refer to the same
* principal. Returns "true" if the two GSSCredentials refer to
* the same entity; "false" otherwise."
*
* Well, when do two credentials refer to the same principal? Do
* they need to have one GSSName in common for the different
* GSSName's that the credential elements return? Or do all
* GSSName's have to be in common when the names are exported with
* their respective mechanisms for the credential elements?
*/
return false;
}
/**
* Returns a hashcode value for this GSSCredential.
*
* @return a hashCode value
*/
public int hashCode() {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
// NOTE: The specification does not define the criteria to compare
// credentials.
/*
* XXX
* Decide on a criteria for equals first then do this.
*/
return 1;
}
/**
* Returns the specified mechanism's credential-element.
*
* @param mechOid the oid for mechanism to retrieve
* @param initiate boolean indicating if the function is
* to throw exception or return null when element is not
* found.
* @return mechanism credential object
* @exception GSSException of invalid mechanism
*/
public GSSCredentialSpi getElement(Oid mechOid, boolean initiate)
throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey key;
GSSCredentialSpi element;
if (mechOid == null) {
/*
* First see if the default mechanism satisfies the
* desired usage.
*/
mechOid = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mechOid,
initiate? INITIATE_ONLY : ACCEPT_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element == null) {
/*
* Now just return any element that satisfies the
* desired usage.
*/
Object[] elements = hashtable.entrySet().toArray();
for (int i = 0; i < elements.length; i++) {
element = (GSSCredentialSpi)
((Map.Entry)elements[i]).getValue();
if (element.isInitiatorCredential() == initiate)
break;
} // for loop
}
}
} else {
if (initiate)
key = new SearchKey(mechOid, INITIATE_ONLY);
else
key = new SearchKey(mechOid, ACCEPT_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
element = hashtable.get(key);
}
}
if (element == null)
throw new GSSExceptionImpl(GSSException.NO_CRED,
"No credential found for: " +
getElementStr(mechOid,
initiate? INITIATE_ONLY : ACCEPT_ONLY));
return element;
}
Set<GSSCredentialSpi> getElements() {
HashSet<GSSCredentialSpi> retVal =
new HashSet<GSSCredentialSpi>(hashtable.size());
Enumeration<GSSCredentialSpi> values = hashtable.elements();
while (values.hasMoreElements()) {
GSSCredentialSpi o = values.nextElement();
retVal.add(o);
}
return retVal;
}
private static String getElementStr(Oid mechOid, int usage) {
String displayString = mechOid.toString();
if (usage == GSSCredential.INITIATE_ONLY) {
displayString =
displayString.concat(" usage: Initiate");
} else if (usage == GSSCredential.ACCEPT_ONLY) {
displayString =
displayString.concat(" usage: Accept");
} else {
displayString =
displayString.concat(" usage: Initiate and Accept");
}
return displayString;
}
public String toString() {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
StringBuilder sb = new StringBuilder("[GSSCredential: ");
Object[] elements = hashtable.entrySet().toArray();
for (int i = 0; i < elements.length; i++) {
try {
sb.append('\n');
element = (GSSCredentialSpi)
((Map.Entry)elements[i]).getValue();
sb.append(element.getName());
sb.append(' ');
sb.append(element.getMechanism());
sb.append(element.isInitiatorCredential() ?
" Initiate" : "");
sb.append(element.isAcceptorCredential() ?
" Accept" : "");
sb.append(" [");
sb.append(element.getClass());
sb.append(']');
} catch (GSSException e) {
// skip to next element
}
}
sb.append(']');
return sb.toString();
}
static class SearchKey {
private Oid mechOid = null;
private int usage = GSSCredential.INITIATE_AND_ACCEPT;
public SearchKey(Oid mechOid, int usage) {
this.mechOid = mechOid;
this.usage = usage;
}
public Oid getMech() {
return mechOid;
}
public int getUsage() {
return usage;
}
public boolean equals(Object other) {
if (! (other instanceof SearchKey))
return false;
SearchKey that = (SearchKey) other;
return ((this.mechOid.equals(that.mechOid)) &&
(this.usage == that.usage));
}
public int hashCode() {
return mechOid.hashCode();
}
}
}

View file

@ -0,0 +1,89 @@
/*
* 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;
import org.ietf.jgss.*;
/**
* This class helps overcome a limitation of the org.ietf.jgss.GSSException
* class that does not allow the thrower to set a string corresponding to
* the major code.
*/
public class GSSExceptionImpl extends GSSException {
private static final long serialVersionUID = 4251197939069005575L;
private String majorMessage;
/**
* A constructor that takes the majorCode as well as the mech oid that
* will be appended to the standard message defined in its super class.
*/
GSSExceptionImpl(int majorCode, Oid mech) {
super(majorCode);
this.majorMessage = super.getMajorString() + ": " + mech;
}
/**
* A constructor that takes the majorCode as well as the message that
* corresponds to it.
*/
public GSSExceptionImpl(int majorCode, String majorMessage) {
super(majorCode);
this.majorMessage = majorMessage;
}
/**
* A constructor that takes the majorCode and the exception cause.
*/
public GSSExceptionImpl(int majorCode, Exception cause) {
super(majorCode);
initCause(cause);
}
/**
* A constructor that takes the majorCode, the message that
* corresponds to it, and the exception cause.
*/
public GSSExceptionImpl(int majorCode, String majorMessage,
Exception cause) {
this(majorCode, majorMessage);
initCause(cause);
}
/**
* Returns the message that was embedded in this object, otherwise it
* returns the default message that an org.ietf.jgss.GSSException
* generates.
*/
public String getMessage() {
if (majorMessage != null)
return majorMessage;
else
return super.getMessage();
}
}

View file

@ -0,0 +1,345 @@
/*
* 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;
import org.ietf.jgss.GSSException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import sun.security.util.*;
/**
* This class represents the mechanism independent part of a GSS-API
* context establishment token. Some mechanisms may choose to encode
* all subsequent tokens as well such that they start with an encoding
* of an instance of this class. e.g., The Kerberos v5 GSS-API Mechanism
* uses this header for all GSS-API tokens.
* <p>
* The format is specified in RFC 2743 section 3.1.
*
* @author Mayank Upadhyay
*/
/*
* The RFC states that implementations should explicitly follow the
* encoding scheme descibed in this section rather than use ASN.1
* compilers. However, we should consider removing duplicate ASN.1
* like code from here and depend on sun.security.util if possible.
*/
public class GSSHeader {
private ObjectIdentifier mechOid = null;
private byte[] mechOidBytes = null;
private int mechTokenLength = 0;
/**
* The tag defined in the GSS-API mechanism independent token
* format.
*/
public static final int TOKEN_ID=0x60;
/**
* Creates a GSSHeader instance whose encoding can be used as the
* prefix for a particular mechanism token.
* @param mechOid the Oid of the mechanism which generated the token
* @param mechTokenLength the length of the subsequent portion that
* the mechanism will be adding.
*/
public GSSHeader(ObjectIdentifier mechOid, int mechTokenLength)
throws IOException {
this.mechOid = mechOid;
DerOutputStream temp = new DerOutputStream();
temp.putOID(mechOid);
mechOidBytes = temp.toByteArray();
this.mechTokenLength = mechTokenLength;
}
/**
* Reads in a GSSHeader from an InputStream. Typically this would be
* used as part of reading the complete token from an InputStream
* that is obtained from a socket.
*/
public GSSHeader(InputStream is)
throws IOException, GSSException {
// debug("Parsing GSS token: ");
int tag = is.read();
// debug("tag=" + tag);
if (tag != TOKEN_ID)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"GSSHeader did not find the right tag");
int length = getLength(is);
DerValue temp = new DerValue(is);
mechOidBytes = temp.toByteArray();
mechOid = temp.getOID();
// debug (" oid=" + mechOid);
// debug (" len starting with oid=" + length);
mechTokenLength = length - mechOidBytes.length;
// debug(" mechToken length=" + mechTokenLength);
}
/**
* Used to obtain the Oid stored in this GSSHeader instance.
* @return the Oid of the mechanism.
*/
public ObjectIdentifier getOid() {
return mechOid;
}
/**
* Used to obtain the length of the mechanism specific token that
* will follow the encoding of this GSSHeader instance.
* @return the length of the mechanism specific token portion that
* will follow this GSSHeader.
*/
public int getMechTokenLength() {
return mechTokenLength;
}
/**
* Used to obtain the length of the encoding of this GSSHeader.
* @return the lenght of the encoding of this GSSHeader instance.
*/
public int getLength() {
int lenField = mechOidBytes.length + mechTokenLength;
return (1 + getLenFieldSize(lenField) + mechOidBytes.length);
}
/**
* Used to determine what the maximum possible mechanism token
* size is if the complete GSSToken returned to the application
* (including a GSSHeader) is not to exceed some pre-determined
* value in size.
* @param mechOid the Oid of the mechanism that will generate
* this GSS-API token
* @param maxTotalSize the pre-determined value that serves as a
* maximum size for the complete GSS-API token (including a
* GSSHeader)
* @return the maximum size of mechanism token that can be used
* so as to not exceed maxTotalSize with the GSS-API token
*/
public static int getMaxMechTokenSize(ObjectIdentifier mechOid,
int maxTotalSize) {
int mechOidBytesSize = 0;
try {
DerOutputStream temp = new DerOutputStream();
temp.putOID(mechOid);
mechOidBytesSize = temp.toByteArray().length;
} catch (IOException e) {
}
// Subtract bytes needed for 0x60 tag and mechOidBytes
maxTotalSize -= (1 + mechOidBytesSize);
// Subtract maximum len bytes
maxTotalSize -= 5;
return maxTotalSize;
/*
* Len field and mechanism token must fit in remaining
* space. The range of the len field that we allow is
* 1 through 5.
*
int mechTokenSize = 0;
for (int lenFieldSize = 1; lenFieldSize <= 5;
lenFieldSize++) {
mechTokenSize = maxTotalSize - lenFieldSize;
if (getLenFieldSize(mechTokenSize + mechOidBytesSize +
lenFieldSize) <= lenFieldSize)
break;
}
return mechTokenSize;
*/
}
/**
* Used to determine the number of bytes that will be need to encode
* the length field of the GSSHeader.
*/
private int getLenFieldSize(int len) {
int retVal = 1;
if (len < 128) {
retVal=1;
} else if (len < (1 << 8)) {
retVal=2;
} else if (len < (1 << 16)) {
retVal=3;
} else if (len < (1 << 24)) {
retVal=4;
} else {
retVal=5; // See getMaxMechTokenSize
}
return retVal;
}
/**
* Encodes this GSSHeader instance onto the provided OutputStream.
* @param os the OutputStream to which the token should be written.
* @return the number of bytes that are output as a result of this
* encoding
*/
public int encode(OutputStream os) throws IOException {
int retVal = 1 + mechOidBytes.length;
os.write(TOKEN_ID);
int length = mechOidBytes.length + mechTokenLength;
retVal += putLength(length, os);
os.write(mechOidBytes);
return retVal;
}
/**
* Get a length from the input stream, allowing for at most 32 bits of
* encoding to be used. (Not the same as getting a tagged integer!)
*
* @return the length or -1 if indefinite length found.
* @exception IOException on parsing error or unsupported lengths.
*/
// shameless lifted from sun.security.util.DerInputStream.
private int getLength(InputStream in) throws IOException {
return getLength(in.read(), in);
}
/**
* Get a length from the input stream, allowing for at most 32 bits of
* encoding to be used. (Not the same as getting a tagged integer!)
*
* @return the length or -1 if indefinite length found.
* @exception IOException on parsing error or unsupported lengths.
*/
// shameless lifted from sun.security.util.DerInputStream.
private int getLength(int lenByte, InputStream in) throws IOException {
int value, tmp;
tmp = lenByte;
if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum
value = tmp;
} else { // long form or indefinite
tmp &= 0x07f;
/*
* NOTE: tmp == 0 indicates indefinite length encoded data.
* tmp > 4 indicates more than 4Gb of data.
*/
if (tmp == 0)
return -1;
if (tmp < 0 || tmp > 4)
throw new IOException("DerInputStream.getLength(): lengthTag="
+ tmp + ", "
+ ((tmp < 0) ? "incorrect DER encoding." : "too big."));
for (value = 0; tmp > 0; tmp --) {
value <<= 8;
value += 0x0ff & in.read();
}
if (value < 0) {
throw new IOException("Invalid length bytes");
}
}
return value;
}
/**
* Put the encoding of the length in the specified stream.
*
* @params len the length of the attribute.
* @param out the outputstream to write the length to
* @return the number of bytes written
* @exception IOException on writing errors.
*/
// Shameless lifted from sun.security.util.DerOutputStream.
private int putLength(int len, OutputStream out) throws IOException {
int retVal = 0;
if (len < 128) {
out.write((byte)len);
retVal=1;
} else if (len < (1 << 8)) {
out.write((byte)0x081);
out.write((byte)len);
retVal=2;
} else if (len < (1 << 16)) {
out.write((byte)0x082);
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=3;
} else if (len < (1 << 24)) {
out.write((byte)0x083);
out.write((byte)(len >> 16));
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=4;
} else {
out.write((byte)0x084);
out.write((byte)(len >> 24));
out.write((byte)(len >> 16));
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=5;
}
return retVal;
}
// XXX Call these two in some central class
private void debug(String str) {
System.err.print(str);
}
private String getHexBytes(byte[] bytes, int len)
throws IOException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
int b1 = (bytes[i]>>4) & 0x0f;
int b2 = bytes[i] & 0x0f;
sb.append(Integer.toHexString(b1));
sb.append(Integer.toHexString(b2));
sb.append(' ');
}
return sb.toString();
}
}

View file

@ -0,0 +1,276 @@
/*
* 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;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* This class provides the default implementation of the GSSManager
* interface.
*/
public class GSSManagerImpl extends GSSManager {
// Undocumented property
private static final String USE_NATIVE_PROP =
"sun.security.jgss.native";
private static final Boolean USE_NATIVE;
static {
USE_NATIVE =
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
String osname = System.getProperty("os.name");
if (osname.startsWith("SunOS") ||
osname.contains("OS X") ||
osname.startsWith("Linux")) {
return Boolean.valueOf(System.getProperty
(USE_NATIVE_PROP));
}
return Boolean.FALSE;
}
});
}
private ProviderList list;
// Used by java SPNEGO impl to make sure native is disabled
public GSSManagerImpl(GSSCaller caller, boolean useNative) {
list = new ProviderList(caller, useNative);
}
// Used by HTTP/SPNEGO NegotiatorImpl
public GSSManagerImpl(GSSCaller caller) {
list = new ProviderList(caller, USE_NATIVE);
}
public GSSManagerImpl() {
list = new ProviderList(GSSCaller.CALLER_UNKNOWN, USE_NATIVE);
}
public Oid[] getMechs(){
return list.getMechs();
}
public Oid[] getNamesForMech(Oid mech)
throws GSSException {
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameTypes().clone();
}
public Oid[] getMechsForName(Oid nameType){
Oid[] mechs = list.getMechs();
Oid[] retVal = new Oid[mechs.length];
int pos = 0;
// Compatibility with RFC 2853 old NT_HOSTBASED_SERVICE value.
if (nameType.equals(GSSNameImpl.oldHostbasedServiceName)) {
nameType = GSSName.NT_HOSTBASED_SERVICE;
}
// Iterate thru all mechs in GSS
for (int i = 0; i < mechs.length; i++) {
// what nametypes does this mech support?
Oid mech = mechs[i];
try {
Oid[] namesForMech = getNamesForMech(mech);
// Is the desired Oid present in that list?
if (nameType.containedIn(namesForMech)) {
retVal[pos++] = mech;
}
} catch (GSSException e) {
// Squelch it and just skip over this mechanism
GSSUtil.debug("Skip " + mech +
": error retrieving supported name types");
}
}
// Trim the list if needed
if (pos < retVal.length) {
Oid[] temp = new Oid[pos];
for (int i = 0; i < pos; i++)
temp[i] = retVal[i];
retVal = temp;
}
return retVal;
}
public GSSName createName(String nameStr, Oid nameType)
throws GSSException {
return new GSSNameImpl(this, nameStr, nameType);
}
public GSSName createName(byte[] name, Oid nameType)
throws GSSException {
return new GSSNameImpl(this, name, nameType);
}
public GSSName createName(String nameStr, Oid nameType,
Oid mech) throws GSSException {
return new GSSNameImpl(this, nameStr, nameType, mech);
}
public GSSName createName(byte[] name, Oid nameType, Oid mech)
throws GSSException {
return new GSSNameImpl(this, name, nameType, mech);
}
public GSSCredential createCredential(int usage)
throws GSSException {
return wrap(new GSSCredentialImpl(this, usage));
}
public GSSCredential createCredential(GSSName aName,
int lifetime, Oid mech, int usage)
throws GSSException {
return wrap(new GSSCredentialImpl(this, aName, lifetime, mech, usage));
}
public GSSCredential createCredential(GSSName aName,
int lifetime, Oid[] mechs, int usage)
throws GSSException {
return wrap(new GSSCredentialImpl(this, aName, lifetime, mechs, usage));
}
public GSSContext createContext(GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException {
return wrap(new GSSContextImpl(this, peer, mech, myCred, lifetime));
}
public GSSContext createContext(GSSCredential myCred)
throws GSSException {
return wrap(new GSSContextImpl(this, myCred));
}
public GSSContext createContext(byte[] interProcessToken)
throws GSSException {
return wrap(new GSSContextImpl(this, interProcessToken));
}
public void addProviderAtFront(Provider p, Oid mech)
throws GSSException {
list.addProviderAtFront(p, mech);
}
public void addProviderAtEnd(Provider p, Oid mech)
throws GSSException {
list.addProviderAtEnd(p, mech);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name, int initLifetime,
int acceptLifetime, Oid mech, int usage)
throws GSSException {
MechanismFactory factory = list.getMechFactory(mech);
return factory.getCredentialElement(name, initLifetime,
acceptLifetime, usage);
}
// Used by java SPNEGO impl
public GSSNameSpi getNameElement(String name, Oid nameType, Oid mech)
throws GSSException {
// Just use the most preferred MF impl assuming GSSNameSpi
// objects are interoperable among providers
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameElement(name, nameType);
}
// Used by java SPNEGO impl
public GSSNameSpi getNameElement(byte[] name, Oid nameType, Oid mech)
throws GSSException {
// Just use the most preferred MF impl assuming GSSNameSpi
// objects are interoperable among providers
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameElement(name, nameType);
}
GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred,
int lifetime, Oid mech)
throws GSSException {
Provider p = null;
if (myInitiatorCred != null) {
p = myInitiatorCred.getProvider();
}
MechanismFactory factory = list.getMechFactory(mech, p);
return factory.getMechanismContext(peer, myInitiatorCred, lifetime);
}
GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred,
Oid mech)
throws GSSException {
Provider p = null;
if (myAcceptorCred != null) {
p = myAcceptorCred.getProvider();
}
MechanismFactory factory = list.getMechFactory(mech, p);
return factory.getMechanismContext(myAcceptorCred);
}
GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
if ((exportedContext == null) || (exportedContext.length == 0)) {
throw new GSSException(GSSException.NO_CONTEXT);
}
GSSContextSpi result = null;
// Only allow context import with native provider since JGSS
// still has not defined its own interprocess token format
Oid[] mechs = list.getMechs();
for (int i = 0; i < mechs.length; i++) {
MechanismFactory factory = list.getMechFactory(mechs[i]);
if (factory.getProvider().getName().equals("SunNativeGSS")) {
result = factory.getMechanismContext(exportedContext);
if (result != null) break;
}
}
if (result == null) {
throw new GSSException(GSSException.UNAVAILABLE);
}
return result;
}
static {
// Load the extended JGSS interfaces if exist
try {
Class.forName("com.sun.security.jgss.Extender");
} catch (Exception e) {
}
}
static GSSCredential wrap(GSSCredentialImpl cred) {
return sun.security.jgss.JgssExtender.getExtender().wrap(cred);
}
static GSSContext wrap(GSSContextImpl ctxt) {
return sun.security.jgss.JgssExtender.getExtender().wrap(ctxt);
}
}

View file

@ -0,0 +1,508 @@
/*
* 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;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Arrays;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import sun.security.util.ObjectIdentifier;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
/**
* This is the implementation class for GSSName. Conceptually the
* GSSName is a container with mechanism specific name elements. Each
* name element is a representation of how that particular mechanism
* would canonicalize this principal.
*
* Generally a GSSName is created by an application when it supplies
* a sequence of bytes and a nametype that helps each mechanism
* decide how to interpret those bytes.
*
* It is not necessary to create name elements for each available
* mechanism at the time the application creates the GSSName. This
* implementation does this lazily, as and when name elements for
* mechanisms are required to be handed out. (Generally, other GSS
* classes like GSSContext and GSSCredential request specific
* elements depending on the mechanisms that they are dealing with.)
* Assume that getting a mechanism to parse the applciation specified
* bytes is an expensive call.
*
* When a GSSName is canonicalized wrt some mechanism, it is supposed
* to discard all elements of other mechanisms and retain only the
* element for this mechanism. In GSS terminology this is called a
* Mechanism Name or MN. This implementation tries to retain the
* application provided bytes and name type just in case the MN is
* asked to produce an element for a mechanism that is different.
*
* When a GSSName is to be exported, the name element for the desired
* mechanism is converted to a byte representation and written
* out. It might happen that a name element for that mechanism cannot
* be obtained. This happens when the mechanism is just not supported
* in this GSS-API or when the mechanism is supported but bytes
* corresponding to the nametypes that it understands are not
* available in this GSSName.
*
* This class is safe for sharing. Each retrieval of a name element
* from getElement() might potentially add a new element to the
* hashmap of elements, but getElement() is synchronized.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class GSSNameImpl implements GSSName {
/**
* The old Oid used in RFC 2853. Now supported as
* input parameters in:
*
* 1. The four overloaded GSSManager.createName(*) methods
* 2. GSSManager.getMechsForName(Oid)
*
* Note that even if a GSSName is created with this old Oid,
* its internal name type and getStringNameType() output are
* always the new value.
*/
final static Oid oldHostbasedServiceName;
static {
Oid tmp = null;
try {
tmp = new Oid("1.3.6.1.5.6.2");
} catch (Exception e) {
// should never happen
}
oldHostbasedServiceName = tmp;
}
private GSSManagerImpl gssManager = null;
/*
* Store whatever the application passed in. We will use this to
* get individual mechanisms to create name elements as and when
* needed.
* Store both the String and the byte[]. Leave I18N to the
* mechanism by allowing it to extract bytes from the String!
*/
private String appNameStr = null;
private byte[] appNameBytes = null;
private Oid appNameType = null;
/*
* When we figure out what the printable name would be, we store
* both the name and its type.
*/
private String printableName = null;
private Oid printableNameType = null;
private HashMap<Oid, GSSNameSpi> elements = null;
private GSSNameSpi mechElement = null;
static GSSNameImpl wrapElement(GSSManagerImpl gssManager,
GSSNameSpi mechElement) throws GSSException {
return (mechElement == null ?
null : new GSSNameImpl(gssManager, mechElement));
}
GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {
this.gssManager = gssManager;
appNameStr = printableName = mechElement.toString();
appNameType = printableNameType = mechElement.getStringNameType();
this.mechElement = mechElement;
elements = new HashMap<Oid, GSSNameSpi>(1);
elements.put(mechElement.getMechanism(), this.mechElement);
}
GSSNameImpl(GSSManagerImpl gssManager,
Object appName,
Oid appNameType)
throws GSSException {
this(gssManager, appName, appNameType, null);
}
GSSNameImpl(GSSManagerImpl gssManager,
Object appName,
Oid appNameType,
Oid mech)
throws GSSException {
if (oldHostbasedServiceName.equals(appNameType)) {
appNameType = GSSName.NT_HOSTBASED_SERVICE;
}
if (appName == null)
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Cannot import null name");
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
if (NT_EXPORT_NAME.equals(appNameType)) {
importName(gssManager, appName);
} else {
init(gssManager, appName, appNameType, mech);
}
}
private void init(GSSManagerImpl gssManager,
Object appName, Oid appNameType,
Oid mech)
throws GSSException {
this.gssManager = gssManager;
this.elements =
new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);
if (appName instanceof String) {
this.appNameStr = (String) appName;
/*
* If appNameType is null, then the nametype for this printable
* string is determined only by interrogating the
* mechanism. Thus, defer the setting of printableName and
* printableNameType till later.
*/
if (appNameType != null) {
printableName = appNameStr;
printableNameType = appNameType;
}
} else {
this.appNameBytes = (byte[]) appName;
}
this.appNameType = appNameType;
mechElement = getElement(mech);
/*
* printableName will be null if appName was in a byte[] or if
* appName was in a String but appNameType was null.
*/
if (printableName == null) {
printableName = mechElement.toString();
printableNameType = mechElement.getStringNameType();
}
/*
* At this point the GSSNameImpl has the following set:
* appNameStr or appNameBytes
* appNameType (could be null)
* printableName
* printableNameType
* mechElement (which also exists in the hashmap of elements)
*/
}
private void importName(GSSManagerImpl gssManager,
Object appName)
throws GSSException {
int pos = 0;
byte[] bytes = null;
if (appName instanceof String) {
try {
bytes = ((String) appName).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// Won't happen
}
} else
bytes = (byte[]) appName;
if ((bytes[pos++] != 0x04) ||
(bytes[pos++] != 0x01))
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name token id is corrupted!");
int oidLen = (((0xFF & bytes[pos++]) << 8) |
(0xFF & bytes[pos++]));
ObjectIdentifier temp = null;
try {
DerInputStream din = new DerInputStream(bytes, pos,
oidLen);
temp = new ObjectIdentifier(din);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name Object identifier is corrupted!");
}
Oid oid = new Oid(temp.toString());
pos += oidLen;
int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |
((0xFF & bytes[pos++]) << 16) |
((0xFF & bytes[pos++]) << 8) |
(0xFF & bytes[pos++]));
if (mechPortionLen < 0 || pos > bytes.length - mechPortionLen) {
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name mech name is corrupted!");
}
byte[] mechPortion = new byte[mechPortionLen];
System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);
init(gssManager, mechPortion, NT_EXPORT_NAME, oid);
}
public GSSName canonicalize(Oid mech) throws GSSException {
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
return wrapElement(gssManager, getElement(mech));
}
/**
* This method may return false negatives. But if it says two
* names are equals, then there is some mechanism that
* authenticates them as the same principal.
*/
public boolean equals(GSSName other) throws GSSException {
if (this.isAnonymous() || other.isAnonymous())
return false;
if (other == this)
return true;
if (! (other instanceof GSSNameImpl))
return equals(gssManager.createName(other.toString(),
other.getStringNameType()));
/*
* XXX Do a comparison of the appNameStr/appNameBytes if
* available. If that fails, then proceed with this test.
*/
GSSNameImpl that = (GSSNameImpl) other;
GSSNameSpi myElement = this.mechElement;
GSSNameSpi element = that.mechElement;
/*
* XXX If they are not of the same mechanism type, convert both to
* Kerberos since it is guaranteed to be present.
*/
if ((myElement == null) && (element != null)) {
myElement = this.getElement(element.getMechanism());
} else if ((myElement != null) && (element == null)) {
element = that.getElement(myElement.getMechanism());
}
if (myElement != null && element != null) {
return myElement.equals(element);
}
if ((this.appNameType != null) &&
(that.appNameType != null)) {
if (!this.appNameType.equals(that.appNameType)) {
return false;
}
byte[] myBytes = null;
byte[] bytes = null;
try {
myBytes =
(this.appNameStr != null ?
this.appNameStr.getBytes("UTF-8") :
this.appNameBytes);
bytes =
(that.appNameStr != null ?
that.appNameStr.getBytes("UTF-8") :
that.appNameBytes);
} catch (UnsupportedEncodingException e) {
// Won't happen
}
return Arrays.equals(myBytes, bytes);
}
return false;
}
/**
* Returns a hashcode value for this GSSName.
*
* @return a hashCode value
*/
public int hashCode() {
/*
* XXX
* In order to get this to work reliably and properly(!), obtain a
* Kerberos name element for the name and then call hashCode on its
* string representation. But this cannot be done if the nametype
* is not one of those supported by the Kerberos provider and hence
* this name cannot be imported by Kerberos. In that case return a
* constant value!
*/
return 1;
}
public boolean equals(Object another) {
try {
// XXX This can lead to an infinite loop. Extract info
// and create a GSSNameImpl with it.
if (another instanceof GSSName)
return equals((GSSName) another);
} catch (GSSException e) {
// Squelch it and return false
}
return false;
}
/**
* Returns a flat name representation for this object. The name
* format is defined in RFC 2743:
*<pre>
* Length Name Description
* 2 TOK_ID Token Identifier
* For exported name objects, this
* must be hex 04 01.
* 2 MECH_OID_LEN Length of the Mechanism OID
* MECH_OID_LEN MECH_OID Mechanism OID, in DER
* 4 NAME_LEN Length of name
* NAME_LEN NAME Exported name; format defined in
* applicable mechanism draft.
*</pre>
*
* Note that it is not required to canonicalize a name before
* calling export(). i.e., the name need not be an MN. If it is
* not an MN, an implementation defined algorithm can be used for
* choosing the mechanism which should export this name.
*
* @return the flat name representation for this object
* @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
* BAD_NAME, FAILURE.
*/
public byte[] export() throws GSSException {
if (mechElement == null) {
/* Use default mech */
mechElement = getElement(ProviderList.DEFAULT_MECH_OID);
}
byte[] mechPortion = mechElement.export();
byte[] oidBytes = null;
ObjectIdentifier oid = null;
try {
oid = new ObjectIdentifier
(mechElement.getMechanism().toString());
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Invalid OID String ");
}
DerOutputStream dout = new DerOutputStream();
try {
dout.putOID(oid);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Could not ASN.1 Encode "
+ oid.toString());
}
oidBytes = dout.toByteArray();
byte[] retVal = new byte[2
+ 2 + oidBytes.length
+ 4 + mechPortion.length];
int pos = 0;
retVal[pos++] = 0x04;
retVal[pos++] = 0x01;
retVal[pos++] = (byte) (oidBytes.length>>>8);
retVal[pos++] = (byte) oidBytes.length;
System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);
pos += oidBytes.length;
retVal[pos++] = (byte) (mechPortion.length>>>24);
retVal[pos++] = (byte) (mechPortion.length>>>16);
retVal[pos++] = (byte) (mechPortion.length>>>8);
retVal[pos++] = (byte) mechPortion.length;
System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);
return retVal;
}
public String toString() {
return printableName;
}
public Oid getStringNameType() throws GSSException {
return printableNameType;
}
public boolean isAnonymous() {
if (printableNameType == null) {
return false;
} else {
return GSSName.NT_ANONYMOUS.equals(printableNameType);
}
}
public boolean isMN() {
return true; // Since always canonicalized for some mech
}
public synchronized GSSNameSpi getElement(Oid mechOid)
throws GSSException {
GSSNameSpi retVal = elements.get(mechOid);
if (retVal == null) {
if (appNameStr != null) {
retVal = gssManager.getNameElement
(appNameStr, appNameType, mechOid);
} else {
retVal = gssManager.getNameElement
(appNameBytes, appNameType, mechOid);
}
elements.put(mechOid, retVal);
}
return retVal;
}
Set<GSSNameSpi> getElements() {
return new HashSet<GSSNameSpi>(elements.values());
}
private static String getNameTypeStr(Oid nameTypeOid) {
if (nameTypeOid == null)
return "(NT is null)";
if (nameTypeOid.equals(NT_USER_NAME))
return "NT_USER_NAME";
if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))
return "NT_HOSTBASED_SERVICE";
if (nameTypeOid.equals(NT_EXPORT_NAME))
return "NT_EXPORT_NAME";
if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
return "NT_GSS_KRB5_PRINCIPAL";
else
return "Unknown";
}
}

View file

@ -0,0 +1,231 @@
/*
* Copyright (c) 2005, 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;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.EOFException;
import sun.security.util.*;
/**
* Utilities for processing GSS Tokens.
*
*/
public abstract class GSSToken {
/**
* Copies an integer value to a byte array in little endian form.
* @param value the integer value to write
* @param array the byte array into which the integer must be copied. It
* is assumed that the array will be large enough to hold the 4 bytes of
* the integer.
*/
public static final void writeLittleEndian(int value, byte[] array) {
writeLittleEndian(value, array, 0);
}
/**
* Copies an integer value to a byte array in little endian form.
* @param value the integer value to write
* @param array the byte array into which the integer must be copied. It
* is assumed that the array will be large enough to hold the 4 bytes of
* the integer.
* @param pos the position at which to start writing
*/
public static final void writeLittleEndian(int value, byte[] array,
int pos) {
array[pos++] = (byte)(value);
array[pos++] = (byte)((value>>>8));
array[pos++] = (byte)((value>>>16));
array[pos++] = (byte)((value>>>24));
}
public static final void writeBigEndian(int value, byte[] array) {
writeBigEndian(value, array, 0);
}
public static final void writeBigEndian(int value, byte[] array,
int pos) {
array[pos++] = (byte)((value>>>24));
array[pos++] = (byte)((value>>>16));
array[pos++] = (byte)((value>>>8));
array[pos++] = (byte)(value);
}
/**
* Reads an integer value from a byte array in little endian form. This
* method allows the reading of two byte values as well as four bytes
* values both of which are needed in the Kerberos v5 GSS-API mechanism.
*
* @param data the array containing the bytes of the integer value
* @param pos the offset in the array
* @param size the number of bytes to read from the array.
* @return the integer value
*/
public static final int readLittleEndian(byte[] data, int pos, int size) {
int retVal = 0;
int shifter = 0;
while (size > 0) {
retVal += (data[pos] & 0xff) << shifter;
shifter += 8;
pos++;
size--;
}
return retVal;
}
public static final int readBigEndian(byte[] data, int pos, int size) {
int retVal = 0;
int shifter = (size-1)*8;
while (size > 0) {
retVal += (data[pos] & 0xff) << shifter;
shifter -= 8;
pos++;
size--;
}
return retVal;
}
/**
* Writes a two byte integer value to a OutputStream.
*
* @param val the integer value. It will lose the high-order two bytes.
* @param os the OutputStream to write to
* @throws IOException if an error occurs while writing to the OutputStream
*/
public static final void writeInt(int val, OutputStream os)
throws IOException {
os.write(val>>>8);
os.write(val);
}
/**
* Writes a two byte integer value to a byte array.
*
* @param val the integer value. It will lose the high-order two bytes.
* @param dest the byte array to write to
* @param pos the offset to start writing to
*/
public static final int writeInt(int val, byte[] dest, int pos) {
dest[pos++] = (byte)(val>>>8);
dest[pos++] = (byte)val;
return pos;
}
/**
* Reads a two byte integer value from an InputStream.
*
* @param is the InputStream to read from
* @return the integer value
* @throws IOException if some errors occurs while reading the integer
* bytes.
*/
public static final int readInt(InputStream is) throws IOException {
return (((0xFF & is.read()) << 8)
| (0xFF & is.read()));
}
/**
* Reads a two byte integer value from a byte array.
*
* @param src the byte arra to read from
* @param pos the offset to start reading from
* @return the integer value
*/
public static final int readInt(byte[] src, int pos) {
return ((0xFF & src[pos])<<8 | (0xFF & src[pos+1]));
}
/**
* Blocks till the required number of bytes have been read from the
* input stream.
*
* @param is the InputStream to read from
* @param buffer the buffer to store the bytes into
* @throws EOFException if EOF is reached before all bytes are
* read.
* @throws IOException is an error occurs while reading
*/
public static final void readFully(InputStream is, byte[] buffer)
throws IOException {
readFully(is, buffer, 0, buffer.length);
}
/**
* Blocks till the required number of bytes have been read from the
* input stream.
*
* @param is the InputStream to read from
* @param buffer the buffer to store the bytes into
* @param offset the offset to start storing at
* @param len the number of bytes to read
* @throws EOFException if EOF is reached before all bytes are
* read.
* @throws IOException is an error occurs while reading
*/
public static final void readFully(InputStream is,
byte[] buffer, int offset, int len)
throws IOException {
int temp;
while (len > 0) {
temp = is.read(buffer, offset, len);
if (temp == -1)
throw new EOFException("Cannot read all "
+ len
+ " bytes needed to form this token!");
offset += temp;
len -= temp;
}
}
public static final void debug(String str) {
System.err.print(str);
}
public static final String getHexBytes(byte[] bytes) {
return getHexBytes(bytes, 0, bytes.length);
}
public static final String getHexBytes(byte[] bytes, int len) {
return getHexBytes(bytes, 0, len);
}
public static final String getHexBytes(byte[] bytes, int pos, int len) {
StringBuilder sb = new StringBuilder();
for (int i = pos; i < (pos+len); i++) {
int b1 = (bytes[i]>>4) & 0x0f;
int b2 = bytes[i] & 0x0f;
sb.append(Integer.toHexString(b1));
sb.append(Integer.toHexString(b2));
sb.append(' ');
}
return sb.toString();
}
}

View file

@ -0,0 +1,373 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import org.ietf.jgss.*;
import sun.security.jgss.spi.GSSNameSpi;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.action.GetPropertyAction;
import sun.security.jgss.krb5.Krb5NameElement;
import sun.security.jgss.spnego.SpNegoCredElement;
import java.util.Set;
import java.util.HashSet;
import java.util.Vector;
import java.util.Iterator;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import sun.security.action.GetBooleanAction;
import sun.security.util.ConsoleCallbackHandler;
/**
* The GSSUtilImplementation that knows how to work with the internals of
* the GSS-API.
*/
public class GSSUtil {
public static final Oid GSS_KRB5_MECH_OID =
GSSUtil.createOid("1.2.840.113554.1.2.2");
public static final Oid GSS_KRB5_MECH_OID2 =
GSSUtil.createOid("1.3.5.1.5.2");
public static final Oid GSS_KRB5_MECH_OID_MS =
GSSUtil.createOid("1.2.840.48018.1.2.2");
public static final Oid GSS_SPNEGO_MECH_OID =
GSSUtil.createOid("1.3.6.1.5.5.2");
public static final Oid NT_GSS_KRB5_PRINCIPAL =
GSSUtil.createOid("1.2.840.113554.1.2.2.1");
private static final String DEFAULT_HANDLER =
"auth.login.defaultCallbackHandler";
static final boolean DEBUG;
static {
DEBUG = (AccessController.doPrivileged
(new GetBooleanAction("sun.security.jgss.debug"))).
booleanValue();
}
static void debug(String message) {
if (DEBUG) {
assert(message != null);
System.out.println(message);
}
}
// NOTE: this method is only for creating Oid objects with
// known to be valid <code>oidStr</code> given it ignores
// the GSSException
public static Oid createOid(String oidStr) {
try {
return new Oid(oidStr);
} catch (GSSException e) {
debug("Ignored invalid OID: " + oidStr);
return null;
}
}
public static boolean isSpNegoMech(Oid oid) {
return (GSS_SPNEGO_MECH_OID.equals(oid));
}
public static boolean isKerberosMech(Oid oid) {
return (GSS_KRB5_MECH_OID.equals(oid) ||
GSS_KRB5_MECH_OID2.equals(oid) ||
GSS_KRB5_MECH_OID_MS.equals(oid));
}
public static String getMechStr(Oid oid) {
if (isSpNegoMech(oid)) {
return "SPNEGO";
} else if (isKerberosMech(oid)) {
return "Kerberos V5";
} else {
return oid.toString();
}
}
/**
* Note: The current impl only works with Sun's impl of
* GSSName and GSSCredential since it depends on package
* private APIs.
*/
public static Subject getSubject(GSSName name,
GSSCredential creds) {
HashSet<Object> privCredentials = null;
HashSet<Object> pubCredentials = new HashSet<Object>(); // empty Set
Set<GSSCredentialSpi> gssCredentials = null;
Set<KerberosPrincipal> krb5Principals =
new HashSet<KerberosPrincipal>();
if (name instanceof GSSNameImpl) {
try {
GSSNameSpi ne = ((GSSNameImpl) name).getElement
(GSS_KRB5_MECH_OID);
String krbName = ne.toString();
if (ne instanceof Krb5NameElement) {
krbName =
((Krb5NameElement) ne).getKrb5PrincipalName().getName();
}
KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName);
krb5Principals.add(krbPrinc);
} catch (GSSException ge) {
debug("Skipped name " + name + " due to " + ge);
}
}
if (creds instanceof GSSCredentialImpl) {
gssCredentials = ((GSSCredentialImpl) creds).getElements();
privCredentials = new HashSet<Object>(gssCredentials.size());
populateCredentials(privCredentials, gssCredentials);
} else {
privCredentials = new HashSet<Object>(); // empty Set
}
debug("Created Subject with the following");
debug("principals=" + krb5Principals);
debug("public creds=" + pubCredentials);
debug("private creds=" + privCredentials);
return new Subject(false, krb5Principals, pubCredentials,
privCredentials);
}
/**
* Populates the set credentials with elements from gssCredentials. At
* the same time, it converts any subclasses of KerberosTicket
* into KerberosTicket instances and any subclasses of KerberosKey into
* KerberosKey instances. (It is not desirable to expose the customer
* to sun.security.jgss.krb5.Krb5InitCredential which extends
* KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which
* extends KerberosKey.)
*/
private static void populateCredentials(Set<Object> credentials,
Set<?> gssCredentials) {
Object cred;
Iterator<?> elements = gssCredentials.iterator();
while (elements.hasNext()) {
cred = elements.next();
// Retrieve the internal cred out of SpNegoCredElement
if (cred instanceof SpNegoCredElement) {
cred = ((SpNegoCredElement) cred).getInternalCred();
}
if (cred instanceof KerberosTicket) {
if (!cred.getClass().getName().equals
("javax.security.auth.kerberos.KerberosTicket")) {
KerberosTicket tempTkt = (KerberosTicket) cred;
cred = new KerberosTicket(tempTkt.getEncoded(),
tempTkt.getClient(),
tempTkt.getServer(),
tempTkt.getSessionKey().getEncoded(),
tempTkt.getSessionKeyType(),
tempTkt.getFlags(),
tempTkt.getAuthTime(),
tempTkt.getStartTime(),
tempTkt.getEndTime(),
tempTkt.getRenewTill(),
tempTkt.getClientAddresses());
}
credentials.add(cred);
} else if (cred instanceof KerberosKey) {
if (!cred.getClass().getName().equals
("javax.security.auth.kerberos.KerberosKey")) {
KerberosKey tempKey = (KerberosKey) cred;
cred = new KerberosKey(tempKey.getPrincipal(),
tempKey.getEncoded(),
tempKey.getKeyType(),
tempKey.getVersionNumber());
}
credentials.add(cred);
} else {
// Ignore non-KerberosTicket and non-KerberosKey elements
debug("Skipped cred element: " + cred);
}
}
}
/**
* Authenticate using the login module from the specified
* configuration entry.
*
* @param caller the caller of JAAS Login
* @param mech the mech to be used
* @return the authenticated subject
*/
public static Subject login(GSSCaller caller, Oid mech) throws LoginException {
CallbackHandler cb = null;
if (caller instanceof HttpCaller) {
cb = new sun.net.www.protocol.http.spnego.NegotiateCallbackHandler(
((HttpCaller)caller).info());
} else {
String defaultHandler =
java.security.Security.getProperty(DEFAULT_HANDLER);
// get the default callback handler
if ((defaultHandler != null) && (defaultHandler.length() != 0)) {
cb = null;
} else {
cb = new ConsoleCallbackHandler();
}
}
// New instance of LoginConfigImpl must be created for each login,
// since the entry name is not passed as the first argument, but
// generated with caller and mech inside LoginConfigImpl
LoginContext lc = new LoginContext("", null, cb,
new LoginConfigImpl(caller, mech));
lc.login();
return lc.getSubject();
}
/**
* Determines if the application doesn't mind if the mechanism obtains
* the required credentials from outside of the current Subject. Our
* Kerberos v5 mechanism would do a JAAS login on behalf of the
* application if this were the case.
*
* The application indicates this by explicitly setting the system
* property javax.security.auth.useSubjectCredsOnly to false.
*/
public static boolean useSubjectCredsOnly(GSSCaller caller) {
// HTTP/SPNEGO doesn't use the standard JAAS framework. Instead, it
// uses the java.net.Authenticator style, therefore always return
// false here.
if (caller instanceof HttpCaller) {
return false;
}
/*
* Don't use GetBooleanAction because the default value in the JRE
* (when this is unset) has to treated as true.
*/
String propValue = AccessController.doPrivileged(
new GetPropertyAction("javax.security.auth.useSubjectCredsOnly",
"true"));
/*
* This property has to be explicitly set to "false". Invalid
* values should be ignored and the default "true" assumed.
*/
return (!propValue.equalsIgnoreCase("false"));
}
/**
* Determines the SPNEGO interoperability mode with Microsoft;
* by default it is set to true.
*
* To disable it, the application indicates this by explicitly setting
* the system property sun.security.spnego.interop to false.
*/
public static boolean useMSInterop() {
/*
* Don't use GetBooleanAction because the default value in the JRE
* (when this is unset) has to treated as true.
*/
String propValue = AccessController.doPrivileged(
new GetPropertyAction("sun.security.spnego.msinterop",
"true"));
/*
* This property has to be explicitly set to "false". Invalid
* values should be ignored and the default "true" assumed.
*/
return (!propValue.equalsIgnoreCase("false"));
}
/**
* Searches the private credentials of current Subject with the
* specified criteria and returns the matching GSSCredentialSpi
* object out of Sun's impl of GSSCredential. Returns null if
* no Subject present or a Vector which contains 0 or more
* matching GSSCredentialSpi objects.
*/
public static <T extends GSSCredentialSpi> Vector<T>
searchSubject(final GSSNameSpi name,
final Oid mech,
final boolean initiate,
final Class<? extends T> credCls) {
debug("Search Subject for " + getMechStr(mech) +
(initiate? " INIT" : " ACCEPT") + " cred (" +
(name == null? "<<DEF>>" : name.toString()) + ", " +
credCls.getName() + ")");
final AccessControlContext acc = AccessController.getContext();
try {
Vector<T> creds =
AccessController.doPrivileged
(new PrivilegedExceptionAction<Vector<T>>() {
public Vector<T> run() throws Exception {
Subject accSubj = Subject.getSubject(acc);
Vector<T> result = null;
if (accSubj != null) {
result = new Vector<T>();
Iterator<GSSCredentialImpl> iterator =
accSubj.getPrivateCredentials
(GSSCredentialImpl.class).iterator();
while (iterator.hasNext()) {
GSSCredentialImpl cred = iterator.next();
debug("...Found cred" + cred);
try {
GSSCredentialSpi ce =
cred.getElement(mech, initiate);
debug("......Found element: " + ce);
if (ce.getClass().equals(credCls) &&
(name == null ||
name.equals((Object) ce.getName()))) {
result.add(credCls.cast(ce));
} else {
debug("......Discard element");
}
} catch (GSSException ge) {
debug("...Discard cred (" + ge + ")");
}
}
} else debug("No Subject");
return result;
}
});
return creds;
} catch (PrivilegedActionException pae) {
debug("Unexpected exception when searching Subject:");
if (DEBUG) pae.printStackTrace();
return null;
}
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2009, 2012, 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;
import sun.net.www.protocol.http.HttpCallerInfo;
/**
* A special kind of GSSCaller, which origins from HTTP/Negotiate and contains
* info about what triggers the JGSS calls.
*/
public class HttpCaller extends GSSCaller {
final private HttpCallerInfo hci;
public HttpCaller(HttpCallerInfo hci) {
super("HTTP_CLIENT");
this.hci = hci;
}
public HttpCallerInfo info() {
return hci;
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2014, 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;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
/**
* The extending point of basic JGSS-API.
* <p>
* If a module wants to extend basic JGSS-API classes, it should extends this
* class and register itself as "the extender" using the setExtender method.
* When various GSSManager.createXXX methods are called, they will call
* "the extender"'s wrap methods to create objects of extended types
* instead of basic types.
* <p>
* We have only one extension now defined in com.sun.security.jgss, and the
* registering process is triggered in {@link GSSManagerImpl} by calling
* Class.forName("com.sun.security.jgss.Extender"). Only GSSContext
* and GSSCredential are extended now.
* <p>
* The setExtender method should be called before any JGSS call.
*/
public class JgssExtender {
// "The extender"
private static volatile JgssExtender theOne = new JgssExtender();
/**
* Gets "the extender". GSSManager calls this method so that it can
* wrap basic objects into extended objects.
* @return the extender
*/
public static JgssExtender getExtender() {
return theOne;
}
/**
* Set "the extender" so that GSSManager can create extended objects.
*/
protected static void setExtender(JgssExtender theOne) {
JgssExtender.theOne = theOne;
}
/**
* Wraps a plain GSSCredential object into an extended type.
*/
public GSSCredential wrap(GSSCredential cred) {
return cred;
}
/**
* Wraps a plain GSSContext object into an extended type.
*/
public GSSContext wrap(GSSContext ctxt) {
return ctxt;
}
}

View file

@ -0,0 +1,198 @@
/*
* Copyright (c) 2005, 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;
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import org.ietf.jgss.Oid;
/**
* A Configuration implementation especially designed for JGSS.
*
* @author weijun.wang
* @since 1.6
*/
public class LoginConfigImpl extends Configuration {
private final Configuration config;
private final GSSCaller caller;
private final String mechName;
private static final sun.security.util.Debug debug =
sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]");
/**
* A new instance of LoginConfigImpl must be created for each login request
* since it's only used by a single (caller, mech) pair
* @param caller defined in GSSUtil as CALLER_XXX final fields
* @param mech defined in GSSUtil as XXX_MECH_OID final fields
*/
public LoginConfigImpl(GSSCaller caller, Oid mech) {
this.caller = caller;
if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID)) {
mechName = "krb5";
} else {
throw new IllegalArgumentException(mech.toString() + " not supported");
}
config = java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction <Configuration> () {
public Configuration run() {
return Configuration.getConfiguration();
}
});
}
/**
* @param name Almost useless, since the (caller, mech) is already passed
* into constructor. The only use will be detecting OTHER which
* is called in LoginContext
*/
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry[] entries = null;
// This is the second call from LoginContext, which we will just ignore
if ("OTHER".equalsIgnoreCase(name)) {
return null;
}
String[] alts = null;
// Compatibility:
// For the 4 old callers, old entry names will be used if the new
// entry name is not provided.
if ("krb5".equals(mechName)) {
if (caller == GSSCaller.CALLER_INITIATE) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
"com.sun.security.jgss.initiate",
};
} else if (caller == GSSCaller.CALLER_ACCEPT) {
alts = new String[] {
"com.sun.security.jgss.krb5.accept",
"com.sun.security.jgss.accept",
};
} else if (caller == GSSCaller.CALLER_SSL_CLIENT) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
"com.sun.net.ssl.client",
};
} else if (caller == GSSCaller.CALLER_SSL_SERVER) {
alts = new String[] {
"com.sun.security.jgss.krb5.accept",
"com.sun.net.ssl.server",
};
} else if (caller instanceof HttpCaller) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
};
} else if (caller == GSSCaller.CALLER_UNKNOWN) {
throw new AssertionError("caller not defined");
}
} else {
throw new IllegalArgumentException(mechName + " not supported");
// No other mech at the moment, maybe --
/*
switch (caller) {
case GSSUtil.CALLER_INITIATE:
case GSSUtil.CALLER_SSL_CLIENT:
case GSSUtil.CALLER_HTTP_NEGOTIATE:
alts = new String[] {
"com.sun.security.jgss." + mechName + ".initiate",
};
break;
case GSSUtil.CALLER_ACCEPT:
case GSSUtil.CALLER_SSL_SERVER:
alts = new String[] {
"com.sun.security.jgss." + mechName + ".accept",
};
break;
case GSSUtil.CALLER_UNKNOWN:
// should never use
throw new AssertionError("caller cannot be unknown");
default:
throw new AssertionError("caller not defined");
}
*/
}
for (String alt: alts) {
entries = config.getAppConfigurationEntry(alt);
if (debug != null) {
debug.println("Trying " + alt +
((entries == null)?": does not exist.":": Found!"));
}
if (entries != null) {
break;
}
}
if (entries == null) {
if (debug != null) {
debug.println("Cannot read JGSS entry, use default values instead.");
}
entries = getDefaultConfigurationEntry();
}
return entries;
}
/**
* Default value for a caller-mech pair when no entry is defined in
* the system-wide Configuration object.
*/
private AppConfigurationEntry[] getDefaultConfigurationEntry() {
HashMap <String, String> options = new HashMap <String, String> (2);
if (mechName == null || mechName.equals("krb5")) {
if (isServerSide(caller)) {
// Assuming the keytab file can be found through
// krb5 config file or under user home directory
options.put("useKeyTab", "true");
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
options.put("principal", "*");
options.put("isInitiator", "false");
} else {
options.put("useTicketCache", "true");
options.put("doNotPrompt", "false");
}
return new AppConfigurationEntry[] {
new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options)
};
}
return null;
}
private static boolean isServerSide (GSSCaller caller) {
return GSSCaller.CALLER_ACCEPT == caller ||
GSSCaller.CALLER_SSL_SERVER == caller;
}
}

View file

@ -0,0 +1,547 @@
/*
* Copyright (c) 2000, 2009, 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;
import java.lang.reflect.InvocationTargetException;
import org.ietf.jgss.*;
import java.security.AccessController;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Enumeration;
import java.util.Iterator;
import sun.security.jgss.spi.*;
import sun.security.jgss.wrapper.NativeGSSFactory;
import sun.security.jgss.wrapper.SunNativeProvider;
import sun.security.action.GetPropertyAction;
/**
* This class stores the list of providers that this
* GSS-Implementation is configured to use. The GSSManagerImpl class
* queries this class whenever it needs a mechanism's factory.<p>
*
* This class stores an ordered list of pairs of the form
* {@code <provider, oid>}. When it attempts to instantiate a mechanism
* defined by oid o, it steps through the list looking for an entry
* with oid=o, or with oid=null. (An entry with oid=null matches all
* mechanisms.) When it finds such an entry, the corresponding
* provider is approached for the mechanism's factory class.
* At instantiation time this list in initialized to contain those
* system wide providers that contain a property of the form
* "GssApiMechanism.x.y.z..." where "x.y.z..." is a numeric object
* identifier with numbers x, y, z, etc. Such a property is defined
* to map to that provider's implementation of the MechanismFactory
* interface for the mechanism x.y.z...
* As and when a MechanismFactory is instantiated, it is
* cached for future use. <p>
*
* An application can cause more providers to be added by means of
* the addProviderAtFront and addProviderAtEnd methods on
* GSSManager which get delegated to this class. The
* addProviderAtFront method can also cause a change in the ordering
* of the providers without adding any new providers, by causing a
* provider to move up in a list. The method addProviderAtEnd can
* only add providers at the end of the list if they are not already
* in the list. The rationale is that an application will call
* addProviderAtFront when it wants a provider to be used in
* preference over the default ones. And it will call
* addProviderAtEnd when it wants a provider to be used in case
* the system ones don't suffice.<p>
*
* If a mechanism's factory is being obtained from a provider as a
* result of encountering a entryof the form {@code <provider, oid>} where
* oid is non-null, then the assumption is that the application added
* this entry and it wants this mechanism to be obtained from this
* provider. Thus is the provider does not actually contain the
* requested mechanism, an exception will be thrown. However, if the
* entry were of the form {@code <provider, null>}, then it is viewed more
* liberally and is simply skipped over if the provider does not claim to
* support the requested mechanism.
*/
public final class ProviderList {
private static final String PROV_PROP_PREFIX = "GssApiMechanism.";
private static final int PROV_PROP_PREFIX_LEN =
PROV_PROP_PREFIX.length();
private static final String SPI_MECH_FACTORY_TYPE
= "sun.security.jgss.spi.MechanismFactory";
// Undocumented property?
private static final String DEFAULT_MECH_PROP =
"sun.security.jgss.mechanism";
public static final Oid DEFAULT_MECH_OID;
static {
/*
* Set the default mechanism. Kerberos v5 is the default
* mechanism unless it is overridden by a system property.
* with a valid OID value
*/
Oid defOid = null;
String defaultOidStr = AccessController.doPrivileged
(new GetPropertyAction(DEFAULT_MECH_PROP));
if (defaultOidStr != null) {
defOid = GSSUtil.createOid(defaultOidStr);
}
DEFAULT_MECH_OID =
(defOid == null ? GSSUtil.GSS_KRB5_MECH_OID : defOid);
}
private ArrayList<PreferencesEntry> preferences =
new ArrayList<PreferencesEntry>(5);
private HashMap<PreferencesEntry, MechanismFactory> factories =
new HashMap<PreferencesEntry, MechanismFactory>(5);
private HashSet<Oid> mechs = new HashSet<Oid>(5);
final private GSSCaller caller;
public ProviderList(GSSCaller caller, boolean useNative) {
this.caller = caller;
Provider[] provList;
if (useNative) {
provList = new Provider[1];
provList[0] = new SunNativeProvider();
} else {
provList = Security.getProviders();
}
for (int i = 0; i < provList.length; i++) {
Provider prov = provList[i];
try {
addProviderAtEnd(prov, null);
} catch (GSSException ge) {
// Move on to the next provider
GSSUtil.debug("Error in adding provider " +
prov.getName() + ": " + ge);
}
} // End of for loop
}
/**
* Determines if the given provider property represents a GSS-API
* Oid to MechanismFactory mapping.
* @return true if this is a GSS-API property, false otherwise.
*/
private boolean isMechFactoryProperty(String prop) {
return (prop.startsWith(PROV_PROP_PREFIX) ||
prop.regionMatches(true, 0, // Try ignoring case
PROV_PROP_PREFIX, 0,
PROV_PROP_PREFIX_LEN));
}
private Oid getOidFromMechFactoryProperty(String prop)
throws GSSException {
String oidPart = prop.substring(PROV_PROP_PREFIX_LEN);
return new Oid(oidPart);
}
// So the existing code do not have to be changed
synchronized public MechanismFactory getMechFactory(Oid mechOid)
throws GSSException {
if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID;
return getMechFactory(mechOid, null);
}
/**
* Obtains a MechanismFactory for a given mechanism. If the
* specified provider is not null, then the impl from the
* provider is used. Otherwise, the most preferred impl based
* on the configured preferences is used.
* @param mechOid the oid of the desired mechanism
* @return a MechanismFactory for the desired mechanism.
* @throws GSSException when the specified provider does not
* support the desired mechanism, or when no provider supports
* the desired mechanism.
*/
synchronized public MechanismFactory getMechFactory(Oid mechOid,
Provider p)
throws GSSException {
if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID;
if (p == null) {
// Iterate thru all preferences to find right provider
String className;
PreferencesEntry entry;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
entry = list.next();
if (entry.impliesMechanism(mechOid)) {
MechanismFactory retVal = getMechFactory(entry, mechOid);
if (retVal != null) return retVal;
}
} // end of while loop
throw new GSSExceptionImpl(GSSException.BAD_MECH, mechOid);
} else {
// Use the impl from the specified provider; return null if the
// the mech is unsupported by the specified provider.
PreferencesEntry entry = new PreferencesEntry(p, mechOid);
return getMechFactory(entry, mechOid);
}
}
/**
* Helper routine that uses a preferences entry to obtain an
* implementation of a MechanismFactory from it.
* @param e the preferences entry that contains the provider and
* either a null of an explicit oid that matched the oid of the
* desired mechanism.
* @param mechOid the oid of the desired mechanism
* @throws GSSException If the application explicitly requested
* this entry's provider to be used for the desired mechanism but
* some problem is encountered
*/
private MechanismFactory getMechFactory(PreferencesEntry e, Oid mechOid)
throws GSSException {
Provider p = e.getProvider();
/*
* See if a MechanismFactory was previously instantiated for
* this provider and mechanism combination.
*/
PreferencesEntry searchEntry = new PreferencesEntry(p, mechOid);
MechanismFactory retVal = factories.get(searchEntry);
if (retVal == null) {
/*
* Apparently not. Now try to instantiate this class from
* the provider.
*/
String prop = PROV_PROP_PREFIX + mechOid.toString();
String className = p.getProperty(prop);
if (className != null) {
retVal = getMechFactoryImpl(p, className, mechOid, caller);
factories.put(searchEntry, retVal);
} else {
/*
* This provider does not support this mechanism.
* If the application explicitly requested that
* this provider be used for this mechanism, then
* throw an exception
*/
if (e.getOid() != null) {
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName() +
" does not support mechanism " + mechOid);
}
}
}
return retVal;
}
/**
* Helper routine to obtain a MechanismFactory implementation
* from the same class loader as the provider of this
* implementation.
* @param p the provider whose classloader must be used for
* instantiating the desired MechanismFactory
* @ param className the name of the MechanismFactory class
* @throws GSSException If some error occurs when trying to
* instantiate this MechanismFactory.
*/
private static MechanismFactory getMechFactoryImpl(Provider p,
String className,
Oid mechOid,
GSSCaller caller)
throws GSSException {
try {
Class<?> baseClass = Class.forName(SPI_MECH_FACTORY_TYPE);
/*
* Load the implementation class with the same class loader
* that was used to load the provider.
* In order to get the class loader of a class, the
* caller's class loader must be the same as or an ancestor of
* the class loader being returned. Otherwise, the caller must
* have "getClassLoader" permission, or a SecurityException
* will be thrown.
*/
ClassLoader cl = p.getClass().getClassLoader();
Class<?> implClass;
if (cl != null) {
implClass = cl.loadClass(className);
} else {
implClass = Class.forName(className);
}
if (baseClass.isAssignableFrom(implClass)) {
java.lang.reflect.Constructor<?> c =
implClass.getConstructor(GSSCaller.class);
MechanismFactory mf = (MechanismFactory) (c.newInstance(caller));
if (mf instanceof NativeGSSFactory) {
((NativeGSSFactory) mf).setMech(mechOid);
}
return mf;
} else {
throw createGSSException(p, className, "is not a " +
SPI_MECH_FACTORY_TYPE, null);
}
} catch (ClassNotFoundException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (NoSuchMethodException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (InvocationTargetException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (InstantiationException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (IllegalAccessException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (SecurityException e) {
throw createGSSException(p, className, "cannot be created", e);
}
}
// Only used by getMechFactoryImpl
private static GSSException createGSSException(Provider p,
String className,
String trailingMsg,
Exception cause) {
String errClassInfo = className + " configured by " +
p.getName() + " for GSS-API Mechanism Factory ";
return new GSSExceptionImpl(GSSException.BAD_MECH,
errClassInfo + trailingMsg,
cause);
}
public Oid[] getMechs() {
return mechs.toArray(new Oid[] {});
}
synchronized public void addProviderAtFront(Provider p, Oid mechOid)
throws GSSException {
PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
PreferencesEntry oldEntry;
boolean foundSomeMech;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
oldEntry = list.next();
if (newEntry.implies(oldEntry))
list.remove();
}
if (mechOid == null) {
foundSomeMech = addAllMechsFromProvider(p);
} else {
String oidStr = mechOid.toString();
if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName()
+ " does not support "
+ oidStr);
mechs.add(mechOid);
foundSomeMech = true;
}
if (foundSomeMech) {
preferences.add(0, newEntry);
}
}
synchronized public void addProviderAtEnd(Provider p, Oid mechOid)
throws GSSException {
PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
PreferencesEntry oldEntry;
boolean foundSomeMech;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
oldEntry = list.next();
if (oldEntry.implies(newEntry))
return;
}
// System.out.println("addProviderAtEnd: No it is not redundant");
if (mechOid == null)
foundSomeMech = addAllMechsFromProvider(p);
else {
String oidStr = mechOid.toString();
if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName()
+ " does not support "
+ oidStr);
mechs.add(mechOid);
foundSomeMech = true;
}
if (foundSomeMech) {
preferences.add(newEntry);
}
}
/**
* Helper routine to go through all properties contined in a
* provider and add its mechanisms to the list of supported
* mechanisms. If no default mechanism has been assinged so far,
* it sets the default MechanismFactory and Oid as well.
* @param p the provider to query
* @return true if there is at least one mechanism that this
* provider contributed, false otherwise
*/
private boolean addAllMechsFromProvider(Provider p) {
String prop;
boolean retVal = false;
// Get all props for this provider
Enumeration<Object> props = p.keys();
// See if there are any GSS prop's
while (props.hasMoreElements()) {
prop = (String) props.nextElement();
if (isMechFactoryProperty(prop)) {
// Ok! This is a GSS provider!
try {
Oid mechOid = getOidFromMechFactoryProperty(prop);
mechs.add(mechOid);
retVal = true;
} catch (GSSException e) {
// Skip to next property
GSSUtil.debug("Ignore the invalid property " +
prop + " from provider " + p.getName());
}
} // Processed GSS property
} // while loop
return retVal;
}
/**
* Stores a provider and a mechanism oid indicating that the
* provider should be used for the mechanism. If the mechanism
* Oid is null, then it indicates that this preference holds for
* any mechanism.<p>
*
* The ProviderList maintains an ordered list of
* PreferencesEntry's and iterates thru them as it tries to
* instantiate MechanismFactory's.
*/
private static final class PreferencesEntry {
private Provider p;
private Oid oid;
PreferencesEntry(Provider p, Oid oid) {
this.p = p;
this.oid = oid;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PreferencesEntry)) {
return false;
}
PreferencesEntry that = (PreferencesEntry)other;
if (this.p.getName().equals(that.p.getName())) {
if (this.oid != null && that.oid != null) {
return this.oid.equals(that.oid);
} else {
return (this.oid == null && that.oid == null);
}
}
return false;
}
public int hashCode() {
int result = 17;
result = 37 * result + p.getName().hashCode();
if (oid != null) {
result = 37 * result + oid.hashCode();
}
return result;
}
/**
* Determines if a preference implies another. A preference
* implies another if the latter is subsumed by the
* former. e.g., <Provider1, null> implies <Provider1, OidX>
* because the null in the former indicates that it should
* be used for all mechanisms.
*/
boolean implies(Object other) {
if (other instanceof PreferencesEntry) {
PreferencesEntry temp = (PreferencesEntry) other;
return (equals(temp) ||
p.getName().equals(temp.p.getName()) &&
oid == null);
} else {
return false;
}
}
Provider getProvider() {
return p;
}
Oid getOid() {
return oid;
}
/**
* Determines if this entry is applicable to the desired
* mechanism. The entry is applicable to the desired mech if
* it contains the same oid or if it contains a null oid
* indicating that it is applicable to all mechs.
* @param mechOid the desired mechanism
* @return true if the provider in this entry should be
* queried for this mechanism.
*/
boolean impliesMechanism(Oid oid) {
return (this.oid == null || this.oid.equals(oid));
}
// For debugging
public String toString() {
StringBuilder sb = new StringBuilder("<");
sb.append(p.getName());
sb.append(", ");
sb.append(oid);
sb.append(">");
return sb.toString();
}
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2000, 2016, 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;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidParameterException;
import java.security.ProviderException;
import sun.security.jgss.krb5.Krb5MechFactory;
import sun.security.jgss.spnego.SpNegoMechFactory;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
/**
* Defines the Sun JGSS provider.
* Will merger this with the Sun security provider
* sun.security.provider.Sun when the JGSS src is merged with the JDK
* src.
*
* Mechanisms supported are:
*
* - Kerberos v5 as defined in RFC 1964.
* Oid is 1.2.840.113554.1.2.2
*
* - SPNEGO as defined in RFC 2478
* Oid is 1.3.6.1.5.5.2
*
* [Dummy mechanism is no longer compiled:
* - Dummy mechanism. This is primarily useful to test a multi-mech
* environment.
* Oid is 1.3.6.1.4.1.42.2.26.1.2]
*
* @author Mayank Upadhyay
*/
public final class SunProvider extends Provider {
private static final long serialVersionUID = -238911724858694198L;
private static final String INFO = "Sun " +
"(Kerberos v5, SPNEGO)";
// "(Kerberos v5, Dummy GSS-API Mechanism)";
private static final class ProviderService extends Provider.Service {
ProviderService(Provider p, String type, String algo, String cn) {
super(p, type, algo, cn, null, null);
}
@Override
public Object newInstance(Object ctrParamObj)
throws NoSuchAlgorithmException {
String type = getType();
if (ctrParamObj != null) {
throw new InvalidParameterException
("constructorParameter not used with " + type +
" engines");
}
String algo = getAlgorithm();
try {
if (type.equals("GssApiMechanism")) {
if (algo.equals("1.2.840.113554.1.2.2")) {
return new Krb5MechFactory();
} else if (algo.equals("1.3.6.1.5.5.2")) {
return new SpNegoMechFactory();
}
}
} catch (Exception ex) {
throw new NoSuchAlgorithmException
("Error constructing " + type + " for " +
algo + " using SunJGSS", ex);
}
throw new ProviderException("No impl for " + algo +
" " + type);
}
}
public SunProvider() {
/* We are the Sun JGSS provider */
super("SunJGSS", PROVIDER_VER, INFO);
final Provider p = this;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
putService(new ProviderService(p, "GssApiMechanism",
"1.2.840.113554.1.2.2",
"sun.security.jgss.krb5.Krb5MechFactory"));
putService(new ProviderService(p, "GssApiMechanism",
"1.3.6.1.5.5.2",
"sun.security.jgss.spnego.SpNegoMechFactory"));
return null;
}
});
}
}

View file

@ -0,0 +1,419 @@
/*
* 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;
import org.ietf.jgss.MessageProp;
import java.util.LinkedList;
/**
* A utility class that implements a number list that keeps track of which
* tokens have arrived by storing their token numbers in the list. It helps
* detect old tokens, out of sequence tokens, and duplicate tokens.
*
* Each element of the list is an interval [a, b]. Its existence in the
* list implies that all token numbers in the range a, a+1, ..., b-1, b
* have arrived. Gaps in arrived token numbers are represented by the
* numbers that fall in between two elements of the list. eg. {[a,b],
* [c,d]} indicates that the token numbers b+1, ..., c-1 have not arrived
* yet.
*
* The maximum number of intervals that we keep track of is
* MAX_INTERVALS. Thus if there are too many gaps, then some of the older
* sequence numbers are deleted from the list. The earliest sequence number
* that exists in the list is the windowStart. The next expected sequence
* number, or expectedNumber, is one greater than the latest sequence
* number in the list.
*
* The list keeps track the first token number that should have arrived
* (initNumber) so that it is able to detect if certain numbers occur after
* the first valid token number but before windowStart. That would happen
* if the number of elements (intervals) exceeds MAX_INTERVALS and some
* initial elements had to be deleted.
*
* The working of the list is optimized for the normal case where the
* tokens arrive in sequence.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class TokenTracker {
static final int MAX_INTERVALS = 5;
private int initNumber;
private int windowStart;
private int expectedNumber;
private int windowStartIndex = 0;
private LinkedList<Entry> list = new LinkedList<Entry>();
public TokenTracker(int initNumber) {
this.initNumber = initNumber;
this.windowStart = initNumber;
this.expectedNumber = initNumber;
// Make an entry with one less than the expected first token
Entry entry = new Entry(initNumber-1);
list.add(entry);
}
/**
* Returns the index for the entry into which this number will fit. If
* there is none, it returns the index of the last interval
* which precedes this number. It returns -1 if the number needs to be
* a in a new interval ahead of the whole list.
*/
private int getIntervalIndex(int number) {
Entry entry = null;
int i;
// Start from the rear to optimize for the normal case
for (i = list.size() - 1; i >= 0; i--) {
entry = list.get(i);
if (entry.compareTo(number) <= 0)
break;
}
return i;
}
/**
* Sets the sequencing and replay information for the given token
* number.
*
* The following represents the number line with positions of
* initNumber, windowStart, expectedNumber marked on it. Regions in
* between them show the different sequencing and replay state
* possibilites for tokens that fall in there.
*
* (1) windowStart
* initNumber expectedNumber
* | |
* ---|---------------------------|---
* GAP | DUP/UNSEQ | GAP
*
*
* (2) initNumber windowStart expectedNumber
* | | |
* ---|---------------|--------------|---
* GAP | OLD | DUP/UNSEQ | GAP
*
*
* (3) windowStart
* expectedNumber initNumber
* | |
* ---|---------------------------|---
* DUP/UNSEQ | GAP | DUP/UNSEQ
*
*
* (4) expectedNumber initNumber windowStart
* | | |
* ---|---------------|--------------|---
* DUP/UNSEQ | GAP | OLD | DUP/UNSEQ
*
*
*
* (5) windowStart expectedNumber initNumber
* | | |
* ---|---------------|--------------|---
* OLD | DUP/UNSEQ | GAP | OLD
*
*
*
* (This analysis leaves out the possibility that expectedNumber passes
* initNumber after wrapping around. That may be added later.)
*/
synchronized public final void getProps(int number, MessageProp prop) {
boolean gap = false;
boolean old = false;
boolean unsequenced = false;
boolean duplicate = false;
// System.out.println("\n\n==========");
// System.out.println("TokenTracker.getProps(): number=" + number);
// System.out.println(toString());
int pos = getIntervalIndex(number);
Entry entry = null;
if (pos != -1)
entry = list.get(pos);
// Optimize for the expected case:
if (number == expectedNumber) {
expectedNumber++;
} else {
// Next trivial case is to check for duplicate
if (entry != null && entry.contains(number))
duplicate = true;
else {
if (expectedNumber >= initNumber) {
// Cases (1) and (2)
if (number > expectedNumber) {
gap = true;
} else if (number >= windowStart) {
unsequenced = true;
} else if (number >= initNumber) {
old = true;
} else {
gap = true;
}
} else {
// Cases (3), (4) and (5)
if (number > expectedNumber) {
if (number < initNumber) {
gap = true;
} else if (windowStart >= initNumber) {
if (number >= windowStart) {
unsequenced = true;
} else
old = true;
} else {
old = true;
}
} else if (windowStart > expectedNumber) {
unsequenced = true;
} else if (number < windowStart) {
old = true;
} else
unsequenced = true;
}
}
}
if (!duplicate && !old)
add(number, pos);
if (gap)
expectedNumber = number+1;
prop.setSupplementaryStates(duplicate, old, unsequenced, gap,
0, null);
// System.out.println("Leaving with state:");
// System.out.println(toString());
// System.out.println("==========\n");
}
/**
* Adds the number to the list just after the entry that is currently
* at position prevEntryPos. If prevEntryPos is -1, then the number
* will begin a new interval at the front of the list.
*/
private void add(int number, int prevEntryPos) {
Entry entry;
Entry entryBefore = null;
Entry entryAfter = null;
boolean appended = false;
boolean prepended = false;
if (prevEntryPos != -1) {
entryBefore = list.get(prevEntryPos);
// Can this number simply be added to the previous interval?
if (number == (entryBefore.getEnd() + 1)) {
entryBefore.setEnd(number);
appended = true;
}
}
// Now check the interval that follows this number
int nextEntryPos = prevEntryPos + 1;
if ((nextEntryPos) < list.size()) {
entryAfter = list.get(nextEntryPos);
// Can this number simply be added to the next interval?
if (number == (entryAfter.getStart() - 1)) {
if (!appended) {
entryAfter.setStart(number);
} else {
// Merge the two entries
entryAfter.setStart(entryBefore.getStart());
list.remove(prevEntryPos);
// Index of any entry following this gets decremented
if (windowStartIndex > prevEntryPos)
windowStartIndex--;
}
prepended = true;
}
}
if (prepended || appended)
return;
/*
* At this point we know that the number will start a new interval
* which needs to be added to the list. We might have to recyle an
* older entry in the list.
*/
if (list.size() < MAX_INTERVALS) {
entry = new Entry(number);
if (prevEntryPos < windowStartIndex)
windowStartIndex++; // due to the insertion which will happen
} else {
/*
* Delete the entry that marks the start of the current window.
* The marker will automatically point to the next entry in the
* list when this happens. If the current entry is at the end
* of the list then set the marker to the start of the list.
*/
int oldWindowStartIndex = windowStartIndex;
if (windowStartIndex == (list.size() - 1))
windowStartIndex = 0;
entry = list.remove(oldWindowStartIndex);
windowStart = list.get(windowStartIndex).getStart();
entry.setStart(number);
entry.setEnd(number);
if (prevEntryPos >= oldWindowStartIndex) {
prevEntryPos--; // due to the deletion that just happened
} else {
/*
* If the start of the current window just moved from the
* end of the list to the front of the list, and if the new
* entry will be added to the front of the list, then
* the new entry is the actual window start.
* eg, Consider { [-10, -8], ..., [-6, -3], [3, 9]}. In
* this list, suppose the element [3, 9] is the start of
* the window and has to be deleted to make place to add
* [-12, -12]. The resultant list will be
* {[-12, -12], [-10, -8], ..., [-6, -3]} and the new start
* of the window should be the element [-12, -12], not
* [-10, -8] which succeeded [3, 9] in the old list.
*/
if (oldWindowStartIndex != windowStartIndex) {
// windowStartIndex is 0 at this point
if (prevEntryPos == -1)
// The new entry is going to the front
windowStart = number;
} else {
// due to the insertion which will happen:
windowStartIndex++;
}
}
}
// Finally we are ready to actually add to the list at index
// 'prevEntryPos+1'
list.add(prevEntryPos+1, entry);
}
public String toString() {
StringBuilder sb = new StringBuilder("TokenTracker: ");
sb.append(" initNumber=").append(initNumber);
sb.append(" windowStart=").append(windowStart);
sb.append(" expectedNumber=").append(expectedNumber);
sb.append(" windowStartIndex=").append(windowStartIndex);
sb.append("\n\tIntervals are: {");
for (int i = 0; i < list.size(); i++) {
if (i != 0)
sb.append(", ");
sb.append(list.get(i).toString());
}
sb.append('}');
return sb.toString();
}
/**
* An entry in the list that represents the sequence of received
* tokens. Each entry is actaully an interval of numbers, all of which
* have been received.
*/
class Entry {
private int start;
private int end;
Entry(int number) {
start = number;
end = number;
}
/**
* Returns -1 if this interval represented by this entry precedes
* the number, 0 if the number is contained in the interval,
* and -1 if the interval occurs after the number.
*/
final int compareTo(int number) {
if (start > number)
return 1;
else if (end < number)
return -1;
else
return 0;
}
final boolean contains(int number) {
return (number >= start &&
number <= end);
}
final void append(int number) {
if (number == (end + 1))
end = number;
}
final void setInterval(int start, int end) {
this.start = start;
this.end = end;
}
final void setEnd(int end) {
this.end = end;
}
final void setStart(int start) {
this.start = start;
}
final int getStart() {
return start;
}
final int getEnd() {
return end;
}
public String toString() {
return ("[" + start + ", " + end + "]");
}
}
}

View 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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,170 @@
/*
* 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 sun.security.krb5.*;
import java.net.InetAddress;
import sun.security.krb5.internal.AuthorizationData;
import sun.security.krb5.internal.KerberosTime;
class InitSecContextToken extends InitialToken {
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(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())
// Use the same sequence number as the peer
// (Behaviour exhibited by the Windows SSPI server)
context.resetMySequenceNumber(peerSeqNumber);
context.setAuthTime(
new KerberosTime(apReq.getCreds().getAuthTime()).toString());
context.setTktFlags(apReq.getCreds().getFlags());
AuthorizationData ad = apReq.getCreds().getAuthzData();
context.setAuthzData(ad);
}
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;
}
}

View 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!
StringBuilder sb = new StringBuilder("\"");
sb.append(delegateTo.getName()).append('\"');
String realm = delegateTo.getRealmAsString();
sb.append(" \"krbtgt/").append(realm).append('@');
sb.append(realm).append('\"');
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
DelegationPermission perm =
new DelegationPermission(sb.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;
}

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

File diff suppressed because it is too large Load diff

View 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 {
}

View file

@ -0,0 +1,367 @@
/*
* Copyright (c) 2000, 2012, 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 javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosPrincipal;
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;
private Krb5InitCredential(Krb5NameElement name,
byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal server,
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);
this.name = name;
try {
// Cache this for later use by the sun.security.krb5 package.
krb5Credentials = new Credentials(asn1Encoding,
client.getName(),
server.getName(),
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 server,
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);
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);
}
return new Krb5InitCredential(name,
tgt.getEncoded(),
tgt.getClient(),
tgt.getServer(),
tgt.getSessionKey().getEncoded(),
tgt.getSessionKeyType(),
tgt.getFlags(),
tgt.getAuthTime(),
tgt.getStartTime(),
tgt.getEndTime(),
tgt.getRenewTill(),
tgt.getClientAddresses());
}
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 sPrinc = delegatedCred.getServer();
KerberosPrincipal client = null;
KerberosPrincipal server = null;
Krb5NameElement credName = null;
if (cPrinc != null) {
String fullName = cPrinc.getName();
credName = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
client = new KerberosPrincipal(fullName);
}
// XXX Compare name to credName
if (sPrinc != null) {
server =
new KerberosPrincipal(sPrinc.getName(),
KerberosPrincipal.KRB_NT_SRV_INST);
}
return new Krb5InitCredential(credName,
delegatedCred,
delegatedCred.getEncoded(),
client,
server,
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 {
int retVal = 0;
Date d = getEndTime();
if (d == null) {
return 0;
}
retVal = (int)(d.getTime() - (new Date().getTime()));
return 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.getTicket(
realCaller,
clientPrincipal, null, 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;
}
}
}

View file

@ -0,0 +1,236 @@
/*
* 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.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() {
this(GSSCaller.CALLER_UNKNOWN);
}
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);
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;
}
}

View file

@ -0,0 +1,348 @@
/*
* 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.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);
}
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;
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2012, 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 java.util.Date;
import sun.security.krb5.internal.Ticket;
/**
* 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");
}
}

View 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;
}
}

View file

@ -0,0 +1,266 @@
/*
* Copyright (c) 2003, 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 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.
* If the ticket can not be found in the Subject, and if
* useSubjectCredsOnly is false, then obtain ticket from
* a LoginContext.
*/
static KerberosTicket getTicket(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);
// 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,
serverPrincipal, 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();
return 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());
};
public static Credentials ticketToCreds(KerberosTicket kerbTicket)
throws KrbException, IOException {
return new Credentials(
kerbTicket.getEncoded(),
kerbTicket.getClient().getName(),
kerbTicket.getServer().getName(),
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);
}
}

View file

@ -0,0 +1,721 @@
/*
* Copyright (c) 2000, 2015, 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(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();
}
}

View file

@ -0,0 +1,643 @@
/*
* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import 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, 0, 8);
}
/**
* 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
}

View 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();
}
}

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

View 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(),
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;
}
}

View file

@ -0,0 +1,216 @@
/*
* Copyright (c) 2002, 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.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) {
@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);
}
}
}
} else {
if (serverPrincipal == null ||
ticket.getServer().getName().equals(serverPrincipal)) {
if (clientPrincipal == null ||
clientPrincipal.equals(
ticket.getClient().getName())) {
if (oneOnly) {
return ticket;
} else {
// Record names so that tickets will
// all belong to same principals
if (clientPrincipal == null) {
clientPrincipal =
ticket.getClient().getName();
}
if (serverPrincipal == null) {
serverPrincipal =
ticket.getServer().getName();
}
answer.add(credClass.cast(ticket));
}
}
}
}
}
}
}
}
return answer;
}
}
}

View 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 */
}
}

View 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 */);
}
}

View file

@ -0,0 +1,407 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific instance of a GSS
* security context.
* A GSSContextSpi object can be thought of having 3 states:
* -before initialization
* -during initialization with its peer
* -after it is established
* <p>
* The context options can only be requested in state 1. In state 3,
* the per message operations are available to the callers. The get
* methods for the context options will return the requested options
* while in state 1 and 2, and the established values in state 3.
* Some mechanisms may allow the access to the per-message operations
* and the context flags before the context is fully established. The
* isProtReady method is used to indicate that these services are
* available.
* <p>
* <strong>
* Context establishment tokens are defined in a mechanism independent
* format in section 3.1 of RFC 2743. The GSS-Framework will add
* and remove the mechanism independent header portion of this token format
* depending on whether a token is received or is being sent. The mechanism
* should only generate or expect to read the inner-context token portion.
* <br>
* On the other hands, tokens used for per-message calls are generated
* entirely by the mechanism. It is possible that the mechanism chooses to
* encase inner-level per-message tokens in a header similar to that used
* for initial tokens, however, this is upto the mechanism to do. The token
* to/from the per-message calls are opaque to the GSS-Framework.
* </strong>
* <p>
* An attempt has been made to allow for reading the peer's tokens from an
* InputStream and writing tokens for the peer to an OutputStream. This
* allows applications to pass in streams that are obtained from their network
* connections and thus minimize the buffer copies that will happen. This
* is especially important for tokens generated by wrap() which are
* proportional in size to the length of the application data being
* wrapped, and are probably also the most frequently used type of tokens.
* <p>
* It is anticipated that most applications will want to use wrap() in a
* fashion where they obtain the application bytes to wrap from a byte[]
* but want to output the wrap token straight to an
* OutputStream. Similarly, they will want to use unwrap() where they read
* the token directly form an InputStream but output it to some byte[] for
* the application to process. Unfortunately the high level GSS bindings
* do not contain overloaded forms of wrap() and unwrap() that do just
* this, however we have accomodated those cases here with the expectation
* that this will be rolled into the high level bindings sooner or later.
*
* @author Mayank Upadhyay
*/
public interface GSSContextSpi {
public Provider getProvider();
// The specification for the following methods mirrors the
// specification of the same methods in the GSSContext interface, as
// defined in RFC 2853.
public void requestLifetime(int lifetime) throws GSSException;
public void requestMutualAuth(boolean state) throws GSSException;
public void requestReplayDet(boolean state) throws GSSException;
public void requestSequenceDet(boolean state) throws GSSException;
public void requestCredDeleg(boolean state) throws GSSException;
public void requestAnonymity(boolean state) throws GSSException;
public void requestConf(boolean state) throws GSSException;
public void requestInteg(boolean state) throws GSSException;
public void requestDelegPolicy(boolean state) throws GSSException;
public void setChannelBinding(ChannelBinding cb) throws GSSException;
public boolean getCredDelegState();
public boolean getMutualAuthState();
public boolean getReplayDetState();
public boolean getSequenceDetState();
public boolean getAnonymityState();
public boolean getDelegPolicyState();
public boolean isTransferable() throws GSSException;
public boolean isProtReady();
public boolean isInitiator();
public boolean getConfState();
public boolean getIntegState();
public int getLifetime();
public boolean isEstablished();
public GSSNameSpi getSrcName() throws GSSException;
public GSSNameSpi getTargName() throws GSSException;
public Oid getMech() throws GSSException;
public GSSCredentialSpi getDelegCred() throws GSSException;
/**
* Initiator context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
* <p>
* This method is called by the GSS-Framework when the application
* calls the initSecContext method on the GSSContext implementation
* that it has a reference to.
* <p>
* All overloaded forms of GSSContext.initSecContext() can be handled
* with this mechanism level initSecContext. Since the output token
* from this method is a fixed size, not exeedingly large, and a one
* time deal, an overloaded form that takes an OutputStream has not
* been defined. The GSS-Framwork can write the returned byte[] to any
* application provided OutputStream. Similarly, any application input
* int he form of byte arrays will be wrapped in an input stream by the
* GSS-Framework and then passed here.
* <p>
* <strong>
* The GSS-Framework will strip off the leading mechanism independent
* GSS-API header. In other words, only the mechanism specific
* inner-context token of RFC 2743 section 3.1 will be available on the
* InputStream.
* </strong>
*
* @param is contains the inner context token portion of the GSS token
* received from the peer. On the first call to initSecContext, there
* will be no token hence it will be ignored.
* @param mechTokenSize the size of the inner context token as read by
* the GSS-Framework from the mechanism independent GSS-API level
* header.
* @return any inner-context token required to be sent to the peer as
* part of a GSS token. The mechanism should not add the mechanism
* independent part of the token. The GSS-Framework will add that on
* the way out.
* @exception GSSException may be thrown
*/
public byte[] initSecContext(InputStream is, int mechTokenSize)
throws GSSException;
/**
* Acceptor's context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
* <p>
* This method is called by the GSS-Framework when the application
* calls the acceptSecContext method on the GSSContext implementation
* that it has a reference to.
* <p>
* All overloaded forms of GSSContext.acceptSecContext() can be handled
* with this mechanism level acceptSecContext. Since the output token
* from this method is a fixed size, not exeedingly large, and a one
* time deal, an overloaded form that takes an OutputStream has not
* been defined. The GSS-Framwork can write the returned byte[] to any
* application provided OutputStream. Similarly, any application input
* int he form of byte arrays will be wrapped in an input stream by the
* GSS-Framework and then passed here.
* <p>
* <strong>
* The GSS-Framework will strip off the leading mechanism independent
* GSS-API header. In other words, only the mechanism specific
* inner-context token of RFC 2743 section 3.1 will be available on the
* InputStream.
* </strong>
*
* @param is contains the inner context token portion of the GSS token
* received from the peer.
* @param mechTokenSize the size of the inner context token as read by
* the GSS-Framework from the mechanism independent GSS-API level
* header.
* @return any inner-context token required to be sent to the peer as
* part of a GSS token. The mechanism should not add the mechanism
* independent part of the token. The GSS-Framework will add that on
* the way out.
* @exception GSSException may be thrown
*/
public byte[] acceptSecContext(InputStream is, int mechTokenSize)
throws GSSException;
/**
* Queries the context for largest data size to accommodate
* the specified protection and for the token to remain less then
* maxTokSize.
*
* @param qop the quality of protection that the context will be
* asked to provide.
* @param confReq a flag indicating whether confidentiality will be
* requested or not
* @param maxTokSize the maximum size of the output token
* @return the maximum size for the input message that can be
* provided to the wrap() method in order to guarantee that these
* requirements are met.
* @exception GSSException may be thrown
*/
public int getWrapSizeLimit(int qop, boolean confReq, int maxTokSize)
throws GSSException;
/**
* Provides per-message token encapsulation.
*
* @param is the user-provided message to be protected
* @param os the token to be sent to the peer. It includes
* the message from <i>is</i> with the requested protection.
* @param msgProp on input it contains the requested qop and
* confidentiality state, on output, the applied values
* @exception GSSException may be thrown
* @see unwrap
*/
public void wrap(InputStream is, OutputStream os, MessageProp msgProp)
throws GSSException;
/**
* For apps that want simplicity and don't care about buffer copies.
*/
public byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* For apps that care about buffer copies but either cannot use streams
* or want to avoid them for whatever reason. (Say, they are using
* block ciphers.)
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int wrap(byte[] inBuf, int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* For apps that want to read from a specific application provided
* buffer but want to write directly to the network stream.
*/
/*
* Can be achieved by converting the input buffer to a
* ByteInputStream. Provided to keep the API consistent
* with unwrap.
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public void wrap(byte[] inBuf, int offset, int len,
OutputStream os, MessageProp msgProp)
throws GSSException;
*/
/**
* Retrieves the message token previously encapsulated in the wrap
* call.
*
* @param is the token from the peer
* @param os unprotected message data
* @param msgProp will contain the applied qop and confidentiality
* of the input token and any informatory status values
* @exception GSSException may be thrown
* @see wrap
*/
public void unwrap(InputStream is, OutputStream os,
MessageProp msgProp) throws GSSException;
/**
* For apps that want simplicity and don't care about buffer copies.
*/
public byte[] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* For apps that care about buffer copies but either cannot use streams
* or want to avoid them for whatever reason. (Say, they are using
* block ciphers.)
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int unwrap(byte[] inBuf, int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* For apps that care about buffer copies and want to read
* straight from the network, but also want the output in a specific
* application provided buffer, say to reduce buffer allocation or
* subsequent copy.
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int unwrap(InputStream is,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* Applies per-message integrity services.
*
* @param is the user-provided message
* @param os the token to be sent to the peer along with the
* message token. The message token <b>is not</b> encapsulated.
* @param msgProp on input the desired QOP and output the applied QOP
* @exception GSSException
*/
public void getMIC(InputStream is, OutputStream os,
MessageProp msgProp)
throws GSSException;
public byte[] getMIC(byte[] inMsg, int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* Checks the integrity of the supplied tokens.
* This token was previously generated by getMIC.
*
* @param is token generated by getMIC
* @param msgStr the message to check integrity for
* @param mProp will contain the applied QOP and confidentiality
* states of the token as well as any informatory status codes
* @exception GSSException may be thrown
*/
public void verifyMIC(InputStream is, InputStream msgStr,
MessageProp mProp) throws GSSException;
public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException;
/**
* Produces a token representing this context. After this call
* the context will no longer be usable until an import is
* performed on the returned token.
*
* @return exported context token
* @exception GSSException may be thrown
*/
public byte[] export() throws GSSException;
/**
* Releases context resources and terminates the
* context between 2 peer.
*
* @exception GSSException may be thrown
*/
public void dispose() throws GSSException;
/**
* Return the mechanism-specific attribute associated with {@code type}.
*
* @param type the type of the attribute requested
* @return the attribute
* @throws GSSException see {@link ExtendedGSSContext#inquireSecContext}
* for details
*/
public Object inquireSecContext(String type)
throws GSSException;
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2000, 2012, 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.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific credential
* element. A GSSCredential is conceptually a container class of several
* credential elements from different mechanisms.
*
* @author Mayank Upadhyay
*/
public interface GSSCredentialSpi {
public Provider getProvider();
/**
* Called to invalidate this credential element and release
* any system recourses and cryptographic information owned
* by the credential.
*
* @exception GSSException with major codes NO_CRED and FAILURE
*/
public void dispose() throws GSSException;
/**
* 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 GSSNameSpi getName() throws GSSException;
/**
* Returns the init lifetime remaining.
*
* @return the init lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getInitLifetime() throws GSSException;
/**
* Returns the accept lifetime remaining.
*
* @return the accept lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getAcceptLifetime() throws GSSException;
/**
* Determines if this credential element can be used by a context
* initiator.
* @return true if it can be used for initiating contexts
*/
public boolean isInitiatorCredential() throws GSSException;
/**
* Determines if this credential element can be used by a context
* acceptor.
* @return true if it can be used for accepting contexts
*/
public boolean isAcceptorCredential() throws GSSException;
/**
* Returns the oid representing the underlying credential
* mechanism oid.
*
* @return the Oid for this credential mechanism
* @exception GSSException may be thrown
*/
public Oid getMechanism();
/**
* Impersonates another client.
*
* @param name the client to impersonate
* @return the new credential
* @exception GSSException may be thrown
*/
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException;
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2000, 2005, 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.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific name element. A
* GSSName is conceptually a container class of several name elements from
* different mechanisms.
*
* @author Mayank Upadhyay
*/
public interface GSSNameSpi {
public Provider getProvider();
/**
* Equals method for the GSSNameSpi objects.
* If either name denotes an anonymous principal, the call should
* return false.
*
* @param name 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 name) throws GSSException;
/**
* 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);
/**
* Returns a hashcode value for this GSSNameSpi.
*
* @return a hashCode value
*/
public int hashCode();
/**
* Returns a flat name representation for this object. The name
* format is defined in RFC 2078.
*
* @return the flat name representation for this object
* @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
* BAD_NAME, FAILURE.
*/
public byte[] export() throws GSSException;
/**
* Get the mechanism type that this NameElement corresponds to.
*
* @return the Oid of the mechanism type
*/
public Oid getMechanism();
/**
* 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();
/**
* Returns the oid describing the format of the printable name.
*
* @return the Oid for the format of the printed name
*/
public Oid getStringNameType();
/**
* Indicates if this name object represents an Anonymous name.
*/
public boolean isAnonymousName();
}

View file

@ -0,0 +1,214 @@
/*
* 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.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by the factory class for every
* plugin mechanism. The GSSManager locates an implementation of this
* interface by querying the security providers installed on the
* system. For a provider to support a mechanism defined by Oid x.y.z,
* the provider master file would have to contain a mapping from the
* property "GssApiMechanism.x.y.z" to an implementation class that serves
* as the factory for that mechanism.
* <p>
* e.g., If a provider master file contained the a mapping from the
* property "GssApiMechanism.1.2.840.113554.1.2.2" to the class name
* "com.foo.krb5.Krb5GssFactory", then the GSS-API framework would assume
* that com.foo.krb5.Krb5GssFactory implements the MechanismFactory
* interface and that it can be used to obtain elements required by for
* supporting this mechanism.
*
* @author Mayank Upadhyay
*/
public interface MechanismFactory {
/**
* Returns the Oid of the mechanism that this factory supports.
* @return the Oid
*/
public Oid getMechanismOid();
/**
* Returns the provider that this factory came from.
* @return the provider
*/
public Provider getProvider();
/**
* Returns the GSS-API nametypes that this mechanism can
* support. Having this method helps the GSS-Framework decide quickly
* if a certain mechanism can be skipped when importing a name.
* @return an array of the Oid's corresponding to the different GSS-API
* nametypes supported
* @see org.ietf.jgss.GSSName
*/
public Oid[] getNameTypes() throws GSSException;
/**
* Creates a credential element for this mechanism to be included as
* part of a GSSCredential implementation. A GSSCredential is
* conceptually a container class of several credential elements from
* different mechanisms. A GSS-API credential can be used either for
* initiating GSS security contexts or for accepting them. This method
* also accepts parameters that indicate what usage is expected and how
* long the life of the credential should be. It is not necessary that
* the mechanism honor the request for lifetime. An application will
* always query an acquired GSSCredential to determine what lifetime it
* got back.<p>
*
* <b>Not all mechanisms support the concept of one credential element
* that can be used for both initiating and accepting a context. In the
* event that an application requests usage INITIATE_AND_ACCEPT for a
* credential from such a mechanism, the GSS framework will need to
* obtain two different credential elements from the mechanism, one
* that will have usage INITIATE_ONLY and another that will have usage
* ACCEPT_ONLY. The mechanism will help the GSS-API realize this by
* returning a credential element with usage INITIATE_ONLY or
* ACCEPT_ONLY prompting it to make another call to
* getCredentialElement, this time with the other usage mode. The
* mechanism indicates the missing mode by returning a 0 lifetime for
* it.</b>
*
* @param name the mechanism level name element for the entity whose
* credential is desired. A null value indicates that a mechanism
* dependent default choice is to be made.
* @param initLifetime indicates the lifetime (in seconds) that is
* requested for this credential to be used at the context initiator's
* end. This value should be ignored if the usage is
* ACCEPT_ONLY. Predefined contants are available in the
* org.ietf.jgss.GSSCredential interface.
* @param acceptLifetime indicates the lifetime (in seconds) that is
* requested for this credential to be used at the context acceptor's
* end. This value should be ignored if the usage is
* INITIATE_ONLY. Predefined contants are available in the
* org.ietf.jgss.GSSCredential interface.
* @param usage One of the values GSSCredential.INIATE_ONLY,
* GSSCredential.ACCEPT_ONLY, and GSSCredential.INITIATE_AND_ACCEPT.
* @see org.ietf.jgss.GSSCredential
* @throws GSSException if one of the error situations described in RFC
* 2743 with the GSS_Acquire_Cred or GSS_Add_Cred calls occurs.
*/
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime, int usage) throws GSSException;
/**
* Creates a name element for this mechanism to be included as part of
* a GSSName implementation. A GSSName is conceptually a container
* class of several name elements from different mechanisms. A GSSName
* can be created either with a String or with a sequence of
* bytes. This factory method accepts the name in a String. Such a name
* can generally be assumed to be printable and may be returned from
* the name element's toString() method.
*
* @param nameStr a string containing the characters describing this
* entity to the mechanism
* @param nameType an Oid serving as a clue as to how the mechanism should
* interpret the nameStr
* @throws GSSException if any of the errors described in RFC 2743 for
* the GSS_Import_Name or GSS_Canonicalize_Name calls occur.
*/
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException;
/**
* This is a variation of the factory method that accepts a String for
* the characters that make up the name. Usually the String characters
* are assumed to be printable. The bytes passed in to this method have
* to be converted to characters using some encoding of the mechanism's
* choice. It is recommended that UTF-8 be used. (Note that UTF-8
* preserves the encoding for 7-bit ASCII characters.)
* <p>
* An exported name will generally be passed in using this method.
*
* @param name the bytes describing this entity to the mechanism
* @param nameType an Oid serving as a clue as to how the mechanism should
* interpret the nameStr
* @throws GSSException if any of the errors described in RFC 2743 for
* the GSS_Import_Name or GSS_Canonicalize_Name calls occur.
*/
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException;
/**
* Creates a security context for this mechanism so that it can be used
* on the context initiator's side.
*
* @param peer the name element from this mechanism that represents the
* peer
* @param myInitiatorCred a credential element for the context
* initiator obtained previously from this mechanism. The identity of
* the context initiator can be obtained from this credential. Passing
* a value of null here indicates that a default entity of the
* mechanism's choice should be assumed to be the context initiator and
* that default credentials should be applied.
* @param lifetime the requested lifetime (in seconds) for the security
* context. Predefined contants are available in the
* org.ietf.jgss.GSSContext interface.
* @throws GSSException if any of the errors described in RFC 2743 in
* the GSS_Init_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred,
int lifetime) throws GSSException;
/**
* Creates a security context for this mechanism so thatit can be used
* on the context acceptor's side.
*
* @param myAcceptorCred a credential element for the context acceptor
* obtained previously from this mechanism. The identity of the context
* acceptor cna be obtained from this credential. Passing a value of
* null here indicates that tha default entity of the mechanism's
* choice should be assumed to be the context acceptor and default
* credentials should be applied.
*
* @throws GSSException if any of the errors described in RFC 2743 in
* the GSS_Accept_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException;
/**
* Creates a security context from a previously exported (serialized)
* security context. Note that this is different from Java
* serialization and is defined at a mechanism level to interoperate
* over the wire with non-Java implementations. Either the initiator or
* the acceptor can export and then import a security context.
* Implementations of mechanism contexts are not required to implement
* exporting and importing.
*
* @param exportedContext the bytes representing this security context
* @throws GSSException is any of the errors described in RFC 2743 in
* the GSS_Import_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException;
}

View file

@ -0,0 +1,229 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.util.*;
/**
* Implements the SPNEGO NegTokenInit token
* as specified in RFC 2478
*
* NegTokenInit ::= SEQUENCE {
* mechTypes [0] MechTypeList OPTIONAL,
* reqFlags [1] ContextFlags OPTIONAL,
* mechToken [2] OCTET STRING OPTIONAL,
* mechListMIC [3] OCTET STRING OPTIONAL
* }
*
* MechTypeList ::= SEQUENCE OF MechType
*
* MechType::= OBJECT IDENTIFIER
*
* ContextFlags ::= BIT STRING {
* delegFlag (0),
* mutualFlag (1),
* replayFlag (2),
* sequenceFlag (3),
* anonFlag (4),
* confFlag (5),
* integFlag (6)
* }
*
* @author Seema Malkani
* @since 1.6
*/
public class NegTokenInit extends SpNegoToken {
// DER-encoded mechTypes
private byte[] mechTypes = null;
private Oid[] mechTypeList = null;
private BitArray reqFlags = null;
private byte[] mechToken = null;
private byte[] mechListMIC = null;
NegTokenInit(byte[] mechTypes, BitArray flags,
byte[] token, byte[] mechListMIC)
{
super(NEG_TOKEN_INIT_ID);
this.mechTypes = mechTypes;
this.reqFlags = flags;
this.mechToken = token;
this.mechListMIC = mechListMIC;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to parse SPNEGO tokens
public NegTokenInit(byte[] in) throws GSSException {
super(NEG_TOKEN_INIT_ID);
parseToken(in);
}
final byte[] encode() throws GSSException {
try {
// create negInitToken
DerOutputStream initToken = new DerOutputStream();
// DER-encoded mechTypes with CONTEXT 00
if (mechTypes != null) {
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), mechTypes);
}
// write context flags with CONTEXT 01
if (reqFlags != null) {
DerOutputStream flags = new DerOutputStream();
flags.putUnalignedBitString(reqFlags);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), flags);
}
// mechToken with CONTEXT 02
if (mechToken != null) {
DerOutputStream dataValue = new DerOutputStream();
dataValue.putOctetString(mechToken);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), dataValue);
}
// mechListMIC with CONTEXT 03
if (mechListMIC != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"sending MechListMIC");
}
DerOutputStream mic = new DerOutputStream();
mic.putOctetString(mechListMIC);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), mic);
}
// insert in a SEQUENCE
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, initToken);
return out.toByteArray();
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenInit token : " + e.getMessage());
}
}
private void parseToken(byte[] in) throws GSSException {
try {
DerValue der = new DerValue(in);
// verify NegotiationToken type token
if (!der.isContextSpecific((byte) NEG_TOKEN_INIT_ID)) {
throw new IOException("SPNEGO NegoTokenInit : " +
"did not have right token type");
}
DerValue tmp1 = der.data.getDerValue();
if (tmp1.tag != DerValue.tag_Sequence) {
throw new IOException("SPNEGO NegoTokenInit : " +
"did not have the Sequence tag");
}
// parse various fields if present
int lastField = -1;
while (tmp1.data.available() > 0) {
DerValue tmp2 = tmp1.data.getDerValue();
if (tmp2.isContextSpecific((byte)0x00)) {
// get the DER-encoded sequence of mechTypes
lastField = checkNextField(lastField, 0);
DerInputStream mValue = tmp2.data;
mechTypes = mValue.toByteArray();
// read all the mechTypes
DerValue[] mList = mValue.getSequence(0);
mechTypeList = new Oid[mList.length];
ObjectIdentifier mech = null;
for (int i = 0; i < mList.length; i++) {
mech = mList[i].getOID();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"reading Mechanism Oid = " + mech);
}
mechTypeList[i] = new Oid(mech.toString());
}
} else if (tmp2.isContextSpecific((byte)0x01)) {
lastField = checkNextField(lastField, 1);
// received reqFlags, skip it
} else if (tmp2.isContextSpecific((byte)0x02)) {
lastField = checkNextField(lastField, 2);
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"reading Mech Token");
}
mechToken = tmp2.data.getOctetString();
} else if (tmp2.isContextSpecific((byte)0x03)) {
lastField = checkNextField(lastField, 3);
if (!GSSUtil.useMSInterop()) {
mechListMIC = tmp2.data.getOctetString();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"MechListMIC Token = " +
getHexBytes(mechListMIC));
}
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenInit token : " + e.getMessage());
}
}
byte[] getMechTypes() {
return mechTypes;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to find the mechs in SPNEGO tokens
public Oid[] getMechTypeList() {
return mechTypeList;
}
BitArray getReqFlags() {
return reqFlags;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to access the mech token portion of SPNEGO tokens
public byte[] getMechToken() {
return mechToken;
}
byte[] getMechListMIC() {
return mechListMIC;
}
}

View file

@ -0,0 +1,213 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.util.*;
/**
* Implements the SPNEGO NegTokenTarg token
* as specified in RFC 2478
*
* NegTokenTarg ::= SEQUENCE {
* negResult [0] ENUMERATED {
* accept_completed (0),
* accept_incomplete (1),
* reject (2) } OPTIONAL,
* supportedMech [1] MechType OPTIONAL,
* responseToken [2] OCTET STRING OPTIONAL,
* mechListMIC [3] OCTET STRING OPTIONAL
* }
*
* MechType::= OBJECT IDENTIFIER
*
*
* @author Seema Malkani
* @since 1.6
*/
public class NegTokenTarg extends SpNegoToken {
private int negResult = 0;
private Oid supportedMech = null;
private byte[] responseToken = null;
private byte[] mechListMIC = null;
NegTokenTarg(int result, Oid mech, byte[] token, byte[] mechListMIC)
{
super(NEG_TOKEN_TARG_ID);
this.negResult = result;
this.supportedMech = mech;
this.responseToken = token;
this.mechListMIC = mechListMIC;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to parse SPNEGO tokens
public NegTokenTarg(byte[] in) throws GSSException {
super(NEG_TOKEN_TARG_ID);
parseToken(in);
}
final byte[] encode() throws GSSException {
try {
// create negTargToken
DerOutputStream targToken = new DerOutputStream();
// write the negotiated result with CONTEXT 00
DerOutputStream result = new DerOutputStream();
result.putEnumerated(negResult);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), result);
// supportedMech with CONTEXT 01
if (supportedMech != null) {
DerOutputStream mech = new DerOutputStream();
byte[] mechType = supportedMech.getDER();
mech.write(mechType);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), mech);
}
// response Token with CONTEXT 02
if (responseToken != null) {
DerOutputStream rspToken = new DerOutputStream();
rspToken.putOctetString(responseToken);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), rspToken);
}
// mechListMIC with CONTEXT 03
if (mechListMIC != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"sending MechListMIC");
}
DerOutputStream mic = new DerOutputStream();
mic.putOctetString(mechListMIC);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), mic);
} else if (GSSUtil.useMSInterop()) {
// required for MS-interoperability
if (responseToken != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"sending additional token for MS Interop");
}
DerOutputStream rspToken = new DerOutputStream();
rspToken.putOctetString(responseToken);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), rspToken);
}
}
// insert in a SEQUENCE
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, targToken);
return out.toByteArray();
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenTarg token : " + e.getMessage());
}
}
private void parseToken(byte[] in) throws GSSException {
try {
DerValue der = new DerValue(in);
// verify NegotiationToken type token
if (!der.isContextSpecific((byte) NEG_TOKEN_TARG_ID)) {
throw new IOException("SPNEGO NegoTokenTarg : " +
"did not have the right token type");
}
DerValue tmp1 = der.data.getDerValue();
if (tmp1.tag != DerValue.tag_Sequence) {
throw new IOException("SPNEGO NegoTokenTarg : " +
"did not have the Sequence tag");
}
// parse various fields if present
int lastField = -1;
while (tmp1.data.available() > 0) {
DerValue tmp2 = tmp1.data.getDerValue();
if (tmp2.isContextSpecific((byte)0x00)) {
lastField = checkNextField(lastField, 0);
negResult = tmp2.data.getEnumerated();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: negotiated" +
" result = " + getNegoResultString(negResult));
}
} else if (tmp2.isContextSpecific((byte)0x01)) {
lastField = checkNextField(lastField, 1);
ObjectIdentifier mech = tmp2.data.getOID();
supportedMech = new Oid(mech.toString());
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"supported mechanism = " + supportedMech);
}
} else if (tmp2.isContextSpecific((byte)0x02)) {
lastField = checkNextField(lastField, 2);
responseToken = tmp2.data.getOctetString();
} else if (tmp2.isContextSpecific((byte)0x03)) {
lastField = checkNextField(lastField, 3);
if (!GSSUtil.useMSInterop()) {
mechListMIC = tmp2.data.getOctetString();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"MechListMIC Token = " +
getHexBytes(mechListMIC));
}
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenTarg token : " + e.getMessage());
}
}
int getNegotiatedResult() {
return negResult;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to find the supported mech in SPNEGO tokens
public Oid getSupportedMech() {
return supportedMech;
}
byte[] getResponseToken() {
return responseToken;
}
byte[] getMechListMIC() {
return mechListMIC;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2005, 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.spnego;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.spi.GSSNameSpi;
import sun.security.jgss.spi.GSSCredentialSpi;
/**
* This class is the cred element implementation for SPNEGO mech.
* NOTE: The current implementation can only support one mechanism.
* This should be changed once multi-mechanism support is needed.
*
* @author Valerie Peng
* @since 1.6
*/
public class SpNegoCredElement implements GSSCredentialSpi {
private GSSCredentialSpi cred = null;
public SpNegoCredElement(GSSCredentialSpi cred) throws GSSException {
this.cred = cred;
}
Oid getInternalMech() {
return cred.getMechanism();
}
// Used by GSSUtil.populateCredentials()
public GSSCredentialSpi getInternalCred() {
return cred;
}
public Provider getProvider() {
return SpNegoMechFactory.PROVIDER;
}
public void dispose() throws GSSException {
cred.dispose();
}
public GSSNameSpi getName() throws GSSException {
return cred.getName();
}
public int getInitLifetime() throws GSSException {
return cred.getInitLifetime();
}
public int getAcceptLifetime() throws GSSException {
return cred.getAcceptLifetime();
}
public boolean isInitiatorCredential() throws GSSException {
return cred.isInitiatorCredential();
}
public boolean isAcceptorCredential() throws GSSException {
return cred.isAcceptorCredential();
}
public Oid getMechanism() {
return GSSUtil.GSS_SPNEGO_MECH_OID;
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
return cred.impersonate(name);
}
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2005, 2009, 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.spnego;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.jgss.krb5.Krb5MechFactory;
import sun.security.jgss.krb5.Krb5InitCredential;
import sun.security.jgss.krb5.Krb5AcceptCredential;
import sun.security.jgss.krb5.Krb5NameElement;
import java.security.Provider;
import java.util.Vector;
/**
* SpNego Mechanism plug in for JGSS
* This is the properties object required by the JGSS framework.
* All mechanism specific information is defined here.
*
* @author Seema Malkani
* @since 1.6
*/
public final class SpNegoMechFactory implements MechanismFactory {
static final Provider PROVIDER =
new sun.security.jgss.SunProvider();
static final Oid GSS_SPNEGO_MECH_OID =
GSSUtil.createOid("1.3.6.1.5.5.2");
private static Oid[] nameTypes =
new Oid[] { GSSName.NT_USER_NAME,
GSSName.NT_HOSTBASED_SERVICE,
GSSName.NT_EXPORT_NAME};
// The default underlying mech of SPNEGO, must not be SPNEGO itself.
private static final Oid DEFAULT_SPNEGO_MECH_OID =
ProviderList.DEFAULT_MECH_OID.equals(GSS_SPNEGO_MECH_OID)?
GSSUtil.GSS_KRB5_MECH_OID:
ProviderList.DEFAULT_MECH_OID;
// Use an instance of a GSSManager whose provider list
// does not include native provider
final GSSManagerImpl manager;
final Oid[] availableMechs;
private static SpNegoCredElement getCredFromSubject(GSSNameSpi name,
boolean initiate)
throws GSSException {
Vector<SpNegoCredElement> creds =
GSSUtil.searchSubject(name, GSS_SPNEGO_MECH_OID,
initiate, SpNegoCredElement.class);
SpNegoCredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
GSSCredentialSpi cred = result.getInternalCred();
if (GSSUtil.isKerberosMech(cred.getMechanism())) {
if (initiate) {
Krb5InitCredential krbCred = (Krb5InitCredential) cred;
Krb5MechFactory.checkInitCredPermission
((Krb5NameElement) krbCred.getName());
} else {
Krb5AcceptCredential krbCred = (Krb5AcceptCredential) cred;
Krb5MechFactory.checkAcceptCredPermission
((Krb5NameElement) krbCred.getName(), name);
}
}
}
return result;
}
public SpNegoMechFactory() {
this(GSSCaller.CALLER_UNKNOWN);
}
public SpNegoMechFactory(GSSCaller caller) {
manager = new GSSManagerImpl(caller, false);
Oid[] mechs = manager.getMechs();
availableMechs = new Oid[mechs.length-1];
for (int i = 0, j = 0; i < mechs.length; i++) {
// Skip SpNego mechanism
if (!mechs[i].equals(GSS_SPNEGO_MECH_OID)) {
availableMechs[j++] = mechs[i];
}
}
// Move the preferred mech to first place
for (int i=0; i<availableMechs.length; i++) {
if (availableMechs[i].equals(DEFAULT_SPNEGO_MECH_OID)) {
if (i != 0) {
availableMechs[i] = availableMechs[0];
availableMechs[0] = DEFAULT_SPNEGO_MECH_OID;
}
break;
}
}
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
return manager.getNameElement(
nameStr, nameType, DEFAULT_SPNEGO_MECH_OID);
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
return manager.getNameElement(name, nameType, DEFAULT_SPNEGO_MECH_OID);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime,
int usage) throws GSSException {
SpNegoCredElement credElement = getCredFromSubject
(name, (usage != GSSCredential.ACCEPT_ONLY));
if (credElement == null) {
// get CredElement for the default Mechanism
credElement = new SpNegoCredElement
(manager.getCredentialElement(name, initLifetime,
acceptLifetime, null, usage));
}
return credElement;
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred, int lifetime)
throws GSSException {
// get SpNego mechanism context
if (myInitiatorCred == null) {
myInitiatorCred = getCredFromSubject(null, true);
} else if (!(myInitiatorCred instanceof SpNegoCredElement)) {
// convert to SpNegoCredElement
SpNegoCredElement cred = new SpNegoCredElement(myInitiatorCred);
return new SpNegoContext(this, peer, cred, lifetime);
}
return new SpNegoContext(this, peer, myInitiatorCred, lifetime);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException {
// get SpNego mechanism context
if (myAcceptorCred == null) {
myAcceptorCred = getCredFromSubject(null, false);
} else if (!(myAcceptorCred instanceof SpNegoCredElement)) {
// convert to SpNegoCredElement
SpNegoCredElement cred = new SpNegoCredElement(myAcceptorCred);
return new SpNegoContext(this, cred);
}
return new SpNegoContext(this, myAcceptorCred);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
// get SpNego mechanism context
return new SpNegoContext(this, exportedContext);
}
public final Oid getMechanismOid() {
return GSS_SPNEGO_MECH_OID;
}
public Provider getProvider() {
return PROVIDER;
}
public Oid[] getNameTypes() {
// nameTypes is cloned in GSSManager.getNamesForMech
return nameTypes;
}
}

View file

@ -0,0 +1,207 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.util.*;
import sun.security.jgss.*;
/**
* Astract class for SPNEGO tokens.
* Implementation is based on RFC 2478
*
* NegotiationToken ::= CHOICE {
* negTokenInit [0] NegTokenInit,
* negTokenTarg [1] NegTokenTarg }
*
*
* @author Seema Malkani
* @since 1.6
*/
abstract class SpNegoToken extends GSSToken {
static final int NEG_TOKEN_INIT_ID = 0x00;
static final int NEG_TOKEN_TARG_ID = 0x01;
static enum NegoResult {
ACCEPT_COMPLETE,
ACCEPT_INCOMPLETE,
REJECT,
};
private int tokenType;
// property
static final boolean DEBUG = SpNegoContext.DEBUG;
/**
* The object identifier corresponding to the SPNEGO GSS-API
* mechanism.
*/
public static ObjectIdentifier OID;
static {
try {
OID = new ObjectIdentifier(SpNegoMechFactory.
GSS_SPNEGO_MECH_OID.toString());
} catch (IOException ioe) {
// should not happen
}
}
/**
* Creates SPNEGO token of the specified type.
*/
protected SpNegoToken(int tokenType) {
this.tokenType = tokenType;
}
/**
* Returns the individual encoded SPNEGO token
*
* @return the encoded token
* @exception GSSException
*/
abstract byte[] encode() throws GSSException;
/**
* Returns the encoded SPNEGO token
* Note: inserts the required CHOICE tags
*
* @return the encoded token
* @exception GSSException
*/
byte[] getEncoded() throws IOException, GSSException {
// get the token encoded value
DerOutputStream token = new DerOutputStream();
token.write(encode());
// now insert the CHOICE
switch (tokenType) {
case NEG_TOKEN_INIT_ID:
// Insert CHOICE of Negotiation Token
DerOutputStream initToken = new DerOutputStream();
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) NEG_TOKEN_INIT_ID), token);
return initToken.toByteArray();
case NEG_TOKEN_TARG_ID:
// Insert CHOICE of Negotiation Token
DerOutputStream targToken = new DerOutputStream();
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) NEG_TOKEN_TARG_ID), token);
return targToken.toByteArray();
default:
return token.toByteArray();
}
}
/**
* Returns the SPNEGO token type
*
* @return the token type
*/
final int getType() {
return tokenType;
}
/**
* Returns a string representing the token type.
*
* @param tokenType the token type for which a string name is desired
* @return the String name of this token type
*/
static String getTokenName(int type) {
switch (type) {
case NEG_TOKEN_INIT_ID:
return "SPNEGO NegTokenInit";
case NEG_TOKEN_TARG_ID:
return "SPNEGO NegTokenTarg";
default:
return "SPNEGO Mechanism Token";
}
}
/**
* Returns the enumerated type of the Negotiation result.
*
* @param result the negotiated result represented by integer
* @return the enumerated type of Negotiated result
*/
static NegoResult getNegoResultType(int result) {
switch (result) {
case 0:
return NegoResult.ACCEPT_COMPLETE;
case 1:
return NegoResult.ACCEPT_INCOMPLETE;
case 2:
return NegoResult.REJECT;
default:
// unknown - return optimistic result
return NegoResult.ACCEPT_COMPLETE;
}
}
/**
* Returns a string representing the negotiation result.
*
* @param result the negotiated result
* @return the String message of this negotiated result
*/
static String getNegoResultString(int result) {
switch (result) {
case 0:
return "Accept Complete";
case 1:
return "Accept InComplete";
case 2:
return "Reject";
default:
return ("Unknown Negotiated Result: " + result);
}
}
/**
* Checks if the context tag in a sequence is in correct order. The "last"
* value must be smaller than "current".
* @param last the last tag seen
* @param current the current tag
* @return the current tag, used as the next value for last
* @throws GSSException if there's a wrong order
*/
static int checkNextField(int last, int current) throws GSSException {
if (last < current) {
return current;
} else {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SpNegoToken token : wrong order");
}
}
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2005, 2017, 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.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.jgss.spi.GSSNameSpi;
/**
* This class is essentially a wrapper class for the gss_cred_id_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
public class GSSCredElement implements GSSCredentialSpi {
private int usage;
long pCred; // Pointer to the gss_cred_id_t structure
private GSSNameElement name = null;
private GSSLibStub cStub;
// Perform the necessary ServicePermission check on this cred
void doServicePermCheck() throws GSSException {
if (GSSUtil.isKerberosMech(cStub.getMech())) {
if (System.getSecurityManager() != null) {
if (isInitiatorCredential()) {
String tgsName = Krb5Util.getTGSName(name);
Krb5Util.checkServicePermission(tgsName, "initiate");
}
if (isAcceptorCredential() &&
name != GSSNameElement.DEF_ACCEPTOR) {
String krbName = name.getKrbName();
Krb5Util.checkServicePermission(krbName, "accept");
}
}
}
}
// Construct delegation cred using the actual context mech and srcName
GSSCredElement(long pCredentials, GSSNameElement srcName, Oid mech)
throws GSSException {
pCred = pCredentials;
cStub = GSSLibStub.getInstance(mech);
usage = GSSCredential.INITIATE_ONLY;
name = srcName;
}
GSSCredElement(GSSNameElement name, int lifetime, int usage,
GSSLibStub stub) throws GSSException {
cStub = stub;
this.usage = usage;
if (name != null) { // Could be GSSNameElement.DEF_ACCEPTOR
this.name = name;
doServicePermCheck();
pCred = cStub.acquireCred(this.name.pName, lifetime, usage);
} else {
pCred = cStub.acquireCred(0, lifetime, usage);
this.name = new GSSNameElement(cStub.getCredName(pCred), cStub);
doServicePermCheck();
}
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public void dispose() throws GSSException {
name = null;
if (pCred != 0) {
pCred = cStub.releaseCred(pCred);
}
}
public GSSNameElement getName() throws GSSException {
return (name == GSSNameElement.DEF_ACCEPTOR ?
null : name);
}
public int getInitLifetime() throws GSSException {
if (isInitiatorCredential()) {
return cStub.getCredTime(pCred);
} else return 0;
}
public int getAcceptLifetime() throws GSSException {
if (isAcceptorCredential()) {
return cStub.getCredTime(pCred);
} else return 0;
}
public boolean isInitiatorCredential() {
return (usage != GSSCredential.ACCEPT_ONLY);
}
public boolean isAcceptorCredential() {
return (usage != GSSCredential.INITIATE_ONLY);
}
public Oid getMechanism() {
return cStub.getMech();
}
public String toString() {
// No hex bytes available for native impl
return "N/A";
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
dispose();
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
throw new GSSException(GSSException.FAILURE, -1,
"Not supported yet");
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2005, 2014, 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.wrapper;
import java.util.Hashtable;
import org.ietf.jgss.Oid;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.ChannelBinding;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.GSSException;
import sun.security.jgss.GSSUtil;
/**
* This class is essentially a JNI calling stub for all wrapper classes.
*
* @author Valerie Peng
* @since 1.6
*/
class GSSLibStub {
private Oid mech;
private long pMech;
/**
* Initialization routine to dynamically load function pointers.
*
* @param lib library name to dlopen
* @param debug set to true for reporting native debugging info
* @return true if succeeded, false otherwise.
*/
static native boolean init(String lib, boolean debug);
private static native long getMechPtr(byte[] oidDerEncoding);
// Miscellaneous routines
static native Oid[] indicateMechs();
native Oid[] inquireNamesForMech() throws GSSException;
// Name related routines
native void releaseName(long pName);
native long importName(byte[] name, Oid type);
native boolean compareName(long pName1, long pName2);
native long canonicalizeName(long pName);
native byte[] exportName(long pName) throws GSSException;
native Object[] displayName(long pName) throws GSSException;
// Credential related routines
native long acquireCred(long pName, int lifetime, int usage)
throws GSSException;
native long releaseCred(long pCred);
native long getCredName(long pCred);
native int getCredTime(long pCred);
native int getCredUsage(long pCred);
// Context related routines
native NativeGSSContext importContext(byte[] interProcToken);
native byte[] initContext(long pCred, long targetName, ChannelBinding cb,
byte[] inToken, NativeGSSContext context);
native byte[] acceptContext(long pCred, ChannelBinding cb,
byte[] inToken, NativeGSSContext context);
native long[] inquireContext(long pContext);
native Oid getContextMech(long pContext);
native long getContextName(long pContext, boolean isSrc);
native int getContextTime(long pContext);
native long deleteContext(long pContext);
native int wrapSizeLimit(long pContext, int flags, int qop, int outSize);
native byte[] exportContext(long pContext);
native byte[] getMic(long pContext, int qop, byte[] msg);
native void verifyMic(long pContext, byte[] token, byte[] msg,
MessageProp prop) ;
native byte[] wrap(long pContext, byte[] msg, MessageProp prop);
native byte[] unwrap(long pContext, byte[] msgToken, MessageProp prop);
private static Hashtable<Oid, GSSLibStub>
table = new Hashtable<Oid, GSSLibStub>(5);
static GSSLibStub getInstance(Oid mech) throws GSSException {
GSSLibStub s = table.get(mech);
if (s == null) {
s = new GSSLibStub(mech);
table.put(mech, s);
}
return s;
}
private GSSLibStub(Oid mech) throws GSSException {
SunNativeProvider.debug("Created GSSLibStub for mech " + mech);
this.mech = mech;
this.pMech = getMechPtr(mech.getDER());
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof GSSLibStub)) {
return false;
}
return (mech.equals(((GSSLibStub) obj).getMech()));
}
public int hashCode() {
return mech.hashCode();
}
Oid getMech() {
return mech;
}
}

View file

@ -0,0 +1,296 @@
/*
* Copyright (c) 2005, 2017, 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.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import java.security.Security;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import sun.security.krb5.Realm;
import sun.security.jgss.GSSUtil;
import sun.security.util.ObjectIdentifier;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.GSSNameSpi;
import javax.security.auth.kerberos.ServicePermission;
/**
* This class is essentially a wrapper class for the gss_name_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
public class GSSNameElement implements GSSNameSpi {
long pName = 0; // Pointer to the gss_name_t structure
private String printableName;
private Oid printableType;
private GSSLibStub cStub;
static final GSSNameElement DEF_ACCEPTOR = new GSSNameElement();
private static Oid getNativeNameType(Oid nameType, GSSLibStub stub) {
if (GSSUtil.NT_GSS_KRB5_PRINCIPAL.equals(nameType)) {
Oid[] supportedNTs = null;
try {
supportedNTs = stub.inquireNamesForMech();
} catch (GSSException ge) {
if (ge.getMajor() == GSSException.BAD_MECH &&
GSSUtil.isSpNegoMech(stub.getMech())) {
// Workaround known Heimdal issue and retry with KRB5
try {
stub = GSSLibStub.getInstance
(GSSUtil.GSS_KRB5_MECH_OID);
supportedNTs = stub.inquireNamesForMech();
} catch (GSSException ge2) {
// Should never happen
SunNativeProvider.debug("Name type list unavailable: " +
ge2.getMajorString());
}
} else {
SunNativeProvider.debug("Name type list unavailable: " +
ge.getMajorString());
}
}
if (supportedNTs != null) {
for (int i = 0; i < supportedNTs.length; i++) {
if (supportedNTs[i].equals(nameType)) return nameType;
}
// Special handling the specified name type
SunNativeProvider.debug("Override " + nameType +
" with mechanism default(null)");
return null; // Use mechanism specific default
}
}
return nameType;
}
private GSSNameElement() {
printableName = "<DEFAULT ACCEPTOR>";
}
GSSNameElement(long pNativeName, GSSLibStub stub) throws GSSException {
assert(stub != null);
if (pNativeName == 0) {
throw new GSSException(GSSException.BAD_NAME);
}
// Note: pNativeName is assumed to be a MN.
pName = pNativeName;
cStub = stub;
setPrintables();
}
GSSNameElement(byte[] nameBytes, Oid nameType, GSSLibStub stub)
throws GSSException {
assert(stub != null);
if (nameBytes == null) {
throw new GSSException(GSSException.BAD_NAME);
}
cStub = stub;
byte[] name = nameBytes;
if (nameType != null) {
// Special handling the specified name type if
// necessary
nameType = getNativeNameType(nameType, stub);
if (GSSName.NT_EXPORT_NAME.equals(nameType)) {
// Need to add back the mech Oid portion (stripped
// off by GSSNameImpl class prior to calling this
// method) for "NT_EXPORT_NAME"
byte[] mechBytes = null;
DerOutputStream dout = new DerOutputStream();
Oid mech = cStub.getMech();
try {
dout.putOID(new ObjectIdentifier(mech.toString()));
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE, e);
}
mechBytes = dout.toByteArray();
name = new byte[2 + 2 + mechBytes.length + 4 + nameBytes.length];
int pos = 0;
name[pos++] = 0x04;
name[pos++] = 0x01;
name[pos++] = (byte) (mechBytes.length>>>8);
name[pos++] = (byte) mechBytes.length;
System.arraycopy(mechBytes, 0, name, pos, mechBytes.length);
pos += mechBytes.length;
name[pos++] = (byte) (nameBytes.length>>>24);
name[pos++] = (byte) (nameBytes.length>>>16);
name[pos++] = (byte) (nameBytes.length>>>8);
name[pos++] = (byte) nameBytes.length;
System.arraycopy(nameBytes, 0, name, pos, nameBytes.length);
}
}
pName = cStub.importName(name, nameType);
setPrintables();
SecurityManager sm = System.getSecurityManager();
if (sm != null && !Realm.AUTODEDUCEREALM) {
String krbName = getKrbName();
int atPos = krbName.lastIndexOf('@');
if (atPos != -1) {
String atRealm = krbName.substring(atPos);
// getNativeNameType() can modify NT_GSS_KRB5_PRINCIPAL to null
if ((nameType == null
|| nameType.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
&& new String(nameBytes).endsWith(atRealm)) {
// Created from Kerberos name with realm, no need to check
} else {
try {
sm.checkPermission(new ServicePermission(atRealm, "-"));
} catch (SecurityException se) {
// Do not chain the actual exception to hide info
throw new GSSException(GSSException.FAILURE);
}
}
}
}
SunNativeProvider.debug("Imported " + printableName + " w/ type " +
printableType);
}
private void setPrintables() throws GSSException {
Object[] printables = null;
printables = cStub.displayName(pName);
assert((printables != null) && (printables.length == 2));
printableName = (String) printables[0];
assert(printableName != null);
printableType = (Oid) printables[1];
if (printableType == null) {
printableType = GSSName.NT_USER_NAME;
}
}
// Need to be public for GSSUtil.getSubject()
public String getKrbName() throws GSSException {
long mName = 0;
GSSLibStub stub = cStub;
if (!GSSUtil.isKerberosMech(cStub.getMech())) {
stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID);
}
mName = stub.canonicalizeName(pName);
Object[] printables2 = stub.displayName(mName);
stub.releaseName(mName);
SunNativeProvider.debug("Got kerberized name: " + printables2[0]);
return (String) printables2[0];
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public boolean equals(GSSNameSpi other) throws GSSException {
if (!(other instanceof GSSNameElement)) {
return false;
}
return cStub.compareName(pName, ((GSSNameElement)other).pName);
}
public boolean equals(Object other) {
if (!(other instanceof GSSNameElement)) {
return false;
}
try {
return equals((GSSNameElement) other);
} catch (GSSException ex) {
return false;
}
}
public int hashCode() {
return Long.hashCode(pName);
}
public byte[] export() throws GSSException {
byte[] nameVal = cStub.exportName(pName);
// Need to strip off the mech Oid portion of the exported
// bytes since GSSNameImpl class will subsequently add it.
int pos = 0;
if ((nameVal[pos++] != 0x04) ||
(nameVal[pos++] != 0x01))
throw new GSSException(GSSException.BAD_NAME);
int mechOidLen = (((0xFF & nameVal[pos++]) << 8) |
(0xFF & nameVal[pos++]));
ObjectIdentifier temp = null;
try {
DerInputStream din = new DerInputStream(nameVal, pos,
mechOidLen);
temp = new ObjectIdentifier(din);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.BAD_NAME, e);
}
Oid mech2 = new Oid(temp.toString());
assert(mech2.equals(getMechanism()));
pos += mechOidLen;
int mechPortionLen = (((0xFF & nameVal[pos++]) << 24) |
((0xFF & nameVal[pos++]) << 16) |
((0xFF & nameVal[pos++]) << 8) |
(0xFF & nameVal[pos++]));
if (mechPortionLen < 0) {
throw new GSSException(GSSException.BAD_NAME);
}
byte[] mechPortion = new byte[mechPortionLen];
System.arraycopy(nameVal, pos, mechPortion, 0, mechPortionLen);
return mechPortion;
}
public Oid getMechanism() {
return cStub.getMech();
}
public String toString() {
return printableName;
}
public Oid getStringNameType() {
return printableType;
}
public boolean isAnonymousName() {
return (GSSName.NT_ANONYMOUS.equals(printableType));
}
public void dispose() {
if (pName != 0) {
cStub.releaseName(pName);
pName = 0;
}
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
dispose();
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2005, 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.wrapper;
import org.ietf.jgss.*;
import javax.security.auth.kerberos.ServicePermission;
/**
* This class is an utility class for Kerberos related stuff.
* @author Valerie Peng
* @since 1.6
*/
class Krb5Util {
// Return the Kerberos TGS principal name using the domain
// of the specified <code>name</code>
static String getTGSName(GSSNameElement name)
throws GSSException {
String krbPrinc = name.getKrbName();
int atIndex = krbPrinc.indexOf('@');
String realm = krbPrinc.substring(atIndex + 1);
StringBuilder sb = new StringBuilder("krbtgt/");
sb.append(realm).append('@').append(realm);
return sb.toString();
}
// Perform the Service Permission check using the specified
// <code>target</code> and <code>action</code>
static void checkServicePermission(String target, String action) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
SunNativeProvider.debug("Checking ServicePermission(" +
target + ", " + action + ")");
ServicePermission perm =
new ServicePermission(target, action);
sm.checkPermission(perm);
}
}
}

View file

@ -0,0 +1,631 @@
/*
* Copyright (c) 2005, 2017, 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.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSHeader;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.*;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;
import sun.security.jgss.spnego.NegTokenInit;
import sun.security.jgss.spnego.NegTokenTarg;
import javax.security.auth.kerberos.DelegationPermission;
import java.io.*;
/**
* This class is essentially a wrapper class for the gss_ctx_id_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
class NativeGSSContext implements GSSContextSpi {
private static final int GSS_C_DELEG_FLAG = 1;
private static final int GSS_C_MUTUAL_FLAG = 2;
private static final int GSS_C_REPLAY_FLAG = 4;
private static final int GSS_C_SEQUENCE_FLAG = 8;
private static final int GSS_C_CONF_FLAG = 16;
private static final int GSS_C_INTEG_FLAG = 32;
private static final int GSS_C_ANON_FLAG = 64;
private static final int GSS_C_PROT_READY_FLAG = 128;
private static final int GSS_C_TRANS_FLAG = 256;
private static final int NUM_OF_INQUIRE_VALUES = 6;
private long pContext = 0; // Pointer to the gss_ctx_id_t structure
private GSSNameElement srcName;
private GSSNameElement targetName;
private GSSCredElement cred;
private boolean isInitiator;
private boolean isEstablished;
private Oid actualMech; // Assigned during context establishment
private ChannelBinding cb;
private GSSCredElement delegatedCred;
private int flags;
private int lifetime = GSSCredential.DEFAULT_LIFETIME;
private final GSSLibStub cStub;
private boolean skipDelegPermCheck;
private boolean skipServicePermCheck;
// Retrieve the (preferred) mech out of SPNEGO tokens, i.e.
// NegTokenInit & NegTokenTarg
private static Oid getMechFromSpNegoToken(byte[] token,
boolean isInitiator)
throws GSSException {
Oid mech = null;
if (isInitiator) {
GSSHeader header = null;
try {
header = new GSSHeader(new ByteArrayInputStream(token));
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
int negTokenLen = header.getMechTokenLength();
byte[] negToken = new byte[negTokenLen];
System.arraycopy(token, token.length-negTokenLen,
negToken, 0, negToken.length);
NegTokenInit ntok = new NegTokenInit(negToken);
if (ntok.getMechToken() != null) {
Oid[] mechList = ntok.getMechTypeList();
mech = mechList[0];
}
} else {
NegTokenTarg ntok = new NegTokenTarg(token);
mech = ntok.getSupportedMech();
}
return mech;
}
// Perform the Service permission check
private void doServicePermCheck() throws GSSException {
if (System.getSecurityManager() != null) {
String action = (isInitiator? "initiate" : "accept");
// Need to check Service permission for accessing
// initiator cred for SPNEGO during context establishment
if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator
&& !isEstablished) {
if (srcName == null) {
// Check by creating default initiator KRB5 cred
GSSCredElement tempCred =
new GSSCredElement(null, lifetime,
GSSCredential.INITIATE_ONLY,
GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID));
tempCred.dispose();
} else {
String tgsName = Krb5Util.getTGSName(srcName);
Krb5Util.checkServicePermission(tgsName, action);
}
}
String targetStr = targetName.getKrbName();
Krb5Util.checkServicePermission(targetStr, action);
skipServicePermCheck = true;
}
}
// Perform the Delegation permission check
private void doDelegPermCheck() throws GSSException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
String targetStr = targetName.getKrbName();
String tgsStr = Krb5Util.getTGSName(targetName);
StringBuilder sb = new StringBuilder("\"");
sb.append(targetStr).append("\" \"");
sb.append(tgsStr).append('\"');
String krbPrincPair = sb.toString();
SunNativeProvider.debug("Checking DelegationPermission (" +
krbPrincPair + ")");
DelegationPermission perm =
new DelegationPermission(krbPrincPair);
sm.checkPermission(perm);
skipDelegPermCheck = true;
}
}
private byte[] retrieveToken(InputStream is, int mechTokenLen)
throws GSSException {
try {
byte[] result = null;
if (mechTokenLen != -1) {
// Need to add back the GSS header for a complete GSS token
SunNativeProvider.debug("Precomputed mechToken length: " +
mechTokenLen);
GSSHeader gssHeader = new GSSHeader
(new ObjectIdentifier(cStub.getMech().toString()),
mechTokenLen);
ByteArrayOutputStream baos = new ByteArrayOutputStream(600);
byte[] mechToken = new byte[mechTokenLen];
int len = is.read(mechToken);
assert(mechTokenLen == len);
gssHeader.encode(baos);
baos.write(mechToken);
result = baos.toByteArray();
} else {
// Must be unparsed GSS token or SPNEGO's NegTokenTarg token
assert(mechTokenLen == -1);
DerValue dv = new DerValue(is);
result = dv.toByteArray();
}
SunNativeProvider.debug("Complete Token length: " +
result.length);
return result;
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
// Constructor for context initiator
NativeGSSContext(GSSNameElement peer, GSSCredElement myCred,
int time, GSSLibStub stub) throws GSSException {
if (peer == null) {
throw new GSSException(GSSException.FAILURE, 1, "null peer");
}
cStub = stub;
cred = myCred;
targetName = peer;
isInitiator = true;
lifetime = time;
if (GSSUtil.isKerberosMech(cStub.getMech())) {
doServicePermCheck();
if (cred == null) {
cred = new GSSCredElement(null, lifetime,
GSSCredential.INITIATE_ONLY, cStub);
}
srcName = cred.getName();
}
}
// Constructor for context acceptor
NativeGSSContext(GSSCredElement myCred, GSSLibStub stub)
throws GSSException {
cStub = stub;
cred = myCred;
if (cred != null) targetName = cred.getName();
isInitiator = false;
// Defer Service permission check for default acceptor cred
// to acceptSecContext()
if (GSSUtil.isKerberosMech(cStub.getMech()) && targetName != null) {
doServicePermCheck();
}
// srcName and potentially targetName (when myCred is null)
// will be set in GSSLibStub.acceptContext(...)
}
// Constructor for imported context
NativeGSSContext(long pCtxt, GSSLibStub stub) throws GSSException {
assert(pContext != 0);
pContext = pCtxt;
cStub = stub;
// Set everything except cred, cb, delegatedCred
long[] info = cStub.inquireContext(pContext);
if (info.length != NUM_OF_INQUIRE_VALUES) {
throw new RuntimeException("Bug w/ GSSLibStub.inquireContext()");
}
srcName = new GSSNameElement(info[0], cStub);
targetName = new GSSNameElement(info[1], cStub);
isInitiator = (info[2] != 0);
isEstablished = (info[3] != 0);
flags = (int) info[4];
lifetime = (int) info[5];
// Do Service Permission check when importing SPNEGO context
// just to be safe
Oid mech = cStub.getMech();
if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) {
doServicePermCheck();
}
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public byte[] initSecContext(InputStream is, int mechTokenLen)
throws GSSException {
byte[] outToken = null;
if ((!isEstablished) && (isInitiator)) {
byte[] inToken = null;
// Ignore the specified input stream on the first call
if (pContext != 0) {
inToken = retrieveToken(is, mechTokenLen);
SunNativeProvider.debug("initSecContext=> inToken len=" +
inToken.length);
}
if (!getCredDelegState()) skipDelegPermCheck = true;
if (GSSUtil.isKerberosMech(cStub.getMech()) && !skipDelegPermCheck) {
doDelegPermCheck();
}
long pCred = (cred == null? 0 : cred.pCred);
outToken = cStub.initContext(pCred, targetName.pName,
cb, inToken, this);
SunNativeProvider.debug("initSecContext=> outToken len=" +
(outToken == null ? 0 : outToken.length));
// Only inspect the token when the permission check
// has not been performed
if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) {
// WORKAROUND for SEAM bug#6287358
actualMech = getMechFromSpNegoToken(outToken, true);
if (GSSUtil.isKerberosMech(actualMech)) {
if (!skipServicePermCheck) doServicePermCheck();
if (!skipDelegPermCheck) doDelegPermCheck();
}
}
if (isEstablished) {
if (srcName == null) {
srcName = new GSSNameElement
(cStub.getContextName(pContext, true), cStub);
}
if (cred == null) {
cred = new GSSCredElement(srcName, lifetime,
GSSCredential.INITIATE_ONLY,
cStub);
}
}
}
return outToken;
}
public byte[] acceptSecContext(InputStream is, int mechTokenLen)
throws GSSException {
byte[] outToken = null;
if ((!isEstablished) && (!isInitiator)) {
byte[] inToken = retrieveToken(is, mechTokenLen);
SunNativeProvider.debug("acceptSecContext=> inToken len=" +
inToken.length);
long pCred = (cred == null? 0 : cred.pCred);
outToken = cStub.acceptContext(pCred, cb, inToken, this);
SunNativeProvider.debug("acceptSecContext=> outToken len=" +
(outToken == null? 0 : outToken.length));
if (targetName == null) {
targetName = new GSSNameElement
(cStub.getContextName(pContext, false), cStub);
// Replace the current default acceptor cred now that
// the context acceptor name is available
if (cred != null) cred.dispose();
cred = new GSSCredElement(targetName, lifetime,
GSSCredential.ACCEPT_ONLY, cStub);
}
// Only inspect token when the permission check has not
// been performed
if (GSSUtil.isSpNegoMech(cStub.getMech()) &&
(outToken != null) && !skipServicePermCheck) {
if (GSSUtil.isKerberosMech(getMechFromSpNegoToken
(outToken, false))) {
doServicePermCheck();
}
}
}
return outToken;
}
public boolean isEstablished() {
return isEstablished;
}
public void dispose() throws GSSException {
srcName = null;
targetName = null;
cred = null;
delegatedCred = null;
if (pContext != 0) {
pContext = cStub.deleteContext(pContext);
pContext = 0;
}
}
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize)
throws GSSException {
return cStub.wrapSizeLimit(pContext, (confReq? 1:0), qop,
maxTokenSize);
}
public byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
byte[] data = inBuf;
if ((offset != 0) || (len != inBuf.length)) {
data = new byte[len];
System.arraycopy(inBuf, offset, data, 0, len);
}
return cStub.wrap(pContext, data, msgProp);
}
public void wrap(byte[] inBuf, int offset, int len,
OutputStream os, MessageProp msgProp)
throws GSSException {
try {
byte[] result = wrap(inBuf, offset, len, msgProp);
os.write(result);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf,
int outOffset, MessageProp msgProp)
throws GSSException {
byte[] result = wrap(inBuf, inOffset, len, msgProp);
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
byte[] data = new byte[inStream.available()];
int length = inStream.read(data);
byte[] token = wrap(data, 0, length, msgProp);
outStream.write(token);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public byte[] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp)
throws GSSException {
if ((offset != 0) || (len != inBuf.length)) {
byte[] temp = new byte[len];
System.arraycopy(inBuf, offset, temp, 0, len);
return cStub.unwrap(pContext, temp, msgProp);
} else {
return cStub.unwrap(pContext, inBuf, msgProp);
}
}
public int unwrap(byte[] inBuf, int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException {
byte[] result = null;
if ((inOffset != 0) || (len != inBuf.length)) {
byte[] temp = new byte[len];
System.arraycopy(inBuf, inOffset, temp, 0, len);
result = cStub.unwrap(pContext, temp, msgProp);
} else {
result = cStub.unwrap(pContext, inBuf, msgProp);
}
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
byte[] wrapped = new byte[inStream.available()];
int wLength = inStream.read(wrapped);
byte[] data = unwrap(wrapped, 0, wLength, msgProp);
outStream.write(data);
outStream.flush();
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public int unwrap(InputStream inStream,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException {
byte[] wrapped = null;
int wLength = 0;
try {
wrapped = new byte[inStream.available()];
wLength = inStream.read(wrapped);
byte[] result = unwrap(wrapped, 0, wLength, msgProp);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
byte[] result = unwrap(wrapped, 0, wLength, msgProp);
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public byte[] getMIC(byte[] in, int offset, int len,
MessageProp msgProp) throws GSSException {
int qop = (msgProp == null? 0:msgProp.getQOP());
byte[] inMsg = in;
if ((offset != 0) || (len != in.length)) {
inMsg = new byte[len];
System.arraycopy(in, offset, inMsg, 0, len);
}
return cStub.getMic(pContext, qop, inMsg);
}
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
int length = 0;
byte[] msg = new byte[inStream.available()];
length = inStream.read(msg);
byte[] msgToken = getMIC(msg, 0, length, msgProp);
if ((msgToken != null) && msgToken.length != 0) {
outStream.write(msgToken);
}
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public void verifyMIC(byte[] inToken, int tOffset, int tLen,
byte[] inMsg, int mOffset, int mLen,
MessageProp msgProp) throws GSSException {
byte[] token = inToken;
byte[] msg = inMsg;
if ((tOffset != 0) || (tLen != inToken.length)) {
token = new byte[tLen];
System.arraycopy(inToken, tOffset, token, 0, tLen);
}
if ((mOffset != 0) || (mLen != inMsg.length)) {
msg = new byte[mLen];
System.arraycopy(inMsg, mOffset, msg, 0, mLen);
}
cStub.verifyMic(pContext, token, msg, msgProp);
}
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException {
try {
byte[] msg = new byte[msgStream.available()];
int mLength = msgStream.read(msg);
byte[] tok = new byte[tokStream.available()];
int tLength = tokStream.read(tok);
verifyMIC(tok, 0, tLength, msg, 0, mLength, msgProp);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public byte[] export() throws GSSException {
byte[] result = cStub.exportContext(pContext);
pContext = 0;
return result;
}
private void changeFlags(int flagMask, boolean isEnable) {
if (isInitiator && pContext == 0) {
if (isEnable) {
flags |= flagMask;
} else {
flags &= ~flagMask;
}
}
}
public void requestMutualAuth(boolean state) throws GSSException {
changeFlags(GSS_C_MUTUAL_FLAG, state);
}
public void requestReplayDet(boolean state) throws GSSException {
changeFlags(GSS_C_REPLAY_FLAG, state);
}
public void requestSequenceDet(boolean state) throws GSSException {
changeFlags(GSS_C_SEQUENCE_FLAG, state);
}
public void requestCredDeleg(boolean state) throws GSSException {
changeFlags(GSS_C_DELEG_FLAG, state);
}
public void requestAnonymity(boolean state) throws GSSException {
changeFlags(GSS_C_ANON_FLAG, state);
}
public void requestConf(boolean state) throws GSSException {
changeFlags(GSS_C_CONF_FLAG, state);
}
public void requestInteg(boolean state) throws GSSException {
changeFlags(GSS_C_INTEG_FLAG, state);
}
public void requestDelegPolicy(boolean state) throws GSSException {
// Not supported, ignore
}
public void requestLifetime(int lifetime) throws GSSException {
if (isInitiator && pContext == 0) {
this.lifetime = lifetime;
}
}
public void setChannelBinding(ChannelBinding cb) throws GSSException {
if (pContext == 0) {
this.cb = cb;
}
}
private boolean checkFlags(int flagMask) {
return ((flags & flagMask) != 0);
}
public boolean getCredDelegState() {
return checkFlags(GSS_C_DELEG_FLAG);
}
public boolean getMutualAuthState() {
return checkFlags(GSS_C_MUTUAL_FLAG);
}
public boolean getReplayDetState() {
return checkFlags(GSS_C_REPLAY_FLAG);
}
public boolean getSequenceDetState() {
return checkFlags(GSS_C_SEQUENCE_FLAG);
}
public boolean getAnonymityState() {
return checkFlags(GSS_C_ANON_FLAG);
}
public boolean isTransferable() throws GSSException {
return checkFlags(GSS_C_TRANS_FLAG);
}
public boolean isProtReady() {
return checkFlags(GSS_C_PROT_READY_FLAG);
}
public boolean getConfState() {
return checkFlags(GSS_C_CONF_FLAG);
}
public boolean getIntegState() {
return checkFlags(GSS_C_INTEG_FLAG);
}
public boolean getDelegPolicyState() {
return false;
}
public int getLifetime() {
return cStub.getContextTime(pContext);
}
public GSSNameSpi getSrcName() throws GSSException {
return srcName;
}
public GSSNameSpi getTargName() throws GSSException {
return targetName;
}
public Oid getMech() throws GSSException {
if (isEstablished && actualMech != null) {
return actualMech;
} else {
return cStub.getMech();
}
}
public GSSCredentialSpi getDelegCred() throws GSSException {
return delegatedCred;
}
public boolean isInitiator() {
return isInitiator;
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
dispose();
}
public Object inquireSecContext(String type)
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE, -1,
"Inquire type not supported.");
}
}

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import java.io.UnsupportedEncodingException;
import java.security.Provider;
import java.util.Vector;
import org.ietf.jgss.*;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.*;
/**
* JGSS plugin for generic mechanisms provided through native GSS framework.
*
* @author Valerie Peng
*/
public final class NativeGSSFactory implements MechanismFactory {
GSSLibStub cStub = null;
private final GSSCaller caller;
private GSSCredElement getCredFromSubject(GSSNameElement name,
boolean initiate)
throws GSSException {
Oid mech = cStub.getMech();
Vector<GSSCredElement> creds = GSSUtil.searchSubject
(name, mech, initiate, GSSCredElement.class);
// If Subject is present but no native creds available
if (creds != null && creds.isEmpty()) {
if (GSSUtil.useSubjectCredsOnly(caller)) {
throw new GSSException(GSSException.NO_CRED);
}
}
GSSCredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
result.doServicePermCheck();
}
return result;
}
public NativeGSSFactory(GSSCaller caller) {
this.caller = caller;
// Have to call setMech(Oid) explicitly before calling other
// methods. Otherwise, NPE may be thrown unexpectantly
}
public void setMech(Oid mech) throws GSSException {
cStub = GSSLibStub.getInstance(mech);
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
try {
byte[] nameBytes =
(nameStr == null ? null : nameStr.getBytes("UTF-8"));
return new GSSNameElement(nameBytes, nameType, cStub);
} catch (UnsupportedEncodingException uee) {
// Shouldn't happen
throw new GSSExceptionImpl(GSSException.FAILURE, uee);
}
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
return new GSSNameElement(name, nameType, cStub);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime,
int acceptLifetime,
int usage)
throws GSSException {
GSSNameElement nname = null;
if (name != null && !(name instanceof GSSNameElement)) {
nname = (GSSNameElement)
getNameElement(name.toString(), name.getStringNameType());
} else nname = (GSSNameElement) name;
if (usage == GSSCredential.INITIATE_AND_ACCEPT) {
// Force separate acqusition of cred element since
// MIT's impl does not correctly report NO_CRED error.
usage = GSSCredential.INITIATE_ONLY;
}
GSSCredElement credElement =
getCredFromSubject(nname, (usage == GSSCredential.INITIATE_ONLY));
if (credElement == null) {
// No cred in the Subject
if (usage == GSSCredential.INITIATE_ONLY) {
credElement = new GSSCredElement(nname, initLifetime,
usage, cStub);
} else if (usage == GSSCredential.ACCEPT_ONLY) {
if (nname == null) {
nname = GSSNameElement.DEF_ACCEPTOR;
}
credElement = new GSSCredElement(nname, acceptLifetime,
usage, cStub);
} else {
throw new GSSException(GSSException.FAILURE, -1,
"Unknown usage mode requested");
}
}
return credElement;
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myCred,
int lifetime)
throws GSSException {
if (peer == null) {
throw new GSSException(GSSException.BAD_NAME);
} else if (!(peer instanceof GSSNameElement)) {
peer = (GSSNameElement)
getNameElement(peer.toString(), peer.getStringNameType());
}
if (myCred == null) {
myCred = getCredFromSubject(null, true);
} else if (!(myCred instanceof GSSCredElement)) {
throw new GSSException(GSSException.NO_CRED);
}
return new NativeGSSContext((GSSNameElement) peer,
(GSSCredElement) myCred,
lifetime, cStub);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myCred)
throws GSSException {
if (myCred == null) {
myCred = getCredFromSubject(null, false);
} else if (!(myCred instanceof GSSCredElement)) {
throw new GSSException(GSSException.NO_CRED);
}
return new NativeGSSContext((GSSCredElement) myCred, cStub);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
return cStub.importContext(exportedContext);
}
public final Oid getMechanismOid() {
return cStub.getMech();
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public Oid[] getNameTypes() throws GSSException {
return cStub.inquireNamesForMech();
}
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2005, 2016, 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.wrapper;
import java.util.HashMap;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.ietf.jgss.Oid;
import sun.security.action.PutAllAction;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
/**
* Defines the Sun NativeGSS provider for plugging in the
* native GSS mechanisms to Java GSS.
*
* List of supported mechanisms depends on the local
* machine configuration.
*
* @author Yu-Ching Valerie Peng
*/
public final class SunNativeProvider extends Provider {
private static final long serialVersionUID = -238911724858694204L;
private static final String NAME = "SunNativeGSS";
private static final String INFO = "Sun Native GSS provider";
private static final String MF_CLASS =
"sun.security.jgss.wrapper.NativeGSSFactory";
private static final String LIB_PROP = "sun.security.jgss.lib";
private static final String DEBUG_PROP = "sun.security.nativegss.debug";
private static HashMap<String, String> MECH_MAP;
static final Provider INSTANCE = new SunNativeProvider();
static boolean DEBUG;
static void debug(String message) {
if (DEBUG) {
if (message == null) {
throw new NullPointerException();
}
System.out.println(NAME + ": " + message);
}
}
static {
MECH_MAP =
AccessController.doPrivileged(
new PrivilegedAction<HashMap<String, String>>() {
public HashMap<String, String> run() {
DEBUG = Boolean.parseBoolean
(System.getProperty(DEBUG_PROP));
try {
System.loadLibrary("j2gss");
} catch (Error err) {
debug("No j2gss library found!");
if (DEBUG) err.printStackTrace();
return null;
}
String[] gssLibs = new String[0];
String defaultLib = System.getProperty(LIB_PROP);
if (defaultLib == null || defaultLib.trim().equals("")) {
String osname = System.getProperty("os.name");
if (osname.startsWith("SunOS")) {
gssLibs = new String[]{ "libgss.so" };
} else if (osname.startsWith("Linux")) {
gssLibs = new String[]{
"libgssapi.so",
"libgssapi_krb5.so",
"libgssapi_krb5.so.2",
};
} else if (osname.contains("OS X")) {
gssLibs = new String[]{
"libgssapi_krb5.dylib",
"/usr/lib/sasl2/libgssapiv2.2.so",
};
}
} else {
gssLibs = new String[]{ defaultLib };
}
for (String libName: gssLibs) {
if (GSSLibStub.init(libName, DEBUG)) {
debug("Loaded GSS library: " + libName);
Oid[] mechs = GSSLibStub.indicateMechs();
HashMap<String, String> map =
new HashMap<String, String>();
for (int i = 0; i < mechs.length; i++) {
debug("Native MF for " + mechs[i]);
map.put("GssApiMechanism." + mechs[i],
MF_CLASS);
}
return map;
}
}
return null;
}
});
}
public SunNativeProvider() {
/* We are the Sun NativeGSS provider */
super(NAME, PROVIDER_VER, INFO);
if (MECH_MAP != null) {
AccessController.doPrivileged(new PutAllAction(this, MECH_MAP));
}
}
}

View file

@ -0,0 +1,41 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
public class Asn1Exception extends KrbException {
private static final long serialVersionUID = 8291288984575084132L;
public Asn1Exception(int i) {
super(i);
}
}

View file

@ -0,0 +1,343 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.util.Arrays;
import sun.security.util.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* This class encapsulates the concept of a Kerberos checksum.
*/
public class Checksum {
private int cksumType;
private byte[] checksum;
// ----------------------------------------------+-------------+-----------
// Checksum type |sumtype |checksum
// |value | size
// ----------------------------------------------+-------------+-----------
public static final int CKSUMTYPE_NULL = 0; // 0
public static final int CKSUMTYPE_CRC32 = 1; // 4
public static final int CKSUMTYPE_RSA_MD4 = 2; // 16
public static final int CKSUMTYPE_RSA_MD4_DES = 3; // 24
public static final int CKSUMTYPE_DES_MAC = 4; // 16
public static final int CKSUMTYPE_DES_MAC_K = 5; // 8
public static final int CKSUMTYPE_RSA_MD4_DES_K = 6; // 16
public static final int CKSUMTYPE_RSA_MD5 = 7; // 16
public static final int CKSUMTYPE_RSA_MD5_DES = 8; // 24
// draft-ietf-krb-wg-crypto-07.txt
public static final int CKSUMTYPE_HMAC_SHA1_DES3_KD = 12; // 20
// draft-raeburn-krb-rijndael-krb-07.txt
public static final int CKSUMTYPE_HMAC_SHA1_96_AES128 = 15; // 96
public static final int CKSUMTYPE_HMAC_SHA1_96_AES256 = 16; // 96
// draft-brezak-win2k-krb-rc4-hmac-04.txt
public static final int CKSUMTYPE_HMAC_MD5_ARCFOUR = -138;
static int CKSUMTYPE_DEFAULT;
static int SAFECKSUMTYPE_DEFAULT;
private static boolean DEBUG = Krb5.DEBUG;
static {
initStatic();
}
public static void initStatic() {
String temp = null;
Config cfg = null;
try {
cfg = Config.getInstance();
temp = cfg.get("libdefaults", "default_checksum");
if (temp != null)
{
CKSUMTYPE_DEFAULT = Config.getType(temp);
} else {
/*
* If the default checksum is not
* specified in the configuration we
* set it to RSA_MD5. We follow the MIT and
* SEAM implementation.
*/
CKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5;
}
} catch (Exception exc) {
if (DEBUG) {
System.out.println("Exception in getting default checksum "+
"value from the configuration " +
"Setting default checksum to be RSA-MD5");
exc.printStackTrace();
}
CKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5;
}
try {
temp = cfg.get("libdefaults", "safe_checksum_type");
if (temp != null)
{
SAFECKSUMTYPE_DEFAULT = Config.getType(temp);
} else {
SAFECKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5_DES;
}
} catch (Exception exc) {
if (DEBUG) {
System.out.println("Exception in getting safe default " +
"checksum value " +
"from the configuration Setting " +
"safe default checksum to be RSA-MD5");
exc.printStackTrace();
}
SAFECKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5_DES;
}
}
/**
* Constructs a new Checksum using the raw data and type.
* @param data the byte array of checksum.
* @param new_cksumType the type of checksum.
*
*/
// used in InitialToken
public Checksum(byte[] data, int new_cksumType) {
cksumType = new_cksumType;
checksum = data;
}
/**
* Constructs a new Checksum by calculating the checksum over the data
* using specified checksum type.
* @param new_cksumType the type of checksum.
* @param data the data that needs to be performed a checksum calculation on.
*/
public Checksum(int new_cksumType, byte[] data)
throws KdcErrException, KrbCryptoException {
cksumType = new_cksumType;
CksumType cksumEngine = CksumType.getInstance(cksumType);
if (!cksumEngine.isSafe()) {
checksum = cksumEngine.calculateChecksum(data, data.length);
} else {
throw new KdcErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
}
}
/**
* Constructs a new Checksum by calculating the keyed checksum
* over the data using specified checksum type.
* @param new_cksumType the type of checksum.
* @param data the data that needs to be performed a checksum calculation on.
*/
// KrbSafe, KrbTgsReq
public Checksum(int new_cksumType, byte[] data,
EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
cksumType = new_cksumType;
CksumType cksumEngine = CksumType.getInstance(cksumType);
if (!cksumEngine.isSafe())
throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
checksum =
cksumEngine.calculateKeyedChecksum(data,
data.length,
key.getBytes(),
usage);
}
/**
* Verifies the keyed checksum over the data passed in.
*/
public boolean verifyKeyedChecksum(byte[] data, EncryptionKey key,
int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
CksumType cksumEngine = CksumType.getInstance(cksumType);
if (!cksumEngine.isSafe())
throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
return cksumEngine.verifyKeyedChecksum(data,
data.length,
key.getBytes(),
checksum,
usage);
}
/*
public Checksum(byte[] data) throws KdcErrException, KrbCryptoException {
this(Checksum.CKSUMTYPE_DEFAULT, data);
}
*/
boolean isEqual(Checksum cksum) throws KdcErrException {
if (cksumType != cksum.cksumType)
return false;
CksumType cksumEngine = CksumType.getInstance(cksumType);
return CksumType.isChecksumEqual(checksum, cksum.checksum);
}
/**
* Constructs an instance of Checksum from an ASN.1 encoded representation.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
private Checksum(DerValue encoding) throws Asn1Exception, IOException {
DerValue der;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
cksumType = der.getData().getBigInteger().intValue();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x01) {
checksum = der.getData().getOctetString();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
if (encoding.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes a Checksum object.
* <pre>{@code
* Checksum ::= SEQUENCE {
* cksumtype [0] Int32,
* checksum [1] OCTET STRING
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
* @return byte array of enocded Checksum.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(cksumType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x00), temp);
temp = new DerOutputStream();
temp.putOctetString(checksum);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Parse (unmarshal) a checksum object from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicates if this data field is optional
* @return an instance of Checksum.
*
*/
public static Checksum parse(DerInputStream data,
byte explicitTag, boolean optional)
throws Asn1Exception, IOException {
if ((optional) &&
(((byte)data.peekByte() & (byte)0x1F) != explicitTag)) {
return null;
}
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new Checksum(subDer);
}
}
/**
* Returns the raw bytes of the checksum, not in ASN.1 encoded form.
*/
public final byte[] getBytes() {
return checksum;
}
public final int getType() {
return cksumType;
}
@Override public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Checksum)) {
return false;
}
try {
return isEqual((Checksum)obj);
} catch (KdcErrException kee) {
return false;
}
}
@Override public int hashCode() {
int result = 17;
result = 37 * result + cksumType;
if (checksum != null) {
result = 37 * result + Arrays.hashCode(checksum);
}
return result;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2001, 2007, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.security.SecureRandom;
public final class Confounder {
private static SecureRandom srand = new SecureRandom();
private Confounder() { // not instantiable
}
public static byte[] bytes(int size) {
byte[] data = new byte[size];
srand.nextBytes(data);
return data;
}
public static int intValue() {
return srand.nextInt();
}
public static long longValue() {
return srand.nextLong();
}
}

View file

@ -0,0 +1,549 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.CredentialsCache;
import sun.security.krb5.internal.crypto.EType;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import java.net.InetAddress;
/**
* This class encapsulates the concept of a Kerberos service
* credential. That includes a Kerberos ticket and an associated
* session key.
*/
public class Credentials {
Ticket ticket;
PrincipalName client;
PrincipalName server;
EncryptionKey key;
TicketFlags flags;
KerberosTime authTime;
KerberosTime startTime;
KerberosTime endTime;
KerberosTime renewTill;
HostAddresses cAddr;
EncryptionKey serviceKey;
AuthorizationData authzData;
private static boolean DEBUG = Krb5.DEBUG;
private static CredentialsCache cache;
static boolean alreadyLoaded = false;
private static boolean alreadyTried = false;
// Read native ticket with session key type in the given list
private static native Credentials acquireDefaultNativeCreds(int[] eTypes);
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_server,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
KerberosTime new_startTime,
KerberosTime new_endTime,
KerberosTime renewTill,
HostAddresses cAddr,
AuthorizationData authzData) {
this(new_ticket, new_client, new_server, new_key, new_flags,
authTime, new_startTime, new_endTime, renewTill, cAddr);
this.authzData = authzData;
}
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_server,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
KerberosTime new_startTime,
KerberosTime new_endTime,
KerberosTime renewTill,
HostAddresses cAddr) {
ticket = new_ticket;
client = new_client;
server = new_server;
key = new_key;
flags = new_flags;
this.authTime = authTime;
startTime = new_startTime;
endTime = new_endTime;
this.renewTill = renewTill;
this.cAddr = cAddr;
}
public Credentials(byte[] encoding,
String client,
String server,
byte[] keyBytes,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] cAddrs) throws KrbException, IOException {
this(new Ticket(encoding),
new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL),
new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST),
new EncryptionKey(keyType, keyBytes),
(flags == null? null: new TicketFlags(flags)),
(authTime == null? null: new KerberosTime(authTime)),
(startTime == null? null: new KerberosTime(startTime)),
(endTime == null? null: new KerberosTime(endTime)),
(renewTill == null? null: new KerberosTime(renewTill)),
null); // caddrs are in the encoding at this point
}
/**
* Acquires a service ticket for the specified service
* principal. If the service ticket is not already available, it
* obtains a new one from the KDC.
*/
/*
public Credentials(Credentials tgt, PrincipalName service)
throws KrbException {
}
*/
public final PrincipalName getClient() {
return client;
}
public final PrincipalName getServer() {
return server;
}
public final EncryptionKey getSessionKey() {
return key;
}
public final Date getAuthTime() {
if (authTime != null) {
return authTime.toDate();
} else {
return null;
}
}
public final Date getStartTime() {
if (startTime != null)
{
return startTime.toDate();
}
return null;
}
public final Date getEndTime() {
if (endTime != null)
{
return endTime.toDate();
}
return null;
}
public final Date getRenewTill() {
if (renewTill != null)
{
return renewTill.toDate();
}
return null;
}
public final boolean[] getFlags() {
if (flags == null) // Can be in a KRB-CRED
return null;
return flags.toBooleanArray();
}
public final InetAddress[] getClientAddresses() {
if (cAddr == null)
return null;
return cAddr.getInetAddresses();
}
public final byte[] getEncoded() {
byte[] retVal = null;
try {
retVal = ticket.asn1Encode();
} catch (Asn1Exception e) {
if (DEBUG)
System.out.println(e);
} catch (IOException ioe) {
if (DEBUG)
System.out.println(ioe);
}
return retVal;
}
public boolean isForwardable() {
return flags.get(Krb5.TKT_OPTS_FORWARDABLE);
}
public boolean isRenewable() {
return flags.get(Krb5.TKT_OPTS_RENEWABLE);
}
public Ticket getTicket() {
return ticket;
}
public TicketFlags getTicketFlags() {
return flags;
}
public AuthorizationData getAuthzData() {
return authzData;
}
/**
* Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE
* flag set
* @return true if OK-AS_DELEGATE flag is set, otherwise, return false.
*/
public boolean checkDelegate() {
return flags.get(Krb5.TKT_OPTS_DELEGATE);
}
/**
* Reset TKT_OPTS_DELEGATE to false, called at credentials acquirement
* when one of the cross-realm TGTs does not have the OK-AS-DELEGATE
* flag set. This info must be preservable and restorable through
* the Krb5Util.credsToTicket/ticketToCreds() methods so that even if
* the service ticket is cached it still remembers the cross-realm
* authentication result.
*/
public void resetDelegate() {
flags.set(Krb5.TKT_OPTS_DELEGATE, false);
}
public Credentials renew() throws KrbException, IOException {
KDCOptions options = new KDCOptions();
options.set(KDCOptions.RENEW, true);
/*
* Added here to pass KrbKdcRep.check:73
*/
options.set(KDCOptions.RENEWABLE, true);
return new KrbTgsReq(options,
this,
server,
null, // from
null, // till
null, // rtime
null, // eTypes
cAddr,
null,
null,
null).sendAndGetCreds();
}
/**
* Returns a TGT for the given client principal from a ticket cache.
*
* @param princ the client principal. A value of null means that the
* default principal name in the credentials cache will be used.
* @param ticketCache the path to the tickets file. A value
* of null will be accepted to indicate that the default
* path should be searched
* @return the TGT credentials or null if none were found. If the tgt
* expired, it is the responsibility of the caller to determine this.
*/
public static Credentials acquireTGTFromCache(PrincipalName princ,
String ticketCache)
throws KrbException, IOException {
if (ticketCache == null) {
// The default ticket cache on Windows and Mac is not a file.
String os = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("os.name"));
if (os.toUpperCase(Locale.ENGLISH).startsWith("WINDOWS") ||
os.toUpperCase(Locale.ENGLISH).contains("OS X")) {
Credentials creds = acquireDefaultCreds();
if (creds == null) {
if (DEBUG) {
System.out.println(">>> Found no TGT's in LSA");
}
return null;
}
if (princ != null) {
if (creds.getClient().equals(princ)) {
if (DEBUG) {
System.out.println(">>> Obtained TGT from LSA: "
+ creds);
}
return creds;
} else {
if (DEBUG) {
System.out.println(">>> LSA contains TGT for "
+ creds.getClient()
+ " not "
+ princ);
}
return null;
}
} else {
if (DEBUG) {
System.out.println(">>> Obtained TGT from LSA: "
+ creds);
}
return creds;
}
}
}
/*
* Returns the appropriate cache. If ticketCache is null, it is the
* default cache otherwise it is the cache filename contained in it.
*/
CredentialsCache ccache =
CredentialsCache.getInstance(princ, ticketCache);
if (ccache == null) {
return null;
}
sun.security.krb5.internal.ccache.Credentials tgtCred =
ccache.getDefaultCreds();
if (tgtCred == null) {
return null;
}
if (EType.isSupported(tgtCred.getEType())) {
return tgtCred.setKrbCreds();
} else {
if (DEBUG) {
System.out.println(
">>> unsupported key type found the default TGT: " +
tgtCred.getEType());
}
return null;
}
}
/**
* Acquires default credentials.
* <br>The possible locations for default credentials cache is searched in
* the following order:
* <ol>
* <li> The directory and cache file name specified by "KRB5CCNAME" system.
* property.
* <li> The directory and cache file name specified by "KRB5CCNAME"
* environment variable.
* <li> A cache file named krb5cc_{user.name} at {user.home} directory.
* </ol>
* @return a <code>KrbCreds</code> object if the credential is found,
* otherwise return null.
*/
// this method is intentionally changed to not check if the caller's
// principal name matches cache file's principal name.
// It assumes that the GSS call has
// the privilege to access the default cache file.
// This method is only called on Windows and Mac OS X, the native
// acquireDefaultNativeCreds is also available on these platforms.
public static synchronized Credentials acquireDefaultCreds() {
Credentials result = null;
if (cache == null) {
cache = CredentialsCache.getInstance();
}
if (cache != null) {
sun.security.krb5.internal.ccache.Credentials temp =
cache.getDefaultCreds();
if (temp != null) {
if (DEBUG) {
System.out.println(">>> KrbCreds found the default ticket"
+ " granting ticket in credential cache.");
}
if (EType.isSupported(temp.getEType())) {
result = temp.setKrbCreds();
} else {
if (DEBUG) {
System.out.println(
">>> unsupported key type found the default TGT: " +
temp.getEType());
}
}
}
}
if (result == null) {
// Doesn't seem to be a default cache on this system or
// TGT has unsupported encryption type
if (!alreadyTried) {
// See if there's any native code to load
try {
ensureLoaded();
} catch (Exception e) {
if (DEBUG) {
System.out.println("Can not load credentials cache");
e.printStackTrace();
}
alreadyTried = true;
}
}
if (alreadyLoaded) {
// There is some native code
if (DEBUG) {
System.out.println(">> Acquire default native Credentials");
}
try {
result = acquireDefaultNativeCreds(
EType.getDefaults("default_tkt_enctypes"));
} catch (KrbException ke) {
// when there is no default_tkt_enctypes.
}
}
}
return result;
}
/**
* Acquires credentials for a specified service using initial credential.
* When the service has a different realm
* from the initial credential, we do cross-realm authentication
* - first, we use the current credential to get
* a cross-realm credential from the local KDC, then use that
* cross-realm credential to request service credential
* from the foreigh KDC.
*
* @param service the name of service principal using format
* components@realm
* @param ccreds client's initial credential.
* @exception IOException if an error occurs in reading the credentials
* cache
* @exception KrbException if an error occurs specific to Kerberos
* @return a <code>Credentials</code> object.
*/
public static Credentials acquireServiceCreds(String service,
Credentials ccreds)
throws KrbException, IOException {
return CredentialsUtil.acquireServiceCreds(service, ccreds);
}
public static Credentials acquireS4U2selfCreds(PrincipalName user,
Credentials ccreds) throws KrbException, IOException {
return CredentialsUtil.acquireS4U2selfCreds(user, ccreds);
}
public static Credentials acquireS4U2proxyCreds(String service,
Ticket second, PrincipalName client, Credentials ccreds)
throws KrbException, IOException {
return CredentialsUtil.acquireS4U2proxyCreds(
service, second, client, ccreds);
}
public CredentialsCache getCache() {
return cache;
}
public EncryptionKey getServiceKey() {
return serviceKey;
}
/*
* Prints out debug info.
*/
public static void printDebug(Credentials c) {
System.out.println(">>> DEBUG: ----Credentials----");
System.out.println("\tclient: " + c.client.toString());
System.out.println("\tserver: " + c.server.toString());
System.out.println("\tticket: sname: " + c.ticket.sname.toString());
if (c.startTime != null) {
System.out.println("\tstartTime: " + c.startTime.getTime());
}
System.out.println("\tendTime: " + c.endTime.getTime());
System.out.println(" ----Credentials end----");
}
static void ensureLoaded() {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void> () {
public Void run() {
if (System.getProperty("os.name").contains("OS X")) {
System.loadLibrary("osxkrb5");
} else {
System.loadLibrary("w2k_lsa_auth");
}
return null;
}
});
alreadyLoaded = true;
}
public String toString() {
StringBuilder sb = new StringBuilder("Credentials:");
sb.append( "\n client=").append(client);
sb.append( "\n server=").append(server);
if (authTime != null) {
sb.append("\n authTime=").append(authTime);
}
if (startTime != null) {
sb.append("\n startTime=").append(startTime);
}
sb.append( "\n endTime=").append(endTime);
sb.append( "\n renewTill=").append(renewTill);
sb.append( "\n flags=").append(flags);
sb.append( "\nEType (skey)=").append(key.getEType());
sb.append( "\n (tkt key)=").append(ticket.encPart.eType);
return sb.toString();
}
public sun.security.krb5.internal.ccache.Credentials toCCacheCreds() {
return new sun.security.krb5.internal.ccache.Credentials(
getClient(), getServer(),
getSessionKey(),
date2kt(getAuthTime()),
date2kt(getStartTime()),
date2kt(getEndTime()),
date2kt(getRenewTill()),
false,
flags,
new HostAddresses(getClientAddresses()),
getAuthzData(),
getTicket(),
null);
}
private static KerberosTime date2kt(Date d) {
return d == null ? null : new KerberosTime(d);
}
}

View file

@ -0,0 +1,380 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.util.*;
import sun.security.krb5.internal.crypto.*;
import sun.security.krb5.internal.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* This class encapsulates Kerberos encrypted data. It allows
* callers access to both the ASN.1 encoded form of the EncryptedData
* type as well as the raw cipher text.
*/
public class EncryptedData implements Cloneable {
int eType;
Integer kvno; // optional
byte[] cipher;
byte[] plain; // not part of ASN.1 encoding
// ----------------+-----------+----------+----------------+---------------
// Encryption type |etype value|block size|minimum pad size|confounder size
// ----------------+-----------+----------+----------------+---------------
public static final int
ETYPE_NULL = 0; // 1 0 0
public static final int
ETYPE_DES_CBC_CRC = 1; // 8 4 8
public static final int
ETYPE_DES_CBC_MD4 = 2; // 8 0 8
public static final int
ETYPE_DES_CBC_MD5 = 3; // 8 0 8
// draft-brezak-win2k-krb-rc4-hmac-04.txt
public static final int
ETYPE_ARCFOUR_HMAC = 23; // 1
// NOTE: the exportable RC4-HMAC is not supported;
// it is no longer a usable encryption type
public static final int
ETYPE_ARCFOUR_HMAC_EXP = 24; // 1
// draft-ietf-krb-wg-crypto-07.txt
public static final int
ETYPE_DES3_CBC_HMAC_SHA1_KD = 16; // 8 0 8
// draft-raeburn-krb-rijndael-krb-07.txt
public static final int
ETYPE_AES128_CTS_HMAC_SHA1_96 = 17; // 16 0 16
public static final int
ETYPE_AES256_CTS_HMAC_SHA1_96 = 18; // 16 0 16
/* used by self */
private EncryptedData() {
}
public Object clone() {
EncryptedData new_encryptedData = new EncryptedData();
new_encryptedData.eType = eType;
if (kvno != null) {
new_encryptedData.kvno = kvno.intValue();
}
if (cipher != null) {
new_encryptedData.cipher = new byte[cipher.length];
System.arraycopy(cipher, 0, new_encryptedData.cipher,
0, cipher.length);
}
return new_encryptedData;
}
// Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
public EncryptedData(
int new_eType,
Integer new_kvno,
byte[] new_cipher) {
eType = new_eType;
kvno = new_kvno;
cipher = new_cipher;
}
/*
// Not used.
public EncryptedData(
EncryptionKey key,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes());
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
// used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv
// Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
public EncryptedData(
EncryptionKey key,
byte[] plaintext,
int usage)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes(), usage);
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
/*
// Not used.
public EncryptedData(
EncryptionKey key,
byte[] ivec,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes(), ivec);
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
/*
// Not used.
EncryptedData(
StringBuffer password,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EncryptionKey key = new EncryptionKey(password);
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes());
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
public byte[] decrypt(
EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
if (eType != key.getEType()) {
throw new KrbCryptoException(
"EncryptedData is encrypted using keytype " +
EType.toString(eType) +
" but decryption key is of type " +
EType.toString(key.getEType()));
}
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes(), usage);
// The service ticket will be used in S4U2proxy request. Therefore
// the raw ticket is still needed.
//cipher = null;
return etypeEngine.decryptedData(plain);
}
/*
// currently destructive on cipher
// Not used.
public byte[] decrypt(
EncryptionKey key,
byte[] ivec, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
// XXX check for matching eType and kvno here
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes(), ivec, usage);
cipher = null;
return etypeEngine.decryptedData(plain);
}
// currently destructive on cipher
// Not used.
byte[] decrypt(StringBuffer password)
throws KdcErrException, KrbApErrException, KrbCryptoException {
EncryptionKey key = new EncryptionKey(password);
// XXX check for matching eType here
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes());
cipher = null;
return etypeEngine.decryptedData(plain);
}
*/
private byte[] decryptedData() throws KdcErrException {
if (plain != null) {
EType etypeEngine = EType.getInstance(eType);
return etypeEngine.decryptedData(plain);
}
return null;
}
/**
* Constructs an instance of EncryptedData type.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*/
/* Used by self */
private EncryptedData(DerValue encoding)
throws Asn1Exception, IOException {
DerValue der = null;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
eType = (der.getData().getBigInteger()).intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if ((encoding.getData().peekByte() & 0x1F) == 1) {
der = encoding.getData().getDerValue();
int i = (der.getData().getBigInteger()).intValue();
kvno = i;
} else {
kvno = null;
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x02) {
cipher = der.getData().getOctetString();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if (encoding.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Returns an ASN.1 encoded EncryptedData type.
*
* <pre>{@code
* EncryptedData ::= SEQUENCE {
* etype [0] Int32 -- EncryptionType --,
* kvno [1] UInt32 OPTIONAL,
* cipher [2] OCTET STRING -- ciphertext
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*
* @return byte array of encoded EncryptedData object.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(this.eType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x00), temp);
temp = new DerOutputStream();
if (kvno != null) {
// encode as an unsigned integer (UInt32)
temp.putInteger(BigInteger.valueOf(this.kvno.longValue()));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x01), temp);
temp = new DerOutputStream();
}
temp.putOctetString(this.cipher);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
(byte)0x02), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Parse (unmarshal) an EncryptedData from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
* @return an instance of EncryptedData.
*
*/
public static EncryptedData parse(DerInputStream data,
byte explicitTag,
boolean optional)
throws Asn1Exception, IOException {
if ((optional) &&
(((byte)data.peekByte() & (byte)0x1F) != explicitTag))
return null;
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new EncryptedData(subDer);
}
}
/**
* Reset asn.1 data stream after decryption, remove redundant bytes.
* @param data the decrypted data from decrypt().
* @return the reset byte array which holds exactly one asn1 datum
* including its tag and length.
*
*/
public byte[] reset(byte[] data) {
byte[] bytes = null;
// for asn.1 encoded data, we use length field to
// determine the data length and remove redundant paddings.
if ((data[1] & 0xFF) < 128) {
bytes = new byte[data[1] + 2];
System.arraycopy(data, 0, bytes, 0, data[1] + 2);
} else {
if ((data[1] & 0xFF) > 128) {
int len = data[1] & (byte)0x7F;
int result = 0;
for (int i = 0; i < len; i++) {
result |= (data[i + 2] & 0xFF) << (8 * (len - i - 1));
}
bytes = new byte[result + len + 2];
System.arraycopy(data, 0, bytes, 0, result + len + 2);
}
}
return bytes;
}
public int getEType() {
return eType;
}
public Integer getKeyVersionNumber() {
return kvno;
}
/**
* Returns the raw cipher text bytes, not in ASN.1 encoding.
*/
public byte[] getBytes() {
return cipher;
}
}

View file

@ -0,0 +1,583 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.util.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import sun.security.krb5.internal.ktab.KeyTab;
import sun.security.krb5.internal.ccache.CCacheOutputStream;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
/**
* This class encapsulates the concept of an EncryptionKey. An encryption
* key is defined in RFC 4120 as:
*
* EncryptionKey ::= SEQUENCE {
* keytype [0] Int32 -- actually encryption type --,
* keyvalue [1] OCTET STRING
* }
*
* keytype
* This field specifies the encryption type of the encryption key
* that follows in the keyvalue field. Although its name is
* "keytype", it actually specifies an encryption type. Previously,
* multiple cryptosystems that performed encryption differently but
* were capable of using keys with the same characteristics were
* permitted to share an assigned number to designate the type of
* key; this usage is now deprecated.
*
* keyvalue
* This field contains the key itself, encoded as an octet string.
*/
public class EncryptionKey
implements Cloneable {
public static final EncryptionKey NULL_KEY =
new EncryptionKey(new byte[] {}, EncryptedData.ETYPE_NULL, null);
private int keyType;
private byte[] keyValue;
private Integer kvno; // not part of ASN1 encoding;
private static final boolean DEBUG = Krb5.DEBUG;
public synchronized int getEType() {
return keyType;
}
public final Integer getKeyVersionNumber() {
return kvno;
}
/**
* Returns the raw key bytes, not in any ASN.1 encoding.
*/
public final byte[] getBytes() {
// This method cannot be called outside sun.security, hence no
// cloning. getEncoded() calls this method.
return keyValue;
}
public synchronized Object clone() {
return new EncryptionKey(keyValue, keyType, kvno);
}
/**
* Obtains all versions of the secret key of the principal from a
* keytab.
*
* @param princ the principal whose secret key is desired
* @param keytab the path to the keytab file. A value of null
* will be accepted to indicate that the default path should be
* searched.
* @return an array of secret keys or null if none were found.
*/
public static EncryptionKey[] acquireSecretKeys(PrincipalName princ,
String keytab) {
if (princ == null)
throw new IllegalArgumentException(
"Cannot have null pricipal name to look in keytab.");
// KeyTab getInstance(keytab) will call KeyTab.getInstance()
// if keytab is null
KeyTab ktab = KeyTab.getInstance(keytab);
return ktab.readServiceKeys(princ);
}
/**
* Obtains a key for a given etype of a principal with possible new salt
* and s2kparams
* @param cname NOT null
* @param password NOT null
* @param etype
* @param snp can be NULL
* @return never null
*/
public static EncryptionKey acquireSecretKey(PrincipalName cname,
char[] password, int etype, PAData.SaltAndParams snp)
throws KrbException {
String salt;
byte[] s2kparams;
if (snp != null) {
salt = snp.salt != null ? snp.salt : cname.getSalt();
s2kparams = snp.params;
} else {
salt = cname.getSalt();
s2kparams = null;
}
return acquireSecretKey(password, salt, etype, s2kparams);
}
/**
* Obtains a key for a given etype with salt and optional s2kparams
* @param password NOT null
* @param salt NOT null
* @param etype
* @param s2kparams can be NULL
* @return never null
*/
public static EncryptionKey acquireSecretKey(char[] password,
String salt, int etype, byte[] s2kparams)
throws KrbException {
return new EncryptionKey(
stringToKey(password, salt, s2kparams, etype),
etype, null);
}
/**
* Generate a list of keys using the given principal and password.
* Construct a key for each configured etype.
* Caller is responsible for clearing password.
*/
/*
* Usually, when keyType is decoded from ASN.1 it will contain a
* value indicating what the algorithm to be used is. However, when
* converting from a password to a key for the AS-EXCHANGE, this
* keyType will not be available. Use builtin list of default etypes
* as the default in that case. If default_tkt_enctypes was set in
* the libdefaults of krb5.conf, then use that sequence.
*/
public static EncryptionKey[] acquireSecretKeys(char[] password,
String salt) throws KrbException {
int[] etypes = EType.getDefaults("default_tkt_enctypes");
EncryptionKey[] encKeys = new EncryptionKey[etypes.length];
for (int i = 0; i < etypes.length; i++) {
if (EType.isSupported(etypes[i])) {
encKeys[i] = new EncryptionKey(
stringToKey(password, salt, null, etypes[i]),
etypes[i], null);
} else {
if (DEBUG) {
System.out.println("Encryption Type " +
EType.toString(etypes[i]) +
" is not supported/enabled");
}
}
}
return encKeys;
}
// Used in Krb5AcceptCredential, self
public EncryptionKey(byte[] keyValue,
int keyType,
Integer kvno) {
if (keyValue != null) {
this.keyValue = new byte[keyValue.length];
System.arraycopy(keyValue, 0, this.keyValue, 0, keyValue.length);
} else {
throw new IllegalArgumentException("EncryptionKey: " +
"Key bytes cannot be null!");
}
this.keyType = keyType;
this.kvno = kvno;
}
/**
* Constructs an EncryptionKey by using the specified key type and key
* value. It is used to recover the key when retrieving data from
* credential cache file.
*
*/
// Used in JSSE (KerberosWrapper), Credentials,
// javax.security.auth.kerberos.KeyImpl
public EncryptionKey(int keyType,
byte[] keyValue) {
this(keyValue, keyType, null);
}
private static byte[] stringToKey(char[] password, String salt,
byte[] s2kparams, int keyType) throws KrbCryptoException {
char[] slt = salt.toCharArray();
char[] pwsalt = new char[password.length + slt.length];
System.arraycopy(password, 0, pwsalt, 0, password.length);
System.arraycopy(slt, 0, pwsalt, password.length, slt.length);
Arrays.fill(slt, '0');
try {
switch (keyType) {
case EncryptedData.ETYPE_DES_CBC_CRC:
case EncryptedData.ETYPE_DES_CBC_MD5:
return Des.string_to_key_bytes(pwsalt);
case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
return Des3.stringToKey(pwsalt);
case EncryptedData.ETYPE_ARCFOUR_HMAC:
return ArcFourHmac.stringToKey(password);
case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
return Aes128.stringToKey(password, salt, s2kparams);
case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
return Aes256.stringToKey(password, salt, s2kparams);
default:
throw new IllegalArgumentException("encryption type " +
EType.toString(keyType) + " not supported");
}
} catch (GeneralSecurityException e) {
KrbCryptoException ke = new KrbCryptoException(e.getMessage());
ke.initCause(e);
throw ke;
} finally {
Arrays.fill(pwsalt, '0');
}
}
// Used in javax.security.auth.kerberos.KeyImpl
public EncryptionKey(char[] password,
String salt,
String algorithm) throws KrbCryptoException {
if (algorithm == null || algorithm.equalsIgnoreCase("DES")
|| algorithm.equalsIgnoreCase("des-cbc-md5")) {
keyType = EncryptedData.ETYPE_DES_CBC_MD5;
} else if (algorithm.equalsIgnoreCase("des-cbc-crc")) {
keyType = EncryptedData.ETYPE_DES_CBC_CRC;
} else if (algorithm.equalsIgnoreCase("DESede")
|| algorithm.equalsIgnoreCase("des3-cbc-sha1-kd")) {
keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;
} else if (algorithm.equalsIgnoreCase("AES128")
|| algorithm.equalsIgnoreCase("aes128-cts-hmac-sha1-96")) {
keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;
} else if (algorithm.equalsIgnoreCase("ArcFourHmac")
|| algorithm.equalsIgnoreCase("rc4-hmac")) {
keyType = EncryptedData.ETYPE_ARCFOUR_HMAC;
} else if (algorithm.equalsIgnoreCase("AES256")
|| algorithm.equalsIgnoreCase("aes256-cts-hmac-sha1-96")) {
keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;
// validate if AES256 is enabled
if (!EType.isSupported(keyType)) {
throw new IllegalArgumentException("Algorithm " + algorithm +
" not enabled");
}
} else {
throw new IllegalArgumentException("Algorithm " + algorithm +
" not supported");
}
keyValue = stringToKey(password, salt, null, keyType);
kvno = null;
}
/**
* Generates a sub-sessionkey from a given session key.
*
* Used in AcceptSecContextToken and KrbApReq by acceptor- and initiator-
* side respectively.
*/
public EncryptionKey(EncryptionKey key) throws KrbCryptoException {
// generate random sub-session key
keyValue = Confounder.bytes(key.keyValue.length);
for (int i = 0; i < keyValue.length; i++) {
keyValue[i] ^= key.keyValue[i];
}
keyType = key.keyType;
// check for key parity and weak keys
try {
// check for DES key
if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) ||
(keyType == EncryptedData.ETYPE_DES_CBC_CRC)) {
// fix DES key parity
if (!DESKeySpec.isParityAdjusted(keyValue, 0)) {
keyValue = Des.set_parity(keyValue);
}
// check for weak key
if (DESKeySpec.isWeak(keyValue, 0)) {
keyValue[7] = (byte)(keyValue[7] ^ 0xF0);
}
}
// check for 3DES key
if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
// fix 3DES key parity
if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) {
keyValue = Des3.parityFix(keyValue);
}
// check for weak keys
byte[] oneKey = new byte[8];
for (int i=0; i<keyValue.length; i+=8) {
System.arraycopy(keyValue, i, oneKey, 0, 8);
if (DESKeySpec.isWeak(oneKey, 0)) {
keyValue[i+7] = (byte)(keyValue[i+7] ^ 0xF0);
}
}
}
} catch (GeneralSecurityException e) {
KrbCryptoException ke = new KrbCryptoException(e.getMessage());
ke.initCause(e);
throw ke;
}
}
/**
* Constructs an instance of EncryptionKey type.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*
*/
// Used in javax.security.auth.kerberos.KeyImpl
public EncryptionKey(DerValue encoding) throws Asn1Exception, IOException {
DerValue der;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
keyType = der.getData().getBigInteger().intValue();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x01) {
keyValue = der.getData().getOctetString();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Returns the ASN.1 encoding of this EncryptionKey.
*
* <pre>{@code
* EncryptionKey ::= SEQUENCE {
* keytype[0] INTEGER,
* keyvalue[1] OCTET STRING }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*
* @return byte array of encoded EncryptionKey object.
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*/
public synchronized byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(keyType);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
(byte)0x00), temp);
temp = new DerOutputStream();
temp.putOctetString(keyValue);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
(byte)0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
public synchronized void destroy() {
if (keyValue != null)
for (int i = 0; i < keyValue.length; i++)
keyValue[i] = 0;
}
/**
* Parse (unmarshal) an Encryption key from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
* @return an instance of EncryptionKey.
*
*/
public static EncryptionKey parse(DerInputStream data, byte
explicitTag, boolean optional) throws
Asn1Exception, IOException {
if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=
explicitTag)) {
return null;
}
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new EncryptionKey(subDer);
}
}
/**
* Writes key value in FCC format to a <code>CCacheOutputStream</code>.
*
* @param cos a <code>CCacheOutputStream</code> to be written to.
* @exception IOException if an I/O exception occurs.
* @see sun.security.krb5.internal.ccache.CCacheOutputStream
*
*/
public synchronized void writeKey(CCacheOutputStream cos)
throws IOException {
cos.write16(keyType);
// we use KRB5_FCC_FVNO_3
cos.write16(keyType); // key type is recorded twice.
cos.write32(keyValue.length);
for (int i = 0; i < keyValue.length; i++) {
cos.write8(keyValue[i]);
}
}
public String toString() {
return new String("EncryptionKey: keyType=" + keyType
+ " kvno=" + kvno
+ " keyValue (hex dump)="
+ (keyValue == null || keyValue.length == 0 ?
" Empty Key" : '\n'
+ Krb5.hexDumper.encodeBuffer(keyValue)
+ '\n'));
}
/**
* Find a key with given etype
*/
public static EncryptionKey findKey(int etype, EncryptionKey[] keys)
throws KrbException {
return findKey(etype, null, keys);
}
/**
* Determines if a kvno matches another kvno. Used in the method
* findKey(type, kvno, keys). Always returns true if either input
* is null or zero, in case any side does not have kvno info available.
*
* Note: zero is included because N/A is not a legal value for kvno
* in javax.security.auth.kerberos.KerberosKey. Therefore, the info
* that the kvno is N/A might be lost when converting between this
* class and KerberosKey.
*/
private static boolean versionMatches(Integer v1, Integer v2) {
if (v1 == null || v1 == 0 || v2 == null || v2 == 0) {
return true;
}
return v1.equals(v2);
}
/**
* Find a key with given etype and kvno
* @param kvno if null, return any (first?) key
*/
public static EncryptionKey findKey(int etype, Integer kvno, EncryptionKey[] keys)
throws KrbException {
// check if encryption type is supported
if (!EType.isSupported(etype)) {
throw new KrbException("Encryption type " +
EType.toString(etype) + " is not supported/enabled");
}
int ktype;
boolean etypeFound = false;
// When no matched kvno is found, returns tke key of the same
// etype with the highest kvno
int kvno_found = 0;
EncryptionKey key_found = null;
for (int i = 0; i < keys.length; i++) {
ktype = keys[i].getEType();
if (EType.isSupported(ktype)) {
Integer kv = keys[i].getKeyVersionNumber();
if (etype == ktype) {
etypeFound = true;
if (versionMatches(kvno, kv)) {
return keys[i];
} else if (kv > kvno_found) {
// kv is not null
key_found = keys[i];
kvno_found = kv;
}
}
}
}
// Key not found.
// allow DES key to be used for the DES etypes
if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
for (int i = 0; i < keys.length; i++) {
ktype = keys[i].getEType();
if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
Integer kv = keys[i].getKeyVersionNumber();
etypeFound = true;
if (versionMatches(kvno, kv)) {
return new EncryptionKey(etype, keys[i].getBytes());
} else if (kv > kvno_found) {
key_found = new EncryptionKey(etype, keys[i].getBytes());
kvno_found = kv;
}
}
}
}
if (etypeFound) {
return key_found;
// For compatibility, will not fail here.
//throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);
}
return null;
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2011, 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.krb5;
import javax.security.auth.kerberos.KeyTab;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.PrincipalName;
/**
* An unsafe tunnel to get non-public access to classes in the
* javax.security.auth.kerberos package.
*/
public interface JavaxSecurityAuthKerberosAccess {
/**
* Returns a snapshot to the backing keytab
*/
public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
KeyTab ktab);
}

View file

@ -0,0 +1,555 @@
/*
* Copyright (c) 2000, 2014, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.security.PrivilegedAction;
import java.security.Security;
import java.util.Locale;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.NetClient;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.StringTokenizer;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import sun.security.krb5.internal.KRBError;
/**
* KDC-REQ/KDC-REP communication. No more base class for KrbAsReq and
* KrbTgsReq. This class is now communication only.
*/
public final class KdcComm {
// The following settings can be configured in [libdefaults]
// section of krb5.conf, which are global for all realms. Each of
// them can also be defined in a realm, which overrides value here.
/**
* max retry time for a single KDC, default Krb5.KDC_RETRY_LIMIT (3)
*/
private static int defaultKdcRetryLimit;
/**
* timeout requesting a ticket from KDC, in millisec, default 30 sec
*/
private static int defaultKdcTimeout;
/**
* max UDP packet size, default unlimited (-1)
*/
private static int defaultUdpPrefLimit;
private static final boolean DEBUG = Krb5.DEBUG;
private static final String BAD_POLICY_KEY = "krb5.kdc.bad.policy";
/**
* What to do when a KDC is unavailable, specified in the
* java.security file with key krb5.kdc.bad.policy.
* Possible values can be TRY_LAST or TRY_LESS. Reloaded when refreshed.
*/
private enum BpType {
NONE, TRY_LAST, TRY_LESS
}
private static int tryLessMaxRetries = 1;
private static int tryLessTimeout = 5000;
private static BpType badPolicy;
static {
initStatic();
}
/**
* Read global settings
*/
public static void initStatic() {
String value = AccessController.doPrivileged(
new PrivilegedAction<String>() {
public String run() {
return Security.getProperty(BAD_POLICY_KEY);
}
});
if (value != null) {
value = value.toLowerCase(Locale.ENGLISH);
String[] ss = value.split(":");
if ("tryless".equals(ss[0])) {
if (ss.length > 1) {
String[] params = ss[1].split(",");
try {
int tmp0 = Integer.parseInt(params[0]);
if (params.length > 1) {
tryLessTimeout = Integer.parseInt(params[1]);
}
// Assign here in case of exception at params[1]
tryLessMaxRetries = tmp0;
} catch (NumberFormatException nfe) {
// Ignored. Please note that tryLess is recognized and
// used, parameters using default values
if (DEBUG) {
System.out.println("Invalid " + BAD_POLICY_KEY +
" parameter for tryLess: " +
value + ", use default");
}
}
}
badPolicy = BpType.TRY_LESS;
} else if ("trylast".equals(ss[0])) {
badPolicy = BpType.TRY_LAST;
} else {
badPolicy = BpType.NONE;
}
} else {
badPolicy = BpType.NONE;
}
int timeout = -1;
int max_retries = -1;
int udp_pref_limit = -1;
try {
Config cfg = Config.getInstance();
String temp = cfg.get("libdefaults", "kdc_timeout");
timeout = parseTimeString(temp);
temp = cfg.get("libdefaults", "max_retries");
max_retries = parsePositiveIntString(temp);
temp = cfg.get("libdefaults", "udp_preference_limit");
udp_pref_limit = parsePositiveIntString(temp);
} catch (Exception exc) {
// ignore any exceptions; use default values
if (DEBUG) {
System.out.println ("Exception in getting KDC communication " +
"settings, using default value " +
exc.getMessage());
}
}
defaultKdcTimeout = timeout > 0 ? timeout : 30*1000; // 30 seconds
defaultKdcRetryLimit =
max_retries > 0 ? max_retries : Krb5.KDC_RETRY_LIMIT;
if (udp_pref_limit < 0) {
defaultUdpPrefLimit = Krb5.KDC_DEFAULT_UDP_PREF_LIMIT;
} else if (udp_pref_limit > Krb5.KDC_HARD_UDP_LIMIT) {
defaultUdpPrefLimit = Krb5.KDC_HARD_UDP_LIMIT;
} else {
defaultUdpPrefLimit = udp_pref_limit;
}
KdcAccessibility.reset();
}
/**
* The instance fields
*/
private String realm;
public KdcComm(String realm) throws KrbException {
if (realm == null) {
realm = Config.getInstance().getDefaultRealm();
if (realm == null) {
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Cannot find default realm");
}
}
this.realm = realm;
}
public byte[] send(byte[] obuf)
throws IOException, KrbException {
int udpPrefLimit = getRealmSpecificValue(
realm, "udp_preference_limit", defaultUdpPrefLimit);
boolean useTCP = (udpPrefLimit > 0 &&
(obuf != null && obuf.length > udpPrefLimit));
return send(obuf, useTCP);
}
private byte[] send(byte[] obuf, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
return null;
Config cfg = Config.getInstance();
if (realm == null) {
realm = cfg.getDefaultRealm();
if (realm == null) {
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Cannot find default realm");
}
}
String kdcList = cfg.getKDCList(realm);
if (kdcList == null) {
throw new KrbException("Cannot get kdc for realm " + realm);
}
// tempKdc may include the port number also
Iterator<String> tempKdc = KdcAccessibility.list(kdcList).iterator();
if (!tempKdc.hasNext()) {
throw new KrbException("Cannot get kdc for realm " + realm);
}
byte[] ibuf = null;
try {
ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP);
} catch(Exception first) {
boolean ok = false;
while(tempKdc.hasNext()) {
try {
ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP);
ok = true;
break;
} catch(Exception ignore) {}
}
if (!ok) throw first;
}
if (ibuf == null) {
throw new IOException("Cannot get a KDC reply");
}
return ibuf;
}
// send the AS Request to the specified KDC
// failover to using TCP if useTCP is not set and response is too big
private byte[] sendIfPossible(byte[] obuf, String tempKdc, boolean useTCP)
throws IOException, KrbException {
try {
byte[] ibuf = send(obuf, tempKdc, useTCP);
KRBError ke = null;
try {
ke = new KRBError(ibuf);
} catch (Exception e) {
// OK
}
if (ke != null && ke.getErrorCode() ==
Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
ibuf = send(obuf, tempKdc, true);
}
KdcAccessibility.removeBad(tempKdc);
return ibuf;
} catch(Exception e) {
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: error trying " +
tempKdc);
e.printStackTrace(System.out);
}
KdcAccessibility.addBad(tempKdc);
throw e;
}
}
// send the AS Request to the specified KDC
private byte[] send(byte[] obuf, String tempKdc, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
return null;
int port = Krb5.KDC_INET_DEFAULT_PORT;
int retries = getRealmSpecificValue(
realm, "max_retries", defaultKdcRetryLimit);
int timeout = getRealmSpecificValue(
realm, "kdc_timeout", defaultKdcTimeout);
if (badPolicy == BpType.TRY_LESS &&
KdcAccessibility.isBad(tempKdc)) {
if (retries > tryLessMaxRetries) {
retries = tryLessMaxRetries; // less retries
}
if (timeout > tryLessTimeout) {
timeout = tryLessTimeout; // less time
}
}
String kdc = null;
String portStr = null;
if (tempKdc.charAt(0) == '[') { // Explicit IPv6 in []
int pos = tempKdc.indexOf(']', 1);
if (pos == -1) {
throw new IOException("Illegal KDC: " + tempKdc);
}
kdc = tempKdc.substring(1, pos);
if (pos != tempKdc.length() - 1) { // with port number
if (tempKdc.charAt(pos+1) != ':') {
throw new IOException("Illegal KDC: " + tempKdc);
}
portStr = tempKdc.substring(pos+2);
}
} else {
int colon = tempKdc.indexOf(':');
if (colon == -1) { // Hostname or IPv4 host only
kdc = tempKdc;
} else {
int nextColon = tempKdc.indexOf(':', colon+1);
if (nextColon > 0) { // >=2 ":", IPv6 with no port
kdc = tempKdc;
} else { // 1 ":", hostname or IPv4 with port
kdc = tempKdc.substring(0, colon);
portStr = tempKdc.substring(colon+1);
}
}
}
if (portStr != null) {
int tempPort = parsePositiveIntString(portStr);
if (tempPort > 0)
port = tempPort;
}
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: kdc=" + kdc
+ (useTCP ? " TCP:":" UDP:")
+ port + ", timeout="
+ timeout
+ ", number of retries ="
+ retries
+ ", #bytes=" + obuf.length);
}
KdcCommunication kdcCommunication =
new KdcCommunication(kdc, port, useTCP, timeout, retries, obuf);
try {
byte[] ibuf = AccessController.doPrivileged(kdcCommunication);
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: #bytes read="
+ (ibuf != null ? ibuf.length : 0));
}
return ibuf;
} catch (PrivilegedActionException e) {
Exception wrappedException = e.getException();
if (wrappedException instanceof IOException) {
throw (IOException) wrappedException;
} else {
throw (KrbException) wrappedException;
}
}
}
private static class KdcCommunication
implements PrivilegedExceptionAction<byte[]> {
private String kdc;
private int port;
private boolean useTCP;
private int timeout;
private int retries;
private byte[] obuf;
public KdcCommunication(String kdc, int port, boolean useTCP,
int timeout, int retries, byte[] obuf) {
this.kdc = kdc;
this.port = port;
this.useTCP = useTCP;
this.timeout = timeout;
this.retries = retries;
this.obuf = obuf;
}
// The caller only casts IOException and KrbException so don't
// add any new ones!
public byte[] run() throws IOException, KrbException {
byte[] ibuf = null;
for (int i=1; i <= retries; i++) {
String proto = useTCP?"TCP":"UDP";
if (DEBUG) {
System.out.println(">>> KDCCommunication: kdc=" + kdc
+ " " + proto + ":"
+ port + ", timeout="
+ timeout
+ ",Attempt =" + i
+ ", #bytes=" + obuf.length);
}
try (NetClient kdcClient = NetClient.getInstance(
proto, kdc, port, timeout)) {
kdcClient.send(obuf);
ibuf = kdcClient.receive();
break;
} catch (SocketTimeoutException se) {
if (DEBUG) {
System.out.println ("SocketTimeOutException with " +
"attempt: " + i);
}
if (i == retries) {
ibuf = null;
throw se;
}
}
}
return ibuf;
}
}
/**
* Parses a time value string. If it ends with "s", parses as seconds.
* Otherwise, parses as milliseconds.
* @param s the time string
* @return the integer value in milliseconds, or -1 if input is null or
* has an invalid format
*/
private static int parseTimeString(String s) {
if (s == null) {
return -1;
}
if (s.endsWith("s")) {
int seconds = parsePositiveIntString(s.substring(0, s.length()-1));
return (seconds < 0) ? -1 : (seconds*1000);
} else {
return parsePositiveIntString(s);
}
}
/**
* Returns krb5.conf setting of {@code key} for a specific realm,
* which can be:
* 1. defined in the sub-stanza for the given realm inside [realms], or
* 2. defined in [libdefaults], or
* 3. defValue
* @param realm the given realm in which the setting is requested. Returns
* the global setting if null
* @param key the key for the setting
* @param defValue default value
* @return a value for the key
*/
private int getRealmSpecificValue(String realm, String key, int defValue) {
int v = defValue;
if (realm == null) return v;
int temp = -1;
try {
String value =
Config.getInstance().get("realms", realm, key);
if (key.equals("kdc_timeout")) {
temp = parseTimeString(value);
} else {
temp = parsePositiveIntString(value);
}
} catch (Exception exc) {
// Ignored, defValue will be picked up
}
if (temp > 0) v = temp;
return v;
}
private static int parsePositiveIntString(String intString) {
if (intString == null)
return -1;
int ret = -1;
try {
ret = Integer.parseInt(intString);
} catch (Exception exc) {
return -1;
}
if (ret >= 0)
return ret;
return -1;
}
/**
* Maintains a KDC accessible list. Unavailable KDCs are put into a
* blacklist, when a KDC in the blacklist is available, it's removed
* from there. No insertion order in the blacklist.
*
* There are two methods to deal with KDCs in the blacklist. 1. Only try
* them when there's no KDC not on the blacklist. 2. Still try them, but
* with lesser number of retries and smaller timeout value.
*/
static class KdcAccessibility {
// Known bad KDCs
private static Set<String> bads = new HashSet<>();
private static synchronized void addBad(String kdc) {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: add " + kdc);
}
bads.add(kdc);
}
private static synchronized void removeBad(String kdc) {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: remove " + kdc);
}
bads.remove(kdc);
}
private static synchronized boolean isBad(String kdc) {
return bads.contains(kdc);
}
private static synchronized void reset() {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: reset");
}
bads.clear();
}
// Returns a preferred KDC list by putting the bad ones at the end
private static synchronized List<String> list(String kdcList) {
StringTokenizer st = new StringTokenizer(kdcList);
List<String> list = new ArrayList<>();
if (badPolicy == BpType.TRY_LAST) {
List<String> badkdcs = new ArrayList<>();
while (st.hasMoreTokens()) {
String t = st.nextToken();
if (bads.contains(t)) badkdcs.add(t);
else list.add(t);
}
// Bad KDCs are put at last
list.addAll(badkdcs);
} else {
// All KDCs are returned in their original order,
// This include TRY_LESS and NONE
while (st.hasMoreTokens()) {
list.add(st.nextToken());
}
}
return list;
}
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.krb5;
import javax.security.auth.kerberos.KeyTab;
import jdk.internal.misc.Unsafe;
public class KerberosSecrets {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static JavaxSecurityAuthKerberosAccess javaxSecurityAuthKerberosAccess;
public static void setJavaxSecurityAuthKerberosAccess
(JavaxSecurityAuthKerberosAccess jsaka) {
javaxSecurityAuthKerberosAccess = jsaka;
}
public static JavaxSecurityAuthKerberosAccess
getJavaxSecurityAuthKerberosAccess() {
if (javaxSecurityAuthKerberosAccess == null)
unsafe.ensureClassInitialized(KeyTab.class);
return javaxSecurityAuthKerberosAccess;
}
}

View file

@ -0,0 +1,198 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.util.*;
import java.io.IOException;
/**
* This class encapsulates a KRB-AP-REP sent from the service to the
* client.
*/
public class KrbApRep {
private byte[] obuf;
private byte[] ibuf;
private EncAPRepPart encPart; // although in plain text
private APRep apRepMessg;
/**
* Constructs a KRB-AP-REP to send to a client.
* @throws KrbException
* @throws IOException
*/
// Used in AcceptSecContextToken
public KrbApRep(KrbApReq incomingReq,
boolean useSeqNumber,
EncryptionKey subKey)
throws KrbException, IOException {
SeqNumber seqNum = new LocalSeqNumber();
init(incomingReq, subKey, seqNum);
}
/**
* Constructs a KRB-AP-REQ from the bytes received from a service.
* @throws KrbException
* @throws IOException
*/
// Used in AcceptSecContextToken
public KrbApRep(byte[] message, Credentials tgtCreds,
KrbApReq outgoingReq) throws KrbException, IOException {
this(message, tgtCreds);
authenticate(outgoingReq);
}
private void init(KrbApReq apReq,
EncryptionKey subKey,
SeqNumber seqNumber)
throws KrbException, IOException {
createMessage(
apReq.getCreds().key,
apReq.getCtime(),
apReq.cusec(),
subKey,
seqNumber);
obuf = apRepMessg.asn1Encode();
}
/**
* Constructs a KrbApRep object.
* @param msg a byte array of reply message.
* @param tgs_creds client's credential.
* @exception KrbException
* @exception IOException
*/
private KrbApRep(byte[] msg, Credentials tgs_creds)
throws KrbException, IOException {
this(new DerValue(msg), tgs_creds);
}
/**
* Constructs a KrbApRep object.
* @param msg a byte array of reply message.
* @param tgs_creds client's credential.
* @exception KrbException
* @exception IOException
*/
private KrbApRep(DerValue encoding, Credentials tgs_creds)
throws KrbException, IOException {
APRep rep = null;
try {
rep = new APRep(encoding);
} catch (Asn1Exception e) {
rep = null;
KRBError err = new KRBError(encoding);
String errStr = err.getErrorString();
String eText;
if (errStr.charAt(errStr.length() - 1) == 0)
eText = errStr.substring(0, errStr.length() - 1);
else
eText = errStr;
KrbException ke = new KrbException(err.getErrorCode(), eText);
ke.initCause(e);
throw ke;
}
byte[] temp = rep.encPart.decrypt(tgs_creds.key,
KeyUsage.KU_ENC_AP_REP_PART);
byte[] enc_ap_rep_part = rep.encPart.reset(temp);
encoding = new DerValue(enc_ap_rep_part);
encPart = new EncAPRepPart(encoding);
}
private void authenticate(KrbApReq apReq)
throws KrbException, IOException {
if (encPart.ctime.getSeconds() != apReq.getCtime().getSeconds() ||
encPart.cusec != apReq.getCtime().getMicroSeconds())
throw new KrbApErrException(Krb5.KRB_AP_ERR_MUT_FAIL);
}
/**
* Returns the optional subkey stored in
* this message. Returns null if none is stored.
*/
public EncryptionKey getSubKey() {
// XXX Can encPart be null
return encPart.getSubKey();
}
/**
* Returns the optional sequence number stored in the
* this message. Returns null if none is stored.
*/
public Integer getSeqNumber() {
// XXX Can encPart be null
return encPart.getSeqNumber();
}
/**
* Returns the ASN.1 encoding that should be sent to the peer.
*/
public byte[] getMessage() {
return obuf;
}
private void createMessage(
EncryptionKey key,
KerberosTime ctime,
int cusec,
EncryptionKey subKey,
SeqNumber seqNumber)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
Integer seqno = null;
if (seqNumber != null)
seqno = seqNumber.current();
encPart = new EncAPRepPart(ctime,
cusec,
subKey,
seqno);
byte[] encPartEncoding = encPart.asn1Encode();
EncryptedData encEncPart = new EncryptedData(key, encPartEncoding,
KeyUsage.KU_ENC_AP_REP_PART);
apRepMessg = new APRep(encEncPart);
}
}

View file

@ -0,0 +1,515 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import sun.security.jgss.krb5.Krb5AcceptCredential;
import java.net.InetAddress;
import sun.security.util.*;
import java.io.IOException;
import java.util.Arrays;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import sun.security.krb5.internal.rcache.AuthTimeWithHash;
/**
* This class encapsulates a KRB-AP-REQ that a client sends to a
* server for authentication.
*/
public class KrbApReq {
private byte[] obuf;
private KerberosTime ctime;
private int cusec;
private Authenticator authenticator;
private Credentials creds;
private APReq apReqMessg;
// Used by acceptor side
private static ReplayCache rcache = ReplayCache.getInstance();
private static boolean DEBUG = Krb5.DEBUG;
private static final char[] hexConst = "0123456789ABCDEF".toCharArray();
/**
* Constructs an AP-REQ message to send to the peer.
* @param tgsCred the <code>Credentials</code> to be used to construct the
* AP Request protocol message.
* @param mutualRequired Whether mutual authentication is required
* @param useSubkey Whether the subkey is to be used to protect this
* specific application session. If this is not set then the
* session key from the ticket will be used.
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
/*
// Not Used
public KrbApReq(Credentials tgsCred,
boolean mutualRequired,
boolean useSubKey,
boolean useSeqNumber) throws Asn1Exception,
KrbCryptoException, KrbException, IOException {
this(tgsCred, mutualRequired, useSubKey, useSeqNumber, null);
}
*/
/**
* Constructs an AP-REQ message to send to the peer.
* @param tgsCred the <code>Credentials</code> to be used to construct the
* AP Request protocol message.
* @param mutualRequired Whether mutual authentication is required
* @param useSubKey Whether the subkey is to be used to protect this
* specific application session. If this is not set then the
* session key from the ticket will be used.
* @param cksum checksum of the application data that accompanies
* the KRB_AP_REQ.
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
// Used in InitSecContextToken
public KrbApReq(Credentials tgsCred,
boolean mutualRequired,
boolean useSubKey,
boolean useSeqNumber,
Checksum cksum) throws Asn1Exception,
KrbCryptoException, KrbException, IOException {
APOptions apOptions = (mutualRequired?
new APOptions(Krb5.AP_OPTS_MUTUAL_REQUIRED):
new APOptions());
if (DEBUG)
System.out.println(">>> KrbApReq: APOptions are " + apOptions);
EncryptionKey subKey = (useSubKey?
new EncryptionKey(tgsCred.getSessionKey()):
null);
SeqNumber seqNum = new LocalSeqNumber();
init(apOptions,
tgsCred,
cksum,
subKey,
seqNum,
null, // AuthorizationData authzData
KeyUsage.KU_AP_REQ_AUTHENTICATOR);
}
/**
* Constructs an AP-REQ message from the bytes received from the
* peer.
* @param message The message received from the peer
* @param cred <code>KrbAcceptCredential</code> containing keys to decrypt
* the message; key selected will depend on etype used to encrypt data
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
// Used in InitSecContextToken (for AP_REQ and not TGS REQ)
public KrbApReq(byte[] message,
Krb5AcceptCredential cred,
InetAddress initiator)
throws KrbException, IOException {
obuf = message;
if (apReqMessg == null)
decode();
authenticate(cred, initiator);
}
/**
* Constructs an AP-REQ message from the bytes received from the
* peer.
* @param value The <code>DerValue</code> that contains the
* DER enoded AP-REQ protocol message
* @param keys <code>EncrtyptionKey</code>s to decrypt the message;
*
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
/*
public KrbApReq(DerValue value, EncryptionKey[] key, InetAddress initiator)
throws KrbException, IOException {
obuf = value.toByteArray();
if (apReqMessg == null)
decode(value);
authenticate(keys, initiator);
}
KrbApReq(APOptions options,
Credentials tgs_creds,
Checksum cksum,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData)
throws KrbException, IOException {
init(options, tgs_creds, cksum, subKey, seqNumber, authorizationData);
}
*/
/** used by KrbTgsReq **/
KrbApReq(APOptions apOptions,
Ticket ticket,
EncryptionKey key,
PrincipalName cname,
Checksum cksum,
KerberosTime ctime,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
init(apOptions, ticket, key, cname,
cksum, ctime, subKey, seqNumber, authorizationData,
KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR);
}
private void init(APOptions options,
Credentials tgs_creds,
Checksum cksum,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData,
int usage)
throws KrbException, IOException {
ctime = KerberosTime.now();
init(options,
tgs_creds.ticket,
tgs_creds.key,
tgs_creds.client,
cksum,
ctime,
subKey,
seqNumber,
authorizationData,
usage);
}
private void init(APOptions apOptions,
Ticket ticket,
EncryptionKey key,
PrincipalName cname,
Checksum cksum,
KerberosTime ctime,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData,
int usage)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
createMessage(apOptions, ticket, key, cname,
cksum, ctime, subKey, seqNumber, authorizationData,
usage);
obuf = apReqMessg.asn1Encode();
}
void decode() throws KrbException, IOException {
DerValue encoding = new DerValue(obuf);
decode(encoding);
}
void decode(DerValue encoding) throws KrbException, IOException {
apReqMessg = null;
try {
apReqMessg = new APReq(encoding);
} catch (Asn1Exception e) {
apReqMessg = null;
KRBError err = new KRBError(encoding);
String errStr = err.getErrorString();
String eText;
if (errStr.charAt(errStr.length() - 1) == 0)
eText = errStr.substring(0, errStr.length() - 1);
else
eText = errStr;
KrbException ke = new KrbException(err.getErrorCode(), eText);
ke.initCause(e);
throw ke;
}
}
private void authenticate(Krb5AcceptCredential cred, InetAddress initiator)
throws KrbException, IOException {
int encPartKeyType = apReqMessg.ticket.encPart.getEType();
Integer kvno = apReqMessg.ticket.encPart.getKeyVersionNumber();
EncryptionKey[] keys = cred.getKrb5EncryptionKeys(apReqMessg.ticket.sname);
EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, kvno, keys);
if (dkey == null) {
throw new KrbException(Krb5.API_INVALID_ARG,
"Cannot find key of appropriate type to decrypt AP-REQ - " +
EType.toString(encPartKeyType));
}
byte[] bytes = apReqMessg.ticket.encPart.decrypt(dkey,
KeyUsage.KU_TICKET);
byte[] temp = apReqMessg.ticket.encPart.reset(bytes);
EncTicketPart enc_ticketPart = new EncTicketPart(temp);
checkPermittedEType(enc_ticketPart.key.getEType());
byte[] bytes2 = apReqMessg.authenticator.decrypt(enc_ticketPart.key,
KeyUsage.KU_AP_REQ_AUTHENTICATOR);
byte[] temp2 = apReqMessg.authenticator.reset(bytes2);
authenticator = new Authenticator(temp2);
ctime = authenticator.ctime;
cusec = authenticator.cusec;
authenticator.ctime =
authenticator.ctime.withMicroSeconds(authenticator.cusec);
if (!authenticator.cname.equals(enc_ticketPart.cname)) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH);
}
if (!authenticator.ctime.inClockSkew())
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
String alg = AuthTimeWithHash.DEFAULT_HASH_ALG;
byte[] hash;
try {
hash = MessageDigest.getInstance(AuthTimeWithHash.realAlg(alg))
.digest(apReqMessg.authenticator.cipher);
} catch (NoSuchAlgorithmException ex) {
throw new AssertionError("Impossible " + alg);
}
char[] h = new char[hash.length * 2];
for (int i=0; i<hash.length; i++) {
h[2*i] = hexConst[(hash[i]&0xff)>>4];
h[2*i+1] = hexConst[hash[i]&0xf];
}
AuthTimeWithHash time = new AuthTimeWithHash(
authenticator.cname.toString(),
apReqMessg.ticket.sname.toString(),
authenticator.ctime.getSeconds(),
authenticator.cusec,
alg,
new String(h));
rcache.checkAndStore(KerberosTime.now(), time);
if (initiator != null) {
// sender host address
HostAddress sender = new HostAddress(initiator);
if (enc_ticketPart.caddr != null
&& !enc_ticketPart.caddr.inList(sender)) {
if (DEBUG) {
System.out.println(">>> KrbApReq: initiator is "
+ sender.getInetAddress()
+ ", but caddr is "
+ Arrays.toString(
enc_ticketPart.caddr.getInetAddresses()));
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
}
}
// XXX check for repeated authenticator
// if found
// throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
// else
// save authenticator to check for later
KerberosTime now = KerberosTime.now();
if ((enc_ticketPart.starttime != null &&
enc_ticketPart.starttime.greaterThanWRTClockSkew(now)) ||
enc_ticketPart.flags.get(Krb5.TKT_OPTS_INVALID))
throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_NYV);
// if the current time is later than end time by more
// than the allowable clock skew, throws ticket expired exception.
if (enc_ticketPart.endtime != null &&
now.greaterThanWRTClockSkew(enc_ticketPart.endtime)) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_EXPIRED);
}
creds = new Credentials(
apReqMessg.ticket,
authenticator.cname,
apReqMessg.ticket.sname,
enc_ticketPart.key,
enc_ticketPart.flags,
enc_ticketPart.authtime,
enc_ticketPart.starttime,
enc_ticketPart.endtime,
enc_ticketPart.renewTill,
enc_ticketPart.caddr,
enc_ticketPart.authorizationData);
if (DEBUG) {
System.out.println(">>> KrbApReq: authenticate succeed.");
}
}
/**
* Returns the credentials that are contained in the ticket that
* is part of this AP-REQ.
*/
public Credentials getCreds() {
return creds;
}
KerberosTime getCtime() {
if (ctime != null)
return ctime;
return authenticator.ctime;
}
int cusec() {
return cusec;
}
APOptions getAPOptions() throws KrbException, IOException {
if (apReqMessg == null)
decode();
if (apReqMessg != null)
return apReqMessg.apOptions;
return null;
}
/**
* Returns true if mutual authentication is required and hence an
* AP-REP will need to be generated.
* @throws KrbException
* @throws IOException
*/
public boolean getMutualAuthRequired() throws KrbException, IOException {
if (apReqMessg == null)
decode();
if (apReqMessg != null)
return apReqMessg.apOptions.get(Krb5.AP_OPTS_MUTUAL_REQUIRED);
return false;
}
boolean useSessionKey() throws KrbException, IOException {
if (apReqMessg == null)
decode();
if (apReqMessg != null)
return apReqMessg.apOptions.get(Krb5.AP_OPTS_USE_SESSION_KEY);
return false;
}
/**
* Returns the optional subkey stored in the Authenticator for
* this message. Returns null if none is stored.
*/
public EncryptionKey getSubKey() {
// XXX Can authenticator be null
return authenticator.getSubKey();
}
/**
* Returns the optional sequence number stored in the
* Authenticator for this message. Returns null if none is
* stored.
*/
public Integer getSeqNumber() {
// XXX Can authenticator be null
return authenticator.getSeqNumber();
}
/**
* Returns the optional Checksum stored in the
* Authenticator for this message. Returns null if none is
* stored.
*/
public Checksum getChecksum() {
return authenticator.getChecksum();
}
/**
* Returns the ASN.1 encoding that should be sent to the peer.
*/
public byte[] getMessage() {
return obuf;
}
/**
* Returns the principal name of the client that generated this
* message.
*/
public PrincipalName getClient() {
return creds.getClient();
}
private void createMessage(APOptions apOptions,
Ticket ticket,
EncryptionKey key,
PrincipalName cname,
Checksum cksum,
KerberosTime ctime,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData,
int usage)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
Integer seqno = null;
if (seqNumber != null)
seqno = seqNumber.current();
authenticator =
new Authenticator(cname,
cksum,
ctime.getMicroSeconds(),
ctime,
subKey,
seqno,
authorizationData);
byte[] temp = authenticator.asn1Encode();
EncryptedData encAuthenticator =
new EncryptedData(key, temp, usage);
apReqMessg =
new APReq(apOptions, ticket, encAuthenticator);
}
// Check that key is one of the permitted types
private static void checkPermittedEType(int target) throws KrbException {
int[] etypes = EType.getDefaults("permitted_enctypes");
if (!EType.isSupported(target, etypes)) {
throw new KrbException(EType.toString(target) +
" encryption type not in permitted_enctypes list");
}
}
}

View file

@ -0,0 +1,115 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
abstract class KrbAppMessage {
private static boolean DEBUG = Krb5.DEBUG;
/**
* Common checks for KRB-PRIV and KRB-SAFE
*/
void check(KerberosTime packetTimestamp,
Integer packetUsec,
Integer packetSeqNumber,
HostAddress packetSAddress,
HostAddress packetRAddress,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress,
boolean timestampRequired,
boolean seqNumberRequired,
PrincipalName packetPrincipal)
throws KrbApErrException {
if (!Krb5.AP_EMPTY_ADDRESSES_ALLOWED || sAddress != null) {
if (packetSAddress == null || sAddress == null ||
!packetSAddress.equals(sAddress)) {
if (DEBUG && packetSAddress == null) {
System.out.println("packetSAddress is null");
}
if (DEBUG && sAddress == null) {
System.out.println("sAddress is null");
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
}
}
if (!Krb5.AP_EMPTY_ADDRESSES_ALLOWED || rAddress != null) {
if (packetRAddress == null || rAddress == null ||
!packetRAddress.equals(rAddress))
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
}
if (packetTimestamp != null) {
if (packetUsec != null) {
packetTimestamp =
packetTimestamp.withMicroSeconds(packetUsec.intValue());
}
if (!packetTimestamp.inClockSkew()) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
}
} else {
if (timestampRequired) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
}
}
// XXX check replay cache
// if (rcache.repeated(packetTimestamp, packetUsec, packetSAddress))
// throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
// XXX consider moving up to api level
if (seqNumber == null && seqNumberRequired == true)
throw new KrbApErrException(Krb5.API_INVALID_ARG);
if (packetSeqNumber != null && seqNumber != null) {
if (packetSeqNumber.intValue() != seqNumber.current())
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADORDER);
// should be done only when no more exceptions are possible
seqNumber.step();
} else {
if (seqNumberRequired) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADORDER);
}
}
// Must not be relaxed, per RFC 4120
if (packetTimestamp == null && packetSeqNumber == null)
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
// XXX check replay cache
// rcache.save_identifier(packetTimestamp, packetUsec, packetSAddress,
// packetPrincipal, pcaketRealm);
}
}

View file

@ -0,0 +1,184 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.krb5.internal.crypto.EType;
import sun.security.util.*;
import java.io.IOException;
import java.util.Objects;
import javax.security.auth.kerberos.KeyTab;
import sun.security.jgss.krb5.Krb5Util;
/**
* This class encapsulates a AS-REP message that the KDC sends to the
* client.
*/
class KrbAsRep extends KrbKdcRep {
private ASRep rep; // The AS-REP message
private Credentials creds; // The Credentials provide by the AS-REP
// message, created by initiator after calling
// the decrypt() method
private boolean DEBUG = Krb5.DEBUG;
KrbAsRep(byte[] ibuf) throws
KrbException, Asn1Exception, IOException {
DerValue encoding = new DerValue(ibuf);
try {
rep = new ASRep(encoding);
} catch (Asn1Exception e) {
rep = null;
KRBError err = new KRBError(encoding);
String errStr = err.getErrorString();
String eText = null; // pick up text sent by the server (if any)
if (errStr != null && errStr.length() > 0) {
if (errStr.charAt(errStr.length() - 1) == 0)
eText = errStr.substring(0, errStr.length() - 1);
else
eText = errStr;
}
KrbException ke;
if (eText == null) {
// no text sent from server
ke = new KrbException(err);
} else {
if (DEBUG) {
System.out.println("KRBError received: " + eText);
}
// override default text with server text
ke = new KrbException(err, eText);
}
ke.initCause(e);
throw ke;
}
}
// KrbAsReqBuilder need to read back the PA for key generation
PAData[] getPA() {
return rep.pAData;
}
/**
* Called by KrbAsReqBuilder to resolve a AS-REP message using a keytab.
* @param ktab the keytab, not null
* @param asReq the original AS-REQ sent, used to validate AS-REP
* @param cname the user principal name, used to locate keys in ktab
*/
void decryptUsingKeyTab(KeyTab ktab, KrbAsReq asReq, PrincipalName cname)
throws KrbException, Asn1Exception, IOException {
EncryptionKey dkey = null;
int encPartKeyType = rep.encPart.getEType();
Integer encPartKvno = rep.encPart.kvno;
try {
dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno,
Krb5Util.keysFromJavaxKeyTab(ktab, cname));
} catch (KrbException ke) {
if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) {
// Fallback to no kvno. In some cases, keytab is generated
// not by sysadmin but Java's ktab command
dkey = EncryptionKey.findKey(encPartKeyType,
Krb5Util.keysFromJavaxKeyTab(ktab, cname));
}
}
if (dkey == null) {
throw new KrbException(Krb5.API_INVALID_ARG,
"Cannot find key for type/kvno to decrypt AS REP - " +
EType.toString(encPartKeyType) + "/" + encPartKvno);
}
decrypt(dkey, asReq);
}
/**
* Called by KrbAsReqBuilder to resolve a AS-REP message using a password.
* @param password user provided password. not null
* @param asReq the original AS-REQ sent, used to validate AS-REP
* @param cname the user principal name, used to provide salt
*/
void decryptUsingPassword(char[] password,
KrbAsReq asReq, PrincipalName cname)
throws KrbException, Asn1Exception, IOException {
int encPartKeyType = rep.encPart.getEType();
EncryptionKey dkey = EncryptionKey.acquireSecretKey(
cname,
password,
encPartKeyType,
PAData.getSaltAndParams(encPartKeyType, rep.pAData));
decrypt(dkey, asReq);
}
/**
* Decrypts encrypted content inside AS-REP. Called by initiator.
* @param dkey the decryption key to use
* @param asReq the original AS-REQ sent, used to validate AS-REP
*/
private void decrypt(EncryptionKey dkey, KrbAsReq asReq)
throws KrbException, Asn1Exception, IOException {
byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey,
KeyUsage.KU_ENC_AS_REP_PART);
byte[] enc_as_rep_part = rep.encPart.reset(enc_as_rep_bytes);
DerValue encoding = new DerValue(enc_as_rep_part);
EncASRepPart enc_part = new EncASRepPart(encoding);
rep.encKDCRepPart = enc_part;
ASReq req = asReq.getMessage();
check(true, req, rep);
creds = new Credentials(
rep.ticket,
req.reqBody.cname,
enc_part.sname,
enc_part.key,
enc_part.flags,
enc_part.authtime,
enc_part.starttime,
enc_part.endtime,
enc_part.renewTill,
enc_part.caddr);
if (DEBUG) {
System.out.println(">>> KrbAsRep cons in KrbAsReq.getReply " +
req.reqBody.cname.getNameString());
}
}
Credentials getCreds() {
return Objects.requireNonNull(creds, "Creds not available yet.");
}
sun.security.krb5.internal.ccache.Credentials getCCreds() {
return new sun.security.krb5.internal.ccache.Credentials(rep);
}
}

View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.Nonce;
import sun.security.krb5.internal.crypto.KeyUsage;
import java.io.IOException;
import java.time.Instant;
/**
* This class encapsulates the KRB-AS-REQ message that the client
* sends to the KDC.
*/
public class KrbAsReq {
private ASReq asReqMessg;
private boolean DEBUG = Krb5.DEBUG;
/**
* Constructs an AS-REQ message.
*/
// Can be null? has default?
public KrbAsReq(EncryptionKey pakey, // ok
KDCOptions options, // ok, new KDCOptions()
PrincipalName cname, // NO and must have realm
PrincipalName sname, // ok, krgtgt@CREALM
KerberosTime from, // ok
KerberosTime till, // ok, will use
KerberosTime rtime, // ok
int[] eTypes, // NO
HostAddresses addresses // ok
)
throws KrbException, IOException {
if (options == null) {
options = new KDCOptions();
}
// check if they are valid arguments. The optional fields should be
// consistent with settings in KDCOptions. Mar 17 2000
if (options.get(KDCOptions.FORWARDED) ||
options.get(KDCOptions.PROXY) ||
options.get(KDCOptions.ENC_TKT_IN_SKEY) ||
options.get(KDCOptions.RENEW) ||
options.get(KDCOptions.VALIDATE)) {
// this option is only specified in a request to the
// ticket-granting server
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
}
if (options.get(KDCOptions.POSTDATED)) {
// if (from == null)
// throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
} else {
if (from != null) from = null;
}
PAData[] paData = null;
if (pakey != null) {
PAEncTSEnc ts = new PAEncTSEnc();
byte[] temp = ts.asn1Encode();
EncryptedData encTs = new EncryptedData(pakey, temp,
KeyUsage.KU_PA_ENC_TS);
paData = new PAData[1];
paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
encTs.asn1Encode());
}
if (cname.getRealm() == null) {
throw new RealmException(Krb5.REALM_NULL,
"default realm not specified ");
}
if (DEBUG) {
System.out.println(">>> KrbAsReq creating message");
}
Config cfg = Config.getInstance();
// check to use addresses in tickets
if (addresses == null && cfg.useAddresses()) {
addresses = HostAddresses.getLocalAddresses();
}
if (sname == null) {
String realm = cname.getRealmAsString();
sname = PrincipalName.tgsService(realm, realm);
}
if (till == null) {
String d = cfg.get("libdefaults", "ticket_lifetime");
if (d != null) {
till = new KerberosTime(Instant.now().plusSeconds(Config.duration(d)));
} else {
till = new KerberosTime(0); // Choose KDC maximum allowed
}
}
if (rtime == null) {
String d = cfg.get("libdefaults", "renew_lifetime");
if (d != null) {
rtime = new KerberosTime(Instant.now().plusSeconds(Config.duration(d)));
}
}
if (rtime != null) {
options.set(KDCOptions.RENEWABLE, true);
if (till.greaterThan(rtime)) {
rtime = till;
}
}
// enc-authorization-data and additional-tickets never in AS-REQ
KDCReqBody kdc_req_body = new KDCReqBody(options,
cname,
sname,
from,
till,
rtime,
Nonce.value(),
eTypes,
addresses,
null,
null);
asReqMessg = new ASReq(
paData,
kdc_req_body);
}
byte[] encoding() throws IOException, Asn1Exception {
return asReqMessg.asn1Encode();
}
// Used by KrbAsRep to validate AS-REP
ASReq getMessage() {
return asReqMessg;
}
}

View file

@ -0,0 +1,411 @@
/*
* Copyright (c) 2010, 2012, 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.krb5;
import java.io.IOException;
import java.util.Arrays;
import javax.security.auth.kerberos.KeyTab;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.internal.HostAddresses;
import sun.security.krb5.internal.KDCOptions;
import sun.security.krb5.internal.KRBError;
import sun.security.krb5.internal.KerberosTime;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.PAData;
import sun.security.krb5.internal.crypto.EType;
/**
* A manager class for AS-REQ communications.
*
* This class does:
* 1. Gather information to create AS-REQ
* 2. Create and send AS-REQ
* 3. Receive AS-REP and KRB-ERROR (-KRB_ERR_RESPONSE_TOO_BIG) and parse them
* 4. Emit credentials and secret keys (for JAAS storeKey=true with password)
*
* This class does not:
* 1. Deal with real communications (KdcComm does it, and TGS-REQ)
* a. Name of KDCs for a realm
* b. Server availability, timeout, UDP or TCP
* d. KRB_ERR_RESPONSE_TOO_BIG
* 2. Stores its own copy of password, this means:
* a. Do not change/wipe it before Builder finish
* b. Builder will not wipe it for you
*
* With this class:
* 1. KrbAsReq has only one constructor
* 2. Krb5LoginModule and Kinit call a single builder
* 3. Better handling of sensitive info
*
* @since 1.7
*/
public final class KrbAsReqBuilder {
// Common data for AS-REQ fields
private KDCOptions options;
private PrincipalName cname;
private PrincipalName sname;
private KerberosTime from;
private KerberosTime till;
private KerberosTime rtime;
private HostAddresses addresses;
// Secret source: can't be changed once assigned, only one (of the two
// sources) can be set to non-null
private final char[] password;
private final KeyTab ktab;
// Used to create a ENC-TIMESTAMP in the 2nd AS-REQ
private PAData[] paList; // PA-DATA from both KRB-ERROR and AS-REP.
// Used by getKeys() only.
// Only AS-REP should be enough per RFC,
// combined in case etypes are different.
// The generated and received:
private KrbAsReq req;
private KrbAsRep rep;
private static enum State {
INIT, // Initialized, can still add more initialization info
REQ_OK, // AS-REQ performed
DESTROYED, // Destroyed, not usable anymore
}
private State state;
// Called by other constructors
private void init(PrincipalName cname)
throws KrbException {
this.cname = cname;
state = State.INIT;
}
/**
* Creates a builder to be used by {@code cname} with existing keys.
*
* @param cname the client of the AS-REQ. Must not be null. Might have no
* realm, where default realm will be used. This realm will be the target
* realm for AS-REQ. I believe a client should only get initial TGT from
* its own realm.
* @param ktab must not be null. If empty, might be quite useless.
* This argument will neither be modified nor stored by the method.
* @throws KrbException
*/
public KrbAsReqBuilder(PrincipalName cname, KeyTab ktab)
throws KrbException {
init(cname);
this.ktab = ktab;
this.password = null;
}
/**
* Creates a builder to be used by {@code cname} with a known password.
*
* @param cname the client of the AS-REQ. Must not be null. Might have no
* realm, where default realm will be used. This realm will be the target
* realm for AS-REQ. I believe a client should only get initial TGT from
* its own realm.
* @param pass must not be null. This argument will neither be modified
* nor stored by the method.
* @throws KrbException
*/
public KrbAsReqBuilder(PrincipalName cname, char[] pass)
throws KrbException {
init(cname);
this.password = pass.clone();
this.ktab = null;
}
/**
* Retrieves an array of secret keys for the client. This is used when
* the client supplies password but need keys to act as an acceptor. For
* an initiator, it must be called after AS-REQ is performed (state is OK).
* For an acceptor, it can be called when this KrbAsReqBuilder object is
* constructed (state is INIT).
* @param isInitiator if the caller is an initiator
* @return generated keys from password. PA-DATA from server might be used.
* All "default_tkt_enctypes" keys will be generated, Never null.
* @throws IllegalStateException if not constructed from a password
* @throws KrbException
*/
public EncryptionKey[] getKeys(boolean isInitiator) throws KrbException {
checkState(isInitiator?State.REQ_OK:State.INIT, "Cannot get keys");
if (password != null) {
int[] eTypes = EType.getDefaults("default_tkt_enctypes");
EncryptionKey[] result = new EncryptionKey[eTypes.length];
/*
* Returns an array of keys. Before KrbAsReqBuilder, all etypes
* use the same salt which is either the default one or a new salt
* coming from PA-DATA. After KrbAsReqBuilder, each etype uses its
* own new salt from PA-DATA. For an etype with no PA-DATA new salt
* at all, what salt should it use?
*
* Commonly, the stored keys are only to be used by an acceptor to
* decrypt service ticket in AP-REQ. Most impls only allow keys
* from a keytab on acceptor, but unfortunately (?) Java supports
* acceptor using password. In this case, if the service ticket is
* encrypted using an etype which we don't have PA-DATA new salt,
* using the default salt might be wrong (say, case-insensitive
* user name). Instead, we would use the new salt of another etype.
*/
String salt = null; // the saved new salt
try {
for (int i=0; i<eTypes.length; i++) {
// First round, only calculate those have a PA entry
PAData.SaltAndParams snp =
PAData.getSaltAndParams(eTypes[i], paList);
if (snp != null) {
// Never uses a salt for rc4-hmac, it does not use
// a salt at all
if (eTypes[i] != EncryptedData.ETYPE_ARCFOUR_HMAC &&
snp.salt != null) {
salt = snp.salt;
}
result[i] = EncryptionKey.acquireSecretKey(cname,
password,
eTypes[i],
snp);
}
}
// No new salt from PA, maybe empty, maybe only rc4-hmac
if (salt == null) salt = cname.getSalt();
for (int i=0; i<eTypes.length; i++) {
// Second round, calculate those with no PA entry
if (result[i] == null) {
result[i] = EncryptionKey.acquireSecretKey(password,
salt,
eTypes[i],
null);
}
}
} catch (IOException ioe) {
KrbException ke = new KrbException(Krb5.ASN1_PARSE_ERROR);
ke.initCause(ioe);
throw ke;
}
return result;
} else {
throw new IllegalStateException("Required password not provided");
}
}
/**
* Sets or clears options. If cleared, default options will be used
* at creation time.
* @param options
*/
public void setOptions(KDCOptions options) {
checkState(State.INIT, "Cannot specify options");
this.options = options;
}
public void setTill(KerberosTime till) {
checkState(State.INIT, "Cannot specify till");
this.till = till;
}
public void setRTime(KerberosTime rtime) {
checkState(State.INIT, "Cannot specify rtime");
this.rtime = rtime;
}
/**
* Sets or clears target. If cleared, KrbAsReq might choose krbtgt
* for cname realm
* @param sname
*/
public void setTarget(PrincipalName sname) {
checkState(State.INIT, "Cannot specify target");
this.sname = sname;
}
/**
* Adds or clears addresses. KrbAsReq might add some if empty
* field not allowed
* @param addresses
*/
public void setAddresses(HostAddresses addresses) {
checkState(State.INIT, "Cannot specify addresses");
this.addresses = addresses;
}
/**
* Build a KrbAsReq object from all info fed above. Normally this method
* will be called twice: initial AS-REQ and second with pakey
* @param key null (initial AS-REQ) or pakey (with preauth)
* @return the KrbAsReq object
* @throws KrbException
* @throws IOException
*/
private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
int[] eTypes;
if (password != null) {
eTypes = EType.getDefaults("default_tkt_enctypes");
} else {
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
eTypes = EType.getDefaults("default_tkt_enctypes",
ks);
for (EncryptionKey k: ks) k.destroy();
}
return new KrbAsReq(key,
options,
cname,
sname,
from,
till,
rtime,
eTypes,
addresses);
}
/**
* Parses AS-REP, decrypts enc-part, retrieves ticket and session key
* @throws KrbException
* @throws Asn1Exception
* @throws IOException
*/
private KrbAsReqBuilder resolve()
throws KrbException, Asn1Exception, IOException {
if (ktab != null) {
rep.decryptUsingKeyTab(ktab, req, cname);
} else {
rep.decryptUsingPassword(password, req, cname);
}
if (rep.getPA() != null) {
if (paList == null || paList.length == 0) {
paList = rep.getPA();
} else {
int extraLen = rep.getPA().length;
if (extraLen > 0) {
int oldLen = paList.length;
paList = Arrays.copyOf(paList, paList.length + extraLen);
System.arraycopy(rep.getPA(), 0, paList, oldLen, extraLen);
}
}
}
return this;
}
/**
* Communication until AS-REP or non preauth-related KRB-ERROR received
* @throws KrbException
* @throws IOException
*/
private KrbAsReqBuilder send() throws KrbException, IOException {
boolean preAuthFailedOnce = false;
KdcComm comm = new KdcComm(cname.getRealmAsString());
EncryptionKey pakey = null;
while (true) {
try {
req = build(pakey);
rep = new KrbAsRep(comm.send(req.encoding()));
return this;
} catch (KrbException ke) {
if (!preAuthFailedOnce && (
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED ||
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) {
if (Krb5.DEBUG) {
System.out.println("KrbAsReqBuilder: " +
"PREAUTH FAILED/REQ, re-send AS-REQ");
}
preAuthFailedOnce = true;
KRBError kerr = ke.getError();
int paEType = PAData.getPreferredEType(kerr.getPA(),
EType.getDefaults("default_tkt_enctypes")[0]);
if (password == null) {
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
pakey = EncryptionKey.findKey(paEType, ks);
if (pakey != null) pakey = (EncryptionKey)pakey.clone();
for (EncryptionKey k: ks) k.destroy();
} else {
pakey = EncryptionKey.acquireSecretKey(cname,
password,
paEType,
PAData.getSaltAndParams(
paEType, kerr.getPA()));
}
paList = kerr.getPA(); // Update current paList
} else {
throw ke;
}
}
}
}
/**
* Performs AS-REQ send and AS-REP receive.
* Maybe a state is needed here, to divide prepare process and getCreds.
* @throws KrbException
* @throws Asn1Exception
* @throws IOException
*/
public KrbAsReqBuilder action()
throws KrbException, Asn1Exception, IOException {
checkState(State.INIT, "Cannot call action");
state = State.REQ_OK;
return send().resolve();
}
/**
* Gets Credentials object after action
*/
public Credentials getCreds() {
checkState(State.REQ_OK, "Cannot retrieve creds");
return rep.getCreds();
}
/**
* Gets another type of Credentials after action
*/
public sun.security.krb5.internal.ccache.Credentials getCCreds() {
checkState(State.REQ_OK, "Cannot retrieve CCreds");
return rep.getCCreds();
}
/**
* Destroys the object and clears keys and password info.
*/
public void destroy() {
state = State.DESTROYED;
if (password != null) {
Arrays.fill(password, (char)0);
}
}
/**
* Checks if the current state is the specified one.
* @param st the expected state
* @param msg error message if state is not correct
* @throws IllegalStateException if state is not correct
*/
private void checkState(State st, String msg) {
if (state != st) {
throw new IllegalStateException(msg + " at " + st + " state");
}
}
}

View file

@ -0,0 +1,174 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import java.io.IOException;
import sun.security.util.DerValue;
/**
* This class encapsulates the KRB-CRED message that a client uses to
* send its delegated credentials to a server.
*
* Supports delegation of one ticket only.
* @author Mayank Upadhyay
*/
public class KrbCred {
private static boolean DEBUG = Krb5.DEBUG;
private byte[] obuf = null;
private KRBCred credMessg = null;
private Ticket ticket = null;
private EncKrbCredPart encPart = null;
private Credentials creds = null;
private KerberosTime timeStamp = null;
// Used in InitialToken with null key
public KrbCred(Credentials tgt,
Credentials serviceTicket,
EncryptionKey key)
throws KrbException, IOException {
PrincipalName client = tgt.getClient();
PrincipalName tgService = tgt.getServer();
if (!serviceTicket.getClient().equals(client))
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Client principal does not match");
// XXX Check Windows flag OK-TO-FORWARD-TO
// Invoke TGS-REQ to get a forwarded TGT for the peer
KDCOptions options = new KDCOptions();
options.set(KDCOptions.FORWARDED, true);
options.set(KDCOptions.FORWARDABLE, true);
KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService,
null, null, null, null,
null, // No easy way to get addresses right
null, null, null);
credMessg = createMessage(tgsReq.sendAndGetCreds(), key);
obuf = credMessg.asn1Encode();
}
KRBCred createMessage(Credentials delegatedCreds, EncryptionKey key)
throws KrbException, IOException {
EncryptionKey sessionKey
= delegatedCreds.getSessionKey();
PrincipalName princ = delegatedCreds.getClient();
PrincipalName tgService = delegatedCreds.getServer();
KrbCredInfo credInfo = new KrbCredInfo(sessionKey,
princ, delegatedCreds.flags, delegatedCreds.authTime,
delegatedCreds.startTime, delegatedCreds.endTime,
delegatedCreds.renewTill, tgService,
delegatedCreds.cAddr);
timeStamp = KerberosTime.now();
KrbCredInfo[] credInfos = {credInfo};
EncKrbCredPart encPart =
new EncKrbCredPart(credInfos,
timeStamp, null, null, null, null);
EncryptedData encEncPart = new EncryptedData(key,
encPart.asn1Encode(), KeyUsage.KU_ENC_KRB_CRED_PART);
Ticket[] tickets = {delegatedCreds.ticket};
credMessg = new KRBCred(tickets, encEncPart);
return credMessg;
}
// Used in InitialToken, NULL_KEY might be used
public KrbCred(byte[] asn1Message, EncryptionKey key)
throws KrbException, IOException {
credMessg = new KRBCred(asn1Message);
ticket = credMessg.tickets[0];
if (credMessg.encPart.getEType() == 0) {
key = EncryptionKey.NULL_KEY;
}
byte[] temp = credMessg.encPart.decrypt(key,
KeyUsage.KU_ENC_KRB_CRED_PART);
byte[] plainText = credMessg.encPart.reset(temp);
DerValue encoding = new DerValue(plainText);
EncKrbCredPart encPart = new EncKrbCredPart(encoding);
timeStamp = encPart.timeStamp;
KrbCredInfo credInfo = encPart.ticketInfo[0];
EncryptionKey credInfoKey = credInfo.key;
PrincipalName pname = credInfo.pname;
TicketFlags flags = credInfo.flags;
KerberosTime authtime = credInfo.authtime;
KerberosTime starttime = credInfo.starttime;
KerberosTime endtime = credInfo.endtime;
KerberosTime renewTill = credInfo.renewTill;
PrincipalName sname = credInfo.sname;
HostAddresses caddr = credInfo.caddr;
if (DEBUG) {
System.out.println(">>>Delegated Creds have pname=" + pname
+ " sname=" + sname
+ " authtime=" + authtime
+ " starttime=" + starttime
+ " endtime=" + endtime
+ "renewTill=" + renewTill);
}
creds = new Credentials(ticket, pname, sname, credInfoKey,
flags, authtime, starttime, endtime, renewTill, caddr);
}
/**
* Returns the delegated credentials from the peer.
*/
public Credentials[] getDelegatedCreds() {
Credentials[] allCreds = {creds};
return allCreds;
}
/**
* Returns the ASN.1 encoding that should be sent to the peer.
*/
public byte[] getMessage() {
return obuf;
}
}

View file

@ -0,0 +1,45 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
/**
* KrbCryptoExceptoin is a wrapper exception for exceptions thrown by JCE.
*
* @author Yanni Zhang
*/
public class KrbCryptoException extends KrbException {
private static final long serialVersionUID = -1657367919979982250L;
public KrbCryptoException (String s) {
super(s);
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.KRBError;
public class KrbException extends Exception {
private static final long serialVersionUID = -4993302876451928596L;
private int returnCode;
private KRBError error;
public KrbException(String s) {
super(s);
}
public KrbException(Throwable cause) {
super(cause);
}
public KrbException(int i) {
returnCode = i;
}
public KrbException(int i, String s) {
this(s);
returnCode = i;
}
public KrbException(KRBError e) {
returnCode = e.getErrorCode();
error = e;
}
public KrbException(KRBError e, String s) {
this(s);
returnCode = e.getErrorCode();
error = e;
}
public KRBError getError() {
return error;
}
public int returnCode() {
return returnCode;
}
public String returnCodeSymbol() {
return returnCodeSymbol(returnCode);
}
public static String returnCodeSymbol(int i) {
return "not yet implemented";
}
public String returnCodeMessage() {
return Krb5.getErrorMessage(returnCode);
}
public static String errorMessage(int i) {
return Krb5.getErrorMessage(i);
}
public String krbErrorMessage() {
StringBuilder sb = new StringBuilder();
sb.append("krb_error ").append(returnCode);
String msg = getMessage();
if (msg != null) {
sb.append(" ");
sb.append(msg);
}
return sb.toString();
}
/**
* Returns messages like:
* "Integrity check on decrypted field failed (31) - \
* Could not decrypt service ticket"
* If the error code is 0 then the first half is skipped.
*/
public String getMessage() {
StringBuilder message = new StringBuilder();
int returnCode = returnCode();
if (returnCode != 0) {
message.append(returnCodeMessage());
message.append(" (").append(returnCode()).append(')');
}
String consMessage = super.getMessage();
if (consMessage != null && consMessage.length() != 0) {
if (returnCode != 0)
message.append(" - ");
message.append(consMessage);
}
return message.toString();
}
public String toString() {
return ("KrbException: " + getMessage());
}
@Override public int hashCode() {
int result = 17;
result = 37 * result + returnCode;
if (error != null) {
result = 37 * result + error.hashCode();
}
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof KrbException)) {
return false;
}
KrbException other = (KrbException)obj;
if (returnCode != other.returnCode) {
return false;
}
return (error == null)?(other.error == null):
(error.equals(other.error));
}
}

View file

@ -0,0 +1,122 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
abstract class KrbKdcRep {
static void check(
boolean isAsReq,
KDCReq req,
KDCRep rep
) throws KrbApErrException {
if (isAsReq && !req.reqBody.cname.equals(rep.cname)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (req.reqBody.getNonce() != rep.encKDCRepPart.nonce) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (
((req.reqBody.addresses != null && rep.encKDCRepPart.caddr != null) &&
!req.reqBody.addresses.equals(rep.encKDCRepPart.caddr))) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
// We allow KDC to return a non-forwardable ticket if request has -f
for (int i = 2; i < 6; i++) {
if (req.reqBody.kdcOptions.get(i) !=
rep.encKDCRepPart.flags.get(i)) {
if (Krb5.DEBUG) {
System.out.println("> KrbKdcRep.check: at #" + i
+ ". request for " + req.reqBody.kdcOptions.get(i)
+ ", received " + rep.encKDCRepPart.flags.get(i));
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
}
// Reply to a renewable request should be renewable, but if request does
// not contain renewable, KDC is free to issue a renewable ticket (for
// example, if ticket_lifetime is too big).
if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE) &&
!rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if ((req.reqBody.from == null) || req.reqBody.from.isZero()) {
// verify this is allowed
if ((rep.encKDCRepPart.starttime != null) &&
!rep.encKDCRepPart.starttime.inClockSkew()) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
}
}
if ((req.reqBody.from != null) && !req.reqBody.from.isZero()) {
// verify this is allowed
if ((rep.encKDCRepPart.starttime != null) &&
!req.reqBody.from.equals(rep.encKDCRepPart.starttime)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
}
if (!req.reqBody.till.isZero() &&
rep.encKDCRepPart.endtime.greaterThan(req.reqBody.till)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) {
if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) {
// verify this is required
if ((rep.encKDCRepPart.renewTill == null) ||
rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.rtime)
) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
}
}
}
}

View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import sun.security.util.*;
import java.io.IOException;
/** XXX This class does not appear to be used. **/
class KrbPriv extends KrbAppMessage {
private byte[] obuf;
private byte[] userData;
private KrbPriv(byte[] userData,
Credentials creds,
EncryptionKey subKey,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr
) throws KrbException, IOException {
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
obuf = mk_priv(
userData,
reqKey,
timestamp,
seqNumber,
saddr,
raddr
);
}
private KrbPriv(byte[] msg,
Credentials creds,
EncryptionKey subKey,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr,
boolean timestampRequired,
boolean seqNumberRequired
) throws KrbException, IOException {
KRBPriv krb_priv = new KRBPriv(msg);
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
userData = rd_priv(krb_priv,
reqKey,
seqNumber,
saddr,
raddr,
timestampRequired,
seqNumberRequired,
creds.client
);
}
public byte[] getMessage() throws KrbException {
return obuf;
}
public byte[] getData() {
return userData;
}
private byte[] mk_priv(byte[] userData,
EncryptionKey key,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress
) throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
Integer usec = null;
Integer seqno = null;
if (timestamp != null)
usec = timestamp.getMicroSeconds();
if (seqNumber != null) {
seqno = seqNumber.current();
seqNumber.step();
}
EncKrbPrivPart unenc_encKrbPrivPart =
new EncKrbPrivPart(userData,
timestamp,
usec,
seqno,
sAddress,
rAddress
);
byte[] temp = unenc_encKrbPrivPart.asn1Encode();
EncryptedData encKrbPrivPart =
new EncryptedData(key, temp,
KeyUsage.KU_ENC_KRB_PRIV_PART);
KRBPriv krb_priv = new KRBPriv(encKrbPrivPart);
temp = krb_priv.asn1Encode();
return krb_priv.asn1Encode();
}
private byte[] rd_priv(KRBPriv krb_priv,
EncryptionKey key,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress,
boolean timestampRequired,
boolean seqNumberRequired,
PrincipalName cname
) throws Asn1Exception, KdcErrException,
KrbApErrException, IOException, KrbCryptoException {
byte[] bytes = krb_priv.encPart.decrypt(key,
KeyUsage.KU_ENC_KRB_PRIV_PART);
byte[] temp = krb_priv.encPart.reset(bytes);
DerValue ref = new DerValue(temp);
EncKrbPrivPart enc_part = new EncKrbPrivPart(ref);
check(enc_part.timestamp,
enc_part.usec,
enc_part.seqNumber,
enc_part.sAddress,
enc_part.rAddress,
seqNumber,
sAddress,
rAddress,
timestampRequired,
seqNumberRequired,
cname
);
return enc_part.userData;
}
}

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
class KrbSafe extends KrbAppMessage {
private byte[] obuf;
private byte[] userData;
public KrbSafe(byte[] userData,
Credentials creds,
EncryptionKey subKey,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr
) throws KrbException, IOException {
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
obuf = mk_safe(userData,
reqKey,
timestamp,
seqNumber,
saddr,
raddr
);
}
public KrbSafe(byte[] msg,
Credentials creds,
EncryptionKey subKey,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr,
boolean timestampRequired,
boolean seqNumberRequired
) throws KrbException, IOException {
KRBSafe krb_safe = new KRBSafe(msg);
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
userData = rd_safe(
krb_safe,
reqKey,
seqNumber,
saddr,
raddr,
timestampRequired,
seqNumberRequired,
creds.client
);
}
public byte[] getMessage() {
return obuf;
}
public byte[] getData() {
return userData;
}
private byte[] mk_safe(byte[] userData,
EncryptionKey key,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress
) throws Asn1Exception, IOException, KdcErrException,
KrbApErrException, KrbCryptoException {
Integer usec = null;
Integer seqno = null;
if (timestamp != null)
usec = timestamp.getMicroSeconds();
if (seqNumber != null) {
seqno = seqNumber.current();
seqNumber.step();
}
KRBSafeBody krb_safeBody =
new KRBSafeBody(userData,
timestamp,
usec,
seqno,
sAddress,
rAddress
);
byte[] temp = krb_safeBody.asn1Encode();
Checksum cksum = new Checksum(
Checksum.SAFECKSUMTYPE_DEFAULT,
temp,
key,
KeyUsage.KU_KRB_SAFE_CKSUM
);
KRBSafe krb_safe = new KRBSafe(krb_safeBody, cksum);
temp = krb_safe.asn1Encode();
return krb_safe.asn1Encode();
}
private byte[] rd_safe(KRBSafe krb_safe,
EncryptionKey key,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress,
boolean timestampRequired,
boolean seqNumberRequired,
PrincipalName cname
) throws Asn1Exception, KdcErrException,
KrbApErrException, IOException, KrbCryptoException {
byte[] temp = krb_safe.safeBody.asn1Encode();
if (!krb_safe.cksum.verifyKeyedChecksum(temp, key,
KeyUsage.KU_KRB_SAFE_CKSUM)) {
throw new KrbApErrException(
Krb5.KRB_AP_ERR_MODIFIED);
}
check(krb_safe.safeBody.timestamp,
krb_safe.safeBody.usec,
krb_safe.safeBody.seqNumber,
krb_safe.safeBody.sAddress,
krb_safe.safeBody.rAddress,
seqNumber,
sAddress,
rAddress,
timestampRequired,
seqNumberRequired,
cname
);
return krb_safe.safeBody.userData;
}
}

View file

@ -0,0 +1,338 @@
/*
* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.krb5;
import sun.security.krb5.internal.Krb5;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Random;
import java.util.StringTokenizer;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.NamingManager;
/**
* This class discovers the location of Kerberos services by querying DNS,
* as defined in RFC 4120.
*
* @author Seema Malkani
* @since 1.7
*/
class KrbServiceLocator {
private static final String SRV_RR = "SRV";
private static final String[] SRV_RR_ATTR = new String[] {SRV_RR};
private static final String SRV_TXT = "TXT";
private static final String[] SRV_TXT_ATTR = new String[] {SRV_TXT};
private static final Random random = new Random();
private static final boolean DEBUG = Krb5.DEBUG;
private KrbServiceLocator() {
}
/**
* Locates the KERBEROS service for a given domain.
* Queries DNS for a list of KERBEROS Service Text Records (TXT) for a
* given domain name.
* Information on the mapping of DNS hostnames and domain names
* to Kerberos realms is stored using DNS TXT records
*
* @param realmName A string realm name.
* @return An ordered list of hostports for the Kerberos service or null if
* the service has not been located.
*/
static String[] getKerberosService(String realmName) {
// search realm in SRV TXT records
String dnsUrl = "dns:///_kerberos." + realmName;
String[] records = null;
try {
// Create the DNS context using NamingManager rather than using
// the initial context constructor. This avoids having the initial
// context constructor call itself (when processing the URL
// argument in the getAttributes call).
Context ctx = NamingManager.getURLContext("dns", new Hashtable<>(0));
if (!(ctx instanceof DirContext)) {
return null; // cannot create a DNS context
}
Attributes attrs = null;
try {
// both connect and accept are needed since DNS is thru UDP
attrs = AccessController.doPrivileged(
(PrivilegedExceptionAction<Attributes>)
() -> ((DirContext)ctx).getAttributes(
dnsUrl, SRV_TXT_ATTR),
null,
new java.net.SocketPermission("*", "connect,accept"));
} catch (PrivilegedActionException e) {
throw (NamingException)e.getCause();
}
Attribute attr;
if (attrs != null && ((attr = attrs.get(SRV_TXT)) != null)) {
int numValues = attr.size();
int numRecords = 0;
String[] txtRecords = new String[numValues];
// gather the text records
int i = 0;
int j = 0;
while (i < numValues) {
try {
txtRecords[j] = (String)attr.get(i);
j++;
} catch (Exception e) {
// ignore bad value
}
i++;
}
numRecords = j;
// trim
if (numRecords < numValues) {
String[] trimmed = new String[numRecords];
System.arraycopy(txtRecords, 0, trimmed, 0, numRecords);
records = trimmed;
} else {
records = txtRecords;
}
}
} catch (NamingException e) {
// ignore
}
return records;
}
/**
* Locates the KERBEROS service for a given domain.
* Queries DNS for a list of KERBEROS Service Location Records (SRV) for a
* given domain name.
*
* @param realmName A string realm name.
* @param protocol the protocol string, can be "_udp" or "_tcp"
* @return An ordered list of hostports for the Kerberos service or null if
* the service has not been located.
*/
static String[] getKerberosService(String realmName, String protocol) {
String dnsUrl = "dns:///_kerberos." + protocol + "." + realmName;
String[] hostports = null;
try {
// Create the DNS context using NamingManager rather than using
// the initial context constructor. This avoids having the initial
// context constructor call itself (when processing the URL
// argument in the getAttributes call).
Context ctx = NamingManager.getURLContext("dns", new Hashtable<>(0));
if (!(ctx instanceof DirContext)) {
return null; // cannot create a DNS context
}
Attributes attrs = null;
try {
// both connect and accept are needed since DNS is thru UDP
attrs = AccessController.doPrivileged(
(PrivilegedExceptionAction<Attributes>)
() -> ((DirContext)ctx).getAttributes(
dnsUrl, SRV_RR_ATTR),
null,
new java.net.SocketPermission("*", "connect,accept"));
} catch (PrivilegedActionException e) {
throw (NamingException)e.getCause();
}
Attribute attr;
if (attrs != null && ((attr = attrs.get(SRV_RR)) != null)) {
int numValues = attr.size();
int numRecords = 0;
SrvRecord[] srvRecords = new SrvRecord[numValues];
// create the service records
int i = 0;
int j = 0;
while (i < numValues) {
try {
srvRecords[j] = new SrvRecord((String) attr.get(i));
j++;
} catch (Exception e) {
// ignore bad value
}
i++;
}
numRecords = j;
// trim
if (numRecords < numValues) {
SrvRecord[] trimmed = new SrvRecord[numRecords];
System.arraycopy(srvRecords, 0, trimmed, 0, numRecords);
srvRecords = trimmed;
}
// Sort the service records in ascending order of their
// priority value. For records with equal priority, move
// those with weight 0 to the top of the list.
if (numRecords > 1) {
Arrays.sort(srvRecords);
}
// extract the host and port number from each service record
hostports = extractHostports(srvRecords);
}
} catch (NamingException e) {
// e.printStackTrace();
// ignore
}
return hostports;
}
/**
* Extract hosts and port numbers from a list of SRV records.
* An array of hostports is returned or null if none were found.
*/
private static String[] extractHostports(SrvRecord[] srvRecords) {
String[] hostports = null;
int head = 0;
int tail = 0;
int sublistLength = 0;
int k = 0;
for (int i = 0; i < srvRecords.length; i++) {
if (hostports == null) {
hostports = new String[srvRecords.length];
}
// find the head and tail of the list of records having the same
// priority value.
head = i;
while (i < srvRecords.length - 1 &&
srvRecords[i].priority == srvRecords[i + 1].priority) {
i++;
}
tail = i;
// select hostports from the sublist
sublistLength = (tail - head) + 1;
for (int j = 0; j < sublistLength; j++) {
hostports[k++] = selectHostport(srvRecords, head, tail);
}
}
return hostports;
}
/*
* Randomly select a service record in the range [head, tail] and return
* its hostport value. Follows the algorithm in RFC 2782.
*/
private static String selectHostport(SrvRecord[] srvRecords, int head,
int tail) {
if (head == tail) {
return srvRecords[head].hostport;
}
// compute the running sum for records between head and tail
int sum = 0;
for (int i = head; i <= tail; i++) {
if (srvRecords[i] != null) {
sum += srvRecords[i].weight;
srvRecords[i].sum = sum;
}
}
String hostport = null;
// If all records have zero weight, select first available one;
// otherwise, randomly select a record according to its weight
int target = (sum == 0 ? 0 : random.nextInt(sum + 1));
for (int i = head; i <= tail; i++) {
if (srvRecords[i] != null && srvRecords[i].sum >= target) {
hostport = srvRecords[i].hostport;
srvRecords[i] = null; // make this record unavailable
break;
}
}
return hostport;
}
/**
* This class holds a DNS service (SRV) record.
* See http://www.ietf.org/rfc/rfc2782.txt
*/
static class SrvRecord implements Comparable<SrvRecord> {
int priority;
int weight;
int sum;
String hostport;
/**
* Creates a service record object from a string record.
* DNS supplies the string record in the following format:
* <pre>
* <Priority> " " <Weight> " " <Port> " " <Host>
* </pre>
*/
SrvRecord(String srvRecord) throws Exception {
StringTokenizer tokenizer = new StringTokenizer(srvRecord, " ");
String port;
if (tokenizer.countTokens() == 4) {
priority = Integer.parseInt(tokenizer.nextToken());
weight = Integer.parseInt(tokenizer.nextToken());
port = tokenizer.nextToken();
hostport = tokenizer.nextToken() + ":" + port;
} else {
throw new IllegalArgumentException();
}
}
/*
* Sort records in ascending order of priority value. For records with
* equal priority move those with weight 0 to the top of the list.
*/
public int compareTo(SrvRecord that) {
if (priority > that.priority) {
return 1; // this > that
} else if (priority < that.priority) {
return -1; // this < that
} else if (weight == 0 && that.weight != 0) {
return -1; // this < that
} else if (weight != 0 && that.weight == 0) {
return 1; // this > that
} else {
return 0; // this == that
}
}
}
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.util.*;
import java.io.IOException;
/**
* This class encapsulates a TGS-REP that is sent from the KDC to the
* Kerberos client.
*/
public class KrbTgsRep extends KrbKdcRep {
private TGSRep rep;
private Credentials creds;
private Ticket secondTicket;
private static final boolean DEBUG = Krb5.DEBUG;
KrbTgsRep(byte[] ibuf, KrbTgsReq tgsReq)
throws KrbException, IOException {
DerValue ref = new DerValue(ibuf);
TGSReq req = tgsReq.getMessage();
TGSRep rep = null;
try {
rep = new TGSRep(ref);
} catch (Asn1Exception e) {
rep = null;
KRBError err = new KRBError(ref);
String errStr = err.getErrorString();
String eText = null; // pick up text sent by the server (if any)
if (errStr != null && errStr.length() > 0) {
if (errStr.charAt(errStr.length() - 1) == 0)
eText = errStr.substring(0, errStr.length() - 1);
else
eText = errStr;
}
KrbException ke;
if (eText == null) {
// no text sent from server
ke = new KrbException(err.getErrorCode());
} else {
// override default text with server text
ke = new KrbException(err.getErrorCode(), eText);
}
ke.initCause(e);
throw ke;
}
byte[] enc_tgs_rep_bytes = rep.encPart.decrypt(tgsReq.tgsReqKey,
tgsReq.usedSubkey() ? KeyUsage.KU_ENC_TGS_REP_PART_SUBKEY :
KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
byte[] enc_tgs_rep_part = rep.encPart.reset(enc_tgs_rep_bytes);
ref = new DerValue(enc_tgs_rep_part);
EncTGSRepPart enc_part = new EncTGSRepPart(ref);
rep.encKDCRepPart = enc_part;
check(false, req, rep);
this.creds = new Credentials(rep.ticket,
rep.cname,
enc_part.sname,
enc_part.key,
enc_part.flags,
enc_part.authtime,
enc_part.starttime,
enc_part.endtime,
enc_part.renewTill,
enc_part.caddr
);
this.rep = rep;
this.secondTicket = tgsReq.getSecondTicket();
}
/**
* Return the credentials that were contained in this KRB-TGS-REP.
*/
public Credentials getCreds() {
return creds;
}
sun.security.krb5.internal.ccache.Credentials setCredentials() {
return new sun.security.krb5.internal.ccache.Credentials(rep, secondTicket);
}
}

View file

@ -0,0 +1,406 @@
/*
* Copyright (c) 2000, 2017, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.net.UnknownHostException;
import java.time.Instant;
/**
* This class encapsulates a Kerberos TGS-REQ that is sent from the
* client to the KDC.
*/
public class KrbTgsReq {
private PrincipalName princName;
private PrincipalName servName;
private TGSReq tgsReqMessg;
private KerberosTime ctime;
private Ticket secondTicket = null;
private boolean useSubkey = false;
EncryptionKey tgsReqKey;
private static final boolean DEBUG = Krb5.DEBUG;
private byte[] obuf;
private byte[] ibuf;
// Used in CredentialsUtil
public KrbTgsReq(Credentials asCreds,
PrincipalName sname)
throws KrbException, IOException {
this(new KDCOptions(),
asCreds,
sname,
null, // KerberosTime from
null, // KerberosTime till
null, // KerberosTime rtime
null, // eTypes, // null, // int[] eTypes
null, // HostAddresses addresses
null, // AuthorizationData authorizationData
null, // Ticket[] additionalTickets
null); // EncryptionKey subSessionKey
}
// S4U2proxy
public KrbTgsReq(Credentials asCreds,
Ticket second,
PrincipalName sname)
throws KrbException, IOException {
this(KDCOptions.with(KDCOptions.CNAME_IN_ADDL_TKT,
KDCOptions.FORWARDABLE),
asCreds,
sname,
null,
null,
null,
null,
null,
null,
new Ticket[] {second}, // the service ticket
null);
}
// S4U2user
public KrbTgsReq(Credentials asCreds,
PrincipalName sname,
PAData extraPA)
throws KrbException, IOException {
this(KDCOptions.with(KDCOptions.FORWARDABLE),
asCreds,
asCreds.getClient(),
sname,
null,
null,
null,
null,
null,
null,
null,
null,
extraPA); // the PA-FOR-USER
}
// Called by Credentials, KrbCred
KrbTgsReq(
KDCOptions options,
Credentials asCreds,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
AuthorizationData authorizationData,
Ticket[] additionalTickets,
EncryptionKey subKey) throws KrbException, IOException {
this(options, asCreds, asCreds.getClient(), sname,
from, till, rtime, eTypes, addresses,
authorizationData, additionalTickets, subKey, null);
}
private KrbTgsReq(
KDCOptions options,
Credentials asCreds,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
AuthorizationData authorizationData,
Ticket[] additionalTickets,
EncryptionKey subKey,
PAData extraPA) throws KrbException, IOException {
princName = cname;
servName = sname;
ctime = KerberosTime.now();
// check if they are valid arguments. The optional fields
// should be consistent with settings in KDCOptions.
if (options.get(KDCOptions.FORWARDABLE) &&
(!(asCreds.flags.get(Krb5.TKT_OPTS_FORWARDABLE)))) {
options.set(KDCOptions.FORWARDABLE, false);
}
if (options.get(KDCOptions.FORWARDED)) {
if (!(asCreds.flags.get(KDCOptions.FORWARDABLE)))
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
}
if (options.get(KDCOptions.PROXIABLE) &&
(!(asCreds.flags.get(Krb5.TKT_OPTS_PROXIABLE)))) {
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
}
if (options.get(KDCOptions.PROXY)) {
if (!(asCreds.flags.get(KDCOptions.PROXIABLE)))
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
}
if (options.get(KDCOptions.ALLOW_POSTDATE) &&
(!(asCreds.flags.get(Krb5.TKT_OPTS_MAY_POSTDATE)))) {
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
}
if (options.get(KDCOptions.RENEWABLE) &&
(!(asCreds.flags.get(Krb5.TKT_OPTS_RENEWABLE)))) {
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
}
if (options.get(KDCOptions.POSTDATED)) {
if (!(asCreds.flags.get(KDCOptions.POSTDATED)))
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
} else {
if (from != null) from = null;
}
if (options.get(KDCOptions.RENEWABLE)) {
if (!(asCreds.flags.get(KDCOptions.RENEWABLE)))
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
} else {
if (rtime != null) rtime = null;
}
if (options.get(KDCOptions.ENC_TKT_IN_SKEY) || options.get(KDCOptions.CNAME_IN_ADDL_TKT)) {
if (additionalTickets == null)
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
// in TGS_REQ there could be more than one additional
// tickets, but in file-based credential cache,
// there is only one additional ticket field.
secondTicket = additionalTickets[0];
} else {
if (additionalTickets != null)
additionalTickets = null;
}
tgsReqMessg = createRequest(
options,
asCreds.ticket,
asCreds.key,
ctime,
princName,
servName,
from,
till,
rtime,
eTypes,
addresses,
authorizationData,
additionalTickets,
subKey,
extraPA);
obuf = tgsReqMessg.asn1Encode();
// XXX We need to revisit this to see if can't move it
// up such that FORWARDED flag set in the options
// is included in the marshaled request.
/*
* If this is based on a forwarded ticket, record that in the
* options, because the returned TgsRep will contain the
* FORWARDED flag set.
*/
if (asCreds.flags.get(KDCOptions.FORWARDED))
options.set(KDCOptions.FORWARDED, true);
}
/**
* Sends a TGS request to the realm of the target.
* @throws KrbException
* @throws IOException
*/
public void send() throws IOException, KrbException {
String realmStr = null;
if (servName != null)
realmStr = servName.getRealmString();
KdcComm comm = new KdcComm(realmStr);
ibuf = comm.send(obuf);
}
public KrbTgsRep getReply()
throws KrbException, IOException {
return new KrbTgsRep(ibuf, this);
}
/**
* Sends the request, waits for a reply, and returns the Credentials.
* Used in Credentials, KrbCred, and internal/CredentialsUtil.
*/
public Credentials sendAndGetCreds() throws IOException, KrbException {
KrbTgsRep tgs_rep = null;
String kdc = null;
send();
tgs_rep = getReply();
return tgs_rep.getCreds();
}
KerberosTime getCtime() {
return ctime;
}
private TGSReq createRequest(
KDCOptions kdc_options,
Ticket ticket,
EncryptionKey key,
KerberosTime ctime,
PrincipalName cname,
PrincipalName sname,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
int[] eTypes,
HostAddresses addresses,
AuthorizationData authorizationData,
Ticket[] additionalTickets,
EncryptionKey subKey,
PAData extraPA)
throws IOException, KrbException, UnknownHostException {
KerberosTime req_till = null;
if (till == null) {
String d = Config.getInstance().get("libdefaults", "ticket_lifetime");
if (d != null) {
req_till = new KerberosTime(Instant.now().plusSeconds(Config.duration(d)));
} else {
req_till = new KerberosTime(0); // Choose KDC maximum allowed
}
} else {
req_till = till;
}
/*
* RFC 4120, Section 5.4.2.
* For KRB_TGS_REP, the ciphertext is encrypted in the
* sub-session key from the Authenticator, or if absent,
* the session key from the ticket-granting ticket used
* in the request.
*
* To support this, use tgsReqKey to remember which key to use.
*/
tgsReqKey = key;
int[] req_eTypes = null;
if (eTypes == null) {
req_eTypes = EType.getDefaults("default_tgs_enctypes");
} else {
req_eTypes = eTypes;
}
EncryptionKey reqKey = null;
EncryptedData encAuthorizationData = null;
if (authorizationData != null) {
byte[] ad = authorizationData.asn1Encode();
if (subKey != null) {
reqKey = subKey;
tgsReqKey = subKey; // Key to use to decrypt reply
useSubkey = true;
encAuthorizationData = new EncryptedData(reqKey, ad,
KeyUsage.KU_TGS_REQ_AUTH_DATA_SUBKEY);
} else
encAuthorizationData = new EncryptedData(key, ad,
KeyUsage.KU_TGS_REQ_AUTH_DATA_SESSKEY);
}
KDCReqBody reqBody = new KDCReqBody(
kdc_options,
cname,
sname,
from,
req_till,
rtime,
Nonce.value(),
req_eTypes,
addresses,
encAuthorizationData,
additionalTickets);
byte[] temp = reqBody.asn1Encode(Krb5.KRB_TGS_REQ);
// if the checksum type is one of the keyed checksum types,
// use session key.
Checksum cksum;
switch (Checksum.CKSUMTYPE_DEFAULT) {
case Checksum.CKSUMTYPE_RSA_MD4_DES:
case Checksum.CKSUMTYPE_DES_MAC:
case Checksum.CKSUMTYPE_DES_MAC_K:
case Checksum.CKSUMTYPE_RSA_MD4_DES_K:
case Checksum.CKSUMTYPE_RSA_MD5_DES:
case Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD:
case Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR:
case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128:
case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256:
cksum = new Checksum(Checksum.CKSUMTYPE_DEFAULT, temp, key,
KeyUsage.KU_PA_TGS_REQ_CKSUM);
break;
case Checksum.CKSUMTYPE_CRC32:
case Checksum.CKSUMTYPE_RSA_MD4:
case Checksum.CKSUMTYPE_RSA_MD5:
default:
cksum = new Checksum(Checksum.CKSUMTYPE_DEFAULT, temp);
}
// Usage will be KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR
byte[] tgs_ap_req = new KrbApReq(
new APOptions(),
ticket,
key,
cname,
cksum,
ctime,
reqKey,
null,
null).getMessage();
PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req);
return new TGSReq(
extraPA != null ?
new PAData[] {extraPA, tgsPAData } :
new PAData[] {tgsPAData},
reqBody);
}
TGSReq getMessage() {
return tgsReqMessg;
}
Ticket getSecondTicket() {
return secondTicket;
}
private static void debug(String message) {
// System.err.println(">>> KrbTgsReq: " + message);
}
boolean usedSubkey() {
return useSubkey;
}
}

View file

@ -0,0 +1,698 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.util.*;
import java.net.*;
import java.util.Vector;
import java.util.Locale;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import sun.security.krb5.internal.ccache.CCacheOutputStream;
import sun.security.krb5.internal.util.KerberosString;
/**
* Implements the ASN.1 PrincipalName type and its realm in a single class.
* <pre>{@code
* Realm ::= KerberosString
*
* PrincipalName ::= SEQUENCE {
* name-type [0] Int32,
* name-string [1] SEQUENCE OF KerberosString
* }
* }</pre>
* This class is immutable.
* @see Realm
*/
public class PrincipalName implements Cloneable {
//name types
/**
* Name type not known
*/
public static final int KRB_NT_UNKNOWN = 0;
/**
* Just the name of the principal as in DCE, or for users
*/
public static final int KRB_NT_PRINCIPAL = 1;
/**
* Service and other unique instance (krbtgt)
*/
public static final int KRB_NT_SRV_INST = 2;
/**
* Service with host name as instance (telnet, rcommands)
*/
public static final int KRB_NT_SRV_HST = 3;
/**
* Service with host as remaining components
*/
public static final int KRB_NT_SRV_XHST = 4;
/**
* Unique ID
*/
public static final int KRB_NT_UID = 5;
/**
* TGS Name
*/
public static final String TGS_DEFAULT_SRV_NAME = "krbtgt";
public static final int TGS_DEFAULT_NT = KRB_NT_SRV_INST;
public static final char NAME_COMPONENT_SEPARATOR = '/';
public static final char NAME_REALM_SEPARATOR = '@';
public static final char REALM_COMPONENT_SEPARATOR = '.';
public static final String NAME_COMPONENT_SEPARATOR_STR = "/";
public static final String NAME_REALM_SEPARATOR_STR = "@";
public static final String REALM_COMPONENT_SEPARATOR_STR = ".";
// Instance fields.
/**
* The name type, from PrincipalName's name-type field.
*/
private final int nameType;
/**
* The name strings, from PrincipalName's name-strings field. This field
* must be neither null nor empty. Each entry of it must also be neither
* null nor empty. Make sure to clone the field when it's passed in or out.
*/
private final String[] nameStrings;
/**
* The realm this principal belongs to.
*/
private final Realm nameRealm; // not null
/**
* When constructing a PrincipalName, whether the realm is included in
* the input, or deduced from default realm or domain-realm mapping.
*/
private final boolean realmDeduced;
// cached default salt, not used in clone
private transient String salt = null;
// There are 3 basic constructors. All other constructors must call them.
// All basic constructors must call validateNameStrings.
// 1. From name components
// 2. From name
// 3. From DER encoding
/**
* Creates a PrincipalName.
*/
public PrincipalName(int nameType, String[] nameStrings, Realm nameRealm) {
if (nameRealm == null) {
throw new IllegalArgumentException("Null realm not allowed");
}
validateNameStrings(nameStrings);
this.nameType = nameType;
this.nameStrings = nameStrings.clone();
this.nameRealm = nameRealm;
this.realmDeduced = false;
}
// This method is called by Windows NativeCred.c
public PrincipalName(String[] nameParts, String realm) throws RealmException {
this(KRB_NT_UNKNOWN, nameParts, new Realm(realm));
}
// Validate a nameStrings argument
private static void validateNameStrings(String[] ns) {
if (ns == null) {
throw new IllegalArgumentException("Null nameStrings not allowed");
}
if (ns.length == 0) {
throw new IllegalArgumentException("Empty nameStrings not allowed");
}
for (String s: ns) {
if (s == null) {
throw new IllegalArgumentException("Null nameString not allowed");
}
if (s.isEmpty()) {
throw new IllegalArgumentException("Empty nameString not allowed");
}
}
}
public Object clone() {
try {
PrincipalName pName = (PrincipalName) super.clone();
UNSAFE.putObject(this, NAME_STRINGS_OFFSET, nameStrings.clone());
return pName;
} catch (CloneNotSupportedException ex) {
throw new AssertionError("Should never happen");
}
}
private static final long NAME_STRINGS_OFFSET;
private static final jdk.internal.misc.Unsafe UNSAFE;
static {
try {
jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe();
NAME_STRINGS_OFFSET = unsafe.objectFieldOffset(
PrincipalName.class.getDeclaredField("nameStrings"));
UNSAFE = unsafe;
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof PrincipalName) {
PrincipalName other = (PrincipalName)o;
return nameRealm.equals(other.nameRealm) &&
Arrays.equals(nameStrings, other.nameStrings);
}
return false;
}
/**
* Returns the ASN.1 encoding of the
* <pre>{@code
* PrincipalName ::= SEQUENCE {
* name-type [0] Int32,
* name-string [1] SEQUENCE OF KerberosString
* }
*
* KerberosString ::= GeneralString (IA5String)
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*
* @param encoding DER-encoded PrincipalName (without Realm)
* @param realm the realm for this name
* @exception Asn1Exception if an error occurs while decoding
* an ASN1 encoded data.
* @exception Asn1Exception if there is an ASN1 encoding error
* @exception IOException if an I/O error occurs
* @exception IllegalArgumentException if encoding is null
* reading encoded data.
*/
public PrincipalName(DerValue encoding, Realm realm)
throws Asn1Exception, IOException {
if (realm == null) {
throw new IllegalArgumentException("Null realm not allowed");
}
realmDeduced = false;
nameRealm = realm;
DerValue der;
if (encoding == null) {
throw new IllegalArgumentException("Null encoding not allowed");
}
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x00) {
BigInteger bint = der.getData().getBigInteger();
nameType = bint.intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x01F) == 0x01) {
DerValue subDer = der.getData().getDerValue();
if (subDer.getTag() != DerValue.tag_SequenceOf) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
Vector<String> v = new Vector<>();
DerValue subSubDer;
while(subDer.getData().available() > 0) {
subSubDer = subDer.getData().getDerValue();
String namePart = new KerberosString(subSubDer).toString();
v.addElement(namePart);
}
nameStrings = new String[v.size()];
v.copyInto(nameStrings);
validateNameStrings(nameStrings);
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Parse (unmarshal) a <code>PrincipalName</code> from a DER
* input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @exception Asn1Exception on error.
* @param data the Der input stream value, which contains one or
* more marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @param realm the realm for the name
* @return an instance of <code>PrincipalName</code>, or null if the
* field is optional and missing.
*/
public static PrincipalName parse(DerInputStream data,
byte explicitTag, boolean
optional,
Realm realm)
throws Asn1Exception, IOException, RealmException {
if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=
explicitTag))
return null;
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
if (realm == null) {
realm = Realm.getDefault();
}
return new PrincipalName(subDer, realm);
}
}
// XXX Error checkin consistent with MIT krb5_parse_name
// Code repetition, realm parsed again by class Realm
private static String[] parseName(String name) {
Vector<String> tempStrings = new Vector<>();
String temp = name;
int i = 0;
int componentStart = 0;
String component;
while (i < temp.length()) {
if (temp.charAt(i) == NAME_COMPONENT_SEPARATOR) {
/*
* If this separator is escaped then don't treat it
* as a separator
*/
if (i > 0 && temp.charAt(i - 1) == '\\') {
temp = temp.substring(0, i - 1) +
temp.substring(i, temp.length());
continue;
}
else {
if (componentStart <= i) {
component = temp.substring(componentStart, i);
tempStrings.addElement(component);
}
componentStart = i + 1;
}
} else {
if (temp.charAt(i) == NAME_REALM_SEPARATOR) {
/*
* If this separator is escaped then don't treat it
* as a separator
*/
if (i > 0 && temp.charAt(i - 1) == '\\') {
temp = temp.substring(0, i - 1) +
temp.substring(i, temp.length());
continue;
} else {
if (componentStart < i) {
component = temp.substring(componentStart, i);
tempStrings.addElement(component);
}
componentStart = i + 1;
break;
}
}
}
i++;
}
if (i == temp.length()) {
component = temp.substring(componentStart, i);
tempStrings.addElement(component);
}
String[] result = new String[tempStrings.size()];
tempStrings.copyInto(result);
return result;
}
/**
* Constructs a PrincipalName from a string.
* @param name the name
* @param type the type
* @param realm the realm, null if not known. Note that when realm is not
* null, it will be always used even if there is a realm part in name. When
* realm is null, will read realm part from name, or try to map a realm
* (for KRB_NT_SRV_HST), or use the default realm, or fail
* @throws RealmException
*/
public PrincipalName(String name, int type, String realm)
throws RealmException {
if (name == null) {
throw new IllegalArgumentException("Null name not allowed");
}
String[] nameParts = parseName(name);
validateNameStrings(nameParts);
if (realm == null) {
realm = Realm.parseRealmAtSeparator(name);
}
// No realm info from parameter and string, must deduce later
realmDeduced = realm == null;
switch (type) {
case KRB_NT_SRV_HST:
if (nameParts.length >= 2) {
String hostName = nameParts[1];
try {
// RFC4120 does not recommend canonicalizing a hostname.
// However, for compatibility reason, we will try
// canonicalize it and see if the output looks better.
String canonicalized = (InetAddress.getByName(hostName)).
getCanonicalHostName();
// Looks if canonicalized is a longer format of hostName,
// we accept cases like
// bunny -> bunny.rabbit.hole
if (canonicalized.toLowerCase(Locale.ENGLISH).startsWith(
hostName.toLowerCase(Locale.ENGLISH)+".")) {
hostName = canonicalized;
}
} catch (UnknownHostException | SecurityException e) {
// not canonicalized or no permission to do so, use old
}
if (hostName.endsWith(".")) {
hostName = hostName.substring(0, hostName.length() - 1);
}
nameParts[1] = hostName.toLowerCase(Locale.ENGLISH);
}
nameStrings = nameParts;
nameType = type;
if (realm != null) {
nameRealm = new Realm(realm);
} else {
// We will try to get realm name from the mapping in
// the configuration. If it is not specified
// we will use the default realm. This nametype does
// not allow a realm to be specified. The name string must of
// the form service@host and this is internally changed into
// service/host by Kerberos
String mapRealm = mapHostToRealm(nameParts[1]);
if (mapRealm != null) {
nameRealm = new Realm(mapRealm);
} else {
nameRealm = Realm.getDefault();
}
}
break;
case KRB_NT_UNKNOWN:
case KRB_NT_PRINCIPAL:
case KRB_NT_SRV_INST:
case KRB_NT_SRV_XHST:
case KRB_NT_UID:
nameStrings = nameParts;
nameType = type;
if (realm != null) {
nameRealm = new Realm(realm);
} else {
nameRealm = Realm.getDefault();
}
break;
default:
throw new IllegalArgumentException("Illegal name type");
}
}
public PrincipalName(String name, int type) throws RealmException {
this(name, type, (String)null);
}
public PrincipalName(String name) throws RealmException {
this(name, KRB_NT_UNKNOWN);
}
public PrincipalName(String name, String realm) throws RealmException {
this(name, KRB_NT_UNKNOWN, realm);
}
public static PrincipalName tgsService(String r1, String r2)
throws KrbException {
return new PrincipalName(PrincipalName.KRB_NT_SRV_INST,
new String[] {PrincipalName.TGS_DEFAULT_SRV_NAME, r1},
new Realm(r2));
}
public String getRealmAsString() {
return getRealmString();
}
public String getPrincipalNameAsString() {
StringBuilder temp = new StringBuilder(nameStrings[0]);
for (int i = 1; i < nameStrings.length; i++)
temp.append(nameStrings[i]);
return temp.toString();
}
public int hashCode() {
return toString().hashCode();
}
public String getName() {
return toString();
}
public int getNameType() {
return nameType;
}
public String[] getNameStrings() {
return nameStrings.clone();
}
public byte[][] toByteArray() {
byte[][] result = new byte[nameStrings.length][];
for (int i = 0; i < nameStrings.length; i++) {
result[i] = new byte[nameStrings[i].length()];
result[i] = nameStrings[i].getBytes();
}
return result;
}
public String getRealmString() {
return nameRealm.toString();
}
public Realm getRealm() {
return nameRealm;
}
public String getSalt() {
if (salt == null) {
StringBuilder salt = new StringBuilder();
salt.append(nameRealm.toString());
for (int i = 0; i < nameStrings.length; i++) {
salt.append(nameStrings[i]);
}
return salt.toString();
}
return salt;
}
public String toString() {
StringBuilder str = new StringBuilder();
for (int i = 0; i < nameStrings.length; i++) {
if (i > 0)
str.append("/");
str.append(nameStrings[i]);
}
str.append("@");
str.append(nameRealm.toString());
return str.toString();
}
public String getNameString() {
StringBuilder str = new StringBuilder();
for (int i = 0; i < nameStrings.length; i++) {
if (i > 0)
str.append("/");
str.append(nameStrings[i]);
}
return str.toString();
}
/**
* Encodes a <code>PrincipalName</code> object. Note that only the type and
* names are encoded. To encode the realm, call getRealm().asn1Encode().
* @return the byte array of the encoded PrncipalName object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
BigInteger bint = BigInteger.valueOf(this.nameType);
temp.putInteger(bint);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp);
temp = new DerOutputStream();
DerValue[] der = new DerValue[nameStrings.length];
for (int i = 0; i < nameStrings.length; i++) {
der[i] = new KerberosString(nameStrings[i]).toDerValue();
}
temp.putSequence(der);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Checks if two <code>PrincipalName</code> objects have identical values in their corresponding data fields.
*
* @param pname the other <code>PrincipalName</code> object.
* @return true if two have identical values, otherwise, return false.
*/
// It is used in <code>sun.security.krb5.internal.ccache</code> package.
public boolean match(PrincipalName pname) {
boolean matched = true;
//name type is just a hint, no two names can be the same ignoring name type.
// if (this.nameType != pname.nameType) {
// matched = false;
// }
if ((this.nameRealm != null) && (pname.nameRealm != null)) {
if (!(this.nameRealm.toString().equalsIgnoreCase(pname.nameRealm.toString()))) {
matched = false;
}
}
if (this.nameStrings.length != pname.nameStrings.length) {
matched = false;
} else {
for (int i = 0; i < this.nameStrings.length; i++) {
if (!(this.nameStrings[i].equalsIgnoreCase(pname.nameStrings[i]))) {
matched = false;
}
}
}
return matched;
}
/**
* Writes data field values of <code>PrincipalName</code> in FCC format to an output stream.
*
* @param cos a <code>CCacheOutputStream</code> for writing data.
* @exception IOException if an I/O exception occurs.
* @see sun.security.krb5.internal.ccache.CCacheOutputStream
*/
public void writePrincipal(CCacheOutputStream cos) throws IOException {
cos.write32(nameType);
cos.write32(nameStrings.length);
byte[] realmBytes = null;
realmBytes = nameRealm.toString().getBytes();
cos.write32(realmBytes.length);
cos.write(realmBytes, 0, realmBytes.length);
byte[] bytes = null;
for (int i = 0; i < nameStrings.length; i++) {
bytes = nameStrings[i].getBytes();
cos.write32(bytes.length);
cos.write(bytes, 0, bytes.length);
}
}
/**
* Returns the instance component of a name.
* In a multi-component name such as a KRB_NT_SRV_INST
* name, the second component is returned.
* Null is returned if there are not two or more
* components in the name.
*
* @return instance component of a multi-component name.
*/
public String getInstanceComponent()
{
if (nameStrings != null && nameStrings.length >= 2)
{
return new String(nameStrings[1]);
}
return null;
}
static String mapHostToRealm(String name) {
String result = null;
try {
String subname = null;
Config c = Config.getInstance();
if ((result = c.get("domain_realm", name)) != null)
return result;
else {
for (int i = 1; i < name.length(); i++) {
if ((name.charAt(i) == '.') && (i != name.length() - 1)) { //mapping could be .ibm.com = AUSTIN.IBM.COM
subname = name.substring(i);
result = c.get("domain_realm", subname);
if (result != null) {
break;
}
else {
subname = name.substring(i + 1); //or mapping could be ibm.com = AUSTIN.IBM.COM
result = c.get("domain_realm", subname);
if (result != null) {
break;
}
}
}
}
}
} catch (KrbException e) {
}
return result;
}
public boolean isRealmDeduced() {
return realmDeduced;
}
}

View file

@ -0,0 +1,419 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.Krb5;
import sun.security.util.*;
import java.io.IOException;
import java.util.*;
import sun.security.krb5.internal.util.KerberosString;
/**
* Implements the ASN.1 Realm type.
*
* {@code Realm ::= GeneralString}
*
* This class is immutable.
*/
public class Realm implements Cloneable {
public static final boolean AUTODEDUCEREALM =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction(
"sun.security.krb5.autodeducerealm"));
private final String realm; // not null nor empty
public Realm(String name) throws RealmException {
realm = parseRealm(name);
}
public static Realm getDefault() throws RealmException {
try {
return new Realm(Config.getInstance().getDefaultRealm());
} catch (RealmException re) {
throw re;
} catch (KrbException ke) {
throw new RealmException(ke);
}
}
// Immutable class, no need to clone
public Object clone() {
return this;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Realm)) {
return false;
}
Realm that = (Realm)obj;
return this.realm.equals(that.realm);
}
public int hashCode() {
return realm.hashCode();
}
/**
* Constructs a Realm object.
* @param encoding a Der-encoded data.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception RealmException if an error occurs while parsing a Realm object.
*/
public Realm(DerValue encoding)
throws Asn1Exception, RealmException, IOException {
if (encoding == null) {
throw new IllegalArgumentException("encoding can not be null");
}
realm = new KerberosString(encoding).toString();
if (realm == null || realm.length() == 0)
throw new RealmException(Krb5.REALM_NULL);
if (!isValidRealmString(realm))
throw new RealmException(Krb5.REALM_ILLCHAR);
}
public String toString() {
return realm;
}
// Extract realm from a string like dummy@REALM
public static String parseRealmAtSeparator(String name)
throws RealmException {
if (name == null) {
throw new IllegalArgumentException
("null input name is not allowed");
}
String temp = new String(name);
String result = null;
int i = 0;
while (i < temp.length()) {
if (temp.charAt(i) == PrincipalName.NAME_REALM_SEPARATOR) {
if (i == 0 || temp.charAt(i - 1) != '\\') {
if (i + 1 < temp.length()) {
result = temp.substring(i + 1, temp.length());
} else {
throw new IllegalArgumentException
("empty realm part not allowed");
}
break;
}
}
i++;
}
if (result != null) {
if (result.length() == 0)
throw new RealmException(Krb5.REALM_NULL);
if (!isValidRealmString(result))
throw new RealmException(Krb5.REALM_ILLCHAR);
}
return result;
}
public static String parseRealmComponent(String name) {
if (name == null) {
throw new IllegalArgumentException
("null input name is not allowed");
}
String temp = new String(name);
String result = null;
int i = 0;
while (i < temp.length()) {
if (temp.charAt(i) == PrincipalName.REALM_COMPONENT_SEPARATOR) {
if (i == 0 || temp.charAt(i - 1) != '\\') {
if (i + 1 < temp.length())
result = temp.substring(i + 1, temp.length());
break;
}
}
i++;
}
return result;
}
protected static String parseRealm(String name) throws RealmException {
String result = parseRealmAtSeparator(name);
if (result == null)
result = name;
if (result == null || result.length() == 0)
throw new RealmException(Krb5.REALM_NULL);
if (!isValidRealmString(result))
throw new RealmException(Krb5.REALM_ILLCHAR);
return result;
}
// This is protected because the definition of a realm
// string is fixed
protected static boolean isValidRealmString(String name) {
if (name == null)
return false;
if (name.length() == 0)
return false;
for (int i = 0; i < name.length(); i++) {
if (name.charAt(i) == '/' ||
name.charAt(i) == ':' ||
name.charAt(i) == '\0') {
return false;
}
}
return true;
}
/**
* Encodes a Realm object.
* @return the byte array of encoded KrbCredInfo object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream out = new DerOutputStream();
out.putDerValue(new KerberosString(this.realm).toDerValue());
return out.toByteArray();
}
/**
* Parse (unmarshal) a realm from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @exception Asn1Exception on error.
* @param data the Der input stream value, which contains one or more marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @return an instance of Realm.
*
*/
public static Realm parse(DerInputStream data, byte explicitTag, boolean optional)
throws Asn1Exception, IOException, RealmException {
if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) {
return null;
}
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new Realm(subDer);
}
}
/**
* Returns an array of realms that may be traversed to obtain
* a TGT from the initiating realm cRealm to the target realm
* sRealm.
* <br>
* This method would read [capaths] to create a path, or generate a
* hierarchical path if [capaths] does not contain a sub-stanza for cRealm
* or the sub-stanza does not contain a tag for sRealm.
* <br>
* The returned list would never be null, and it always contains
* cRealm as the head entry. sRealm is not included as the tail.
*
* @param cRealm the initiating realm, not null
* @param sRealm the target realm, not null, not equals to cRealm
* @return array of realms including at least cRealm as the first
* element
*/
public static String[] getRealmsList(String cRealm, String sRealm) {
try {
// Try [capaths]
return parseCapaths(cRealm, sRealm);
} catch (KrbException ke) {
// Now assume the realms are organized hierarchically.
return parseHierarchy(cRealm, sRealm);
}
}
/**
* Parses the [capaths] stanza of the configuration file for a
* list of realms to traverse to obtain credentials from the
* initiating realm cRealm to the target realm sRealm.
*
* For a given client realm C there is a tag C in [capaths] whose
* subtag S has a value which is a (possibly partial) path from C
* to S. When the path is partial, it contains only the tail of the
* full path. Values of other subtags will be used to build the full
* path. The value "." means a direct path from C to S. If realm S
* does not appear as a subtag, there is no path defined here.
*
* The implementation ignores all values which equals to C or S, or
* a "." in multiple values, or any duplicated realm names.
*
* When a path value has more than two realms, they can be specified
* with multiple key-value pairs each having a single value, but the
* order must not change.
*
* For example:
*
* [capaths]
* TIVOLI.COM = {
* IBM.COM = IBM_LDAPCENTRAL.COM MOONLITE.ORG
* IBM_LDAPCENTRAL.COM = LDAPCENTRAL.NET
* LDAPCENTRAL.NET = .
* }
*
* TIVOLI.COM has a direct path to LDAPCENTRAL.NET, which has a direct
* path to IBM_LDAPCENTRAL.COM. It also has a partial path to IBM.COM
* being "IBM_LDAPCENTRAL.COM MOONLITE.ORG". Merging these info together,
* a full path from TIVOLI.COM to IBM.COM will be
*
* TIVOLI.COM -> LDAPCENTRAL.NET -> IBM_LDAPCENTRAL.COM
* -> IBM_LDAPCENTRAL.COM -> MOONLITE.ORG
*
* Please note the sRealm IBM.COM does not appear in the path.
*
* @param cRealm the initiating realm
* @param sRealm the target realm, not the same as cRealm
* @return array of realms including at least cRealm as the first
* element
* @throws KrbException if the config does not contain a sub-stanza
* for cRealm in [capaths] or the sub-stanza does not contain
* sRealm as a tag
*/
private static String[] parseCapaths(String cRealm, String sRealm)
throws KrbException {
// This line could throw a KrbException
Config cfg = Config.getInstance();
if (!cfg.exists("capaths", cRealm, sRealm)) {
throw new KrbException("No conf");
}
LinkedList<String> path = new LinkedList<>();
String head = sRealm;
while (true) {
String value = cfg.getAll("capaths", cRealm, head);
if (value == null) {
break;
}
String[] more = value.split("\\s+");
boolean changed = false;
for (int i=more.length-1; i>=0; i--) {
if (path.contains(more[i])
|| more[i].equals(".")
|| more[i].equals(cRealm)
|| more[i].equals(sRealm)
|| more[i].equals(head)) {
// Ignore invalid values
continue;
}
changed = true;
path.addFirst(more[i]);
}
if (!changed) break;
head = path.getFirst();
}
path.addFirst(cRealm);
return path.toArray(new String[path.size()]);
}
/**
* Build a list of realm that can be traversed
* to obtain credentials from the initiating realm cRealm
* for a service in the target realm sRealm.
* @param cRealm the initiating realm
* @param sRealm the target realm, not the same as cRealm
* @return array of realms including cRealm as the first element
*/
private static String[] parseHierarchy(String cRealm, String sRealm) {
String[] cComponents = cRealm.split("\\.");
String[] sComponents = sRealm.split("\\.");
int cPos = cComponents.length;
int sPos = sComponents.length;
boolean hasCommon = false;
for (sPos--, cPos--; sPos >=0 && cPos >= 0 &&
sComponents[sPos].equals(cComponents[cPos]);
sPos--, cPos--) {
hasCommon = true;
}
// For those with common components:
// length pos
// SITES1.SALES.EXAMPLE.COM 4 1
// EVERYWHERE.EXAMPLE.COM 3 0
// For those without common components:
// length pos
// DEVEL.EXAMPLE.COM 3 2
// PROD.EXAMPLE.ORG 3 2
LinkedList<String> path = new LinkedList<>();
// Un-common ones for client side
for (int i=0; i<=cPos; i++) {
path.addLast(subStringFrom(cComponents, i));
}
// Common one
if (hasCommon) {
path.addLast(subStringFrom(cComponents, cPos+1));
}
// Un-common ones for server side
for (int i=sPos; i>=0; i--) {
path.addLast(subStringFrom(sComponents, i));
}
// Remove sRealm from path. Note that it might be added at last loop
// or as a common component, if sRealm is a parent of cRealm
path.removeLast();
return path.toArray(new String[path.size()]);
}
/**
* Creates a realm name using components from the given position.
* For example, subStringFrom({"A", "B", "C"}, 1) is "B.C".
*/
private static String subStringFrom(String[] components, int from) {
StringBuilder sb = new StringBuilder();
for (int i=from; i<components.length; i++) {
if (sb.length() != 0) sb.append('.');
sb.append(components[i]);
}
return sb.toString();
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2000, 2012, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
public class RealmException extends KrbException {
private static final long serialVersionUID = -9100385213693792864L;
public RealmException(int i) {
super(i);
}
public RealmException(String s) {
super(s);
}
public RealmException(int i, String s) {
super(i,s);
}
public RealmException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2011, 2012, 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.krb5;
import java.io.IOException;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Vector;
public class SCDynamicStoreConfig {
private static native void installNotificationCallback();
private static native Hashtable<String, Object> getKerberosConfig();
private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
static {
boolean isMac = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Boolean>() {
public Boolean run() {
String osname = System.getProperty("os.name");
if (osname.contains("OS X")) {
System.loadLibrary("osxkrb5");
return true;
}
return false;
}
});
if (isMac) installNotificationCallback();
}
private static Vector<String> unwrapHost(
Collection<Hashtable<String, String>> c) {
Vector<String> vector = new Vector<String>();
for (Hashtable<String, String> m : c) {
vector.add(m.get("host"));
}
return vector;
}
/**
* convertRealmConfigs: Maps the Object graph that we get from JNI to the
* object graph that Config expects. Also the items inside the kdc array
* are wrapped inside Hashtables
*/
@SuppressWarnings("unchecked")
private static Hashtable<String, Object>
convertRealmConfigs(Hashtable<String, ?> configs) {
Hashtable<String, Object> realmsTable = new Hashtable<String, Object>();
for (String realm : configs.keySet()) {
// get the kdc
Hashtable<String, Collection<?>> map =
(Hashtable<String, Collection<?>>) configs.get(realm);
Hashtable<String, Vector<String>> realmMap =
new Hashtable<String, Vector<String>>();
// put the kdc into the realmMap
Collection<Hashtable<String, String>> kdc =
(Collection<Hashtable<String, String>>) map.get("kdc");
if (kdc != null) realmMap.put("kdc", unwrapHost(kdc));
// put the admin server into the realmMap
Collection<Hashtable<String, String>> kadmin =
(Collection<Hashtable<String, String>>) map.get("kadmin");
if (kadmin != null) realmMap.put("admin_server", unwrapHost(kadmin));
// add the full entry to the realmTable
realmsTable.put(realm, realmMap);
}
return realmsTable;
}
/**
* Calls down to JNI to get the raw Kerberos Config and maps the object
* graph to the one that Kerberos Config in Java expects
*
* @return
* @throws IOException
*/
public static Hashtable<String, Object> getConfig() throws IOException {
Hashtable<String, Object> stanzaTable = getKerberosConfig();
if (stanzaTable == null) {
throw new IOException(
"Could not load configuration from SCDynamicStore");
}
if (DEBUG) System.out.println("Raw map from JNI: " + stanzaTable);
return convertNativeConfig(stanzaTable);
}
@SuppressWarnings("unchecked")
private static Hashtable<String, Object> convertNativeConfig(
Hashtable<String, Object> stanzaTable) throws IOException {
// convert SCDynamicStore realm structure to Java realm structure
Hashtable<String, ?> realms =
(Hashtable<String, ?>) stanzaTable.get("realms");
if (realms == null || realms.isEmpty()) {
throw new IOException(
"SCDynamicStore contains an empty Kerberos setting");
}
stanzaTable.remove("realms");
Hashtable<String, Object> realmsTable = convertRealmConfigs(realms);
stanzaTable.put("realms", realmsTable);
WrapAllStringInVector(stanzaTable);
if (DEBUG) System.out.println("stanzaTable : " + stanzaTable);
return stanzaTable;
}
@SuppressWarnings("unchecked")
private static void WrapAllStringInVector(
Hashtable<String, Object> stanzaTable) {
for (String s: stanzaTable.keySet()) {
Object v = stanzaTable.get(s);
if (v instanceof Hashtable) {
WrapAllStringInVector((Hashtable<String,Object>)v);
} else if (v instanceof String) {
Vector<String> vec = new Vector<>();
vec.add((String)v);
stanzaTable.put(s, vec);
}
}
}
}

View file

@ -0,0 +1,110 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.Asn1Exception;
import sun.security.krb5.internal.util.KerberosFlags;
import sun.security.util.*;
import java.io.IOException;
/**
* Implements the ASN.1 APOptions type.
*
* <pre>{@code
* APOptions ::= KerberosFlags
* -- reserved(0),
* -- use-session-key(1),
* -- mutual-required(2)
*
* KerberosFlags ::= BIT STRING (SIZE (32..MAX))
* -- minimum number of bits shall be sent,
* -- but no fewer than 32
*
* }</pre>
* <p>
* This definition reflects the Network Working Group RFC4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
public class APOptions extends KerberosFlags {
public APOptions() {
super(Krb5.AP_OPTS_MAX + 1);
}
public APOptions(int oneBit) throws Asn1Exception {
super(Krb5.AP_OPTS_MAX + 1);
set(oneBit, true);
}
public APOptions(int size, byte[] data) throws Asn1Exception {
super(size, data);
if ((size > data.length * BITS_PER_UNIT) || (size > Krb5.AP_OPTS_MAX + 1)) {
throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH);
}
}
public APOptions(boolean[] data) throws Asn1Exception {
super(data);
if (data.length > Krb5.AP_OPTS_MAX + 1) {
throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH);
}
}
public APOptions(DerValue encoding) throws IOException, Asn1Exception {
this(encoding.getUnalignedBitString(true).toBooleanArray());
}
/**
* Parse (unmarshal) an APOptions from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @param data the Der input stream value, which contains one or more marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional.
* @return an instance of APOptions.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
public static APOptions parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException {
if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag))
return null;
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new APOptions(subDer);
}
}
}

View file

@ -0,0 +1,141 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.EncryptedData;
import sun.security.krb5.Asn1Exception;
import sun.security.util.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* Implements the ASN.1 AP-REP type.
*
* <pre>{@code
* AP-REP ::= [APPLICATION 15] SEQUENCE {
* pvno [0] INTEGER (5),
* msg-type [1] INTEGER (15),
* enc-part [2] EncryptedData -- EncAPRepPart
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
public class APRep {
public int pvno;
public int msgType;
public EncryptedData encPart;
public APRep(EncryptedData new_encPart) {
pvno = Krb5.PVNO;
msgType = Krb5.KRB_AP_REP;
encPart = new_encPart;
}
public APRep(byte[] data) throws Asn1Exception,
KrbApErrException, IOException {
init(new DerValue(data));
}
public APRep(DerValue encoding) throws Asn1Exception,
KrbApErrException, IOException {
init(encoding);
}
/**
* Initializes an APRep object.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception KrbApErrException if the value read from the DER-encoded data
* stream does not match the pre-defined value.
*/
private void init(DerValue encoding) throws Asn1Exception,
KrbApErrException, IOException {
if (((encoding.getTag() & (byte) (0x1F)) != Krb5.KRB_AP_REP)
|| (encoding.isApplication() != true)
|| (encoding.isConstructed() != true)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
DerValue der = encoding.getData().getDerValue();
if (der.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
DerValue subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x00) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
pvno = subDer.getData().getBigInteger().intValue();
if (pvno != Krb5.PVNO) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
}
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x01) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
msgType = subDer.getData().getBigInteger().intValue();
if (msgType != Krb5.KRB_AP_REP) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE);
}
encPart = EncryptedData.parse(der.getData(), (byte) 0x02, false);
if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes an APRep object.
* @return byte array of encoded APRep object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(pvno));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp);
temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(msgType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), temp);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x02), encPart.asn1Encode());
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
DerOutputStream aprep = new DerOutputStream();
aprep.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte) 0x0F), temp);
return aprep.toByteArray();
}
}

View file

@ -0,0 +1,151 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.util.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* Implements the ASN.1 AP-REQ type.
*
* <pre>{@code
* AP-REQ ::= [APPLICATION 14] SEQUENCE {
* pvno [0] INTEGER (5),
* msg-type [1] INTEGER (14),
* ap-options [2] APOptions,
* ticket [3] Ticket,
* authenticator [4] EncryptedData -- Authenticator
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
public class APReq {
public int pvno;
public int msgType;
public APOptions apOptions;
public Ticket ticket;
public EncryptedData authenticator;
public APReq(
APOptions new_apOptions,
Ticket new_ticket,
EncryptedData new_authenticator) {
pvno = Krb5.PVNO;
msgType = Krb5.KRB_AP_REQ;
apOptions = new_apOptions;
ticket = new_ticket;
authenticator = new_authenticator;
}
public APReq(byte[] data) throws Asn1Exception, IOException, KrbApErrException, RealmException {
init(new DerValue(data));
}
public APReq(DerValue encoding) throws Asn1Exception, IOException, KrbApErrException, RealmException {
init(encoding);
}
/**
* Initializes an APReq object.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception KrbApErrException if the value read from the DER-encoded data stream does not match the pre-defined value.
* @exception RealmException if an error occurs while parsing a Realm object.
*/
private void init(DerValue encoding) throws Asn1Exception,
IOException, KrbApErrException, RealmException {
DerValue der, subDer;
if (((encoding.getTag() & (byte) 0x1F) != Krb5.KRB_AP_REQ)
|| (encoding.isApplication() != true)
|| (encoding.isConstructed() != true)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if (der.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x00) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
pvno = subDer.getData().getBigInteger().intValue();
if (pvno != Krb5.PVNO) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
}
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x01) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
msgType = subDer.getData().getBigInteger().intValue();
if (msgType != Krb5.KRB_AP_REQ) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE);
}
apOptions = APOptions.parse(der.getData(), (byte) 0x02, false);
ticket = Ticket.parse(der.getData(), (byte) 0x03, false);
authenticator = EncryptedData.parse(der.getData(), (byte) 0x04, false);
if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes an APReq object.
* @return byte array of encoded APReq object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(pvno));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp);
temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(msgType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), temp);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x02), apOptions.asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x03), ticket.asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x04), authenticator.asn1Encode());
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
DerOutputStream apreq = new DerOutputStream();
apreq.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte) 0x0E), temp);
return apreq.toByteArray();
}
}

View file

@ -0,0 +1,66 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.EncryptedData;
import sun.security.krb5.Asn1Exception;
import sun.security.krb5.Realm;
import sun.security.krb5.RealmException;
import sun.security.util.*;
import java.io.IOException;
public class ASRep extends KDCRep {
public ASRep(
PAData[] new_pAData,
PrincipalName new_cname,
Ticket new_ticket,
EncryptedData new_encPart) throws IOException {
super(new_pAData, new_cname, new_ticket,
new_encPart, Krb5.KRB_AS_REP);
}
public ASRep(byte[] data) throws Asn1Exception,
RealmException, KrbApErrException, IOException {
init(new DerValue(data));
}
public ASRep(DerValue encoding) throws Asn1Exception,
RealmException, KrbApErrException, IOException {
init(encoding);
}
private void init(DerValue encoding) throws Asn1Exception,
RealmException, KrbApErrException, IOException {
init(encoding, Krb5.KRB_AS_REP);
}
}

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.util.*;
import java.io.IOException;
public class ASReq extends KDCReq {
public ASReq(PAData[] new_pAData, KDCReqBody new_reqBody) throws IOException {
super(new_pAData, new_reqBody, Krb5.KRB_AS_REQ);
}
public ASReq(byte[] data) throws Asn1Exception, KrbException, IOException {
init(new DerValue(data));
}
public ASReq(DerValue encoding) throws Asn1Exception, KrbException, IOException {
init(encoding);
}
private void init(DerValue encoding) throws Asn1Exception, IOException, KrbException {
super.init(encoding, Krb5.KRB_AS_REQ);
}
}

View file

@ -0,0 +1,52 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.EncryptionKey;
import java.util.BitSet;
public class AuthContext {
public HostAddress remoteAddress;
public int remotePort;
public HostAddress localAddress;
public int localPort;
public EncryptionKey keyBlock;
public EncryptionKey localSubkey;
public EncryptionKey remoteSubkey;
public BitSet authContextFlags;
public int remoteSeqNumber;
public int localSeqNumber;
public Authenticator authenticator;
public int reqCksumType;
public int safeCksumType;
public byte[] initializationVector;
//public ReplayCache replayCache;
};

View file

@ -0,0 +1,221 @@
/*
* 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.
*/
/*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.util.*;
import java.util.Vector;
import java.io.IOException;
import java.math.BigInteger;
/**
* Implements the ASN.1 Authenticator type.
*
* <pre>{@code
* Authenticator ::= [APPLICATION 2] SEQUENCE {
* authenticator-vno [0] INTEGER (5),
* crealm [1] Realm,
* cname [2] PrincipalName,
* cksum [3] Checksum OPTIONAL,
* cusec [4] Microseconds,
* ctime [5] KerberosTime,
* subkey [6] EncryptionKey OPTIONAL,
* seq-number [7] UInt32 OPTIONAL,
* authorization-data [8] AuthorizationData OPTIONAL
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
public class Authenticator {
public int authenticator_vno;
public PrincipalName cname;
Checksum cksum; //optional
public int cusec;
public KerberosTime ctime;
EncryptionKey subKey; //optional
Integer seqNumber; //optional
public AuthorizationData authorizationData; //optional
public Authenticator(
PrincipalName new_cname,
Checksum new_cksum,
int new_cusec,
KerberosTime new_ctime,
EncryptionKey new_subKey,
Integer new_seqNumber,
AuthorizationData new_authorizationData) {
authenticator_vno = Krb5.AUTHNETICATOR_VNO;
cname = new_cname;
cksum = new_cksum;
cusec = new_cusec;
ctime = new_ctime;
subKey = new_subKey;
seqNumber = new_seqNumber;
authorizationData = new_authorizationData;
}
public Authenticator(byte[] data)
throws Asn1Exception, IOException, KrbApErrException, RealmException {
init(new DerValue(data));
}
public Authenticator(DerValue encoding)
throws Asn1Exception, IOException, KrbApErrException, RealmException {
init(encoding);
}
/**
* Initializes an Authenticator object.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception KrbApErrException if the value read from the DER-encoded data
* stream does not match the pre-defined value.
* @exception RealmException if an error occurs while parsing a Realm object.
*/
private void init(DerValue encoding)
throws Asn1Exception, IOException, KrbApErrException, RealmException {
DerValue der, subDer;
//may not be the correct error code for a tag
//mismatch on an encrypted structure
if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x02)
|| (encoding.isApplication() != true)
|| (encoding.isConstructed() != true)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if (der.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x00) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
authenticator_vno = subDer.getData().getBigInteger().intValue();
if (authenticator_vno != 5) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
}
Realm crealm = Realm.parse(der.getData(), (byte) 0x01, false);
cname = PrincipalName.parse(der.getData(), (byte) 0x02, false, crealm);
cksum = Checksum.parse(der.getData(), (byte) 0x03, true);
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) == 0x04) {
cusec = subDer.getData().getBigInteger().intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
ctime = KerberosTime.parse(der.getData(), (byte) 0x05, false);
if (der.getData().available() > 0) {
subKey = EncryptionKey.parse(der.getData(), (byte) 0x06, true);
} else {
subKey = null;
seqNumber = null;
authorizationData = null;
}
if (der.getData().available() > 0) {
if ((der.getData().peekByte() & 0x1F) == 0x07) {
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x07) {
seqNumber = subDer.getData().getBigInteger().intValue();
}
}
} else {
seqNumber = null;
authorizationData = null;
}
if (der.getData().available() > 0) {
authorizationData = AuthorizationData.parse(der.getData(), (byte) 0x08, true);
} else {
authorizationData = null;
}
if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes an Authenticator object.
* @return byte array of encoded Authenticator object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
Vector<DerValue> v = new Vector<>();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(authenticator_vno));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp.toByteArray()));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), cname.getRealm().asn1Encode()));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x02), cname.asn1Encode()));
if (cksum != null) {
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x03), cksum.asn1Encode()));
}
temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(cusec));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x04), temp.toByteArray()));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x05), ctime.asn1Encode()));
if (subKey != null) {
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x06), subKey.asn1Encode()));
}
if (seqNumber != null) {
temp = new DerOutputStream();
// encode as an unsigned integer (UInt32)
temp.putInteger(BigInteger.valueOf(seqNumber.longValue()));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x07), temp.toByteArray()));
}
if (authorizationData != null) {
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x08), authorizationData.asn1Encode()));
}
DerValue[] der = new DerValue[v.size()];
v.copyInto(der);
temp = new DerOutputStream();
temp.putSequence(der);
DerOutputStream out = new DerOutputStream();
out.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte) 0x02), temp);
return out.toByteArray();
}
public final Checksum getChecksum() {
return cksum;
}
public final Integer getSeqNumber() {
return seqNumber;
}
public final EncryptionKey getSubKey() {
return subKey;
}
}

View file

@ -0,0 +1,184 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.util.*;
import sun.security.krb5.Asn1Exception;
import java.util.Vector;
import java.io.IOException;
import sun.security.krb5.internal.ccache.CCacheOutputStream;
/**
* In RFC4120, the ASN.1 AuthorizationData is defined as:
*
* AuthorizationData ::= SEQUENCE OF SEQUENCE {
* ad-type [0] Int32,
* ad-data [1] OCTET STRING
* }
*
* Here, two classes are used to implement it and they can be represented as follows:
*
* AuthorizationData ::= SEQUENCE OF AuthorizationDataEntry
* AuthorizationDataEntry ::= SEQUENCE {
* ad-type[0] Int32,
* ad-data[1] OCTET STRING
* }
*/
public class AuthorizationData implements Cloneable {
private AuthorizationDataEntry[] entry = null;
private AuthorizationData() {
}
public AuthorizationData(AuthorizationDataEntry[] new_entries)
throws IOException {
if (new_entries != null) {
entry = new AuthorizationDataEntry[new_entries.length];
for (int i = 0; i < new_entries.length; i++) {
if (new_entries[i] == null) {
throw new IOException("Cannot create an AuthorizationData");
} else {
entry[i] = (AuthorizationDataEntry) new_entries[i].clone();
}
}
}
}
public AuthorizationData(AuthorizationDataEntry new_entry) {
entry = new AuthorizationDataEntry[1];
entry[0] = new_entry;
}
public Object clone() {
AuthorizationData new_authorizationData =
new AuthorizationData();
if (entry != null) {
new_authorizationData.entry =
new AuthorizationDataEntry[entry.length];
for (int i = 0; i < entry.length; i++) {
new_authorizationData.entry[i] =
(AuthorizationDataEntry) entry[i].clone();
}
}
return new_authorizationData;
}
/**
* Constructs a new <code>AuthorizationData,</code> instance.
* @param der a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public AuthorizationData(DerValue der) throws Asn1Exception, IOException {
Vector<AuthorizationDataEntry> v = new Vector<>();
if (der.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
while (der.getData().available() > 0) {
v.addElement(new AuthorizationDataEntry(der.getData().getDerValue()));
}
if (v.size() > 0) {
entry = new AuthorizationDataEntry[v.size()];
v.copyInto(entry);
}
}
/**
* Encodes an <code>AuthorizationData</code> object.
* @return byte array of encoded <code>AuthorizationData</code> object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerValue[] der = new DerValue[entry.length];
for (int i = 0; i < entry.length; i++) {
der[i] = new DerValue(entry[i].asn1Encode());
}
bytes.putSequence(der);
return bytes.toByteArray();
}
/**
* Parse (unmarshal) an <code>AuthorizationData</code> object from a DER input stream.
* This form of parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @param data the Der input stream value, which contains one or more marshaled value.
* @param explicitTag tag number.
* @param optional indicates if this data field is optional
* @return an instance of AuthorizationData.
*
*/
public static AuthorizationData parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException {
if ((optional) && (((byte) data.peekByte() & (byte) 0x1F) != explicitTag)) {
return null;
}
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte) 0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new AuthorizationData(subDer);
}
}
/**
* Writes <code>AuthorizationData</code> data fields to a output stream.
*
* @param cos a <code>CCacheOutputStream</code> to be written to.
* @exception IOException if an I/O exception occurs.
*/
public void writeAuth(CCacheOutputStream cos) throws IOException {
for (int i = 0; i < entry.length; i++) {
entry[i].writeEntry(cos);
}
}
public String toString() {
String retVal = "AuthorizationData:\n";
for (int i = 0; i < entry.length; i++) {
retVal += entry[i].toString();
}
return retVal;
}
public int count() {
return entry.length;
}
public AuthorizationDataEntry item(int i) {
return (AuthorizationDataEntry)entry[i].clone();
}
}

View file

@ -0,0 +1,124 @@
/*
* 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.
*/
/*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.util.*;
import java.io.IOException;
import sun.security.krb5.Asn1Exception;
import sun.security.krb5.internal.ccache.CCacheOutputStream;
public class AuthorizationDataEntry implements Cloneable {
public int adType;
public byte[] adData;
private AuthorizationDataEntry() {
}
public AuthorizationDataEntry(
int new_adType,
byte[] new_adData) {
adType = new_adType;
adData = new_adData;
}
public Object clone() {
AuthorizationDataEntry new_authorizationDataEntry =
new AuthorizationDataEntry();
new_authorizationDataEntry.adType = adType;
if (adData != null) {
new_authorizationDataEntry.adData = new byte[adData.length];
System.arraycopy(adData, 0,
new_authorizationDataEntry.adData, 0, adData.length);
}
return new_authorizationDataEntry;
}
/**
* Constructs an instance of AuthorizationDataEntry.
* @param encoding a single DER-encoded value.
*/
public AuthorizationDataEntry(DerValue encoding) throws Asn1Exception, IOException {
DerValue der;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte) 0x1F) == (byte) 0x00) {
adType = der.getData().getBigInteger().intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte) 0x1F) == (byte) 0x01) {
adData = der.getData().getOctetString();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if (encoding.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes an AuthorizationDataEntry object.
* @return byte array of encoded AuthorizationDataEntry object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(adType);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp);
temp = new DerOutputStream();
temp.putOctetString(adData);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Writes the entry's data fields in FCC format to an output stream.
*
* @param cos a <code>CCacheOutputStream</code>.
* @exception IOException if an I/O exception occurs.
*/
public void writeEntry(CCacheOutputStream cos) throws IOException {
cos.write16(adType);
cos.write32(adData.length);
cos.write(adData, 0, adData.length);
}
public String toString() {
return ("adType=" + adType + " adData.length=" + adData.length);
}
}

View file

@ -0,0 +1,310 @@
/*
* Copyright (c) 2001, 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.*;
import java.io.IOException;
/**
* This class is a utility that contains much of the TGS-Exchange
* protocol. It is used by ../Credentials.java for service ticket
* acquisition in both the normal and the x-realm case.
*/
public class CredentialsUtil {
private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
/**
* Used by a middle server to acquire credentials on behalf of a
* client to itself using the S4U2self extension.
* @param client the client to impersonate
* @param ccreds the TGT of the middle service
* @return the new creds (cname=client, sname=middle)
*/
public static Credentials acquireS4U2selfCreds(PrincipalName client,
Credentials ccreds) throws KrbException, IOException {
String uRealm = client.getRealmString();
String localRealm = ccreds.getClient().getRealmString();
if (!uRealm.equals(localRealm)) {
// TODO: we do not support kerberos referral now
throw new KrbException("Cross realm impersonation not supported");
}
if (!ccreds.isForwardable()) {
throw new KrbException("S4U2self needs a FORWARDABLE ticket");
}
KrbTgsReq req = new KrbTgsReq(
ccreds,
ccreds.getClient(),
new PAData(Krb5.PA_FOR_USER,
new PAForUserEnc(client,
ccreds.getSessionKey()).asn1Encode()));
Credentials creds = req.sendAndGetCreds();
if (!creds.getClient().equals(client)) {
throw new KrbException("S4U2self request not honored by KDC");
}
if (!creds.isForwardable()) {
throw new KrbException("S4U2self ticket must be FORWARDABLE");
}
return creds;
}
/**
* Used by a middle server to acquire a service ticket to a backend
* server using the S4U2proxy extension.
* @param backend the name of the backend service
* @param second the client's service ticket to the middle server
* @param ccreds the TGT of the middle server
* @return the creds (cname=client, sname=backend)
*/
public static Credentials acquireS4U2proxyCreds(
String backend, Ticket second,
PrincipalName client, Credentials ccreds)
throws KrbException, IOException {
KrbTgsReq req = new KrbTgsReq(
ccreds,
second,
new PrincipalName(backend));
Credentials creds = req.sendAndGetCreds();
if (!creds.getClient().equals(client)) {
throw new KrbException("S4U2proxy request not honored by KDC");
}
return creds;
}
/**
* Acquires credentials for a specified service using initial
* credential. When the service has a different realm from the initial
* credential, we do cross-realm authentication - first, we use the
* current credential to get a cross-realm credential from the local KDC,
* then use that cross-realm credential to request service credential
* from the foreign KDC.
*
* @param service the name of service principal
* @param ccreds client's initial credential
*/
public static Credentials acquireServiceCreds(
String service, Credentials ccreds)
throws KrbException, IOException {
PrincipalName sname = new PrincipalName(service);
String serviceRealm = sname.getRealmString();
String localRealm = ccreds.getClient().getRealmString();
if (localRealm.equals(serviceRealm)) {
if (DEBUG) {
System.out.println(
">>> Credentials acquireServiceCreds: same realm");
}
return serviceCreds(sname, ccreds);
}
Credentials theCreds = null;
boolean[] okAsDelegate = new boolean[1];
Credentials theTgt = getTGTforRealm(localRealm, serviceRealm,
ccreds, okAsDelegate);
if (theTgt != null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "got right tgt");
System.out.println(">>> Credentials acquireServiceCreds: "
+ "obtaining service creds for " + sname);
}
try {
theCreds = serviceCreds(sname, theTgt);
} catch (Exception exc) {
if (DEBUG) {
System.out.println(exc);
}
theCreds = null;
}
}
if (theCreds != null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "returning creds:");
Credentials.printDebug(theCreds);
}
if (!okAsDelegate[0]) {
theCreds.resetDelegate();
}
return theCreds;
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED,
"No service creds");
}
/**
* Gets a TGT to another realm
* @param localRealm this realm
* @param serviceRealm the other realm, cannot equals to localRealm
* @param ccreds TGT in this realm
* @param okAsDelegate an [out] argument to receive the okAsDelegate
* property. True only if all realms allow delegation.
* @return the TGT for the other realm, null if cannot find a path
* @throws KrbException if something goes wrong
*/
private static Credentials getTGTforRealm(String localRealm,
String serviceRealm, Credentials ccreds, boolean[] okAsDelegate)
throws KrbException {
// Get a list of realms to traverse
String[] realms = Realm.getRealmsList(localRealm, serviceRealm);
int i = 0, k = 0;
Credentials cTgt = null, newTgt = null, theTgt = null;
PrincipalName tempService = null;
String newTgtRealm = null;
okAsDelegate[0] = true;
for (cTgt = ccreds, i = 0; i < realms.length;) {
tempService = PrincipalName.tgsService(serviceRealm, realms[i]);
if (DEBUG) {
System.out.println(
">>> Credentials acquireServiceCreds: main loop: ["
+ i +"] tempService=" + tempService);
}
try {
newTgt = serviceCreds(tempService, cTgt);
} catch (Exception exc) {
newTgt = null;
}
if (newTgt == null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "no tgt; searching thru capath");
}
/*
* No tgt found. Let's go thru the realms list one by one.
*/
for (newTgt = null, k = i+1;
newTgt == null && k < realms.length; k++) {
tempService = PrincipalName.tgsService(realms[k], realms[i]);
if (DEBUG) {
System.out.println(
">>> Credentials acquireServiceCreds: "
+ "inner loop: [" + k
+ "] tempService=" + tempService);
}
try {
newTgt = serviceCreds(tempService, cTgt);
} catch (Exception exc) {
newTgt = null;
}
}
} // Ends 'if (newTgt == null)'
if (newTgt == null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "no tgt; cannot get creds");
}
break;
}
/*
* We have a tgt. It may or may not be for the target.
* If it's for the target realm, we're done looking for a tgt.
*/
newTgtRealm = newTgt.getServer().getInstanceComponent();
if (okAsDelegate[0] && !newTgt.checkDelegate()) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: " +
"global OK-AS-DELEGATE turned off at " +
newTgt.getServer());
}
okAsDelegate[0] = false;
}
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "got tgt");
}
if (newTgtRealm.equals(serviceRealm)) {
/* We got the right tgt */
theTgt = newTgt;
break;
}
/*
* The new tgt is not for the target realm.
* See if the realm of the new tgt is in the list of realms
* and continue looking from there.
*/
for (k = i+1; k < realms.length; k++) {
if (newTgtRealm.equals(realms[k])) {
break;
}
}
if (k < realms.length) {
/*
* (re)set the counter so we start looking
* from the realm we just obtained a tgt for.
*/
i = k;
cTgt = newTgt;
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "continuing with main loop counter reset to " + i);
}
continue;
}
else {
/*
* The new tgt's realm is not in the hierarchy of realms.
* It's probably not safe to get a tgt from
* a tgs that is outside the known list of realms.
* Give up now.
*/
break;
}
} // Ends outermost/main 'for' loop
return theTgt;
}
/*
* This method does the real job to request the service credential.
*/
private static Credentials serviceCreds(
PrincipalName service, Credentials ccreds)
throws KrbException, IOException {
return new KrbTgsReq(ccreds, service).sendAndGetCreds();
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2005, 2009, 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.krb5.internal;
import sun.security.util.*;
import sun.security.krb5.Asn1Exception;
import java.io.IOException;
import sun.security.krb5.internal.util.KerberosString;
/**
* Implements the ASN.1 ETYPE-INFO-ENTRY type.
*
* ETYPE-INFO-ENTRY ::= SEQUENCE {
* etype [0] Int32,
* salt [1] OCTET STRING OPTIONAL
* }
*
* @author Seema Malkani
*/
public class ETypeInfo {
private int etype;
private String salt = null;
private static final byte TAG_TYPE = 0;
private static final byte TAG_VALUE = 1;
private ETypeInfo() {
}
public ETypeInfo(int etype, String salt) {
this.etype = etype;
this.salt = salt;
}
public Object clone() {
return new ETypeInfo(etype, salt);
}
/**
* Constructs a ETypeInfo object.
* @param encoding a DER-encoded data.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public ETypeInfo(DerValue encoding) throws Asn1Exception, IOException {
DerValue der = null;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
// etype
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x00) {
this.etype = der.getData().getBigInteger().intValue();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
// salt
if (encoding.getData().available() > 0) {
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x01) {
byte[] saltBytes = der.getData().getOctetString();
// Although salt is defined as an OCTET STRING, it's the
// encoding from of a string. As RFC 4120 says:
//
// "The salt, ..., is also completely unspecified with respect
// to character set and is probably locale-specific".
//
// It's known that this field is using the same encoding as
// KerberosString in most implementations.
if (KerberosString.MSNAME) {
this.salt = new String(saltBytes, "UTF8");
} else {
this.salt = new String(saltBytes);
}
}
}
if (encoding.getData().available() > 0)
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
/**
* Encodes this object to an OutputStream.
*
* @return byte array of the encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception Asn1Exception on encoding errors.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(etype);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
TAG_TYPE), temp);
if (salt != null) {
temp = new DerOutputStream();
if (KerberosString.MSNAME) {
temp.putOctetString(salt.getBytes("UTF8"));
} else {
temp.putOctetString(salt.getBytes());
}
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
TAG_VALUE), temp);
}
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
// accessor methods
public int getEType() {
return etype;
}
public String getSalt() {
return salt;
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2005, 2009, 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.krb5.internal;
import sun.security.util.*;
import sun.security.krb5.Asn1Exception;
import java.io.IOException;
import sun.security.krb5.internal.util.KerberosString;
/**
* Implements the ASN.1 ETYPE-INFO-ENTRY type.
*
* ETYPE-INFO2-ENTRY ::= SEQUENCE {
* etype [0] Int32,
* salt [1] KerberosString OPTIONAL,
* s2kparams [2] OCTET STRING OPTIONAL
* }
*
* @author Seema Malkani
*/
public class ETypeInfo2 {
private int etype;
private String saltStr = null;
private byte[] s2kparams = null;
private static final byte TAG_TYPE = 0;
private static final byte TAG_VALUE1 = 1;
private static final byte TAG_VALUE2 = 2;
private ETypeInfo2() {
}
public ETypeInfo2(int etype, String salt, byte[] s2kparams) {
this.etype = etype;
this.saltStr = salt;
if (s2kparams != null) {
this.s2kparams = s2kparams.clone();
}
}
public Object clone() {
ETypeInfo2 etypeInfo2 = new ETypeInfo2();
etypeInfo2.etype = etype;
etypeInfo2.saltStr = saltStr;
if (s2kparams != null) {
etypeInfo2.s2kparams = new byte[s2kparams.length];
System.arraycopy(s2kparams, 0, etypeInfo2.s2kparams,
0, s2kparams.length);
}
return etypeInfo2;
}
/**
* Constructs a ETypeInfo2 object.
* @param encoding a DER-encoded data.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public ETypeInfo2(DerValue encoding) throws Asn1Exception, IOException {
DerValue der = null;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
// etype
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x00) {
this.etype = der.getData().getBigInteger().intValue();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
// salt
if (encoding.getData().available() > 0) {
if ((encoding.getData().peekByte() & 0x1F) == 0x01) {
der = encoding.getData().getDerValue();
this.saltStr = new KerberosString(
der.getData().getDerValue()).toString();
}
}
// s2kparams
if (encoding.getData().available() > 0) {
if ((encoding.getData().peekByte() & 0x1F) == 0x02) {
der = encoding.getData().getDerValue();
this.s2kparams = der.getData().getOctetString();
}
}
if (encoding.getData().available() > 0)
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
/**
* Encodes this object to an OutputStream.
*
* @return byte array of the encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception Asn1Exception on encoding errors.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(etype);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
TAG_TYPE), temp);
if (saltStr != null) {
temp = new DerOutputStream();
temp.putDerValue(new KerberosString(saltStr).toDerValue());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
TAG_VALUE1), temp);
}
if (s2kparams != null) {
temp = new DerOutputStream();
temp.putOctetString(s2kparams);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
TAG_VALUE2), temp);
}
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
// accessor methods
public int getEType() {
return etype;
}
public String getSalt() {
return saltStr;
}
public byte[] getParams() {
return ((s2kparams == null) ? null : s2kparams.clone());
}
}

View file

@ -0,0 +1,171 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.util.*;
import java.util.Vector;
import java.io.IOException;
import java.math.BigInteger;
/**
* Implements the ASN.1 EncAPRepPart type.
*
* <pre>{@code
* EncAPRepPart ::= [APPLICATION 27] SEQUENCE {
* ctime [0] KerberosTime,
* cusec [1] Microseconds,
* subkey [2] EncryptionKey OPTIONAL,
* seq-number [3] UInt32 OPTIONAL
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
public class EncAPRepPart {
public KerberosTime ctime;
public int cusec;
EncryptionKey subKey; //optional
Integer seqNumber; //optional
public EncAPRepPart(
KerberosTime new_ctime,
int new_cusec,
EncryptionKey new_subKey,
Integer new_seqNumber) {
ctime = new_ctime;
cusec = new_cusec;
subKey = new_subKey;
seqNumber = new_seqNumber;
}
public EncAPRepPart(byte[] data)
throws Asn1Exception, IOException {
init(new DerValue(data));
}
public EncAPRepPart(DerValue encoding)
throws Asn1Exception, IOException {
init(encoding);
}
/**
* Initializes an EncaPRepPart object.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
private void init(DerValue encoding) throws Asn1Exception, IOException {
DerValue der, subDer;
if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x1B)
|| (encoding.isApplication() != true)
|| (encoding.isConstructed() != true)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if (der.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
ctime = KerberosTime.parse(der.getData(), (byte) 0x00, true);
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x01) {
cusec = subDer.getData().getBigInteger().intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if (der.getData().available() > 0) {
subKey = EncryptionKey.parse(der.getData(), (byte) 0x02, true);
} else {
subKey = null;
seqNumber = null;
}
if (der.getData().available() > 0) {
subDer = der.getData().getDerValue();
if ((subDer.getTag() & 0x1F) != 0x03) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
seqNumber = subDer.getData().getBigInteger().intValue();
} else {
seqNumber = null;
}
if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes an EncAPRepPart object.
* @return byte array of encoded EncAPRepPart object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
Vector<DerValue> v = new Vector<>();
DerOutputStream temp = new DerOutputStream();
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), ctime.asn1Encode()));
temp.putInteger(BigInteger.valueOf(cusec));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), temp.toByteArray()));
if (subKey != null) {
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), subKey.asn1Encode()));
}
if (seqNumber != null) {
temp = new DerOutputStream();
// encode as an unsigned integer (UInt32)
temp.putInteger(BigInteger.valueOf(seqNumber.longValue()));
v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), temp.toByteArray()));
}
DerValue[] der = new DerValue[v.size()];
v.copyInto(der);
temp = new DerOutputStream();
temp.putSequence(der);
DerOutputStream out = new DerOutputStream();
out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
true, (byte) 0x1B), temp);
return out.toByteArray();
}
public final EncryptionKey getSubKey() {
return subKey;
}
public final Integer getSeqNumber() {
return seqNumber;
}
}

View file

@ -0,0 +1,88 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.util.*;
import java.io.IOException;
public class EncASRepPart extends EncKDCRepPart {
public EncASRepPart(
EncryptionKey new_key,
LastReq new_lastReq,
int new_nonce,
KerberosTime new_keyExpiration,
TicketFlags new_flags,
KerberosTime new_authtime,
KerberosTime new_starttime,
KerberosTime new_endtime,
KerberosTime new_renewTill,
PrincipalName new_sname,
HostAddresses new_caddr) {
super(
new_key,
new_lastReq,
new_nonce,
new_keyExpiration,
new_flags,
new_authtime,
new_starttime,
new_endtime,
new_renewTill,
new_sname,
new_caddr,
Krb5.KRB_ENC_AS_REP_PART
);
//may need to use Krb5.KRB_ENC_TGS_REP_PART to mimic
//behavior of other implementaions, instead of above
}
public EncASRepPart(byte[] data) throws Asn1Exception,
IOException, KrbException {
init(new DerValue(data));
}
public EncASRepPart(DerValue encoding) throws Asn1Exception,
IOException, KrbException {
init(encoding);
}
private void init(DerValue encoding) throws Asn1Exception,
IOException, KrbException {
init(encoding, Krb5.KRB_ENC_AS_REP_PART);
}
public byte[] asn1Encode() throws Asn1Exception,
IOException {
return asn1Encode(Krb5.KRB_ENC_AS_REP_PART);
}
}

View file

@ -0,0 +1,224 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.krb5.EncryptionKey;
import sun.security.util.*;
import java.util.Vector;
import java.io.IOException;
import java.math.BigInteger;
/**
* Implements the ASN.1 EncKDCRepPart type.
*
* <pre>{@code
* EncKDCRepPart ::= SEQUENCE {
* key [0] EncryptionKey,
* last-req [1] LastReq,
* nonce [2] UInt32,
* key-expiration [3] KerberosTime OPTIONAL,
* flags [4] TicketFlags,
* authtime [5] KerberosTime,
* starttime [6] KerberosTime OPTIONAL,
* endtime [7] KerberosTime,
* renew-till [8] KerberosTime OPTIONAL,
* srealm [9] Realm,
* sname [10] PrincipalName,
* caddr [11] HostAddresses OPTIONAL
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
public class EncKDCRepPart {
public EncryptionKey key;
public LastReq lastReq;
public int nonce;
public KerberosTime keyExpiration; //optional
public TicketFlags flags;
public KerberosTime authtime;
public KerberosTime starttime; //optional
public KerberosTime endtime;
public KerberosTime renewTill; //optional
public PrincipalName sname;
public HostAddresses caddr; //optional
public int msgType; //not included in sequence
public EncKDCRepPart(
EncryptionKey new_key,
LastReq new_lastReq,
int new_nonce,
KerberosTime new_keyExpiration,
TicketFlags new_flags,
KerberosTime new_authtime,
KerberosTime new_starttime,
KerberosTime new_endtime,
KerberosTime new_renewTill,
PrincipalName new_sname,
HostAddresses new_caddr,
int new_msgType) {
key = new_key;
lastReq = new_lastReq;
nonce = new_nonce;
keyExpiration = new_keyExpiration;
flags = new_flags;
authtime = new_authtime;
starttime = new_starttime;
endtime = new_endtime;
renewTill = new_renewTill;
sname = new_sname;
caddr = new_caddr;
msgType = new_msgType;
}
public EncKDCRepPart() {
}
public EncKDCRepPart(byte[] data, int rep_type)
throws Asn1Exception, IOException, RealmException {
init(new DerValue(data), rep_type);
}
public EncKDCRepPart(DerValue encoding, int rep_type)
throws Asn1Exception, IOException, RealmException {
init(encoding, rep_type);
}
/**
* Initializes an EncKDCRepPart object.
*
* @param encoding a single DER-encoded value.
* @param rep_type type of the encrypted reply message.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception RealmException if an error occurs while decoding an Realm object.
*/
protected void init(DerValue encoding, int rep_type)
throws Asn1Exception, IOException, RealmException {
DerValue der, subDer;
//implementations return the incorrect tag value, so
//we don't use the above line; instead we use the following
msgType = (encoding.getTag() & (byte) 0x1F);
if (msgType != Krb5.KRB_ENC_AS_REP_PART &&
msgType != Krb5.KRB_ENC_TGS_REP_PART) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if (der.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
key = EncryptionKey.parse(der.getData(), (byte) 0x00, false);
lastReq = LastReq.parse(der.getData(), (byte) 0x01, false);
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x02) {
nonce = subDer.getData().getBigInteger().intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
keyExpiration = KerberosTime.parse(der.getData(), (byte) 0x03, true);
flags = TicketFlags.parse(der.getData(), (byte) 0x04, false);
authtime = KerberosTime.parse(der.getData(), (byte) 0x05, false);
starttime = KerberosTime.parse(der.getData(), (byte) 0x06, true);
endtime = KerberosTime.parse(der.getData(), (byte) 0x07, false);
renewTill = KerberosTime.parse(der.getData(), (byte) 0x08, true);
Realm srealm = Realm.parse(der.getData(), (byte) 0x09, false);
sname = PrincipalName.parse(der.getData(), (byte) 0x0A, false, srealm);
if (der.getData().available() > 0) {
caddr = HostAddresses.parse(der.getData(), (byte) 0x0B, true);
}
// We observe extra data from MSAD
/*if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}*/
}
/**
* Encodes an EncKDCRepPart object.
* @param rep_type type of encrypted reply message.
* @return byte array of encoded EncKDCRepPart object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*/
public byte[] asn1Encode(int rep_type) throws Asn1Exception,
IOException {
DerOutputStream temp = new DerOutputStream();
DerOutputStream bytes = new DerOutputStream();
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), key.asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), lastReq.asn1Encode());
temp.putInteger(BigInteger.valueOf(nonce));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), temp);
if (keyExpiration != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), keyExpiration.asn1Encode());
}
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x04), flags.asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x05), authtime.asn1Encode());
if (starttime != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x06), starttime.asn1Encode());
}
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x07), endtime.asn1Encode());
if (renewTill != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x08), renewTill.asn1Encode());
}
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x09), sname.getRealm().asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x0A), sname.asn1Encode());
if (caddr != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x0B), caddr.asn1Encode());
}
//should use the rep_type to build the encoding
//but other implementations do not; it is ignored and
//the cached msgType is used instead
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
bytes = new DerOutputStream();
bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION,
true, (byte) msgType), temp);
return bytes.toByteArray();
}
}

View file

@ -0,0 +1,215 @@
/*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5.internal;
import sun.security.util.*;
import sun.security.krb5.Asn1Exception;
import sun.security.krb5.RealmException;
import java.util.Vector;
import java.io.IOException;
import java.math.BigInteger;
/**
* Implements the ASN.1 EncKrbCredPart type.
*
* <pre>{@code
* EncKrbCredPart ::= [APPLICATION 29] SEQUENCE {
* ticket-info [0] SEQUENCE OF KrbCredInfo,
* nonce [1] UInt32 OPTIONAL,
* timestamp [2] KerberosTime OPTIONAL,
* usec [3] Microseconds OPTIONAL,
* s-address [4] HostAddress OPTIONAL,
* r-address [5] HostAddress OPTIONAL
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
public class EncKrbCredPart {
public KrbCredInfo[] ticketInfo = null;
public KerberosTime timeStamp; //optional
private Integer nonce; //optional
private Integer usec; //optional
private HostAddress sAddress; //optional
private HostAddresses rAddress; //optional
public EncKrbCredPart(
KrbCredInfo[] new_ticketInfo,
KerberosTime new_timeStamp,
Integer new_usec,
Integer new_nonce,
HostAddress new_sAddress,
HostAddresses new_rAddress) throws IOException {
if (new_ticketInfo != null) {
ticketInfo = new KrbCredInfo[new_ticketInfo.length];
for (int i = 0; i < new_ticketInfo.length; i++) {
if (new_ticketInfo[i] == null) {
throw new IOException("Cannot create a EncKrbCredPart");
} else {
ticketInfo[i] = (KrbCredInfo) new_ticketInfo[i].clone();
}
}
}
timeStamp = new_timeStamp;
usec = new_usec;
nonce = new_nonce;
sAddress = new_sAddress;
rAddress = new_rAddress;
}
public EncKrbCredPart(byte[] data) throws Asn1Exception,
IOException, RealmException {
init(new DerValue(data));
}
public EncKrbCredPart(DerValue encoding) throws Asn1Exception,
IOException, RealmException {
init(encoding);
}
/**
* Initializes an EncKrbCredPart object.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
* @exception RealmException if an error occurs while parsing a Realm object.
*/
private void init(DerValue encoding) throws Asn1Exception,
IOException, RealmException {
DerValue der, subDer;
//may not be the correct error code for a tag
//mismatch on an encrypted structure
nonce = null;
timeStamp = null;
usec = null;
sAddress = null;
rAddress = null;
if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x1D)
|| (encoding.isApplication() != true)
|| (encoding.isConstructed() != true)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if (der.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
subDer = der.getData().getDerValue();
if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x00) {
DerValue[] derValues = subDer.getData().getSequence(1);
ticketInfo = new KrbCredInfo[derValues.length];
for (int i = 0; i < derValues.length; i++) {
ticketInfo[i] = new KrbCredInfo(derValues[i]);
}
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if (der.getData().available() > 0) {
if (((byte) (der.getData().peekByte()) & (byte) 0x1F) == (byte) 0x01) {
subDer = der.getData().getDerValue();
nonce = subDer.getData().getBigInteger().intValue();
}
}
if (der.getData().available() > 0) {
timeStamp = KerberosTime.parse(der.getData(), (byte) 0x02, true);
}
if (der.getData().available() > 0) {
if (((byte) (der.getData().peekByte()) & (byte) 0x1F) == (byte) 0x03) {
subDer = der.getData().getDerValue();
usec = subDer.getData().getBigInteger().intValue();
}
}
if (der.getData().available() > 0) {
sAddress = HostAddress.parse(der.getData(), (byte) 0x04, true);
}
if (der.getData().available() > 0) {
rAddress = HostAddresses.parse(der.getData(), (byte) 0x05, true);
}
if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes an EncKrbCredPart object.
* @return byte array of encoded EncKrbCredPart object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
DerValue[] tickets = new DerValue[ticketInfo.length];
for (int i = 0; i < ticketInfo.length; i++) {
tickets[i] = new DerValue(ticketInfo[i].asn1Encode());
}
temp.putSequence(tickets);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), temp);
if (nonce != null) {
temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(nonce.intValue()));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), temp);
}
if (timeStamp != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), timeStamp.asn1Encode());
}
if (usec != null) {
temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(usec.intValue()));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), temp);
}
if (sAddress != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x04), sAddress.asn1Encode());
}
if (rAddress != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x05), rAddress.asn1Encode());
}
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
bytes = new DerOutputStream();
bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION,
true, (byte) 0x1D), temp);
return bytes.toByteArray();
}
}

Some files were not shown because too many files have changed in this diff Show more