blob: 9efe1e0418f02bf21eab4704ba942613784388e9 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.jgss;
27
28import org.ietf.jgss.GSSException;
29import java.io.InputStream;
30import java.io.OutputStream;
31import java.io.IOException;
32import sun.security.util.*;
33
34/**
35 * This class represents the mechanism independent part of a GSS-API
36 * context establishment token. Some mechanisms may choose to encode
37 * all subsequent tokens as well such that they start with an encoding
38 * of an instance of this class. e.g., The Kerberos v5 GSS-API Mechanism
39 * uses this header for all GSS-API tokens.
40 * <p>
41 * The format is specified in RFC 2743 section 3.1.
42 *
43 * @author Mayank Upadhyay
44 */
45
46/*
47 * The RFC states that implementations should explicitly follow the
48 * encoding scheme descibed in this section rather than use ASN.1
49 * compilers. However, we should consider removing duplicate ASN.1
50 * like code from here and depend on sun.security.util if possible.
51 */
52
53public class GSSHeader {
54
55 private ObjectIdentifier mechOid = null;
56 private byte[] mechOidBytes = null;
57 private int mechTokenLength = 0;
58
59 /**
60 * The tag defined in the GSS-API mechanism independent token
61 * format.
62 */
63 public static final int TOKEN_ID=0x60;
64
65 /**
66 * Creates a GSSHeader instance whose encoding can be used as the
67 * prefix for a particular mechanism token.
68 * @param mechOid the Oid of the mechanism which generated the token
69 * @param mechTokenLength the length of the subsequent portion that
70 * the mechanism will be adding.
71 */
72 public GSSHeader(ObjectIdentifier mechOid, int mechTokenLength)
73 throws IOException {
74
75 this.mechOid = mechOid;
76 DerOutputStream temp = new DerOutputStream();
77 temp.putOID(mechOid);
78 mechOidBytes = temp.toByteArray();
79 this.mechTokenLength = mechTokenLength;
80 }
81
82 /**
83 * Reads in a GSSHeader from an InputStream. Typically this would be
84 * used as part of reading the complete token from an InputStream
85 * that is obtained from a socket.
86 */
87 public GSSHeader(InputStream is)
88 throws IOException, GSSException {
89
90 // debug("Parsing GSS token: ");
91
92 int tag = is.read();
93
94 // debug("tag=" + tag);
95
96 if (tag != TOKEN_ID)
97 throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
98 "GSSHeader did not find the right tag");
99
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 /**
195 * Used to determine the number of bytes that will be need to encode
196 * the length field of the GSSHeader.
197 */
198 private int getLenFieldSize(int len) {
199 int retVal = 1;
200 if (len < 128) {
201 retVal=1;
202 } else if (len < (1 << 8)) {
203 retVal=2;
204 } else if (len < (1 << 16)) {
205 retVal=3;
206 } else if (len < (1 << 24)) {
207 retVal=4;
208 } else {
209 retVal=5; // See getMaxMechTokenSize
210 }
211 return retVal;
212 }
213
214 /**
215 * Encodes this GSSHeader instance onto the provided OutputStream.
216 * @param os the OutputStream to which the token should be written.
217 * @return the number of bytes that are output as a result of this
218 * encoding
219 */
220 public int encode(OutputStream os) throws IOException {
221 int retVal = 1 + mechOidBytes.length;
222 os.write(TOKEN_ID);
223 int length = mechOidBytes.length + mechTokenLength;
224 retVal += putLength(length, os);
225 os.write(mechOidBytes);
226 return retVal;
227 }
228
229 /**
230 * Get a length from the input stream, allowing for at most 32 bits of
231 * encoding to be used. (Not the same as getting a tagged integer!)
232 *
233 * @return the length or -1 if indefinite length found.
234 * @exception IOException on parsing error or unsupported lengths.
235 */
236 // shameless lifted from sun.security.util.DerInputStream.
237 private int getLength(InputStream in) throws IOException {
238 return getLength(in.read(), in);
239 }
240
241 /**
242 * Get a length from the input stream, allowing for at most 32 bits of
243 * encoding to be used. (Not the same as getting a tagged integer!)
244 *
245 * @return the length or -1 if indefinite length found.
246 * @exception IOException on parsing error or unsupported lengths.
247 */
248 // shameless lifted from sun.security.util.DerInputStream.
249 private int getLength(int lenByte, InputStream in) 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("DerInputStream.getLength(): lengthTag="
266 + tmp + ", "
267 + ((tmp < 0) ? "incorrect DER encoding." : "too big."));
268
269 for (value = 0; tmp > 0; tmp --) {
270 value <<= 8;
271 value += 0x0ff & in.read();
272 }
273 }
274 return value;
275 }
276
277 /**
278 * Put the encoding of the length in the specified stream.
279 *
280 * @params len the length of the attribute.
281 * @param out the outputstream to write the length to
282 * @return the number of bytes written
283 * @exception IOException on writing errors.
284 */
285 // Shameless lifted from sun.security.util.DerOutputStream.
286 private int putLength(int len, OutputStream out) throws IOException {
287 int retVal = 0;
288 if (len < 128) {
289 out.write((byte)len);
290 retVal=1;
291
292 } else if (len < (1 << 8)) {
293 out.write((byte)0x081);
294 out.write((byte)len);
295 retVal=2;
296
297 } else if (len < (1 << 16)) {
298 out.write((byte)0x082);
299 out.write((byte)(len >> 8));
300 out.write((byte)len);
301 retVal=3;
302
303 } else if (len < (1 << 24)) {
304 out.write((byte)0x083);
305 out.write((byte)(len >> 16));
306 out.write((byte)(len >> 8));
307 out.write((byte)len);
308 retVal=4;
309
310 } else {
311 out.write((byte)0x084);
312 out.write((byte)(len >> 24));
313 out.write((byte)(len >> 16));
314 out.write((byte)(len >> 8));
315 out.write((byte)len);
316 retVal=5;
317 }
318
319 return retVal;
320 }
321
322 // XXX Call these two in some central class
323 private void debug(String str) {
324 System.err.print(str);
325 }
326
327 private String getHexBytes(byte[] bytes, int len)
328 throws IOException {
329
330 StringBuffer sb = new StringBuffer();
331 for (int i = 0; i < len; i++) {
332
333 int b1 = (bytes[i]>>4) & 0x0f;
334 int b2 = bytes[i] & 0x0f;
335
336 sb.append(Integer.toHexString(b1));
337 sb.append(Integer.toHexString(b2));
338 sb.append(' ');
339 }
340 return sb.toString();
341 }
342}