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