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 }