diff options
| author | doug <> | 2015-06-15 07:35:49 +0000 |
|---|---|---|
| committer | doug <> | 2015-06-15 07:35:49 +0000 |
| commit | cc28fbde97d652fc8df85aef456db3f43ee7141f (patch) | |
| tree | d870273c7da5901692fed25ce0bccf96a3a77d50 /src | |
| parent | b5c7960f11d2b64faae5154f9709f34d00462124 (diff) | |
| download | openbsd-cc28fbde97d652fc8df85aef456db3f43ee7141f.tar.gz openbsd-cc28fbde97d652fc8df85aef456db3f43ee7141f.tar.bz2 openbsd-cc28fbde97d652fc8df85aef456db3f43ee7141f.zip | |
Make CBS_get_any_asn1_element() more compliant with DER encoding.
CBS_get_any_asn1_element violates DER encoding by allowing indefinite
form. All callers except bs_ber.c expect DER encoding. The callers
must check to see if it was indefinite or not.
Rather than exposing all callers to this behavior,
cbs_get_any_asn1_element_internal() allows specifying whether you want to
allow the normally forbidden indefinite form. This is used by
CBS_get_any_asn1_element() for strict DER encoding and by a new static
function in bs_ber.c for the relaxed version.
While I was here, I added comments to differentiate between ASN.1
restrictions and CBS limitations.
ok miod@
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/libssl/bs_ber.c | 26 | ||||
| -rw-r--r-- | src/lib/libssl/bs_cbs.c | 68 | ||||
| -rw-r--r-- | src/lib/libssl/bytestring.h | 20 | ||||
| -rw-r--r-- | src/lib/libssl/src/ssl/bs_ber.c | 26 | ||||
| -rw-r--r-- | src/lib/libssl/src/ssl/bs_cbs.c | 68 | ||||
| -rw-r--r-- | src/lib/libssl/src/ssl/bytestring.h | 20 |
6 files changed, 172 insertions, 56 deletions
diff --git a/src/lib/libssl/bs_ber.c b/src/lib/libssl/bs_ber.c index 2ec91fc800..3d39def111 100644 --- a/src/lib/libssl/bs_ber.c +++ b/src/lib/libssl/bs_ber.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: bs_ber.c,v 1.4 2015/04/29 02:11:09 doug Exp $ */ | 1 | /* $OpenBSD: bs_ber.c,v 1.5 2015/06/15 07:35:49 doug Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2014, Google Inc. | 3 | * Copyright (c) 2014, Google Inc. |
| 4 | * | 4 | * |
| @@ -27,6 +27,15 @@ | |||
| 27 | */ | 27 | */ |
| 28 | static const unsigned kMaxDepth = 2048; | 28 | static const unsigned kMaxDepth = 2048; |
| 29 | 29 | ||
| 30 | /* Non-strict version that allows a relaxed DER with indefinite form. */ | ||
| 31 | static int | ||
| 32 | cbs_nonstrict_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, | ||
| 33 | size_t *out_header_len) | ||
| 34 | { | ||
| 35 | return cbs_get_any_asn1_element_internal(cbs, out, | ||
| 36 | out_tag, out_header_len, 0); | ||
| 37 | } | ||
| 38 | |||
| 30 | /* | 39 | /* |
| 31 | * cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found| | 40 | * cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found| |
| 32 | * depending on whether an indefinite length element was found. The value of | 41 | * depending on whether an indefinite length element was found. The value of |
| @@ -49,10 +58,11 @@ cbs_find_ber(CBS *orig_in, char *ber_found, unsigned depth) | |||
| 49 | unsigned tag; | 58 | unsigned tag; |
| 50 | size_t header_len; | 59 | size_t header_len; |
| 51 | 60 | ||
| 52 | if (!CBS_get_any_asn1_element(&in, &contents, &tag, | 61 | if (!cbs_nonstrict_get_any_asn1_element(&in, &contents, &tag, |
| 53 | &header_len)) | 62 | &header_len)) |
| 54 | return 0; | 63 | return 0; |
| 55 | 64 | ||
| 65 | /* Indefinite form not allowed by DER. */ | ||
| 56 | if (CBS_len(&contents) == header_len && header_len > 0 && | 66 | if (CBS_len(&contents) == header_len && header_len > 0 && |
| 57 | CBS_data(&contents)[header_len - 1] == 0x80) { | 67 | CBS_data(&contents)[header_len - 1] == 0x80) { |
| 58 | *ber_found = 1; | 68 | *ber_found = 1; |
| @@ -84,7 +94,8 @@ is_primitive_type(unsigned tag) | |||
| 84 | 94 | ||
| 85 | /* | 95 | /* |
| 86 | * is_eoc returns true if |header_len| and |contents|, as returned by | 96 | * is_eoc returns true if |header_len| and |contents|, as returned by |
| 87 | * |CBS_get_any_asn1_element|, indicate an "end of contents" (EOC) value. | 97 | * |cbs_nonstrict_get_any_asn1_element|, indicate an "end of contents" (EOC) |
| 98 | * value. | ||
| 88 | */ | 99 | */ |
| 89 | static char | 100 | static char |
| 90 | is_eoc(size_t header_len, CBS *contents) | 101 | is_eoc(size_t header_len, CBS *contents) |
| @@ -113,7 +124,8 @@ cbs_convert_ber(CBS *in, CBB *out, char squash_header, char looking_for_eoc, | |||
| 113 | size_t header_len; | 124 | size_t header_len; |
| 114 | CBB *out_contents, out_contents_storage; | 125 | CBB *out_contents, out_contents_storage; |
| 115 | 126 | ||
| 116 | if (!CBS_get_any_asn1_element(in, &contents, &tag, &header_len)) | 127 | if (!cbs_nonstrict_get_any_asn1_element(in, &contents, &tag, |
| 128 | &header_len)) | ||
| 117 | return 0; | 129 | return 0; |
| 118 | 130 | ||
| 119 | out_contents = out; | 131 | out_contents = out; |
| @@ -156,9 +168,9 @@ cbs_convert_ber(CBS *in, CBB *out, char squash_header, char looking_for_eoc, | |||
| 156 | 168 | ||
| 157 | CBS_init(&in_copy, CBS_data(in), | 169 | CBS_init(&in_copy, CBS_data(in), |
| 158 | CBS_len(in)); | 170 | CBS_len(in)); |
| 159 | if (!CBS_get_any_asn1_element(&in_copy, | 171 | if (!cbs_nonstrict_get_any_asn1_element( |
| 160 | &inner_contents, &inner_tag, | 172 | &in_copy, &inner_contents, |
| 161 | &inner_header_len)) | 173 | &inner_tag, &inner_header_len)) |
| 162 | return 0; | 174 | return 0; |
| 163 | 175 | ||
| 164 | if (CBS_len(&inner_contents) > | 176 | if (CBS_len(&inner_contents) > |
diff --git a/src/lib/libssl/bs_cbs.c b/src/lib/libssl/bs_cbs.c index c37f81dd60..ba38303c18 100644 --- a/src/lib/libssl/bs_cbs.c +++ b/src/lib/libssl/bs_cbs.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: bs_cbs.c,v 1.8 2015/06/13 08:46:00 doug Exp $ */ | 1 | /* $OpenBSD: bs_cbs.c,v 1.9 2015/06/15 07:35:49 doug Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2014, Google Inc. | 3 | * Copyright (c) 2014, Google Inc. |
| 4 | * | 4 | * |
| @@ -205,24 +205,45 @@ int | |||
| 205 | CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, | 205 | CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, |
| 206 | size_t *out_header_len) | 206 | size_t *out_header_len) |
| 207 | { | 207 | { |
| 208 | return cbs_get_any_asn1_element_internal(cbs, out, out_tag, | ||
| 209 | out_header_len, 1); | ||
| 210 | } | ||
| 211 | |||
| 212 | /* | ||
| 213 | * Review X.690 for details on ASN.1 DER encoding. | ||
| 214 | * | ||
| 215 | * If non-strict mode is enabled, then DER rules are relaxed | ||
| 216 | * for indefinite constructs (violates DER but a little closer to BER). | ||
| 217 | * Non-strict mode should only be used by bs_ber.c | ||
| 218 | * | ||
| 219 | * Sections 8, 10 and 11 for DER encoding | ||
| 220 | */ | ||
| 221 | int | ||
| 222 | cbs_get_any_asn1_element_internal(CBS *cbs, CBS *out, unsigned *out_tag, | ||
| 223 | size_t *out_header_len, int strict) | ||
| 224 | { | ||
| 208 | uint8_t tag, length_byte; | 225 | uint8_t tag, length_byte; |
| 209 | CBS header = *cbs; | 226 | CBS header = *cbs; |
| 210 | CBS throwaway; | 227 | CBS throwaway; |
| 228 | size_t len; | ||
| 211 | 229 | ||
| 212 | if (out == NULL) | 230 | if (out == NULL) |
| 213 | out = &throwaway; | 231 | out = &throwaway; |
| 214 | 232 | ||
| 233 | /* | ||
| 234 | * Get identifier octet and length octet. Only 1 octet for each | ||
| 235 | * is a CBS limitation. | ||
| 236 | */ | ||
| 215 | if (!CBS_get_u8(&header, &tag) || !CBS_get_u8(&header, &length_byte)) | 237 | if (!CBS_get_u8(&header, &tag) || !CBS_get_u8(&header, &length_byte)) |
| 216 | return 0; | 238 | return 0; |
| 217 | 239 | ||
| 240 | /* CBS limitation: long form tags are not supported. */ | ||
| 218 | if ((tag & 0x1f) == 0x1f) | 241 | if ((tag & 0x1f) == 0x1f) |
| 219 | /* Long form tags are not supported. */ | ||
| 220 | return 0; | 242 | return 0; |
| 221 | 243 | ||
| 222 | if (out_tag != NULL) | 244 | if (out_tag != NULL) |
| 223 | *out_tag = tag; | 245 | *out_tag = tag; |
| 224 | 246 | ||
| 225 | size_t len; | ||
| 226 | if ((length_byte & 0x80) == 0) { | 247 | if ((length_byte & 0x80) == 0) { |
| 227 | /* Short form length. */ | 248 | /* Short form length. */ |
| 228 | len = ((size_t) length_byte) + 2; | 249 | len = ((size_t) length_byte) + 2; |
| @@ -234,21 +255,40 @@ CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, | |||
| 234 | const size_t num_bytes = length_byte & 0x7f; | 255 | const size_t num_bytes = length_byte & 0x7f; |
| 235 | uint32_t len32; | 256 | uint32_t len32; |
| 236 | 257 | ||
| 237 | if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) { | 258 | /* ASN.1 reserved value for future extensions */ |
| 238 | /* indefinite length */ | 259 | if (num_bytes == 0x7f) |
| 239 | if (out_header_len != NULL) | 260 | return 0; |
| 240 | *out_header_len = 2; | 261 | |
| 241 | return CBS_get_bytes(cbs, out, 2); | 262 | /* Handle indefinite form length */ |
| 263 | if (num_bytes == 0) { | ||
| 264 | /* DER encoding doesn't allow for indefinite form. */ | ||
| 265 | if (strict) { | ||
| 266 | return 0; | ||
| 267 | |||
| 268 | } else { | ||
| 269 | if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && | ||
| 270 | num_bytes == 0) { | ||
| 271 | /* indefinite length */ | ||
| 272 | if (out_header_len != NULL) | ||
| 273 | *out_header_len = 2; | ||
| 274 | return CBS_get_bytes(cbs, out, 2); | ||
| 275 | } else { | ||
| 276 | /* Primitive cannot use indefinite. */ | ||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | } | ||
| 242 | } | 280 | } |
| 243 | 281 | ||
| 244 | if (num_bytes == 0 || num_bytes > 4) | 282 | /* CBS limitation. */ |
| 283 | if (num_bytes > 4) | ||
| 245 | return 0; | 284 | return 0; |
| 246 | 285 | ||
| 247 | if (!cbs_get_u(&header, &len32, num_bytes)) | 286 | if (!cbs_get_u(&header, &len32, num_bytes)) |
| 248 | return 0; | 287 | return 0; |
| 249 | 288 | ||
| 289 | /* DER has a minimum length octet requirements. */ | ||
| 250 | if (len32 < 128) | 290 | if (len32 < 128) |
| 251 | /* Length should have used short-form encoding. */ | 291 | /* Should have used short form instead */ |
| 252 | return 0; | 292 | return 0; |
| 253 | 293 | ||
| 254 | if ((len32 >> ((num_bytes - 1) * 8)) == 0) | 294 | if ((len32 >> ((num_bytes - 1) * 8)) == 0) |
| @@ -279,13 +319,7 @@ cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value, int skip_header) | |||
| 279 | out = &throwaway; | 319 | out = &throwaway; |
| 280 | 320 | ||
| 281 | if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) || | 321 | if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) || |
| 282 | tag != tag_value || (header_len > 0 && | 322 | tag != tag_value) |
| 283 | /* | ||
| 284 | * This ensures that the tag is either zero length or | ||
| 285 | * indefinite-length. | ||
| 286 | */ | ||
| 287 | CBS_len(out) == header_len && | ||
| 288 | CBS_data(out)[header_len - 1] == 0x80)) | ||
| 289 | return 0; | 323 | return 0; |
| 290 | 324 | ||
| 291 | if (skip_header && !CBS_skip(out, header_len)) { | 325 | if (skip_header && !CBS_skip(out, header_len)) { |
diff --git a/src/lib/libssl/bytestring.h b/src/lib/libssl/bytestring.h index b98c930da5..d66ab65b91 100644 --- a/src/lib/libssl/bytestring.h +++ b/src/lib/libssl/bytestring.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: bytestring.h,v 1.6 2015/06/13 09:02:45 doug Exp $ */ | 1 | /* $OpenBSD: bytestring.h,v 1.7 2015/06/15 07:35:49 doug Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2014, Google Inc. | 3 | * Copyright (c) 2014, Google Inc. |
| 4 | * | 4 | * |
| @@ -223,9 +223,8 @@ int CBS_peek_asn1_tag(const CBS *cbs, unsigned tag_value); | |||
| 223 | /* | 223 | /* |
| 224 | * CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from | 224 | * CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from |
| 225 | * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to | 225 | * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to |
| 226 | * the tag number and |*out_header_len| to the length of the ASN.1 header. If | 226 | * the tag number and |*out_header_len| to the length of the ASN.1 header. |
| 227 | * the element has indefinite length then |*out| will only contain the | 227 | * Each of |out|, |out_tag|, and |out_header_len| may be NULL to ignore |
| 228 | * header. Each of |out|, |out_tag|, and |out_header_len| may be NULL to ignore | ||
| 229 | * the value. | 228 | * the value. |
| 230 | * | 229 | * |
| 231 | * Tag numbers greater than 30 are not supported (i.e. short form only). | 230 | * Tag numbers greater than 30 are not supported (i.e. short form only). |
| @@ -452,6 +451,19 @@ int CBB_add_asn1_uint64(CBB *cbb, uint64_t value); | |||
| 452 | 451 | ||
| 453 | #ifdef LIBRESSL_INTERNAL | 452 | #ifdef LIBRESSL_INTERNAL |
| 454 | /* | 453 | /* |
| 454 | * CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from | ||
| 455 | * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to | ||
| 456 | * the tag number and |*out_header_len| to the length of the ASN.1 header. If | ||
| 457 | * strict mode is disabled and the element has indefinite length then |*out| | ||
| 458 | * will only contain the header. Each of |out|, |out_tag|, and | ||
| 459 | * |out_header_len| may be NULL to ignore the value. | ||
| 460 | * | ||
| 461 | * Tag numbers greater than 30 are not supported (i.e. short form only). | ||
| 462 | */ | ||
| 463 | int cbs_get_any_asn1_element_internal(CBS *cbs, CBS *out, unsigned *out_tag, | ||
| 464 | size_t *out_header_len, int strict); | ||
| 465 | |||
| 466 | /* | ||
| 455 | * CBS_asn1_ber_to_der reads an ASN.1 structure from |in|. If it finds | 467 | * CBS_asn1_ber_to_der reads an ASN.1 structure from |in|. If it finds |
| 456 | * indefinite-length elements then it attempts to convert the BER data to DER | 468 | * indefinite-length elements then it attempts to convert the BER data to DER |
| 457 | * and sets |*out| and |*out_length| to describe a malloced buffer containing | 469 | * and sets |*out| and |*out_length| to describe a malloced buffer containing |
diff --git a/src/lib/libssl/src/ssl/bs_ber.c b/src/lib/libssl/src/ssl/bs_ber.c index 2ec91fc800..3d39def111 100644 --- a/src/lib/libssl/src/ssl/bs_ber.c +++ b/src/lib/libssl/src/ssl/bs_ber.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: bs_ber.c,v 1.4 2015/04/29 02:11:09 doug Exp $ */ | 1 | /* $OpenBSD: bs_ber.c,v 1.5 2015/06/15 07:35:49 doug Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2014, Google Inc. | 3 | * Copyright (c) 2014, Google Inc. |
| 4 | * | 4 | * |
| @@ -27,6 +27,15 @@ | |||
| 27 | */ | 27 | */ |
| 28 | static const unsigned kMaxDepth = 2048; | 28 | static const unsigned kMaxDepth = 2048; |
| 29 | 29 | ||
| 30 | /* Non-strict version that allows a relaxed DER with indefinite form. */ | ||
| 31 | static int | ||
| 32 | cbs_nonstrict_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, | ||
| 33 | size_t *out_header_len) | ||
| 34 | { | ||
| 35 | return cbs_get_any_asn1_element_internal(cbs, out, | ||
| 36 | out_tag, out_header_len, 0); | ||
| 37 | } | ||
| 38 | |||
| 30 | /* | 39 | /* |
| 31 | * cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found| | 40 | * cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found| |
| 32 | * depending on whether an indefinite length element was found. The value of | 41 | * depending on whether an indefinite length element was found. The value of |
| @@ -49,10 +58,11 @@ cbs_find_ber(CBS *orig_in, char *ber_found, unsigned depth) | |||
| 49 | unsigned tag; | 58 | unsigned tag; |
| 50 | size_t header_len; | 59 | size_t header_len; |
| 51 | 60 | ||
| 52 | if (!CBS_get_any_asn1_element(&in, &contents, &tag, | 61 | if (!cbs_nonstrict_get_any_asn1_element(&in, &contents, &tag, |
| 53 | &header_len)) | 62 | &header_len)) |
| 54 | return 0; | 63 | return 0; |
| 55 | 64 | ||
| 65 | /* Indefinite form not allowed by DER. */ | ||
| 56 | if (CBS_len(&contents) == header_len && header_len > 0 && | 66 | if (CBS_len(&contents) == header_len && header_len > 0 && |
| 57 | CBS_data(&contents)[header_len - 1] == 0x80) { | 67 | CBS_data(&contents)[header_len - 1] == 0x80) { |
| 58 | *ber_found = 1; | 68 | *ber_found = 1; |
| @@ -84,7 +94,8 @@ is_primitive_type(unsigned tag) | |||
| 84 | 94 | ||
| 85 | /* | 95 | /* |
| 86 | * is_eoc returns true if |header_len| and |contents|, as returned by | 96 | * is_eoc returns true if |header_len| and |contents|, as returned by |
| 87 | * |CBS_get_any_asn1_element|, indicate an "end of contents" (EOC) value. | 97 | * |cbs_nonstrict_get_any_asn1_element|, indicate an "end of contents" (EOC) |
| 98 | * value. | ||
| 88 | */ | 99 | */ |
| 89 | static char | 100 | static char |
| 90 | is_eoc(size_t header_len, CBS *contents) | 101 | is_eoc(size_t header_len, CBS *contents) |
| @@ -113,7 +124,8 @@ cbs_convert_ber(CBS *in, CBB *out, char squash_header, char looking_for_eoc, | |||
| 113 | size_t header_len; | 124 | size_t header_len; |
| 114 | CBB *out_contents, out_contents_storage; | 125 | CBB *out_contents, out_contents_storage; |
| 115 | 126 | ||
| 116 | if (!CBS_get_any_asn1_element(in, &contents, &tag, &header_len)) | 127 | if (!cbs_nonstrict_get_any_asn1_element(in, &contents, &tag, |
| 128 | &header_len)) | ||
| 117 | return 0; | 129 | return 0; |
| 118 | 130 | ||
| 119 | out_contents = out; | 131 | out_contents = out; |
| @@ -156,9 +168,9 @@ cbs_convert_ber(CBS *in, CBB *out, char squash_header, char looking_for_eoc, | |||
| 156 | 168 | ||
| 157 | CBS_init(&in_copy, CBS_data(in), | 169 | CBS_init(&in_copy, CBS_data(in), |
| 158 | CBS_len(in)); | 170 | CBS_len(in)); |
| 159 | if (!CBS_get_any_asn1_element(&in_copy, | 171 | if (!cbs_nonstrict_get_any_asn1_element( |
| 160 | &inner_contents, &inner_tag, | 172 | &in_copy, &inner_contents, |
| 161 | &inner_header_len)) | 173 | &inner_tag, &inner_header_len)) |
| 162 | return 0; | 174 | return 0; |
| 163 | 175 | ||
| 164 | if (CBS_len(&inner_contents) > | 176 | if (CBS_len(&inner_contents) > |
diff --git a/src/lib/libssl/src/ssl/bs_cbs.c b/src/lib/libssl/src/ssl/bs_cbs.c index c37f81dd60..ba38303c18 100644 --- a/src/lib/libssl/src/ssl/bs_cbs.c +++ b/src/lib/libssl/src/ssl/bs_cbs.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: bs_cbs.c,v 1.8 2015/06/13 08:46:00 doug Exp $ */ | 1 | /* $OpenBSD: bs_cbs.c,v 1.9 2015/06/15 07:35:49 doug Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2014, Google Inc. | 3 | * Copyright (c) 2014, Google Inc. |
| 4 | * | 4 | * |
| @@ -205,24 +205,45 @@ int | |||
| 205 | CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, | 205 | CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, |
| 206 | size_t *out_header_len) | 206 | size_t *out_header_len) |
| 207 | { | 207 | { |
| 208 | return cbs_get_any_asn1_element_internal(cbs, out, out_tag, | ||
| 209 | out_header_len, 1); | ||
| 210 | } | ||
| 211 | |||
| 212 | /* | ||
| 213 | * Review X.690 for details on ASN.1 DER encoding. | ||
| 214 | * | ||
| 215 | * If non-strict mode is enabled, then DER rules are relaxed | ||
| 216 | * for indefinite constructs (violates DER but a little closer to BER). | ||
| 217 | * Non-strict mode should only be used by bs_ber.c | ||
| 218 | * | ||
| 219 | * Sections 8, 10 and 11 for DER encoding | ||
| 220 | */ | ||
| 221 | int | ||
| 222 | cbs_get_any_asn1_element_internal(CBS *cbs, CBS *out, unsigned *out_tag, | ||
| 223 | size_t *out_header_len, int strict) | ||
| 224 | { | ||
| 208 | uint8_t tag, length_byte; | 225 | uint8_t tag, length_byte; |
| 209 | CBS header = *cbs; | 226 | CBS header = *cbs; |
| 210 | CBS throwaway; | 227 | CBS throwaway; |
| 228 | size_t len; | ||
| 211 | 229 | ||
| 212 | if (out == NULL) | 230 | if (out == NULL) |
| 213 | out = &throwaway; | 231 | out = &throwaway; |
| 214 | 232 | ||
| 233 | /* | ||
| 234 | * Get identifier octet and length octet. Only 1 octet for each | ||
| 235 | * is a CBS limitation. | ||
| 236 | */ | ||
| 215 | if (!CBS_get_u8(&header, &tag) || !CBS_get_u8(&header, &length_byte)) | 237 | if (!CBS_get_u8(&header, &tag) || !CBS_get_u8(&header, &length_byte)) |
| 216 | return 0; | 238 | return 0; |
| 217 | 239 | ||
| 240 | /* CBS limitation: long form tags are not supported. */ | ||
| 218 | if ((tag & 0x1f) == 0x1f) | 241 | if ((tag & 0x1f) == 0x1f) |
| 219 | /* Long form tags are not supported. */ | ||
| 220 | return 0; | 242 | return 0; |
| 221 | 243 | ||
| 222 | if (out_tag != NULL) | 244 | if (out_tag != NULL) |
| 223 | *out_tag = tag; | 245 | *out_tag = tag; |
| 224 | 246 | ||
| 225 | size_t len; | ||
| 226 | if ((length_byte & 0x80) == 0) { | 247 | if ((length_byte & 0x80) == 0) { |
| 227 | /* Short form length. */ | 248 | /* Short form length. */ |
| 228 | len = ((size_t) length_byte) + 2; | 249 | len = ((size_t) length_byte) + 2; |
| @@ -234,21 +255,40 @@ CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag, | |||
| 234 | const size_t num_bytes = length_byte & 0x7f; | 255 | const size_t num_bytes = length_byte & 0x7f; |
| 235 | uint32_t len32; | 256 | uint32_t len32; |
| 236 | 257 | ||
| 237 | if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) { | 258 | /* ASN.1 reserved value for future extensions */ |
| 238 | /* indefinite length */ | 259 | if (num_bytes == 0x7f) |
| 239 | if (out_header_len != NULL) | 260 | return 0; |
| 240 | *out_header_len = 2; | 261 | |
| 241 | return CBS_get_bytes(cbs, out, 2); | 262 | /* Handle indefinite form length */ |
| 263 | if (num_bytes == 0) { | ||
| 264 | /* DER encoding doesn't allow for indefinite form. */ | ||
| 265 | if (strict) { | ||
| 266 | return 0; | ||
| 267 | |||
| 268 | } else { | ||
| 269 | if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && | ||
| 270 | num_bytes == 0) { | ||
| 271 | /* indefinite length */ | ||
| 272 | if (out_header_len != NULL) | ||
| 273 | *out_header_len = 2; | ||
| 274 | return CBS_get_bytes(cbs, out, 2); | ||
| 275 | } else { | ||
| 276 | /* Primitive cannot use indefinite. */ | ||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | } | ||
| 242 | } | 280 | } |
| 243 | 281 | ||
| 244 | if (num_bytes == 0 || num_bytes > 4) | 282 | /* CBS limitation. */ |
| 283 | if (num_bytes > 4) | ||
| 245 | return 0; | 284 | return 0; |
| 246 | 285 | ||
| 247 | if (!cbs_get_u(&header, &len32, num_bytes)) | 286 | if (!cbs_get_u(&header, &len32, num_bytes)) |
| 248 | return 0; | 287 | return 0; |
| 249 | 288 | ||
| 289 | /* DER has a minimum length octet requirements. */ | ||
| 250 | if (len32 < 128) | 290 | if (len32 < 128) |
| 251 | /* Length should have used short-form encoding. */ | 291 | /* Should have used short form instead */ |
| 252 | return 0; | 292 | return 0; |
| 253 | 293 | ||
| 254 | if ((len32 >> ((num_bytes - 1) * 8)) == 0) | 294 | if ((len32 >> ((num_bytes - 1) * 8)) == 0) |
| @@ -279,13 +319,7 @@ cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value, int skip_header) | |||
| 279 | out = &throwaway; | 319 | out = &throwaway; |
| 280 | 320 | ||
| 281 | if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) || | 321 | if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) || |
| 282 | tag != tag_value || (header_len > 0 && | 322 | tag != tag_value) |
| 283 | /* | ||
| 284 | * This ensures that the tag is either zero length or | ||
| 285 | * indefinite-length. | ||
| 286 | */ | ||
| 287 | CBS_len(out) == header_len && | ||
| 288 | CBS_data(out)[header_len - 1] == 0x80)) | ||
| 289 | return 0; | 323 | return 0; |
| 290 | 324 | ||
| 291 | if (skip_header && !CBS_skip(out, header_len)) { | 325 | if (skip_header && !CBS_skip(out, header_len)) { |
diff --git a/src/lib/libssl/src/ssl/bytestring.h b/src/lib/libssl/src/ssl/bytestring.h index b98c930da5..d66ab65b91 100644 --- a/src/lib/libssl/src/ssl/bytestring.h +++ b/src/lib/libssl/src/ssl/bytestring.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: bytestring.h,v 1.6 2015/06/13 09:02:45 doug Exp $ */ | 1 | /* $OpenBSD: bytestring.h,v 1.7 2015/06/15 07:35:49 doug Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2014, Google Inc. | 3 | * Copyright (c) 2014, Google Inc. |
| 4 | * | 4 | * |
| @@ -223,9 +223,8 @@ int CBS_peek_asn1_tag(const CBS *cbs, unsigned tag_value); | |||
| 223 | /* | 223 | /* |
| 224 | * CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from | 224 | * CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from |
| 225 | * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to | 225 | * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to |
| 226 | * the tag number and |*out_header_len| to the length of the ASN.1 header. If | 226 | * the tag number and |*out_header_len| to the length of the ASN.1 header. |
| 227 | * the element has indefinite length then |*out| will only contain the | 227 | * Each of |out|, |out_tag|, and |out_header_len| may be NULL to ignore |
| 228 | * header. Each of |out|, |out_tag|, and |out_header_len| may be NULL to ignore | ||
| 229 | * the value. | 228 | * the value. |
| 230 | * | 229 | * |
| 231 | * Tag numbers greater than 30 are not supported (i.e. short form only). | 230 | * Tag numbers greater than 30 are not supported (i.e. short form only). |
| @@ -452,6 +451,19 @@ int CBB_add_asn1_uint64(CBB *cbb, uint64_t value); | |||
| 452 | 451 | ||
| 453 | #ifdef LIBRESSL_INTERNAL | 452 | #ifdef LIBRESSL_INTERNAL |
| 454 | /* | 453 | /* |
| 454 | * CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from | ||
| 455 | * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to | ||
| 456 | * the tag number and |*out_header_len| to the length of the ASN.1 header. If | ||
| 457 | * strict mode is disabled and the element has indefinite length then |*out| | ||
| 458 | * will only contain the header. Each of |out|, |out_tag|, and | ||
| 459 | * |out_header_len| may be NULL to ignore the value. | ||
| 460 | * | ||
| 461 | * Tag numbers greater than 30 are not supported (i.e. short form only). | ||
| 462 | */ | ||
| 463 | int cbs_get_any_asn1_element_internal(CBS *cbs, CBS *out, unsigned *out_tag, | ||
| 464 | size_t *out_header_len, int strict); | ||
| 465 | |||
| 466 | /* | ||
| 455 | * CBS_asn1_ber_to_der reads an ASN.1 structure from |in|. If it finds | 467 | * CBS_asn1_ber_to_der reads an ASN.1 structure from |in|. If it finds |
| 456 | * indefinite-length elements then it attempts to convert the BER data to DER | 468 | * indefinite-length elements then it attempts to convert the BER data to DER |
| 457 | * and sets |*out| and |*out_length| to describe a malloced buffer containing | 469 | * and sets |*out| and |*out_length| to describe a malloced buffer containing |
