aboutsummaryrefslogtreecommitdiffstats
path: root/libasn1fix/asn1fix_constr.c
diff options
context:
space:
mode:
Diffstat (limited to 'libasn1fix/asn1fix_constr.c')
-rw-r--r--libasn1fix/asn1fix_constr.c364
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;
+}
+