--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java Tue Sep 12 19:03:39 2017 +0200
@@ -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.");
+ }
+}