1 /*
   2  * Copyright (c) 1999, 2013, 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 
  26 package com.sun.jndi.ldap;
  27 
  28 import java.io.UnsupportedEncodingException;
  29 
  30 /**
  31   * A BER decoder. Contains methods to parse a BER buffer.
  32   *
  33   * @author Jagane Sundar
  34   * @author Vincent Ryan
  35   */
  36 public final class BerDecoder extends Ber {
  37 
  38     private int origOffset;  // The start point in buf to decode
  39 
  40     /**
  41      * Creates a BER decoder that reads bytes from the specified buffer.
  42      */
  43     public BerDecoder(byte buf[], int offset, int bufsize) {
  44 
  45         this.buf = buf;         // shared buffer, be careful to use this class
  46         this.bufsize = bufsize;
  47         this.origOffset = offset;
  48 
  49         reset();
  50     }
  51 
  52     /**
  53      * Resets this decode to start parsing from the initial offset
  54      * (ie., same state as after calling the constructor).
  55      */
  56     public void reset() {
  57         offset = origOffset;
  58     }
  59 
  60     /**
  61       * Returns the current parse position.
  62       * It points to the byte that will be parsed next.
  63       * Useful for parsing sequences.
  64       */
  65     public int getParsePosition() {
  66         return offset;
  67     }
  68 
  69     /**
  70       * Parses a possibly variable length field.
  71       */
  72     public int parseLength() throws DecodeException {
  73 
  74         int lengthbyte = parseByte();
  75 
  76         if ((lengthbyte & 0x80) == 0x80) {
  77 
  78             lengthbyte &= 0x7f;
  79 
  80             if (lengthbyte == 0) {
  81                 throw new DecodeException(
  82                     "Indefinite length not supported");
  83             }
  84 
  85             if (lengthbyte > 4) {
  86                 throw new DecodeException("encoding too long");
  87             }
  88 
  89             if (bufsize - offset < lengthbyte) {
  90                 throw new DecodeException("Insufficient data");
  91             }
  92 
  93             int retval = 0;
  94 
  95             for( int i = 0; i < lengthbyte; i++) {
  96                 retval = (retval << 8) + (buf[offset++] & 0xff);
  97             }
  98             if (retval < 0) {
  99               throw new DecodeException("Invalid length bytes");
 100             }
 101             return retval;
 102         } else {
 103             return lengthbyte;
 104         }
 105     }
 106 
 107     /**
 108      * Parses the next sequence in this BER buffer.
 109      * @param rlen An array for returning size of the sequence in bytes. If null,
 110      *          the size is not returned.
 111      * @return The sequence's tag.
 112      */
 113     public int parseSeq(int rlen[]) throws DecodeException {
 114 
 115         int seq = parseByte();
 116         int len = parseLength();
 117         if (rlen != null) {
 118             rlen[0] = len;
 119         }
 120         return seq;
 121     }
 122 
 123     /**
 124      * Used to skip bytes. Usually used when trying to recover from parse error.
 125      * Don't need to be public right now?
 126      * @param i The number of bytes to skip
 127      */
 128     void seek(int i) throws DecodeException {
 129         if (offset + i > bufsize || offset + i < 0) {
 130             throw new DecodeException("array index out of bounds");
 131         }
 132         offset += i;
 133     }
 134 
 135     /**
 136      * Parses the next byte in this BER buffer.
 137      * @return The byte parsed.
 138      */
 139     public int parseByte() throws DecodeException {
 140         if (bufsize - offset < 1) {
 141             throw new DecodeException("Insufficient data");
 142         }
 143         return buf[offset++] & 0xff;
 144     }
 145 
 146 
 147     /**
 148      * Returns the next byte in this BER buffer without consuming it.
 149      * @return The next byte.
 150      */
 151     public int peekByte() throws DecodeException {
 152         if (bufsize - offset < 1) {
 153             throw new DecodeException("Insufficient data");
 154         }
 155         return buf[offset] & 0xff;
 156     }
 157 
 158     /**
 159      * Parses an ASN_BOOLEAN tagged integer from this BER buffer.
 160      * @return true if the tagged integer is 0; false otherwise.
 161      */
 162     public boolean parseBoolean() throws DecodeException {
 163         return ((parseIntWithTag(ASN_BOOLEAN) == 0x00) ? false : true);
 164     }
 165 
 166     /**
 167      * Parses an ASN_ENUMERATED tagged integer from this BER buffer.
 168      * @return The tag of enumeration.
 169      */
 170     public int parseEnumeration() throws DecodeException {
 171         return parseIntWithTag(ASN_ENUMERATED);
 172     }
 173 
 174     /**
 175      * Parses an ASN_INTEGER tagged integer from this BER buffer.
 176      * @return The value of the integer.
 177      */
 178     public int parseInt() throws DecodeException {
 179         return parseIntWithTag(ASN_INTEGER);
 180     }
 181 
 182     /**
 183       * Parses an integer that's preceded by a tag.
 184       *<blockquote><pre>
 185       * BER integer ::= tag length byte {byte}*
 186       *</pre></blockquote>
 187       */
 188     private int parseIntWithTag(int tag) throws DecodeException {
 189         if (parseByte() != tag) {
 190             // Ber could have been reset;
 191             String s;
 192             if (offset > 0) {
 193                 s = Integer.toString(buf[offset - 1] & 0xff);
 194             } else {
 195                 s = "Empty tag";
 196             }
 197             throw new DecodeException("Encountered ASN.1 tag " +
 198                 s + " (expected tag " + Integer.toString(tag) + ")");
 199         }
 200 
 201         int len = parseLength();
 202 
 203         if (len > 4) {
 204             throw new DecodeException("INTEGER too long");
 205         } else if (len > bufsize - offset) {
 206             throw new DecodeException("Insufficient data");
 207         }
 208 
 209         byte fb = buf[offset++];
 210         int value = 0;
 211 
 212         value = fb & 0x7F;
 213         for( int i = 1 /* first byte already read */ ; i < len; i++) {
 214             value <<= 8;
 215             value |= (buf[offset++] & 0xff);
 216         }
 217 
 218         if ((fb & 0x80) == 0x80) {
 219             value = -value;
 220         }
 221 
 222         return value;
 223     }
 224 
 225     /**
 226       * Parses a string.
 227       */
 228     public String parseString(boolean decodeUTF8) throws DecodeException {
 229         return parseStringWithTag(ASN_SIMPLE_STRING, decodeUTF8, null);
 230     }
 231 
 232     /**
 233       * Parses a string of a given tag from this BER buffer.
 234       *<blockquote><pre>
 235       *BER simple string ::= tag length {byte}*
 236       *</pre></blockquote>
 237       * @param rlen An array for holding the relative parsed offset; if null
 238       *  offset not set.
 239       * @param decodeUTF8 If true, use UTF-8 when decoding the string; otherwise
 240       * use ISO-Latin-1 (8859_1). Use true for LDAPv3; false for LDAPv2.
 241       * @param tag The tag that precedes the string.
 242       * @return The non-null parsed string.
 243       */
 244     public String parseStringWithTag(int tag, boolean decodeUTF8, int rlen[])
 245         throws DecodeException {
 246 
 247         int st;
 248         int origOffset = offset;
 249 
 250         if ((st = parseByte()) != tag) {
 251             throw new DecodeException("Encountered ASN.1 tag " +
 252                 Integer.toString((byte)st) + " (expected tag " + tag + ")");
 253         }
 254 
 255         int len = parseLength();
 256 
 257         if (len > bufsize - offset) {
 258             throw new DecodeException("Insufficient data");
 259         }
 260 
 261         String retstr;
 262         if (len == 0) {
 263             retstr = "";
 264         } else {
 265             byte[] buf2 = new byte[len];
 266 
 267             System.arraycopy(buf, offset, buf2, 0, len);
 268             if (decodeUTF8) {
 269                 try {
 270                     retstr = new String(buf2, "UTF8");
 271                 } catch (UnsupportedEncodingException e) {
 272                     throw new DecodeException("UTF8 not available on platform");
 273                 }
 274             } else {
 275                 try {
 276                     retstr = new String(buf2, "8859_1");
 277                 } catch (UnsupportedEncodingException e) {
 278                     throw new DecodeException("8859_1 not available on platform");
 279                 }
 280             }
 281             offset += len;
 282         }
 283 
 284         if (rlen != null) {
 285             rlen[0] = offset - origOffset;
 286         }
 287 
 288         return retstr;
 289     }
 290 
 291     /**
 292      * Parses an octet string of a given type(tag) from this BER buffer.
 293      * <blockquote><pre>
 294      * BER Binary Data of type "tag" ::= tag length {byte}*
 295      *</pre></blockquote>
 296      *
 297      * @param tag The tag to look for.
 298      * @param rlen An array for returning the relative parsed position. If null,
 299      *          the relative parsed position is not returned.
 300      * @return A non-null array containing the octet string.
 301      * @throws DecodeException If the next byte in the BER buffer is not
 302      * {@code tag}, or if length specified in the BER buffer exceeds the
 303      * number of bytes left in the buffer.
 304      */
 305     public byte[] parseOctetString(int tag, int rlen[]) throws DecodeException {
 306 
 307         int origOffset = offset;
 308         int st;
 309         if ((st = parseByte()) != tag) {
 310 
 311             throw new DecodeException("Encountered ASN.1 tag " +
 312                 Integer.toString(st) +
 313                 " (expected tag " + Integer.toString(tag) + ")");
 314         }
 315 
 316         int len = parseLength();
 317 
 318         if (len > bufsize - offset) {
 319             throw new DecodeException("Insufficient data");
 320         }
 321 
 322         byte retarr[] = new byte[len];
 323         if (len > 0) {
 324             System.arraycopy(buf, offset, retarr, 0, len);
 325             offset += len;
 326         }
 327 
 328         if (rlen != null) {
 329             rlen[0] = offset - origOffset;
 330         }
 331 
 332         return retarr;
 333     }
 334 
 335     /**
 336      * Returns the number of unparsed bytes in this BER buffer.
 337      */
 338     public int bytesLeft() {
 339         return bufsize - offset;
 340     }
 341 }