0N/A/*
2362N/A * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.jgss;
0N/A
0N/Aimport org.ietf.jgss.GSSException;
0N/Aimport java.io.InputStream;
0N/Aimport java.io.OutputStream;
0N/Aimport java.io.IOException;
0N/Aimport sun.security.util.*;
0N/A
0N/A/**
0N/A * This class represents the mechanism independent part of a GSS-API
0N/A * context establishment token. Some mechanisms may choose to encode
0N/A * all subsequent tokens as well such that they start with an encoding
0N/A * of an instance of this class. e.g., The Kerberos v5 GSS-API Mechanism
0N/A * uses this header for all GSS-API tokens.
0N/A * <p>
0N/A * The format is specified in RFC 2743 section 3.1.
0N/A *
0N/A * @author Mayank Upadhyay
0N/A */
0N/A
0N/A/*
0N/A * The RFC states that implementations should explicitly follow the
0N/A * encoding scheme descibed in this section rather than use ASN.1
0N/A * compilers. However, we should consider removing duplicate ASN.1
0N/A * like code from here and depend on sun.security.util if possible.
0N/A */
0N/A
0N/Apublic class GSSHeader {
0N/A
0N/A private ObjectIdentifier mechOid = null;
0N/A private byte[] mechOidBytes = null;
0N/A private int mechTokenLength = 0;
0N/A
0N/A /**
0N/A * The tag defined in the GSS-API mechanism independent token
0N/A * format.
0N/A */
0N/A public static final int TOKEN_ID=0x60;
0N/A
0N/A /**
0N/A * Creates a GSSHeader instance whose encoding can be used as the
0N/A * prefix for a particular mechanism token.
0N/A * @param mechOid the Oid of the mechanism which generated the token
0N/A * @param mechTokenLength the length of the subsequent portion that
0N/A * the mechanism will be adding.
0N/A */
0N/A public GSSHeader(ObjectIdentifier mechOid, int mechTokenLength)
0N/A throws IOException {
0N/A
0N/A this.mechOid = mechOid;
0N/A DerOutputStream temp = new DerOutputStream();
0N/A temp.putOID(mechOid);
0N/A mechOidBytes = temp.toByteArray();
0N/A this.mechTokenLength = mechTokenLength;
0N/A }
0N/A
0N/A /**
0N/A * Reads in a GSSHeader from an InputStream. Typically this would be
0N/A * used as part of reading the complete token from an InputStream
0N/A * that is obtained from a socket.
0N/A */
0N/A public GSSHeader(InputStream is)
0N/A throws IOException, GSSException {
0N/A
0N/A // debug("Parsing GSS token: ");
0N/A
0N/A int tag = is.read();
0N/A
0N/A // debug("tag=" + tag);
0N/A
0N/A if (tag != TOKEN_ID)
0N/A throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
0N/A "GSSHeader did not find the right tag");
0N/A
0N/A int length = getLength(is);
0N/A
0N/A DerValue temp = new DerValue(is);
0N/A mechOidBytes = temp.toByteArray();
0N/A mechOid = temp.getOID();
0N/A // debug (" oid=" + mechOid);
0N/A
0N/A // debug (" len starting with oid=" + length);
0N/A mechTokenLength = length - mechOidBytes.length;
0N/A
0N/A // debug(" mechToken length=" + mechTokenLength);
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Used to obtain the Oid stored in this GSSHeader instance.
0N/A * @return the Oid of the mechanism.
0N/A */
0N/A public ObjectIdentifier getOid() {
0N/A return mechOid;
0N/A }
0N/A
0N/A /**
0N/A * Used to obtain the length of the mechanism specific token that
0N/A * will follow the encoding of this GSSHeader instance.
0N/A * @return the length of the mechanism specific token portion that
0N/A * will follow this GSSHeader.
0N/A */
0N/A public int getMechTokenLength() {
0N/A return mechTokenLength;
0N/A }
0N/A
0N/A /**
0N/A * Used to obtain the length of the encoding of this GSSHeader.
0N/A * @return the lenght of the encoding of this GSSHeader instance.
0N/A */
0N/A public int getLength() {
0N/A int lenField = mechOidBytes.length + mechTokenLength;
0N/A return (1 + getLenFieldSize(lenField) + mechOidBytes.length);
0N/A }
0N/A
0N/A /**
0N/A * Used to determine what the maximum possible mechanism token
0N/A * size is if the complete GSSToken returned to the application
0N/A * (including a GSSHeader) is not to exceed some pre-determined
0N/A * value in size.
0N/A * @param mechOid the Oid of the mechanism that will generate
0N/A * this GSS-API token
0N/A * @param maxTotalSize the pre-determined value that serves as a
0N/A * maximum size for the complete GSS-API token (including a
0N/A * GSSHeader)
0N/A * @return the maximum size of mechanism token that can be used
0N/A * so as to not exceed maxTotalSize with the GSS-API token
0N/A */
0N/A public static int getMaxMechTokenSize(ObjectIdentifier mechOid,
0N/A int maxTotalSize) {
0N/A
0N/A int mechOidBytesSize = 0;
0N/A try {
0N/A DerOutputStream temp = new DerOutputStream();
0N/A temp.putOID(mechOid);
0N/A mechOidBytesSize = temp.toByteArray().length;
0N/A } catch (IOException e) {
0N/A }
0N/A
0N/A // Subtract bytes needed for 0x60 tag and mechOidBytes
0N/A maxTotalSize -= (1 + mechOidBytesSize);
0N/A
0N/A // Subtract maximum len bytes
0N/A maxTotalSize -= 5;
0N/A
0N/A return maxTotalSize;
0N/A
0N/A /*
0N/A * Len field and mechanism token must fit in remaining
0N/A * space. The range of the len field that we allow is
0N/A * 1 through 5.
0N/A *
0N/A
0N/A int mechTokenSize = 0;
0N/A for (int lenFieldSize = 1; lenFieldSize <= 5;
0N/A lenFieldSize++) {
0N/A mechTokenSize = maxTotalSize - lenFieldSize;
0N/A if (getLenFieldSize(mechTokenSize + mechOidBytesSize +
0N/A lenFieldSize) <= lenFieldSize)
0N/A break;
0N/A }
0N/A
0N/A return mechTokenSize;
0N/A */
0N/A
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Used to determine the number of bytes that will be need to encode
0N/A * the length field of the GSSHeader.
0N/A */
0N/A private int getLenFieldSize(int len) {
0N/A int retVal = 1;
0N/A if (len < 128) {
0N/A retVal=1;
0N/A } else if (len < (1 << 8)) {
0N/A retVal=2;
0N/A } else if (len < (1 << 16)) {
0N/A retVal=3;
0N/A } else if (len < (1 << 24)) {
0N/A retVal=4;
0N/A } else {
0N/A retVal=5; // See getMaxMechTokenSize
0N/A }
0N/A return retVal;
0N/A }
0N/A
0N/A /**
0N/A * Encodes this GSSHeader instance onto the provided OutputStream.
0N/A * @param os the OutputStream to which the token should be written.
0N/A * @return the number of bytes that are output as a result of this
0N/A * encoding
0N/A */
0N/A public int encode(OutputStream os) throws IOException {
0N/A int retVal = 1 + mechOidBytes.length;
0N/A os.write(TOKEN_ID);
0N/A int length = mechOidBytes.length + mechTokenLength;
0N/A retVal += putLength(length, os);
0N/A os.write(mechOidBytes);
0N/A return retVal;
0N/A }
0N/A
0N/A /**
0N/A * Get a length from the input stream, allowing for at most 32 bits of
0N/A * encoding to be used. (Not the same as getting a tagged integer!)
0N/A *
0N/A * @return the length or -1 if indefinite length found.
0N/A * @exception IOException on parsing error or unsupported lengths.
0N/A */
0N/A // shameless lifted from sun.security.util.DerInputStream.
0N/A private int getLength(InputStream in) throws IOException {
0N/A return getLength(in.read(), in);
0N/A }
0N/A
0N/A /**
0N/A * Get a length from the input stream, allowing for at most 32 bits of
0N/A * encoding to be used. (Not the same as getting a tagged integer!)
0N/A *
0N/A * @return the length or -1 if indefinite length found.
0N/A * @exception IOException on parsing error or unsupported lengths.
0N/A */
0N/A // shameless lifted from sun.security.util.DerInputStream.
0N/A private int getLength(int lenByte, InputStream in) throws IOException {
0N/A int value, tmp;
0N/A
0N/A tmp = lenByte;
0N/A if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum
0N/A value = tmp;
0N/A } else { // long form or indefinite
0N/A tmp &= 0x07f;
0N/A
0N/A /*
0N/A * NOTE: tmp == 0 indicates indefinite length encoded data.
0N/A * tmp > 4 indicates more than 4Gb of data.
0N/A */
0N/A if (tmp == 0)
0N/A return -1;
0N/A if (tmp < 0 || tmp > 4)
0N/A throw new IOException("DerInputStream.getLength(): lengthTag="
0N/A + tmp + ", "
0N/A + ((tmp < 0) ? "incorrect DER encoding." : "too big."));
0N/A
0N/A for (value = 0; tmp > 0; tmp --) {
0N/A value <<= 8;
0N/A value += 0x0ff & in.read();
0N/A }
0N/A }
0N/A return value;
0N/A }
0N/A
0N/A /**
0N/A * Put the encoding of the length in the specified stream.
0N/A *
0N/A * @params len the length of the attribute.
0N/A * @param out the outputstream to write the length to
0N/A * @return the number of bytes written
0N/A * @exception IOException on writing errors.
0N/A */
0N/A // Shameless lifted from sun.security.util.DerOutputStream.
0N/A private int putLength(int len, OutputStream out) throws IOException {
0N/A int retVal = 0;
0N/A if (len < 128) {
0N/A out.write((byte)len);
0N/A retVal=1;
0N/A
0N/A } else if (len < (1 << 8)) {
0N/A out.write((byte)0x081);
0N/A out.write((byte)len);
0N/A retVal=2;
0N/A
0N/A } else if (len < (1 << 16)) {
0N/A out.write((byte)0x082);
0N/A out.write((byte)(len >> 8));
0N/A out.write((byte)len);
0N/A retVal=3;
0N/A
0N/A } else if (len < (1 << 24)) {
0N/A out.write((byte)0x083);
0N/A out.write((byte)(len >> 16));
0N/A out.write((byte)(len >> 8));
0N/A out.write((byte)len);
0N/A retVal=4;
0N/A
0N/A } else {
0N/A out.write((byte)0x084);
0N/A out.write((byte)(len >> 24));
0N/A out.write((byte)(len >> 16));
0N/A out.write((byte)(len >> 8));
0N/A out.write((byte)len);
0N/A retVal=5;
0N/A }
0N/A
0N/A return retVal;
0N/A }
0N/A
0N/A // XXX Call these two in some central class
0N/A private void debug(String str) {
0N/A System.err.print(str);
0N/A }
0N/A
0N/A private String getHexBytes(byte[] bytes, int len)
0N/A throws IOException {
0N/A
0N/A StringBuffer sb = new StringBuffer();
0N/A for (int i = 0; i < len; i++) {
0N/A
0N/A int b1 = (bytes[i]>>4) & 0x0f;
0N/A int b2 = bytes[i] & 0x0f;
0N/A
0N/A sb.append(Integer.toHexString(b1));
0N/A sb.append(Integer.toHexString(b2));
0N/A sb.append(' ');
0N/A }
0N/A return sb.toString();
0N/A }
0N/A}