342 lines · 11.0 KB · Java
RawDownload
/* * 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();    }}