]>
Commit | Line | Data |
---|---|---|
d3d7e46b AS |
1 | /* |
2 | * Copyright (C) 2006 Martin Will | |
407fcca2 | 3 | * Copyright (C) 2000-2017 Andreas Steffen |
19ef2aec TB |
4 | * |
5 | * Copyright (C) secunet Security Networks AG | |
d3d7e46b AS |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
d3d7e46b AS |
16 | */ |
17 | ||
18 | #include <stdio.h> | |
19 | #include <string.h> | |
20 | #include <time.h> | |
21 | ||
f05b4272 | 22 | #include <utils/debug.h> |
d3d7e46b AS |
23 | |
24 | #include "asn1.h" | |
25 | #include "asn1_parser.h" | |
26 | ||
27 | #define ASN1_MAX_LEVEL 10 | |
28 | ||
29 | typedef struct private_asn1_parser_t private_asn1_parser_t; | |
30 | ||
31 | /** | |
32 | * Private data of an asn1_cxt_t object. | |
33 | */ | |
34 | struct private_asn1_parser_t { | |
35 | /** | |
36 | * Public interface. | |
37 | */ | |
38 | asn1_parser_t public; | |
39 | ||
40 | /** | |
41 | * Syntax definition of ASN.1 object | |
42 | */ | |
43 | asn1Object_t const *objects; | |
44 | ||
d3d7e46b AS |
45 | /** |
46 | * Current syntax definition line | |
47 | */ | |
48 | int line; | |
49 | ||
50 | /** | |
51 | * Current stat of the parsing operation | |
52 | */ | |
53 | bool success; | |
54 | ||
55 | /** | |
7daf5226 | 56 | * Declare object data as private - use debug level 4 to log it |
d3d7e46b AS |
57 | */ |
58 | bool private; | |
59 | ||
60 | /** | |
61 | * Top-most type is implicit - ignore it | |
62 | */ | |
63 | bool implicit; | |
64 | ||
65 | /** | |
66 | * Top-most parsing level - defaults to 0 | |
67 | */ | |
68 | u_int level0; | |
69 | ||
70 | /** | |
71 | * Jump back address for loops for each level | |
72 | */ | |
73 | int loopAddr[ASN1_MAX_LEVEL + 1]; | |
74 | ||
75 | /** | |
76 | * Current parsing pointer for each level | |
77 | */ | |
78 | chunk_t blobs[ASN1_MAX_LEVEL + 2]; | |
407fcca2 AS |
79 | |
80 | /** | |
81 | * Parsing a CHOICE on the current level ? | |
82 | */ | |
83 | bool choice[ASN1_MAX_LEVEL + 2]; | |
84 | ||
d3d7e46b AS |
85 | }; |
86 | ||
3cd69cfa AS |
87 | METHOD(asn1_parser_t, iterate, bool, |
88 | private_asn1_parser_t *this, int *objectID, chunk_t *object) | |
d3d7e46b | 89 | { |
407fcca2 | 90 | chunk_t *blob, *blob1, blob_ori; |
d3d7e46b AS |
91 | u_char *start_ptr; |
92 | u_int level; | |
93 | asn1Object_t obj; | |
7daf5226 | 94 | |
d3d7e46b AS |
95 | *object = chunk_empty; |
96 | ||
460025e2 AS |
97 | /* Advance to the next object syntax definition line */ |
98 | obj = this->objects[++(this->line)]; | |
99 | ||
d3d7e46b | 100 | /* Terminate if the end of the object syntax definition has been reached */ |
460025e2 | 101 | if (obj.flags & ASN1_EXIT) |
d3d7e46b AS |
102 | { |
103 | return FALSE; | |
104 | } | |
7daf5226 | 105 | |
407fcca2 | 106 | if (obj.flags & ASN1_END) /* end of loop or choice or option found */ |
d3d7e46b AS |
107 | { |
108 | if (this->loopAddr[obj.level] && this->blobs[obj.level+1].len > 0) | |
109 | { | |
110 | this->line = this->loopAddr[obj.level]; /* another iteration */ | |
111 | obj = this->objects[this->line]; | |
112 | } | |
113 | else | |
114 | { | |
407fcca2 AS |
115 | this->loopAddr[obj.level] = 0; /* exit loop */ |
116 | ||
117 | if (obj.flags & ASN1_CHOICE) /* end of choices */ | |
118 | { | |
119 | if (this->choice[obj.level+1]) | |
120 | { | |
121 | DBG1(DBG_ASN, "L%d - %s: incorrect choice encoding", | |
122 | this->level0 + obj.level, obj.name); | |
123 | this->success = FALSE; | |
124 | goto end; | |
125 | } | |
126 | } | |
127 | ||
128 | if (obj.flags & ASN1_CH) /* end of choice */ | |
129 | { | |
130 | /* parsed a valid choice */ | |
131 | this->choice[obj.level] = FALSE; | |
132 | ||
133 | /* advance to end of choices */ | |
134 | do | |
135 | { | |
136 | this->line++; | |
137 | } | |
138 | while (!((this->objects[this->line].flags & ASN1_END) && | |
139 | (this->objects[this->line].flags & ASN1_CHOICE) && | |
140 | (this->objects[this->line].level == obj.level-1))); | |
141 | this->line--; | |
142 | } | |
143 | ||
d3d7e46b AS |
144 | goto end; |
145 | } | |
146 | } | |
7daf5226 | 147 | |
d3d7e46b AS |
148 | level = this->level0 + obj.level; |
149 | blob = this->blobs + obj.level; | |
407fcca2 | 150 | blob_ori = *blob; |
d3d7e46b AS |
151 | blob1 = blob + 1; |
152 | start_ptr = blob->ptr; | |
7daf5226 | 153 | |
d3d7e46b AS |
154 | /* handle ASN.1 defaults values */ |
155 | if ((obj.flags & ASN1_DEF) && (blob->len == 0 || *start_ptr != obj.type) ) | |
156 | { | |
157 | /* field is missing */ | |
c7f3a056 | 158 | DBG2(DBG_ASN, "L%d - %s:", level, obj.name); |
d3d7e46b AS |
159 | if (obj.type & ASN1_CONSTRUCTED) |
160 | { | |
161 | this->line++ ; /* skip context-specific tag */ | |
162 | } | |
163 | goto end; | |
164 | } | |
7daf5226 | 165 | |
d3d7e46b | 166 | /* handle ASN.1 options */ |
d3d7e46b AS |
167 | if ((obj.flags & ASN1_OPT) |
168 | && (blob->len == 0 || *start_ptr != obj.type)) | |
169 | { | |
170 | /* advance to end of missing option field */ | |
171 | do | |
172 | { | |
173 | this->line++; | |
174 | } | |
175 | while (!((this->objects[this->line].flags & ASN1_END) && | |
176 | (this->objects[this->line].level == obj.level))); | |
177 | goto end; | |
178 | } | |
7daf5226 | 179 | |
d3d7e46b | 180 | /* an ASN.1 object must possess at least a tag and length field */ |
d3d7e46b AS |
181 | if (blob->len < 2) |
182 | { | |
c7f3a056 | 183 | DBG1(DBG_ASN, "L%d - %s: ASN.1 object smaller than 2 octets", |
d3d7e46b AS |
184 | level, obj.name); |
185 | this->success = FALSE; | |
186 | goto end; | |
187 | } | |
7daf5226 | 188 | |
d3d7e46b | 189 | blob1->len = asn1_length(blob); |
7daf5226 | 190 | |
b29832c7 | 191 | if (blob1->len == ASN1_INVALID_LENGTH) |
d3d7e46b | 192 | { |
c7f3a056 | 193 | DBG1(DBG_ASN, "L%d - %s: length of ASN.1 object invalid or too large", |
d3d7e46b AS |
194 | level, obj.name); |
195 | this->success = FALSE; | |
1f9e4d02 | 196 | goto end; |
d3d7e46b | 197 | } |
7daf5226 | 198 | |
d3d7e46b AS |
199 | blob1->ptr = blob->ptr; |
200 | blob->ptr += blob1->len; | |
201 | blob->len -= blob1->len; | |
7daf5226 | 202 | |
407fcca2 AS |
203 | /* handle ASN.1 choice without explicit context encoding */ |
204 | if ((obj.flags & ASN1_CHOICE) && obj.type == ASN1_EOC) | |
205 | { | |
206 | DBG2(DBG_ASN, "L%d - %s:", level, obj.name); | |
207 | this->choice[obj.level+1] = TRUE; | |
208 | *blob1 = blob_ori; | |
209 | goto end; | |
210 | } | |
7daf5226 | 211 | |
407fcca2 | 212 | /* return raw ASN.1 object without prior type checking */ |
d3d7e46b AS |
213 | if (obj.flags & ASN1_RAW) |
214 | { | |
c7f3a056 | 215 | DBG2(DBG_ASN, "L%d - %s:", level, obj.name); |
d3d7e46b AS |
216 | object->ptr = start_ptr; |
217 | object->len = (size_t)(blob->ptr - start_ptr); | |
218 | goto end; | |
219 | } | |
220 | ||
221 | if (*start_ptr != obj.type && !(this->implicit && this->line == 0)) | |
222 | { | |
e81260d4 | 223 | DBG2(DBG_ASN, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", |
d3d7e46b | 224 | level, obj.name, obj.type, *start_ptr); |
c7f3a056 | 225 | DBG3(DBG_ASN, "%b", start_ptr, (u_int)(blob->ptr - start_ptr)); |
d3d7e46b AS |
226 | this->success = FALSE; |
227 | goto end; | |
228 | } | |
7daf5226 | 229 | |
c7f3a056 | 230 | DBG2(DBG_ASN, "L%d - %s:", level, obj.name); |
7daf5226 MW |
231 | |
232 | /* In case of "SEQUENCE OF" or "SET OF" start a loop */ | |
d3d7e46b AS |
233 | if (obj.flags & ASN1_LOOP) |
234 | { | |
235 | if (blob1->len > 0) | |
236 | { | |
237 | /* at least one item, start the loop */ | |
238 | this->loopAddr[obj.level] = this->line + 1; | |
239 | } | |
240 | else | |
241 | { | |
242 | /* no items, advance directly to end of loop */ | |
243 | do | |
244 | { | |
245 | this->line++; | |
246 | } | |
247 | while (!((this->objects[this->line].flags & ASN1_END) && | |
248 | (this->objects[this->line].level == obj.level))); | |
249 | goto end; | |
250 | } | |
251 | } | |
252 | ||
407fcca2 AS |
253 | /* In case of a "CHOICE" start to scan for exactly one valid choice */ |
254 | if (obj.flags & ASN1_CHOICE) | |
255 | { | |
256 | if (blob1->len == 0) | |
257 | { | |
258 | DBG1(DBG_ASN, "L%d - %s: contains no choice", level, obj.name); | |
259 | this->success = FALSE; | |
260 | goto end; | |
261 | } | |
262 | this->choice[obj.level+1] = TRUE; | |
263 | } | |
264 | ||
d3d7e46b AS |
265 | if (obj.flags & ASN1_OBJ) |
266 | { | |
267 | object->ptr = start_ptr; | |
268 | object->len = (size_t)(blob->ptr - start_ptr); | |
269 | if (this->private) | |
270 | { | |
c7f3a056 | 271 | DBG4(DBG_ASN, "%B", object); |
d3d7e46b AS |
272 | } |
273 | else | |
274 | { | |
c7f3a056 | 275 | DBG3(DBG_ASN, "%B", object); |
d3d7e46b AS |
276 | } |
277 | } | |
278 | else if (obj.flags & ASN1_BODY) | |
279 | { | |
280 | *object = *blob1; | |
281 | asn1_debug_simple_object(*object, obj.type, this->private); | |
282 | } | |
283 | ||
284 | end: | |
285 | *objectID = this->line; | |
286 | return this->success; | |
287 | } | |
288 | ||
3cd69cfa AS |
289 | METHOD(asn1_parser_t, get_level, u_int, |
290 | private_asn1_parser_t *this) | |
d3d7e46b AS |
291 | { |
292 | return this->level0 + this->objects[this->line].level; | |
293 | } | |
294 | ||
3cd69cfa AS |
295 | METHOD(asn1_parser_t, set_top_level, void, |
296 | private_asn1_parser_t *this, u_int level0) | |
d3d7e46b AS |
297 | { |
298 | this->level0 = level0; | |
299 | } | |
300 | ||
3cd69cfa AS |
301 | METHOD(asn1_parser_t, set_flags, void, |
302 | private_asn1_parser_t *this, bool implicit, bool private) | |
d3d7e46b AS |
303 | { |
304 | this->implicit = implicit; | |
305 | this->private = private; | |
306 | } | |
307 | ||
3cd69cfa AS |
308 | METHOD(asn1_parser_t, success, bool, |
309 | private_asn1_parser_t *this) | |
d3d7e46b AS |
310 | { |
311 | return this->success; | |
312 | } | |
313 | ||
3cd69cfa AS |
314 | METHOD(asn1_parser_t, destroy, void, |
315 | private_asn1_parser_t *this) | |
d3d7e46b AS |
316 | { |
317 | free(this); | |
318 | } | |
319 | ||
320 | /** | |
321 | * Defined in header. | |
322 | */ | |
460025e2 | 323 | asn1_parser_t* asn1_parser_create(asn1Object_t const *objects, chunk_t blob) |
d3d7e46b | 324 | { |
3cd69cfa AS |
325 | private_asn1_parser_t *this; |
326 | ||
327 | INIT(this, | |
328 | .public = { | |
329 | .iterate = _iterate, | |
330 | .get_level = _get_level, | |
331 | .set_top_level = _set_top_level, | |
332 | .set_flags = _set_flags, | |
333 | .success = _success, | |
334 | .destroy = _destroy, | |
375dacca | 335 | }, |
3cd69cfa AS |
336 | .objects = objects, |
337 | .blobs[0] = blob, | |
338 | .line = -1, | |
339 | .success = TRUE, | |
340 | ); | |
d3d7e46b AS |
341 | |
342 | return &this->public; | |
343 | } |