1/* 2 * Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * 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 if (value < 0) { 274 throw new IOException("Invalid length bytes"); 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 StringBuilder sb = new StringBuilder(); 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} 346