| /* |
| * 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(); |
| } |
| } |