346 lines
11 KiB
Java
346 lines
11 KiB
Java
/*
|
|
* 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 {
|
|
|
|
StringBuffer sb = new StringBuffer();
|
|
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();
|
|
}
|
|
}
|