|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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 com.sun.security.jgss.InquireType; |
|
import java.io.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
private GSSNameElement srcName; |
|
private GSSNameElement targetName; |
|
private GSSCredElement cred; |
|
private boolean isInitiator; |
|
private boolean isEstablished; |
|
private Oid actualMech; |
|
|
|
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. |
|
|
|
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; |
|
} |
|
|
|
|
|
private void doServicePermCheck() throws GSSException { |
|
if (System.getSecurityManager() != null) { |
|
String action = (isInitiator? "initiate" : "accept"); |
|
// Need to check Service permission for accessing |
|
|
|
if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator |
|
&& !isEstablished) { |
|
if (srcName == null) { |
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
private void doDelegPermCheck() throws GSSException { |
|
SecurityManager sm = System.getSecurityManager(); |
|
if (sm != null) { |
|
String targetStr = targetName.getKrbName(); |
|
String tgsStr = Krb5Util.getTGSName(targetName); |
|
StringBuffer buf = new StringBuffer("\""); |
|
buf.append(targetStr).append("\" \""); |
|
buf.append(tgsStr).append('\"'); |
|
String krbPrincPair = buf.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) { |
|
|
|
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 { |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
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(); |
|
} |
|
} |
|
|
|
|
|
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 |
|
|
|
if (GSSUtil.isKerberosMech(cStub.getMech()) && targetName != null) { |
|
doServicePermCheck(); |
|
} |
|
|
|
// srcName and potentially targetName (when myCred is null) |
|
// will be set in GSSLibStub.acceptContext(...) |
|
} |
|
|
|
|
|
NativeGSSContext(long pCtxt, GSSLibStub stub) throws GSSException { |
|
assert(pContext != 0); |
|
pContext = pCtxt; |
|
cStub = stub; |
|
|
|
|
|
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 |
|
|
|
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; |
|
|
|
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 |
|
|
|
if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) { |
|
|
|
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 |
|
|
|
if (cred != null) cred.dispose(); |
|
cred = new GSSCredElement(targetName, lifetime, |
|
GSSCredential.ACCEPT_ONLY, cStub); |
|
} |
|
|
|
// Only inspect token when the permission check has not |
|
|
|
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; |
|
} |
|
|
|
protected void finalize() throws Throwable { |
|
dispose(); |
|
} |
|
|
|
public Object inquireSecContext(InquireType type) |
|
throws GSSException { |
|
throw new GSSException(GSSException.UNAVAILABLE, -1, |
|
"Inquire type not supported."); |
|
} |
|
} |