blob: 2eec4b41057e9fc3c42f7e2fd1d1b0577c7010ce [file] [log] [blame]
/*
* 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();
}
}
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();
}
}