]>
Commit | Line | Data |
---|---|---|
627f6d02 | 1 | /* |
2 | * Abstract Syntax Notation One, ASN.1 | |
3 | * As defined in ISO/IS 8824 and ISO/IS 8825 | |
4 | * This implements a subset of the above International Standards that | |
5 | * is sufficient to implement SNMP. | |
6 | * | |
7 | * Encodes abstract data types into a machine independent stream of bytes. | |
8 | * | |
9 | */ | |
10 | /********************************************************************** | |
11 | Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University | |
12 | ||
13 | All Rights Reserved | |
14 | ||
15 | Permission to use, copy, modify, and distribute this software and its | |
16 | documentation for any purpose and without fee is hereby granted, | |
17 | provided that the above copyright notice appear in all copies and that | |
18 | both that copyright notice and this permission notice appear in | |
19 | supporting documentation, and that the name of CMU not be | |
20 | used in advertising or publicity pertaining to distribution of the | |
21 | software without specific, written prior permission. | |
22 | ||
23 | CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
24 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | |
25 | CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
26 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
27 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
28 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
29 | SOFTWARE. | |
30 | ******************************************************************/ | |
627f6d02 | 31 | |
db5edad5 | 32 | #include "config.h" |
33 | ||
34 | #if HAVE_STDIO_H | |
627f6d02 | 35 | #include <stdio.h> |
db5edad5 | 36 | #endif |
37 | #if HAVE_STDLIB_H | |
627f6d02 | 38 | #include <stdlib.h> |
db5edad5 | 39 | #endif |
40 | #if HAVE_STRING_H | |
627f6d02 | 41 | #include <string.h> |
42 | #endif | |
db5edad5 | 43 | #if HAVE_SYS_TYPES_H |
627f6d02 | 44 | #include <sys/types.h> |
db5edad5 | 45 | #endif |
46 | #if HAVE_NETINET_IN_H | |
627f6d02 | 47 | #include <netinet/in.h> |
627f6d02 | 48 | #endif |
db5edad5 | 49 | #if HAVE_ASSERT_H |
3265e68d | 50 | #include <assert.h> |
627f6d02 | 51 | #endif |
52 | ||
db5edad5 | 53 | #include "asn1.h" |
54 | ||
627f6d02 | 55 | #ifdef DEBUG |
56 | #define ERROR(string) printf("%s(%d): %s",__FILE__, __LINE__, string); | |
57 | #else | |
58 | #define ERROR(string) | |
59 | #endif | |
60 | ||
61 | ||
62 | /* | |
63 | * asn_parse_int - pulls a long out of an ASN int type. | |
64 | * On entry, datalength is input as the number of valid bytes following | |
65 | * "data". On exit, it is returned as the number of valid bytes | |
66 | * following the end of this object. | |
67 | * | |
68 | * Returns a pointer to the first byte past the end | |
69 | * of this object (i.e. the start of the next object). | |
70 | * Returns NULL on any error. | |
71 | */ | |
72 | u_char * | |
29cd62b7 | 73 | asn_parse_int( |
74 | u_char * data, /* IN - pointer to start of object */ | |
75 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
76 | u_char * type, /* OUT - asn type of object */ | |
77 | long *intp, /* IN/OUT - pointer to start of output buffer */ | |
78 | int intsize) | |
79 | { /* IN - size of output buffer */ | |
627f6d02 | 80 | /* |
81 | * ASN.1 integer ::= 0x02 asnlength byte {byte}* | |
82 | */ | |
83 | u_char *bufp = data; | |
da2d50d1 | 84 | u_long asn_length; |
85 | long value = 0; | |
627f6d02 | 86 | |
da2d50d1 | 87 | if (intsize != sizeof(long)) { |
627f6d02 | 88 | ERROR("not long"); |
89 | return NULL; | |
90 | } | |
91 | *type = *bufp++; | |
92 | bufp = asn_parse_length(bufp, &asn_length); | |
da2d50d1 | 93 | if (bufp == NULL) { |
627f6d02 | 94 | ERROR("bad length"); |
95 | return NULL; | |
96 | } | |
da2d50d1 | 97 | if (asn_length + (bufp - data) > *datalength) { |
627f6d02 | 98 | ERROR("overflow of message"); |
99 | return NULL; | |
100 | } | |
da2d50d1 | 101 | if (asn_length > intsize) { |
627f6d02 | 102 | ERROR("I don't support such large integers"); |
103 | return NULL; | |
104 | } | |
da2d50d1 | 105 | *datalength -= (int) asn_length + (bufp - data); |
627f6d02 | 106 | if (*bufp & 0x80) |
da2d50d1 | 107 | value = -1; /* integer is negative */ |
108 | while (asn_length--) | |
627f6d02 | 109 | value = (value << 8) | *bufp++; |
110 | *intp = value; | |
111 | return bufp; | |
112 | } | |
113 | ||
114 | ||
115 | /* | |
116 | * asn_parse_unsigned_int - pulls an unsigned long out of an ASN int type. | |
117 | * On entry, datalength is input as the number of valid bytes following | |
118 | * "data". On exit, it is returned as the number of valid bytes | |
119 | * following the end of this object. | |
120 | * | |
121 | * Returns a pointer to the first byte past the end | |
122 | * of this object (i.e. the start of the next object). | |
123 | * Returns NULL on any error. | |
124 | */ | |
125 | u_char * | |
29cd62b7 | 126 | asn_parse_unsigned_int( |
127 | u_char * data, /* IN - pointer to start of object */ | |
128 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
129 | u_char * type, /* OUT - asn type of object */ | |
130 | u_long * intp, /* IN/OUT - pointer to start of output buffer */ | |
131 | int intsize) | |
132 | { /* IN - size of output buffer */ | |
627f6d02 | 133 | /* |
134 | * ASN.1 integer ::= 0x02 asnlength byte {byte}* | |
135 | */ | |
136 | u_char *bufp = data; | |
da2d50d1 | 137 | u_long asn_length; |
627f6d02 | 138 | u_long value = 0; |
139 | ||
da2d50d1 | 140 | if (intsize != sizeof(long)) { |
627f6d02 | 141 | ERROR("not long"); |
142 | return NULL; | |
143 | } | |
144 | *type = *bufp++; | |
145 | bufp = asn_parse_length(bufp, &asn_length); | |
da2d50d1 | 146 | if (bufp == NULL) { |
627f6d02 | 147 | ERROR("bad length"); |
148 | return NULL; | |
149 | } | |
da2d50d1 | 150 | if (asn_length + (bufp - data) > *datalength) { |
627f6d02 | 151 | ERROR("overflow of message"); |
152 | return NULL; | |
153 | } | |
154 | if ((asn_length > (intsize + 1)) || | |
da2d50d1 | 155 | ((asn_length == intsize + 1) && *bufp != 0x00)) { |
627f6d02 | 156 | ERROR("I don't support such large integers"); |
157 | return NULL; | |
158 | } | |
da2d50d1 | 159 | *datalength -= (int) asn_length + (bufp - data); |
627f6d02 | 160 | if (*bufp & 0x80) |
da2d50d1 | 161 | value = -1; /* integer is negative */ |
162 | while (asn_length--) | |
627f6d02 | 163 | value = (value << 8) | *bufp++; |
164 | *intp = value; | |
165 | return bufp; | |
166 | } | |
167 | ||
168 | ||
169 | /* | |
170 | * asn_build_int - builds an ASN object containing an integer. | |
171 | * On entry, datalength is input as the number of valid bytes following | |
172 | * "data". On exit, it is returned as the number of valid bytes | |
173 | * following the end of this object. | |
174 | * | |
175 | * Returns a pointer to the first byte past the end | |
176 | * of this object (i.e. the start of the next object). | |
177 | * Returns NULL on any error. | |
178 | */ | |
179 | u_char * | |
29cd62b7 | 180 | asn_build_int( |
181 | u_char * data, /* IN - pointer to start of output buffer */ | |
182 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
183 | u_char type, /* IN - asn type of object */ | |
184 | long *intp, /* IN - pointer to start of long integer */ | |
185 | int intsize) | |
186 | { /* IN - size of *intp */ | |
627f6d02 | 187 | /* |
188 | * ASN.1 integer ::= 0x02 asnlength byte {byte}* | |
189 | */ | |
190 | ||
191 | long integer; | |
192 | u_long mask; | |
193 | ||
da2d50d1 | 194 | if (intsize != sizeof(long)) { |
627f6d02 | 195 | ERROR("not long"); |
196 | return NULL; | |
197 | } | |
198 | integer = *intp; | |
199 | /* | |
200 | * Truncate "unnecessary" bytes off of the most significant end of this | |
201 | * 2's complement integer. There should be no sequence of 9 | |
202 | * consecutive 1's or 0's at the most significant end of the | |
203 | * integer. | |
204 | */ | |
264ca1b0 | 205 | mask = 0x1FF << ((8 * (NUM32LEN - 1)) - 1); |
627f6d02 | 206 | /* mask is 0xFF800000 on a big-endian machine */ |
da2d50d1 | 207 | while ((((integer & mask) == 0) || ((integer & mask) == mask)) |
208 | && intsize > 1) { | |
627f6d02 | 209 | intsize--; |
210 | integer <<= 8; | |
211 | } | |
212 | data = asn_build_header(data, datalength, type, intsize); | |
213 | if (data == NULL) | |
214 | return NULL; | |
215 | if (*datalength < intsize) | |
216 | return NULL; | |
217 | *datalength -= intsize; | |
264ca1b0 | 218 | mask = 0xFF << (8 * (NUM32LEN - 1)); |
627f6d02 | 219 | /* mask is 0xFF000000 on a big-endian machine */ |
da2d50d1 | 220 | while (intsize--) { |
264ca1b0 | 221 | *data++ = (u_char) ((integer & mask) >> (8 * (NUM32LEN - 1))); |
627f6d02 | 222 | integer <<= 8; |
223 | } | |
224 | return data; | |
225 | } | |
226 | ||
227 | ||
228 | /* | |
229 | * asn_build_unsigned_int - builds an ASN object containing an integer. | |
230 | * On entry, datalength is input as the number of valid bytes following | |
231 | * "data". On exit, it is returned as the number of valid bytes | |
232 | * following the end of this object. | |
233 | * | |
234 | * Returns a pointer to the first byte past the end | |
235 | * of this object (i.e. the start of the next object). | |
236 | * Returns NULL on any error. | |
237 | */ | |
238 | u_char * | |
29cd62b7 | 239 | asn_build_unsigned_int( |
240 | u_char * data, /* IN - pointer to start of output buffer */ | |
241 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
242 | u_char type, /* IN - asn type of object */ | |
243 | u_long * intp, /* IN - pointer to start of long integer */ | |
244 | int intsize) | |
245 | { /* IN - size of *intp */ | |
627f6d02 | 246 | /* |
247 | * ASN.1 integer ::= 0x02 asnlength byte {byte}* | |
248 | */ | |
249 | ||
250 | u_long integer; | |
251 | u_long mask; | |
252 | int add_null_byte = 0; | |
253 | ||
da2d50d1 | 254 | if (intsize != sizeof(long)) { |
627f6d02 | 255 | ERROR("not long"); |
256 | return NULL; | |
257 | } | |
258 | integer = *intp; | |
264ca1b0 | 259 | mask = 0xFF << (8 * (NUM32LEN - 1)); |
627f6d02 | 260 | /* mask is 0xFF000000 on a big-endian machine */ |
264ca1b0 | 261 | if ((u_char) ((integer & mask) >> (8 * (NUM32LEN - 1))) & 0x80) { |
627f6d02 | 262 | /* if MSB is set */ |
263 | add_null_byte = 1; | |
264 | intsize++; | |
265 | } | |
266 | /* | |
267 | * Truncate "unnecessary" bytes off of the most significant end of this 2's complement integer. | |
268 | * There should be no sequence of 9 consecutive 1's or 0's at the most significant end of the | |
269 | * integer. | |
270 | */ | |
264ca1b0 | 271 | mask = 0x1FF << ((8 * (NUM32LEN - 1)) - 1); |
627f6d02 | 272 | /* mask is 0xFF800000 on a big-endian machine */ |
da2d50d1 | 273 | while ((((integer & mask) == 0) || ((integer & mask) == mask)) && intsize > 1) { |
627f6d02 | 274 | intsize--; |
275 | integer <<= 8; | |
276 | } | |
277 | data = asn_build_header(data, datalength, type, intsize); | |
278 | if (data == NULL) | |
279 | return NULL; | |
280 | if (*datalength < intsize) | |
281 | return NULL; | |
282 | *datalength -= intsize; | |
da2d50d1 | 283 | if (add_null_byte == 1) { |
627f6d02 | 284 | *data++ = '\0'; |
285 | intsize--; | |
286 | } | |
264ca1b0 | 287 | mask = 0xFF << (8 * (NUM32LEN - 1)); |
627f6d02 | 288 | /* mask is 0xFF000000 on a big-endian machine */ |
da2d50d1 | 289 | while (intsize--) { |
264ca1b0 | 290 | *data++ = (u_char) ((integer & mask) >> (8 * (NUM32LEN - 1))); |
627f6d02 | 291 | integer <<= 8; |
292 | } | |
293 | return data; | |
294 | } | |
295 | ||
296 | ||
297 | /* | |
298 | * asn_parse_string - pulls an octet string out of an ASN octet string type. | |
299 | * On entry, datalength is input as the number of valid bytes following | |
300 | * "data". On exit, it is returned as the number of valid bytes | |
301 | * following the beginning of the next object. | |
302 | * | |
303 | * "string" is filled with the octet string. | |
304 | * | |
305 | * Returns a pointer to the first byte past the end | |
306 | * of this object (i.e. the start of the next object). | |
307 | * Returns NULL on any error. | |
308 | */ | |
309 | u_char * | |
29cd62b7 | 310 | asn_parse_string( |
311 | u_char * data, /* IN - pointer to start of object */ | |
312 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
313 | u_char * type, /* OUT - asn type of object */ | |
314 | u_char * string, /* IN/OUT - pointer to start of output buffer */ | |
315 | int *strlength) | |
316 | { /* IN/OUT - size of output buffer */ | |
627f6d02 | 317 | /* |
318 | * ASN.1 octet string ::= primstring | cmpdstring | |
319 | * primstring ::= 0x04 asnlength byte {byte}* | |
320 | * cmpdstring ::= 0x24 asnlength string {string}* | |
321 | */ | |
322 | u_char *bufp = data; | |
da2d50d1 | 323 | u_long asn_length; |
627f6d02 | 324 | |
325 | *type = *bufp++; | |
326 | bufp = asn_parse_length(bufp, &asn_length); | |
327 | if (bufp == NULL) | |
328 | return NULL; | |
da2d50d1 | 329 | if (asn_length + (bufp - data) > *datalength) { |
627f6d02 | 330 | ERROR("overflow of message"); |
331 | return NULL; | |
332 | } | |
da2d50d1 | 333 | if (asn_length > *strlength) { |
627f6d02 | 334 | ERROR("I don't support such long strings"); |
335 | return NULL; | |
336 | } | |
29cd62b7 | 337 | xmemcpy(string, bufp, (int) asn_length); |
da2d50d1 | 338 | *strlength = (int) asn_length; |
339 | *datalength -= (int) asn_length + (bufp - data); | |
627f6d02 | 340 | return bufp + asn_length; |
341 | } | |
342 | ||
343 | ||
344 | /* | |
345 | * asn_build_string - Builds an ASN octet string object containing the input string. | |
346 | * On entry, datalength is input as the number of valid bytes following | |
347 | * "data". On exit, it is returned as the number of valid bytes | |
348 | * following the beginning of the next object. | |
349 | * | |
350 | * Returns a pointer to the first byte past the end | |
351 | * of this object (i.e. the start of the next object). | |
352 | * Returns NULL on any error. | |
353 | */ | |
354 | u_char * | |
29cd62b7 | 355 | asn_build_string( |
356 | u_char * data, /* IN - pointer to start of object */ | |
357 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
358 | u_char type, /* IN - ASN type of string */ | |
359 | u_char * string, /* IN - pointer to start of input buffer */ | |
360 | int strlength) | |
361 | { /* IN - size of input buffer */ | |
627f6d02 | 362 | /* |
363 | * ASN.1 octet string ::= primstring | cmpdstring | |
364 | * primstring ::= 0x04 asnlength byte {byte}* | |
365 | * cmpdstring ::= 0x24 asnlength string {string}* | |
366 | * This code will never send a compound string. | |
367 | */ | |
368 | data = asn_build_header(data, datalength, type, strlength); | |
369 | if (data == NULL) | |
370 | return NULL; | |
371 | if (*datalength < strlength) | |
372 | return NULL; | |
29cd62b7 | 373 | xmemcpy(data, string, strlength); |
627f6d02 | 374 | *datalength -= strlength; |
375 | return data + strlength; | |
376 | } | |
377 | ||
378 | ||
379 | /* | |
380 | * asn_parse_header - interprets the ID and length of the current object. | |
381 | * On entry, datalength is input as the number of valid bytes following | |
382 | * "data". On exit, it is returned as the number of valid bytes | |
383 | * in this object following the id and length. | |
384 | * | |
385 | * Returns a pointer to the first byte of the contents of this object. | |
386 | * Returns NULL on any error. | |
387 | */ | |
388 | u_char * | |
29cd62b7 | 389 | asn_parse_header( |
390 | u_char * data, /* IN - pointer to start of object */ | |
391 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
392 | u_char * type) | |
393 | { /* OUT - ASN type of object */ | |
627f6d02 | 394 | u_char *bufp = data; |
395 | int header_len; | |
396 | u_long asn_length; | |
397 | ||
398 | if (*datalength <= 0) { | |
399 | return NULL; | |
400 | } | |
627f6d02 | 401 | /* this only works on data types < 30, i.e. no extension octets */ |
da2d50d1 | 402 | if (IS_EXTENSION_ID(*bufp)) { |
627f6d02 | 403 | ERROR("can't process ID >= 30"); |
404 | return NULL; | |
405 | } | |
406 | *type = *bufp; | |
407 | bufp = asn_parse_length(bufp + 1, &asn_length); | |
408 | if (bufp == NULL) | |
409 | return NULL; | |
410 | header_len = bufp - data; | |
da2d50d1 | 411 | if (header_len + asn_length > *datalength) { |
627f6d02 | 412 | ERROR("asn length too long"); |
413 | return NULL; | |
414 | } | |
da2d50d1 | 415 | *datalength = (int) asn_length; |
627f6d02 | 416 | return bufp; |
417 | } | |
418 | ||
419 | /* | |
420 | * asn_build_header - builds an ASN header for an object with the ID and | |
421 | * length specified. | |
422 | * On entry, datalength is input as the number of valid bytes following | |
423 | * "data". On exit, it is returned as the number of valid bytes | |
424 | * in this object following the id and length. | |
425 | * | |
426 | * This only works on data types < 30, i.e. no extension octets. | |
427 | * The maximum length is 0xFFFF; | |
428 | * | |
429 | * Returns a pointer to the first byte of the contents of this object. | |
430 | * Returns NULL on any error. | |
431 | */ | |
432 | u_char * | |
29cd62b7 | 433 | asn_build_header( |
434 | u_char * data, /* IN - pointer to start of object */ | |
435 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
436 | u_char type, /* IN - ASN type of object */ | |
437 | int length) | |
438 | { /* IN - length of object */ | |
627f6d02 | 439 | if (*datalength < 1) |
440 | return NULL; | |
441 | *data++ = type; | |
442 | (*datalength)--; | |
443 | return asn_build_length(data, datalength, length); | |
da2d50d1 | 444 | |
627f6d02 | 445 | } |
446 | ||
447 | /* | |
448 | * asn_build_sequence - builds an ASN header for a sequence with the ID and | |
449 | * length specified. | |
450 | * On entry, datalength is input as the number of valid bytes following | |
451 | * "data". On exit, it is returned as the number of valid bytes | |
452 | * in this object following the id and length. | |
453 | * | |
454 | * This only works on data types < 30, i.e. no extension octets. | |
455 | * The maximum length is 0xFFFF; | |
456 | * | |
457 | * Returns a pointer to the first byte of the contents of this object. | |
458 | * Returns NULL on any error. | |
459 | */ | |
460 | u_char * | |
29cd62b7 | 461 | asn_build_sequence( |
462 | u_char * data, /* IN - pointer to start of object */ | |
463 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
464 | u_char type, /* IN - ASN type of object */ | |
465 | int length) | |
466 | { /* IN - length of object */ | |
be335c22 | 467 | assert(*datalength >= 0); |
627f6d02 | 468 | *datalength -= 4; |
da2d50d1 | 469 | if (*datalength < 0) { |
627f6d02 | 470 | *datalength += 4; /* fix up before punting */ |
471 | return NULL; | |
472 | } | |
473 | *data++ = type; | |
da2d50d1 | 474 | *data++ = (u_char) (0x02 | ASN_LONG_LEN); |
475 | *data++ = (u_char) ((length >> 8) & 0xFF); | |
476 | *data++ = (u_char) (length & 0xFF); | |
627f6d02 | 477 | return data; |
478 | } | |
479 | ||
480 | /* | |
481 | * asn_parse_length - interprets the length of the current object. | |
482 | * On exit, length contains the value of this length field. | |
483 | * | |
484 | * Returns a pointer to the first byte after this length | |
485 | * field (aka: the start of the data field). | |
486 | * Returns NULL on any error. | |
487 | */ | |
488 | u_char * | |
29cd62b7 | 489 | asn_parse_length( |
490 | u_char * data, /* IN - pointer to start of length field */ | |
491 | u_long * length) | |
492 | { /* OUT - value of length field */ | |
627f6d02 | 493 | u_char lengthbyte = *data; |
494 | ||
495 | *length = 0; | |
da2d50d1 | 496 | if (lengthbyte & ASN_LONG_LEN) { |
627f6d02 | 497 | lengthbyte &= ~ASN_LONG_LEN; /* turn MSb off */ |
da2d50d1 | 498 | if (lengthbyte == 0) { |
627f6d02 | 499 | ERROR("We don't support indefinite lengths"); |
500 | return NULL; | |
501 | } | |
da2d50d1 | 502 | if (lengthbyte > sizeof(long)) { |
627f6d02 | 503 | ERROR("we can't support data lengths that long"); |
504 | return NULL; | |
505 | } | |
29cd62b7 | 506 | xmemcpy(length, data + 1, (int) lengthbyte); |
627f6d02 | 507 | /* XXX: is this useable on a 64bit platform ? */ |
508 | *length = ntohl(*length); | |
da2d50d1 | 509 | *length >>= (8 * ((sizeof(*length)) - lengthbyte)); |
627f6d02 | 510 | return data + lengthbyte + 1; |
da2d50d1 | 511 | } else { /* short asnlength */ |
512 | *length = (long) lengthbyte; | |
627f6d02 | 513 | return data + 1; |
514 | } | |
515 | } | |
516 | ||
517 | u_char * | |
29cd62b7 | 518 | asn_build_length( |
519 | u_char * data, /* IN - pointer to start of object */ | |
520 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
521 | int length) | |
522 | { /* IN - length of object */ | |
da2d50d1 | 523 | u_char *start_data = data; |
627f6d02 | 524 | |
525 | /* no indefinite lengths sent */ | |
da2d50d1 | 526 | if (length < 0x80) { |
527 | if (*datalength < 1) { | |
627f6d02 | 528 | ERROR("build_length"); |
529 | return NULL; | |
da2d50d1 | 530 | } |
531 | *data++ = (u_char) length; | |
532 | } else if (length <= 0xFF) { | |
533 | if (*datalength < 2) { | |
627f6d02 | 534 | ERROR("build_length"); |
535 | return NULL; | |
da2d50d1 | 536 | } |
537 | *data++ = (u_char) (0x01 | ASN_LONG_LEN); | |
538 | *data++ = (u_char) length; | |
539 | } else { /* 0xFF < length <= 0xFFFF */ | |
540 | if (*datalength < 3) { | |
627f6d02 | 541 | ERROR("build_length"); |
542 | return NULL; | |
da2d50d1 | 543 | } |
544 | *data++ = (u_char) (0x02 | ASN_LONG_LEN); | |
545 | *data++ = (u_char) ((length >> 8) & 0xFF); | |
546 | *data++ = (u_char) (length & 0xFF); | |
627f6d02 | 547 | } |
548 | *datalength -= (data - start_data); | |
549 | return data; | |
550 | ||
551 | } | |
552 | ||
553 | /* | |
554 | * asn_parse_objid - pulls an object indentifier out of an ASN object identifier type. | |
555 | * On entry, datalength is input as the number of valid bytes following | |
556 | * "data". On exit, it is returned as the number of valid bytes | |
557 | * following the beginning of the next object. | |
558 | * | |
559 | * "objid" is filled with the object identifier. | |
560 | * | |
561 | * Returns a pointer to the first byte past the end | |
562 | * of this object (i.e. the start of the next object). | |
563 | * Returns NULL on any error. | |
564 | */ | |
565 | u_char * | |
29cd62b7 | 566 | asn_parse_objid( |
567 | u_char * data, /* IN - pointer to start of object */ | |
568 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
569 | u_char * type, /* OUT - ASN type of object */ | |
570 | oid * objid, /* IN/OUT - pointer to start of output buffer */ | |
571 | int *objidlength) | |
572 | { /* IN/OUT - number of sub-id's in objid */ | |
627f6d02 | 573 | /* |
574 | * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* | |
575 | * subidentifier ::= {leadingbyte}* lastbyte | |
576 | * leadingbyte ::= 1 7bitvalue | |
577 | * lastbyte ::= 0 7bitvalue | |
578 | */ | |
579 | u_char *bufp = data; | |
580 | oid *oidp = objid + 1; | |
581 | u_long subidentifier; | |
da2d50d1 | 582 | long length; |
583 | u_long asn_length; | |
627f6d02 | 584 | |
585 | *type = *bufp++; | |
586 | bufp = asn_parse_length(bufp, &asn_length); | |
587 | if (bufp == NULL) | |
588 | return NULL; | |
da2d50d1 | 589 | if (asn_length + (bufp - data) > *datalength) { |
627f6d02 | 590 | ERROR("overflow of message"); |
591 | return NULL; | |
592 | } | |
da2d50d1 | 593 | *datalength -= (int) asn_length + (bufp - data); |
627f6d02 | 594 | |
595 | /* Handle invalid object identifier encodings of the form 06 00 robustly */ | |
596 | if (asn_length == 0) | |
597 | objid[0] = objid[1] = 0; | |
598 | ||
599 | length = asn_length; | |
da2d50d1 | 600 | (*objidlength)--; /* account for expansion of first byte */ |
601 | while (length > 0 && (*objidlength)-- > 0) { | |
627f6d02 | 602 | subidentifier = 0; |
da2d50d1 | 603 | do { /* shift and add in low order 7 bits */ |
604 | subidentifier = (subidentifier << 7) + (*(u_char *) bufp & ~ASN_BIT8); | |
627f6d02 | 605 | length--; |
da2d50d1 | 606 | } while (*(u_char *) bufp++ & ASN_BIT8); /* last byte has high bit clear */ |
607 | if (subidentifier > (u_long) MAX_SUBID) { | |
627f6d02 | 608 | ERROR("subidentifier too long"); |
609 | return NULL; | |
610 | } | |
da2d50d1 | 611 | *oidp++ = (oid) subidentifier; |
627f6d02 | 612 | } |
613 | ||
614 | /* | |
615 | * The first two subidentifiers are encoded into the first component | |
616 | * with the value (X * 40) + Y, where: | |
da2d50d1 | 617 | * X is the value of the first subidentifier. |
627f6d02 | 618 | * Y is the value of the second subidentifier. |
619 | */ | |
da2d50d1 | 620 | subidentifier = (u_long) objid[1]; |
621 | if (subidentifier == 0x2B) { | |
627f6d02 | 622 | objid[0] = 1; |
623 | objid[1] = 3; | |
624 | } else { | |
da2d50d1 | 625 | objid[1] = (u_char) (subidentifier % 40); |
626 | objid[0] = (u_char) ((subidentifier - objid[1]) / 40); | |
627f6d02 | 627 | } |
628 | ||
da2d50d1 | 629 | *objidlength = (int) (oidp - objid); |
627f6d02 | 630 | return bufp; |
631 | } | |
632 | ||
633 | /* | |
634 | * asn_build_objid - Builds an ASN object identifier object containing the | |
635 | * input string. | |
636 | * On entry, datalength is input as the number of valid bytes following | |
637 | * "data". On exit, it is returned as the number of valid bytes | |
638 | * following the beginning of the next object. | |
639 | * | |
640 | * Returns a pointer to the first byte past the end | |
641 | * of this object (i.e. the start of the next object). | |
642 | * Returns NULL on any error. | |
643 | */ | |
644 | u_char * | |
29cd62b7 | 645 | asn_build_objid( |
646 | u_char * data, /* IN - pointer to start of object */ | |
647 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
648 | u_char type, /* IN - ASN type of object */ | |
649 | oid * objid, /* IN - pointer to start of input buffer */ | |
650 | int objidlength) | |
651 | { /* IN - number of sub-id's in objid */ | |
627f6d02 | 652 | /* |
653 | * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* | |
654 | * subidentifier ::= {leadingbyte}* lastbyte | |
655 | * leadingbyte ::= 1 7bitvalue | |
656 | * lastbyte ::= 0 7bitvalue | |
657 | */ | |
658 | u_char buf[MAX_OID_LEN]; | |
659 | u_char *bp = buf; | |
660 | oid *op = objid; | |
da2d50d1 | 661 | int asnlength; |
627f6d02 | 662 | u_long subid, mask, testmask; |
663 | int bits, testbits; | |
664 | ||
da2d50d1 | 665 | if (objidlength < 2) { |
627f6d02 | 666 | *bp++ = 0; |
667 | objidlength = 0; | |
668 | } else { | |
669 | *bp++ = op[1] + (op[0] * 40); | |
670 | objidlength -= 2; | |
671 | op += 2; | |
672 | } | |
673 | ||
da2d50d1 | 674 | while (objidlength-- > 0) { |
627f6d02 | 675 | subid = *op++; |
da2d50d1 | 676 | if (subid < 127) { /* off by one? */ |
627f6d02 | 677 | *bp++ = subid; |
678 | } else { | |
da2d50d1 | 679 | mask = 0x7F; /* handle subid == 0 case */ |
627f6d02 | 680 | bits = 0; |
681 | /* testmask *MUST* !!!! be of an unsigned type */ | |
da2d50d1 | 682 | for (testmask = 0x7F, testbits = 0; testmask != 0; |
683 | testmask <<= 7, testbits += 7) { | |
684 | if (subid & testmask) { /* if any bits set */ | |
627f6d02 | 685 | mask = testmask; |
686 | bits = testbits; | |
687 | } | |
688 | } | |
689 | /* mask can't be zero here */ | |
da2d50d1 | 690 | for (; mask != 0x7F; mask >>= 7, bits -= 7) { |
627f6d02 | 691 | /* fix a mask that got truncated above */ |
da2d50d1 | 692 | if (mask == 0x1E00000) |
627f6d02 | 693 | mask = 0xFE00000; |
da2d50d1 | 694 | *bp++ = (u_char) (((subid & mask) >> bits) | ASN_BIT8); |
627f6d02 | 695 | } |
da2d50d1 | 696 | *bp++ = (u_char) (subid & mask); |
627f6d02 | 697 | } |
698 | } | |
699 | asnlength = bp - buf; | |
700 | data = asn_build_header(data, datalength, type, asnlength); | |
701 | if (data == NULL) | |
702 | return NULL; | |
703 | if (*datalength < asnlength) | |
704 | return NULL; | |
29cd62b7 | 705 | xmemcpy(data, buf, asnlength); |
627f6d02 | 706 | *datalength -= asnlength; |
707 | return data + asnlength; | |
708 | } | |
709 | ||
710 | /* | |
711 | * asn_parse_null - Interprets an ASN null type. | |
712 | * On entry, datalength is input as the number of valid bytes following | |
713 | * "data". On exit, it is returned as the number of valid bytes | |
714 | * following the beginning of the next object. | |
715 | * | |
716 | * Returns a pointer to the first byte past the end | |
717 | * of this object (i.e. the start of the next object). | |
718 | * Returns NULL on any error. | |
719 | */ | |
720 | u_char * | |
29cd62b7 | 721 | asn_parse_null( |
722 | u_char * data, /* IN - pointer to start of object */ | |
723 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
724 | u_char * type) | |
725 | { /* OUT - ASN type of object */ | |
627f6d02 | 726 | /* |
727 | * ASN.1 null ::= 0x05 0x00 | |
728 | */ | |
da2d50d1 | 729 | u_char *bufp = data; |
730 | u_long asn_length; | |
627f6d02 | 731 | |
732 | *type = *bufp++; | |
733 | bufp = asn_parse_length(bufp, &asn_length); | |
734 | if (bufp == NULL) | |
735 | return NULL; | |
da2d50d1 | 736 | if (asn_length != 0) { |
627f6d02 | 737 | ERROR("Malformed NULL"); |
738 | return NULL; | |
739 | } | |
740 | *datalength -= (bufp - data); | |
741 | return bufp + asn_length; | |
742 | } | |
743 | ||
744 | ||
745 | /* | |
746 | * asn_build_null - Builds an ASN null object. | |
747 | * On entry, datalength is input as the number of valid bytes following | |
748 | * "data". On exit, it is returned as the number of valid bytes | |
749 | * following the beginning of the next object. | |
750 | * | |
751 | * Returns a pointer to the first byte past the end | |
752 | * of this object (i.e. the start of the next object). | |
753 | * Returns NULL on any error. | |
754 | */ | |
755 | u_char * | |
29cd62b7 | 756 | asn_build_null( |
757 | u_char * data, /* IN - pointer to start of object */ | |
758 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
759 | u_char type) | |
760 | { /* IN - ASN type of object */ | |
627f6d02 | 761 | /* |
762 | * ASN.1 null ::= 0x05 0x00 | |
763 | */ | |
764 | return asn_build_header(data, datalength, type, 0); | |
765 | } | |
766 | ||
767 | /* | |
768 | * asn_parse_bitstring - pulls a bitstring out of an ASN bitstring type. | |
769 | * On entry, datalength is input as the number of valid bytes following | |
770 | * "data". On exit, it is returned as the number of valid bytes | |
771 | * following the beginning of the next object. | |
772 | * | |
773 | * "string" is filled with the bit string. | |
774 | * | |
775 | * Returns a pointer to the first byte past the end | |
776 | * of this object (i.e. the start of the next object). | |
777 | * Returns NULL on any error. | |
778 | */ | |
779 | u_char * | |
29cd62b7 | 780 | asn_parse_bitstring( |
781 | u_char * data, /* IN - pointer to start of object */ | |
782 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
783 | u_char * type, /* OUT - asn type of object */ | |
784 | u_char * string, /* IN/OUT - pointer to start of output buffer */ | |
785 | int *strlength) | |
786 | { /* IN/OUT - size of output buffer */ | |
627f6d02 | 787 | /* |
788 | * bitstring ::= 0x03 asnlength unused {byte}* | |
789 | */ | |
790 | u_char *bufp = data; | |
da2d50d1 | 791 | u_long asn_length; |
627f6d02 | 792 | |
793 | *type = *bufp++; | |
794 | bufp = asn_parse_length(bufp, &asn_length); | |
795 | if (bufp == NULL) | |
796 | return NULL; | |
da2d50d1 | 797 | if (asn_length + (bufp - data) > *datalength) { |
627f6d02 | 798 | ERROR("overflow of message"); |
799 | return NULL; | |
800 | } | |
da2d50d1 | 801 | if (asn_length > *strlength) { |
627f6d02 | 802 | ERROR("I don't support such long bitstrings"); |
803 | return NULL; | |
804 | } | |
da2d50d1 | 805 | if (asn_length < 1) { |
627f6d02 | 806 | ERROR("Invalid bitstring"); |
807 | return NULL; | |
808 | } | |
da2d50d1 | 809 | if ( /** *bufp < 0 || **/ *bufp > 7) { |
627f6d02 | 810 | ERROR("Invalid bitstring"); |
811 | return NULL; | |
812 | } | |
29cd62b7 | 813 | xmemcpy(string, bufp, (int) asn_length); |
da2d50d1 | 814 | *strlength = (int) asn_length; |
815 | *datalength -= (int) asn_length + (bufp - data); | |
627f6d02 | 816 | return bufp + asn_length; |
817 | } | |
818 | ||
819 | ||
820 | /* | |
821 | * asn_build_bitstring - Builds an ASN bit string object containing the | |
822 | * input string. | |
823 | * On entry, datalength is input as the number of valid bytes following | |
824 | * "data". On exit, it is returned as the number of valid bytes | |
825 | * following the beginning of the next object. | |
826 | * | |
827 | * Returns a pointer to the first byte past the end | |
828 | * of this object (i.e. the start of the next object). | |
829 | * Returns NULL on any error. | |
830 | */ | |
831 | u_char * | |
29cd62b7 | 832 | asn_build_bitstring( |
833 | u_char * data, /* IN - pointer to start of object */ | |
834 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
835 | u_char type, /* IN - ASN type of string */ | |
836 | u_char * string, /* IN - pointer to start of input buffer */ | |
837 | int strlength) | |
838 | { /* IN - size of input buffer */ | |
627f6d02 | 839 | /* |
840 | * ASN.1 bit string ::= 0x03 asnlength unused {byte}* | |
841 | */ | |
da2d50d1 | 842 | if (strlength < 1 || /** *string < 0 || **/ *string > 7) { |
627f6d02 | 843 | ERROR("Building invalid bitstring"); |
844 | return NULL; | |
845 | } | |
846 | data = asn_build_header(data, datalength, type, strlength); | |
847 | if (data == NULL) | |
848 | return NULL; | |
849 | if (*datalength < strlength) | |
850 | return NULL; | |
29cd62b7 | 851 | xmemcpy(data, string, strlength); |
627f6d02 | 852 | *datalength -= strlength; |
853 | return data + strlength; | |
854 | } | |
855 | ||
856 | ||
857 | /* | |
858 | * asn_parse_unsigned_int64 - pulls a 64 bit unsigned long out of an ASN int | |
859 | * type. | |
860 | * On entry, datalength is input as the number of valid bytes following | |
861 | * "data". On exit, it is returned as the number of valid bytes | |
862 | * following the end of this object. | |
863 | * | |
864 | * Returns a pointer to the first byte past the end | |
865 | * of this object (i.e. the start of the next object). | |
866 | * Returns NULL on any error. | |
867 | */ | |
868 | u_char * | |
29cd62b7 | 869 | asn_parse_unsigned_int64( |
870 | u_char * data, /* IN - pointer to start of object */ | |
871 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
872 | u_char * type, /* OUT - asn type of object */ | |
873 | struct counter64 * cp, /* IN/OUT -pointer to counter struct */ | |
874 | int countersize) | |
875 | { /* IN - size of output buffer */ | |
627f6d02 | 876 | /* |
877 | * ASN.1 integer ::= 0x02 asnlength byte {byte}* | |
878 | */ | |
879 | u_char *bufp = data; | |
da2d50d1 | 880 | u_long asn_length; |
627f6d02 | 881 | u_long low = 0, high = 0; |
882 | int intsize = 4; | |
da2d50d1 | 883 | |
884 | if (countersize != sizeof(struct counter64)) { | |
627f6d02 | 885 | ERROR("not counter64 size"); |
886 | return NULL; | |
887 | } | |
888 | *type = *bufp++; | |
889 | bufp = asn_parse_length(bufp, &asn_length); | |
da2d50d1 | 890 | if (bufp == NULL) { |
627f6d02 | 891 | ERROR("bad length"); |
892 | return NULL; | |
893 | } | |
da2d50d1 | 894 | if (asn_length + (bufp - data) > *datalength) { |
627f6d02 | 895 | ERROR("overflow of message"); |
896 | return NULL; | |
897 | } | |
898 | if ((asn_length > (intsize * 2 + 1)) || | |
da2d50d1 | 899 | ((asn_length == (intsize * 2) + 1) && *bufp != 0x00)) { |
627f6d02 | 900 | ERROR("I don't support such large integers"); |
901 | return NULL; | |
902 | } | |
da2d50d1 | 903 | *datalength -= (int) asn_length + (bufp - data); |
904 | if (*bufp & 0x80) { | |
905 | low = -1; /* integer is negative */ | |
627f6d02 | 906 | high = -1; |
907 | } | |
da2d50d1 | 908 | while (asn_length--) { |
627f6d02 | 909 | high = (high << 8) | ((low & 0xFF000000) >> 24); |
910 | low = (low << 8) | *bufp++; | |
911 | } | |
912 | cp->low = low; | |
913 | cp->high = high; | |
914 | return bufp; | |
915 | } | |
916 | ||
917 | ||
918 | /* | |
919 | * asn_build_unsigned_int64 - builds an ASN object containing a 64 bit integer. | |
920 | * On entry, datalength is input as the number of valid bytes following | |
921 | * "data". On exit, it is returned as the number of valid bytes | |
922 | * following the end of this object. | |
923 | * | |
924 | * Returns a pointer to the first byte past the end | |
925 | * of this object (i.e. the start of the next object). | |
926 | * Returns NULL on any error. | |
927 | */ | |
928 | u_char * | |
29cd62b7 | 929 | asn_build_unsigned_int64( |
930 | u_char * data, /* IN - pointer to start of output buffer */ | |
931 | int *datalength, /* IN/OUT - number of valid bytes left in buffer */ | |
932 | u_char type, /* IN - asn type of object */ | |
933 | struct counter64 * cp, /* IN - pointer to counter struct */ | |
934 | int countersize) | |
935 | { /* IN - size of *intp */ | |
627f6d02 | 936 | /* |
937 | * ASN.1 integer ::= 0x02 asnlength byte {byte}* | |
938 | */ | |
939 | ||
264ca1b0 | 940 | u_num32 low, high; |
941 | u_num32 mask, mask2; | |
627f6d02 | 942 | int add_null_byte = 0; |
943 | int intsize; | |
944 | ||
da2d50d1 | 945 | if (countersize != sizeof(struct counter64)) { |
627f6d02 | 946 | ERROR("not counter64 size"); |
947 | return NULL; | |
948 | } | |
949 | intsize = 8; | |
950 | low = cp->low; | |
951 | high = cp->high; | |
264ca1b0 | 952 | mask = 0xFF << (8 * (NUM32LEN - 1)); |
627f6d02 | 953 | /* mask is 0xFF000000 on a big-endian machine */ |
264ca1b0 | 954 | if ((u_char) ((high & mask) >> (8 * (NUM32LEN - 1))) & 0x80) { |
627f6d02 | 955 | /* if MSB is set */ |
956 | add_null_byte = 1; | |
957 | intsize++; | |
958 | } | |
959 | /* | |
960 | * Truncate "unnecessary" bytes off of the most significant end of this 2's | |
961 | * complement integer. | |
962 | * There should be no sequence of 9 consecutive 1's or 0's at the most | |
963 | * significant end of the integer. | |
964 | */ | |
264ca1b0 | 965 | mask2 = 0x1FF << ((8 * (NUM32LEN - 1)) - 1); |
627f6d02 | 966 | /* mask2 is 0xFF800000 on a big-endian machine */ |
da2d50d1 | 967 | while ((((high & mask2) == 0) || ((high & mask2) == mask2)) |
968 | && intsize > 1) { | |
627f6d02 | 969 | intsize--; |
970 | high = (high << 8) | |
264ca1b0 | 971 | | ((low & mask) >> (8 * (NUM32LEN - 1))); |
627f6d02 | 972 | low <<= 8; |
973 | } | |
974 | data = asn_build_header(data, datalength, type, intsize); | |
975 | if (data == NULL) | |
976 | return NULL; | |
977 | if (*datalength < intsize) | |
978 | return NULL; | |
979 | *datalength -= intsize; | |
da2d50d1 | 980 | if (add_null_byte == 1) { |
627f6d02 | 981 | *data++ = '\0'; |
982 | intsize--; | |
983 | } | |
da2d50d1 | 984 | while (intsize--) { |
264ca1b0 | 985 | *data++ = (u_char) ((high & mask) >> (8 * (NUM32LEN - 1))); |
627f6d02 | 986 | high = (high << 8) |
264ca1b0 | 987 | | ((low & mask) >> (8 * (NUM32LEN - 1))); |
627f6d02 | 988 | low <<= 8; |
da2d50d1 | 989 | |
627f6d02 | 990 | } |
991 | return data; | |
992 | } |