| /* | |
| * Copyright (c) 2000, 2024, 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 described 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 final ObjectIdentifier mechOid; | |
| private final byte[] mechOidBytes; | |
| private final int mechTokenLength; | |
| /** | |
| * 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 length 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; | |
| DerOutputStream temp = new DerOutputStream(); | |
| temp.putOID(mechOid); | |
| mechOidBytesSize = temp.toByteArray().length; | |
| // 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; | |
| 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 > 4) | |
| throw new IOException("DerInputStream.getLength(): lengthTag=" | |
| + tmp + ", 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. | |
| * | |
| * @param 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; | |
| 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; | |
| } | |
| } |