diff options
Diffstat (limited to 'libasn1fix/asn1fix_constr.c')
-rw-r--r-- | libasn1fix/asn1fix_constr.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/libasn1fix/asn1fix_constr.c b/libasn1fix/asn1fix_constr.c new file mode 100644 index 00000000..2aa79827 --- /dev/null +++ b/libasn1fix/asn1fix_constr.c @@ -0,0 +1,364 @@ +#include "asn1fix_internal.h" + +static int _asn1f_check_if_tag_must_be_explicit(arg_t *arg, asn1p_expr_t *v); +static int _asn1f_compare_tags(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b); + + +int +asn1f_fix_constr_ext(arg_t *arg) { + asn1p_expr_t *expr = arg->expr; + asn1p_expr_t *v; + TQ_HEAD(asn1p_expr_t) root_list; + TQ_HEAD(asn1p_expr_t) ext_list; + TQ_HEAD(asn1p_expr_t) *cur_list; + int r_value = 0; + int ext_count = 0; + + switch(expr->expr_type) { + case ASN_CONSTR_SEQUENCE: + case ASN_CONSTR_SET: + case ASN_CONSTR_CHOICE: + break; + default: + return 0; + } + + DEBUG("%s(%s) for line %d", __func__, + expr->Identifier, expr->_lineno); + + TQ_INIT(&root_list); + TQ_INIT(&ext_list); + cur_list = (void *)&root_list; + + while((v = TQ_REMOVE(&(expr->members), next))) { + if(v->expr_type == A1TC_EXTENSIBLE) { + ext_count++; + switch(ext_count) { + case 1: cur_list = (void *)&ext_list; break; + case 2: + cur_list = (void *)&root_list; + if(v->value) { + FATAL("Optional extension marker " + "must not contain " + "an exception mark " + "at line %d", v->_lineno); + r_value = -1; + } + asn1p_expr_free(v); + continue; + case 3: + FATAL("Third extension marker " + "is not allowed at line %d", v->_lineno); + default: + r_value = -1; + } + } + + TQ_ADD(cur_list, v, next); + } + + /* + * Copy the root list and extension list back into the main list. + */ + TQ_HEAD_COPY(&(expr->members), &root_list); + while((v = TQ_REMOVE(&ext_list, next))) + TQ_ADD(&(expr->members), v, next); + + if(arg->mod->module_flags & MSF_EXTENSIBILITY_IMPLIED + && ext_count < 1) { + v = asn1p_expr_new(0); + if(v) { + v->Identifier = strdup("..."); + v->expr_type = A1TC_EXTENSIBLE; + v->meta_type = AMT_TYPE; + if(v->Identifier == NULL) { + asn1p_expr_free(v); + r_value = -1; + } else { + TQ_ADD(&(expr->members), v, next); + } + } else { + r_value = -1; + } + } + + return r_value; +} + + +int +asn1f_fix_constr_tag(arg_t *arg) { + asn1p_expr_t *expr = arg->expr; + asn1p_expr_t *v; + int fl_impl_tags = 0; + int fl_auto_tags = 0; + int root_tagged = 0; /* The root component is manually tagged */ + int ext_tagged = 0; /* The extensions are manually tagged */ + int component_number = 0; + int r_value = 0; + + switch(expr->expr_type) { + case ASN_CONSTR_SEQUENCE: + case ASN_CONSTR_SET: + case ASN_CONSTR_CHOICE: + break; + default: + return 0; + } + + fl_impl_tags = (arg->mod->module_flags & MSF_IMPLICIT_TAGS); + fl_auto_tags = (arg->mod->module_flags & MSF_AUTOMATIC_TAGS); + + DEBUG("%s(%s) {%d, %d} for line %d", __func__, + expr->Identifier, fl_impl_tags, fl_auto_tags, expr->_lineno); + + TQ_FOR(v, &(expr->members), next) { + int must_explicit = 0; + + if(v->expr_type == A1TC_EXTENSIBLE) { + component_number++; + continue; + } + + if(v->tag.tag_class == TC_NOCLASS) { + continue; + } else { + switch(component_number) { + case 0: case 2: + root_tagged = 1; break; + default: + ext_tagged = 1; break; + } + } + + must_explicit = _asn1f_check_if_tag_must_be_explicit(arg, v); + + if(fl_impl_tags) { + if(v->tag.tag_mode != TM_EXPLICIT) { + if(must_explicit) + v->tag.tag_mode = TM_EXPLICIT; + else + v->tag.tag_mode = TM_IMPLICIT; + } + } else { + if(v->tag.tag_mode == TM_DEFAULT) { + v->tag.tag_mode = TM_EXPLICIT; + } + } + + /* + * Perform a final sanity check. + */ + if(must_explicit) { + if(v->tag.tag_mode == TM_IMPLICIT) { + FATAL("%s tagged in IMPLICIT mode " + "but must be EXPLICIT at line %d", + v->Identifier, v->_lineno); + r_value = -1; + } else { + v->tag.tag_mode = TM_EXPLICIT; + } + } + } + + if(ext_tagged && !root_tagged) { + FATAL("In %s at line %d: " + "extensions are tagged " + "but root components are not", + expr->Identifier, expr->_lineno); + r_value = -1; + } else if(!root_tagged && !ext_tagged && fl_auto_tags) { + expr->auto_tags_OK = 1; + } + + return r_value; +} + +int +asn1f_fix_constr_autotag(arg_t *arg) { + asn1p_expr_t *expr = arg->expr; + asn1p_expr_t *v; + asn1_integer_t tag_value = 0; + int r_value = 0; + + switch(expr->expr_type) { + case ASN_CONSTR_SEQUENCE: + case ASN_CONSTR_SET: + case ASN_CONSTR_CHOICE: + if(expr->auto_tags_OK) + break; + /* Automatic tagging is not applicable */ + /* Fall through */ + default: + return 0; + } + + DEBUG("%s(%s) for line %d", __func__, + expr->Identifier, expr->_lineno); + + TQ_FOR(v, &(expr->members), next) { + int must_explicit; + + if(v->expr_type == A1TC_EXTENSIBLE) + break; + + assert(v->tag.tag_class == TC_NOCLASS); + + must_explicit = _asn1f_check_if_tag_must_be_explicit(arg, v); + + v->tag.tag_class = TC_CONTEXT_SPECIFIC; + v->tag.tag_mode = must_explicit ? TM_EXPLICIT : TM_IMPLICIT; + v->tag.tag_value = tag_value++; + } + + return r_value; +} + +/* + * Check that tags are distinct. + */ +int +asn1f_check_constr_tags_distinct(arg_t *arg) { + asn1p_expr_t *expr = arg->expr; + asn1p_expr_t *v; + int r_value = 0; + + switch(expr->expr_type) { + case ASN_CONSTR_SEQUENCE: + case ASN_CONSTR_SET: + case ASN_CONSTR_CHOICE: + break; + default: + return 0; + } + + TQ_FOR(v, &(expr->members), next) { + /* + * In every series of non-mandatory components, + * the tags must be distinct from each other AND the + * tag of the following mandatory component. + * For SET and CHOICE treat everything as a big set of + * non-mandatory components. + */ + if(expr->expr_type != ASN_CONSTR_SEQUENCE || v->marker) { + asn1p_expr_t *nv; + for(nv = v; (nv = TQ_NEXT(nv, next));) { + if(_asn1f_compare_tags(arg, v, nv)) + r_value = -1; + if(expr->expr_type == ASN_CONSTR_SEQUENCE + && !nv->marker) break; + } + } + } + + return r_value; +} + +static int +_asn1f_check_if_tag_must_be_explicit(arg_t *arg, asn1p_expr_t *v) { + asn1p_expr_t *reft; + + reft = asn1f_find_terminal_type(arg, v, 0); + if(reft) { + switch(reft->expr_type) { + case ASN_CONSTR_CHOICE: + return 1; + default: + return 0; + } + } + + return 0; +} + +/* + * Check that the tags are distinct. + */ +static int +_asn1f_compare_tags(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b) { + struct asn1p_type_tag_s ta, tb; + int ra, rb; + int ret; + + ra = asn1f_fetch_tag(arg->asn, arg->mod, a, &ta); + rb = asn1f_fetch_tag(arg->asn, arg->mod, b, &tb); + + /* + * If both tags are explicitly or implicitly given, use them. + */ + if(ra == 0 && rb == 0) { + /* + * Simple case: fetched both tags. + */ + if(ta.tag_value == tb.tag_value + && ta.tag_class == tb.tag_class) { + char *p = (a->expr_type == A1TC_EXTENSIBLE) + ?"potentially ":""; + FATAL("Component \"%s\" at line %d %shas the same tag " + "with component \"%s\" at line %d", + a->Identifier, + a->_lineno, + p, + b->Identifier, + b->_lineno + ); + return -1; + } else { + /* Tags are distinct */ + return 0; + } + } + + /********************************************************** + * Now we must perform some very funny recursion to check + * multiple components of CHOICE type, etc. + */ + + DEBUG("Comparing tags %s:%x <-> %s:%x", + a->Identifier, a->expr_type, + b->Identifier, b->expr_type); + + if(a->meta_type == AMT_TYPEREF) { + asn1p_module_t *mod; + + DEBUG(" %s is a type reference", a->Identifier); + + a = asn1f_lookup_symbol(arg, a->reference, &mod); + if(!a) return 0; /* Already FATAL()'ed somewhere else */ + WITH_MODULE(mod, ret = _asn1f_compare_tags(arg, a, b)); + return ret; + } + + if(a->expr_type == ASN_CONSTR_CHOICE) { + asn1p_expr_t *v; + + DEBUG(" %s is a choice type (%d)", a->Identifier, a->_mark); + + /* + * Iterate over members of CHOICE. + */ + //if(a->_mark & TM_RECURSION) return 0; + TQ_FOR(v, &(a->members), next) { + //a->_mark |= TM_RECURSION; + ret = _asn1f_compare_tags(arg, v, b); + //a->_mark &= ~TM_RECURSION; + if(ret) return ret; + } + return 0; + } + + if(b->expr_type == ASN_CONSTR_CHOICE) { + return _asn1f_compare_tags(arg, b, a); + } + + if(a->_mark & TM_RECURSION) return 0; + if(b->_mark & TM_RECURSION) return 0; + a->_mark |= TM_RECURSION; + b->_mark |= TM_RECURSION; + ret = _asn1f_compare_tags(arg, b, a); + a->_mark &= ~TM_RECURSION; + b->_mark &= ~TM_RECURSION; + + return ret; +} + |