001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.jgss;
027:
028: import org.ietf.jgss.GSSException;
029: import java.io.InputStream;
030: import java.io.OutputStream;
031: import java.io.IOException;
032: import sun.security.util.*;
033:
034: /**
035: * This class represents the mechanism independent part of a GSS-API
036: * context establishment token. Some mechanisms may choose to encode
037: * all subsequent tokens as well such that they start with an encoding
038: * of an instance of this class. e.g., The Kerberos v5 GSS-API Mechanism
039: * uses this header for all GSS-API tokens.
040: * <p>
041: * The format is specified in RFC 2743 section 3.1.
042: *
043: * @author Mayank Upadhyay
044: * @version 1.13, 05/05/07
045: */
046:
047: /*
048: * The RFC states that implementations should explicitly follow the
049: * encoding scheme descibed in this section rather than use ASN.1
050: * compilers. However, we should consider removing duplicate ASN.1
051: * like code from here and depend on sun.security.util if possible.
052: */
053:
054: public class GSSHeader {
055:
056: private ObjectIdentifier mechOid = null;
057: private byte[] mechOidBytes = null;
058: private int mechTokenLength = 0;
059:
060: /**
061: * The tag defined in the GSS-API mechanism independent token
062: * format.
063: */
064: public static final int TOKEN_ID = 0x60;
065:
066: /**
067: * Creates a GSSHeader instance whose encoding can be used as the
068: * prefix for a particular mechanism token.
069: * @param mechOid the Oid of the mechanism which generated the token
070: * @param mechTokenLength the length of the subsequent portion that
071: * the mechanism will be adding.
072: */
073: public GSSHeader(ObjectIdentifier mechOid, int mechTokenLength)
074: throws IOException {
075:
076: this .mechOid = mechOid;
077: DerOutputStream temp = new DerOutputStream();
078: temp.putOID(mechOid);
079: mechOidBytes = temp.toByteArray();
080: this .mechTokenLength = mechTokenLength;
081: }
082:
083: /**
084: * Reads in a GSSHeader from an InputStream. Typically this would be
085: * used as part of reading the complete token from an InputStream
086: * that is obtained from a socket.
087: */
088: public GSSHeader(InputStream is) throws IOException, GSSException {
089:
090: // debug("Parsing GSS token: ");
091:
092: int tag = is.read();
093:
094: // debug("tag=" + tag);
095:
096: if (tag != TOKEN_ID)
097: throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
098: "GSSHeader did not find the right tag");
099:
100: int length = getLength(is);
101:
102: DerValue temp = new DerValue(is);
103: mechOidBytes = temp.toByteArray();
104: mechOid = temp.getOID();
105: // debug (" oid=" + mechOid);
106:
107: // debug (" len starting with oid=" + length);
108: mechTokenLength = length - mechOidBytes.length;
109:
110: // debug(" mechToken length=" + mechTokenLength);
111:
112: }
113:
114: /**
115: * Used to obtain the Oid stored in this GSSHeader instance.
116: * @return the Oid of the mechanism.
117: */
118: public ObjectIdentifier getOid() {
119: return mechOid;
120: }
121:
122: /**
123: * Used to obtain the length of the mechanism specific token that
124: * will follow the encoding of this GSSHeader instance.
125: * @return the length of the mechanism specific token portion that
126: * will follow this GSSHeader.
127: */
128: public int getMechTokenLength() {
129: return mechTokenLength;
130: }
131:
132: /**
133: * Used to obtain the length of the encoding of this GSSHeader.
134: * @return the lenght of the encoding of this GSSHeader instance.
135: */
136: public int getLength() {
137: int lenField = mechOidBytes.length + mechTokenLength;
138: return (1 + getLenFieldSize(lenField) + mechOidBytes.length);
139: }
140:
141: /**
142: * Used to determine what the maximum possible mechanism token
143: * size is if the complete GSSToken returned to the application
144: * (including a GSSHeader) is not to exceed some pre-determined
145: * value in size.
146: * @param mechOid the Oid of the mechanism that will generate
147: * this GSS-API token
148: * @param maxTotalSize the pre-determined value that serves as a
149: * maximum size for the complete GSS-API token (including a
150: * GSSHeader)
151: * @return the maximum size of mechanism token that can be used
152: * so as to not exceed maxTotalSize with the GSS-API token
153: */
154: public static int getMaxMechTokenSize(ObjectIdentifier mechOid,
155: int maxTotalSize) {
156:
157: int mechOidBytesSize = 0;
158: try {
159: DerOutputStream temp = new DerOutputStream();
160: temp.putOID(mechOid);
161: mechOidBytesSize = temp.toByteArray().length;
162: } catch (IOException e) {
163: }
164:
165: // Subtract bytes needed for 0x60 tag and mechOidBytes
166: maxTotalSize -= (1 + mechOidBytesSize);
167:
168: // Subtract maximum len bytes
169: maxTotalSize -= 5;
170:
171: return maxTotalSize;
172:
173: /*
174: * Len field and mechanism token must fit in remaining
175: * space. The range of the len field that we allow is
176: * 1 through 5.
177: *
178:
179: int mechTokenSize = 0;
180: for (int lenFieldSize = 1; lenFieldSize <= 5;
181: lenFieldSize++) {
182: mechTokenSize = maxTotalSize - lenFieldSize;
183: if (getLenFieldSize(mechTokenSize + mechOidBytesSize +
184: lenFieldSize) <= lenFieldSize)
185: break;
186: }
187:
188: return mechTokenSize;
189: */
190:
191: }
192:
193: /**
194: * Used to determine the number of bytes that will be need to encode
195: * the length field of the GSSHeader.
196: */
197: private int getLenFieldSize(int len) {
198: int retVal = 1;
199: if (len < 128) {
200: retVal = 1;
201: } else if (len < (1 << 8)) {
202: retVal = 2;
203: } else if (len < (1 << 16)) {
204: retVal = 3;
205: } else if (len < (1 << 24)) {
206: retVal = 4;
207: } else {
208: retVal = 5; // See getMaxMechTokenSize
209: }
210: return retVal;
211: }
212:
213: /**
214: * Encodes this GSSHeader instance onto the provided OutputStream.
215: * @param os the OutputStream to which the token should be written.
216: * @return the number of bytes that are output as a result of this
217: * encoding
218: */
219: public int encode(OutputStream os) throws IOException {
220: int retVal = 1 + mechOidBytes.length;
221: os.write(TOKEN_ID);
222: int length = mechOidBytes.length + mechTokenLength;
223: retVal += putLength(length, os);
224: os.write(mechOidBytes);
225: return retVal;
226: }
227:
228: /**
229: * Get a length from the input stream, allowing for at most 32 bits of
230: * encoding to be used. (Not the same as getting a tagged integer!)
231: *
232: * @return the length or -1 if indefinite length found.
233: * @exception IOException on parsing error or unsupported lengths.
234: */
235: // shameless lifted from sun.security.util.DerInputStream.
236: private int getLength(InputStream in) throws IOException {
237: return getLength(in.read(), in);
238: }
239:
240: /**
241: * Get a length from the input stream, allowing for at most 32 bits of
242: * encoding to be used. (Not the same as getting a tagged integer!)
243: *
244: * @return the length or -1 if indefinite length found.
245: * @exception IOException on parsing error or unsupported lengths.
246: */
247: // shameless lifted from sun.security.util.DerInputStream.
248: private int getLength(int lenByte, InputStream in)
249: throws IOException {
250: int value, tmp;
251:
252: tmp = lenByte;
253: if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum
254: value = tmp;
255: } else { // long form or indefinite
256: tmp &= 0x07f;
257:
258: /*
259: * NOTE: tmp == 0 indicates indefinite length encoded data.
260: * tmp > 4 indicates more than 4Gb of data.
261: */
262: if (tmp == 0)
263: return -1;
264: if (tmp < 0 || tmp > 4)
265: throw new IOException(
266: "DerInputStream.getLength(): lengthTag="
267: + tmp
268: + ", "
269: + ((tmp < 0) ? "incorrect DER encoding."
270: : "too big."));
271:
272: for (value = 0; tmp > 0; tmp--) {
273: value <<= 8;
274: value += 0x0ff & in.read();
275: }
276: }
277: return value;
278: }
279:
280: /**
281: * Put the encoding of the length in the specified stream.
282: *
283: * @params len the length of the attribute.
284: * @param out the outputstream to write the length to
285: * @return the number of bytes written
286: * @exception IOException on writing errors.
287: */
288: // Shameless lifted from sun.security.util.DerOutputStream.
289: private int putLength(int len, OutputStream out) throws IOException {
290: int retVal = 0;
291: if (len < 128) {
292: out.write((byte) len);
293: retVal = 1;
294:
295: } else if (len < (1 << 8)) {
296: out.write((byte) 0x081);
297: out.write((byte) len);
298: retVal = 2;
299:
300: } else if (len < (1 << 16)) {
301: out.write((byte) 0x082);
302: out.write((byte) (len >> 8));
303: out.write((byte) len);
304: retVal = 3;
305:
306: } else if (len < (1 << 24)) {
307: out.write((byte) 0x083);
308: out.write((byte) (len >> 16));
309: out.write((byte) (len >> 8));
310: out.write((byte) len);
311: retVal = 4;
312:
313: } else {
314: out.write((byte) 0x084);
315: out.write((byte) (len >> 24));
316: out.write((byte) (len >> 16));
317: out.write((byte) (len >> 8));
318: out.write((byte) len);
319: retVal = 5;
320: }
321:
322: return retVal;
323: }
324:
325: // XXX Call these two in some central class
326: private void debug(String str) {
327: System.err.print(str);
328: }
329:
330: private String getHexBytes(byte[] bytes, int len)
331: throws IOException {
332:
333: StringBuffer sb = new StringBuffer();
334: for (int i = 0; i < len; i++) {
335:
336: int b1 = (bytes[i] >> 4) & 0x0f;
337: int b2 = bytes[i] & 0x0f;
338:
339: sb.append(Integer.toHexString(b1));
340: sb.append(Integer.toHexString(b2));
341: sb.append(' ');
342: }
343: return sb.toString();
344: }
345: }
|