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      */
BerDecoder(byte buf[], int offset, int bufsize)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      */
reset()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       */
getParsePosition()65     public int getParsePosition() {
66         return offset;
67     }
68 
69     /**
70       * Parses a possibly variable length field.
71       */
parseLength()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      */
parseSeq(int rlen[])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      */
seek(int i)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      */
parseByte()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      */
peekByte()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      */
parseBoolean()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      */
parseEnumeration()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      */
parseInt()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       */
parseIntWithTag(int tag)188     private int parseIntWithTag(int tag) throws DecodeException {
189 
190 
191         if (parseByte() != tag) {
192             throw new DecodeException("Encountered ASN.1 tag " +
193                 Integer.toString(buf[offset - 1] & 0xff) +
194                 " (expected tag " + Integer.toString(tag) + ")");
195         }
196 
197         int len = parseLength();
198 
199         if (len > 4) {
200             throw new DecodeException("INTEGER too long");
201         } else if (len > bufsize - offset) {
202             throw new DecodeException("Insufficient data");
203         }
204 
205         byte fb = buf[offset++];
206         int value = 0;
207 
208         value = fb & 0x7F;
209         for( int i = 1 /* first byte already read */ ; i < len; i++) {
210             value <<= 8;
211             value |= (buf[offset++] & 0xff);
212         }
213 
214         if ((fb & 0x80) == 0x80) {
215             value = -value;
216         }
217 
218         return value;
219     }
220 
221     /**
222       * Parses a string.
223       */
parseString(boolean decodeUTF8)224     public String parseString(boolean decodeUTF8) throws DecodeException {
225         return parseStringWithTag(ASN_SIMPLE_STRING, decodeUTF8, null);
226     }
227 
228     /**
229       * Parses a string of a given tag from this BER buffer.
230       *<blockquote><pre>
231       *BER simple string ::= tag length {byte}*
232       *</pre></blockquote>
233       * @param rlen An array for holding the relative parsed offset; if null
234       *  offset not set.
235       * @param decodeUTF8 If true, use UTF-8 when decoding the string; otherwise
236       * use ISO-Latin-1 (8859_1). Use true for LDAPv3; false for LDAPv2.
237       * @param tag The tag that precedes the string.
238       * @return The non-null parsed string.
239       */
parseStringWithTag(int tag, boolean decodeUTF8, int rlen[])240     public String parseStringWithTag(int tag, boolean decodeUTF8, int rlen[])
241         throws DecodeException {
242 
243         int st;
244         int origOffset = offset;
245 
246         if ((st = parseByte()) != tag) {
247             throw new DecodeException("Encountered ASN.1 tag " +
248                 Integer.toString((byte)st) + " (expected tag " + tag + ")");
249         }
250 
251         int len = parseLength();
252 
253         if (len > bufsize - offset) {
254             throw new DecodeException("Insufficient data");
255         }
256 
257         String retstr;
258         if (len == 0) {
259             retstr = "";
260         } else {
261             byte[] buf2 = new byte[len];
262 
263             System.arraycopy(buf, offset, buf2, 0, len);
264             if (decodeUTF8) {
265                 try {
266                     retstr = new String(buf2, "UTF8");
267                 } catch (UnsupportedEncodingException e) {
268                     throw new DecodeException("UTF8 not available on platform");
269                 }
270             } else {
271                 try {
272                     retstr = new String(buf2, "8859_1");
273                 } catch (UnsupportedEncodingException e) {
274                     throw new DecodeException("8859_1 not available on platform");
275                 }
276             }
277             offset += len;
278         }
279 
280         if (rlen != null) {
281             rlen[0] = offset - origOffset;
282         }
283 
284         return retstr;
285     }
286 
287     /**
288      * Parses an octet string of a given type(tag) from this BER buffer.
289      * <blockquote><pre>
290      * BER Binary Data of type "tag" ::= tag length {byte}*
291      *</pre></blockquote>
292      *
293      * @param tag The tag to look for.
294      * @param rlen An array for returning the relative parsed position. If null,
295      *          the relative parsed position is not returned.
296      * @return A non-null array containing the octet string.
297      * @throws DecodeException If the next byte in the BER buffer is not
298      * {@code tag}, or if length specified in the BER buffer exceeds the
299      * number of bytes left in the buffer.
300      */
parseOctetString(int tag, int rlen[])301     public byte[] parseOctetString(int tag, int rlen[]) throws DecodeException {
302 
303         int origOffset = offset;
304         int st;
305         if ((st = parseByte()) != tag) {
306 
307             throw new DecodeException("Encountered ASN.1 tag " +
308                 Integer.toString(st) +
309                 " (expected tag " + Integer.toString(tag) + ")");
310         }
311 
312         int len = parseLength();
313 
314         if (len > bufsize - offset) {
315             throw new DecodeException("Insufficient data");
316         }
317 
318         byte retarr[] = new byte[len];
319         if (len > 0) {
320             System.arraycopy(buf, offset, retarr, 0, len);
321             offset += len;
322         }
323 
324         if (rlen != null) {
325             rlen[0] = offset - origOffset;
326         }
327 
328         return retarr;
329     }
330 
331     /**
332      * Returns the number of unparsed bytes in this BER buffer.
333      */
bytesLeft()334     public int bytesLeft() {
335         return bufsize - offset;
336     }
337 }
338