diff --git a/src/asn1-func.c b/src/asn1-func.c index 05ec897..7a33dd4 100755 --- a/src/asn1-func.c +++ b/src/asn1-func.c @@ -1,1316 +1,1317 @@ /* asn1-func.c - Fucntions for the ASN.1 data structures. * Copyright (C) 2000, 2001 Fabio Fiorina * Copyright (C) 2001 Free Software Foundation, Inc. * Copyright (C) 2002, 2003, 2006, 2007, 2010, 2012 g10 Code GmbH * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef BUILD_GENTOOLS #include #endif #include #include #include #include #include #ifdef BUILD_GENTOOLS # include "gen-help.h" #else # include "util.h" # include "ksba.h" #endif #include "asn1-func.h" #ifdef BUILD_GENTOOLS #define gpgrt_log_debug(...) /**/ #endif static AsnNode resolve_identifier (AsnNode root, AsnNode node, int nestlevel); static AsnNode add_node (node_type_t type) { AsnNode punt; punt = xmalloc (sizeof *punt); punt->left = NULL; punt->name = NULL; punt->type = type; punt->valuetype = VALTYPE_NULL; punt->value.v_cstr = NULL; punt->off = -1; punt->nhdr = 0; punt->len = 0; punt->down = NULL; punt->right = NULL; punt->link_next = NULL; return punt; } AsnNode _ksba_asn_new_node (node_type_t type) { return add_node (type); } int _ksba_asn_is_primitive (node_type_t type) { switch (type) { case TYPE_BOOLEAN: case TYPE_INTEGER: case TYPE_BIT_STRING: case TYPE_OCTET_STRING: case TYPE_NULL: case TYPE_OBJECT_ID: case TYPE_OBJECT_DESCRIPTOR: case TYPE_REAL: case TYPE_ENUMERATED: case TYPE_UTF8_STRING: case TYPE_REALTIVE_OID: case TYPE_NUMERIC_STRING: case TYPE_PRINTABLE_STRING: case TYPE_TELETEX_STRING: case TYPE_VIDEOTEX_STRING: case TYPE_IA5_STRING: case TYPE_UTC_TIME: case TYPE_GENERALIZED_TIME: case TYPE_GRAPHIC_STRING: case TYPE_VISIBLE_STRING: case TYPE_GENERAL_STRING: case TYPE_UNIVERSAL_STRING: case TYPE_CHARACTER_STRING: case TYPE_BMP_STRING: case TYPE_PRE_SEQUENCE: return 1; default: return 0; } } /* Change the value field of the node to the content of buffer value - of size LEN. With VALUE of NULL or LEN of 0 the value field is - deleted */ + of size LEN. With VALUE of NULL or LEN of 0 and a VTYPE of + VALTYPE_NULL the value field is deleted */ void _ksba_asn_set_value (AsnNode node, enum asn_value_type vtype, const void *value, size_t len) { return_if_fail (node); if (node->valuetype) { if (node->valuetype == VALTYPE_CSTR) xfree (node->value.v_cstr); else if (node->valuetype == VALTYPE_MEM) xfree (node->value.v_mem.buf); node->valuetype = 0; } switch (vtype) { case VALTYPE_NULL: break; case VALTYPE_BOOL: - return_if_fail (len); - node->value.v_bool = !!(const unsigned *)value; + return_if_fail (len && value); + node->value.v_bool = !!*(const unsigned *)value; break; case VALTYPE_CSTR: + return_if_fail (value); node->value.v_cstr = xstrdup (value); break; case VALTYPE_MEM: node->value.v_mem.len = len; - if (len) + if (len && value) { node->value.v_mem.buf = xmalloc (len); memcpy (node->value.v_mem.buf, value, len); } else node->value.v_mem.buf = NULL; break; case VALTYPE_LONG: - return_if_fail (sizeof (long) == len); + return_if_fail (sizeof (long) == len && value); node->value.v_long = *(long *)value; break; case VALTYPE_ULONG: - return_if_fail (sizeof (unsigned long) == len); + return_if_fail (sizeof (unsigned long) == len && value); node->value.v_ulong = *(unsigned long *)value; break; default: return_if_fail (0); } node->valuetype = vtype; } static void copy_value (AsnNode d, const AsnNode s) { char helpbuf[1]; const void *buf = NULL; size_t len = 0; return_if_fail (d != s); switch (s->valuetype) { case VALTYPE_NULL: break; case VALTYPE_BOOL: len = 1; helpbuf[0] = s->value.v_bool; buf = helpbuf; break; case VALTYPE_CSTR: buf = s->value.v_cstr; break; case VALTYPE_MEM: len = s->value.v_mem.len; buf = len? s->value.v_mem.buf : NULL; break; case VALTYPE_LONG: len = sizeof (long); buf = &s->value.v_long; break; case VALTYPE_ULONG: len = sizeof (unsigned long); buf = &s->value.v_ulong; break; default: return_if_fail (0); } _ksba_asn_set_value (d, s->valuetype, buf, len); d->off = s->off; d->nhdr = s->nhdr; d->len = s->len; } static AsnNode copy_node (const AsnNode s) { AsnNode d = add_node (s->type); if (s->name) d->name = xstrdup (s->name); d->flags = s->flags; copy_value (d, s); return d; } /* Change the name field of the node to NAME. NAME may be NULL */ void _ksba_asn_set_name (AsnNode node, const char *name) { return_if_fail (node); if (node->name) { xfree (node->name); node->name = NULL; } if (name && *name) node->name = xstrdup (name); } static AsnNode set_right (AsnNode node, AsnNode right) { if (node == NULL) return node; node->right = right; if (right) right->left = node; return node; } static AsnNode set_down (AsnNode node, AsnNode down) { if (node == NULL) return node; node->down = down; if (down) down->left = node; return node; } void _ksba_asn_remove_node (AsnNode node) { if (node == NULL) return; xfree (node->name); if (node->valuetype == VALTYPE_CSTR) xfree (node->value.v_cstr); else if (node->valuetype == VALTYPE_MEM) xfree (node->value.v_mem.buf); xfree (node); } /* find the node with the given name. A name part of "?LAST" matches the last element of a SET_OF. A "+" matches the CHOICE with values set. */ static AsnNode find_node (AsnNode root, const char *name, int resolve) { AsnNode p; const char *s; char buf[129]; int i; if (!name || !name[0]) return NULL; /* gpgrt_log_debug ("%s: looking for '%s'\n", __func__, name); */ /* find the first part */ s = name; for (i=0; *s && *s != '.' && i < DIM(buf)-1; s++) buf[i++] = *s; buf[i] = 0; return_null_if_fail (i < DIM(buf)-1); for (p = root; p && (!p->name || strcmp (p->name, buf)); p = p->right) ; /* find other parts */ while (p && *s) { assert (*s == '.'); s++; /* skip the dot */ if (!p->down) return NULL; /* not found */ p = p->down; for (i=0; *s && *s != '.' && i < DIM(buf)-1; s++) buf[i++] = *s; buf[i] = 0; return_null_if_fail (i < DIM(buf)-1); if (!*buf) { /* a double dot can be used to get over an unnamed sequence in a set - Actually a hack to workaround a bug. We should rethink the entire node naming issue */ /* gpgrt_log_debug ("%s: .. to '%s'\n", __func__, p?p->name:""); */ } else if (!strcmp (buf, "?LAST")) { if (!p) return NULL; while (p->right) p = p->right; } else if (*buf == '+' && !buf[1]) { for (; p ; p = p->right) if (p->off != -1) break; /* gpgrt_log_debug ("%s: + to '%s'\n", __func__, p?p->name:""); */ } else { for (; p ; p = p->right) { /* gpgrt_log_debug ("%s: '%s' to '%s'\n", */ /* __func__, buf, p?p->name:""); */ if (p->name && !strcmp (p->name, buf)) break; if (resolve && p->name && p->type == TYPE_IDENTIFIER) { AsnNode p2; p2 = resolve_identifier (root, p, 0); if (p2 && p2->name && !strcmp (p2->name, buf)) break; } } if (resolve && p && p->type == TYPE_IDENTIFIER) p = resolve_identifier (root, p, 0); } } return p; } AsnNode _ksba_asn_find_node (AsnNode root, const char *name) { return find_node (root, name, 0); } static AsnNode _asn1_find_left (AsnNode node) { if ((node == NULL) || (node->left == NULL) || (node->left->down == node)) return NULL; return node->left; } static AsnNode find_up (AsnNode node) { AsnNode p; if (node == NULL) return NULL; p = node; while ((p->left != NULL) && (p->left->right == p)) p = p->left; return p->left; } static void print_value (AsnNode node, FILE *fp) { if (!node->valuetype) return; fprintf (fp, " vt=%d val=", node->valuetype); switch (node->valuetype) { case VALTYPE_BOOL: fputs (node->value.v_bool? "True":"False", fp); break; case VALTYPE_CSTR: fputs (node->value.v_cstr, fp); break; case VALTYPE_MEM: { size_t n; unsigned char *p; for (p=node->value.v_mem.buf, n=node->value.v_mem.len; n; n--, p++) fprintf (fp, "%02X", *p); } break; case VALTYPE_LONG: fprintf (fp, "%ld", node->value.v_long); break; case VALTYPE_ULONG: fprintf (fp, "%lu", node->value.v_ulong); break; default: return_if_fail (0); } } void _ksba_asn_node_dump (AsnNode p, FILE *fp) { const char *typestr; switch (p->type) { case TYPE_NULL: typestr = "NULL"; break; case TYPE_CONSTANT: typestr = "CONST"; break; case TYPE_IDENTIFIER: typestr = "IDENTIFIER"; break; case TYPE_INTEGER: typestr = "INTEGER"; break; case TYPE_ENUMERATED: typestr = "ENUMERATED"; break; case TYPE_UTC_TIME: typestr = "UTCTIME"; break; case TYPE_GENERALIZED_TIME: typestr = "GENERALIZEDTIME"; break; case TYPE_BOOLEAN: typestr = "BOOLEAN"; break; case TYPE_SEQUENCE: typestr = "SEQUENCE"; break; case TYPE_PRE_SEQUENCE: typestr = "PRE_SEQUENCE"; break; case TYPE_BIT_STRING: typestr = "BIT_STR"; break; case TYPE_OCTET_STRING: typestr = "OCT_STR"; break; case TYPE_TAG: typestr = "TAG"; break; case TYPE_DEFAULT: typestr = "DEFAULT"; break; case TYPE_SIZE: typestr = "SIZE"; break; case TYPE_SEQUENCE_OF: typestr = "SEQ_OF"; break; case TYPE_OBJECT_ID: typestr = "OBJ_ID"; break; case TYPE_ANY: typestr = "ANY"; break; case TYPE_SET: typestr = "SET"; break; case TYPE_SET_OF: typestr = "SET_OF"; break; case TYPE_CHOICE: typestr = "CHOICE"; break; case TYPE_DEFINITIONS: typestr = "DEFINITIONS"; break; case TYPE_UTF8_STRING: typestr = "UTF8_STRING"; break; case TYPE_NUMERIC_STRING: typestr = "NUMERIC_STRING"; break; case TYPE_PRINTABLE_STRING: typestr = "PRINTABLE_STRING"; break; case TYPE_TELETEX_STRING: typestr = "TELETEX_STRING"; break; case TYPE_IA5_STRING: typestr = "IA5_STRING"; break; default: typestr = "ERROR\n"; break; } fprintf (fp, "%s", typestr); if (p->name) fprintf (fp, " `%s'", p->name); print_value (p, fp); fputs (" ", fp); switch (p->flags.class) { case CLASS_UNIVERSAL: fputs ("U", fp); break; case CLASS_PRIVATE: fputs ("P", fp); break; case CLASS_APPLICATION: fputs ("A", fp); break; case CLASS_CONTEXT: fputs ("C", fp); break; } if (p->flags.explicit) fputs (",explicit", fp); if (p->flags.implicit) fputs (",implicit", fp); if (p->flags.is_implicit) fputs (",is_implicit", fp); if (p->flags.has_tag) fputs (",tag", fp); if (p->flags.has_default) fputs (",default", fp); if (p->flags.is_true) fputs (",true", fp); if (p->flags.is_false) fputs (",false", fp); if (p->flags.has_list) fputs (",list", fp); if (p->flags.has_min_max) fputs (",min_max", fp); if (p->flags.is_optional) fputs (",optional", fp); if (p->flags.one_param) fputs (",1_param", fp); if (p->flags.has_size) fputs (",size", fp); if (p->flags.has_defined_by) fputs (",def_by", fp); if (p->flags.has_imports) fputs (",imports", fp); if (p->flags.assignment) fputs (",assign",fp); if (p->flags.in_set) fputs (",in_set",fp); if (p->flags.in_choice) fputs (",in_choice",fp); if (p->flags.in_array) fputs (",in_array",fp); if (p->flags.not_used) fputs (",not_used",fp); if (p->flags.skip_this) fputs (",[skip]",fp); if (p->flags.is_any) fputs (",is_any",fp); if (p->off != -1 ) fprintf (fp, " %d.%d.%d", p->off, p->nhdr, p->len ); } void _ksba_asn_node_dump_all (AsnNode root, FILE *fp) { AsnNode p = root; int indent = 0; while (p) { fprintf (fp, "%*s", indent, ""); _ksba_asn_node_dump (p, fp); putc ('\n', fp); if (p->down) { p = p->down; indent += 2; } else if (p == root) { p = NULL; break; } else if (p->right) p = p->right; else { while (1) { p = find_up (p); if (p == root) { p = NULL; break; } indent -= 2; if (p->right) { p = p->right; break; } } } } } /** * ksba_asn_tree_dump: * @tree: A Parse Tree * @name: Name of the element or NULL * @fp: dump to this stream * * If the first character of the name is a '<' the expanded version of * the tree will be printed. * * This function is a debugging aid. **/ void ksba_asn_tree_dump (ksba_asn_tree_t tree, const char *name, FILE *fp) { AsnNode p, root; int k, expand=0, indent = 0; if (!tree || !tree->parse_tree) return; if ( name && *name== '<') { expand = 1; name++; if (!*name) name = NULL; } root = name? _ksba_asn_find_node (tree->parse_tree, name) : tree->parse_tree; if (!root) return; if (expand) root = _ksba_asn_expand_tree (root, NULL); p = root; while (p) { for (k = 0; k < indent; k++) fprintf (fp, " "); _ksba_asn_node_dump (p, fp); putc ('\n', fp); if (p->down) { p = p->down; indent += 2; } else if (p == root) { p = NULL; break; } else if (p->right) p = p->right; else { while (1) { p = find_up (p); if (p == root) { p = NULL; break; } indent -= 2; if (p->right) { p = p->right; break; } } } } if (expand) _ksba_asn_release_nodes (root); } int _ksba_asn_delete_structure (AsnNode root) { AsnNode p, p2, p3; if (root == NULL) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); p = root; while (p) { if (p->down) { p = p->down; } else { /* no down */ p2 = p->right; if (p != root) { p3 = find_up (p); set_down (p3, p2); _ksba_asn_remove_node (p); p = p3; } else { /* p==root */ p3 = _asn1_find_left (p); if (!p3) { p3 = find_up (p); if (p3) set_down (p3, p2); else { if (p->right) p->right->left = NULL; } } else set_right (p3, p2); _ksba_asn_remove_node (p); p = NULL; } } } return 0; } /* check that all identifiers referenced in the tree are available */ int _ksba_asn_check_identifier (AsnNode node) { AsnNode p, p2; char name2[129]; if (!node) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); for (p = node; p; p = _ksba_asn_walk_tree (node, p)) { if (p->type == TYPE_IDENTIFIER && p->valuetype == VALTYPE_CSTR) { if (strlen (node->name)+strlen(p->value.v_cstr)+2 > DIM(name2)) return gpg_error (GPG_ERR_BUG); /* well identifier too long */ strcpy (name2, node->name); strcat (name2, "."); strcat (name2, p->value.v_cstr); p2 = _ksba_asn_find_node (node, name2); if (!p2) { fprintf (stderr,"reference to `%s' not found\n", name2); return gpg_error (GPG_ERR_IDENTIFIER_NOT_FOUND); } /* fprintf (stdout,"found reference for `%s' (", name2); */ /* print_node (p2, stdout); */ /* fputs (")\n", stdout); */ } else if (p->type == TYPE_OBJECT_ID && p->flags.assignment) { /* an object ID in an assignment */ p2 = p->down; if (p2 && (p2->type == TYPE_CONSTANT)) { if (p2->valuetype == VALTYPE_CSTR && !isdigit (p2->value.v_cstr[0])) { /* the first constand below is a reference */ if (strlen (node->name) +strlen(p->value.v_cstr)+2 > DIM(name2)) return gpg_error (GPG_ERR_BUG); /* well identifier too long */ strcpy (name2, node->name); strcat (name2, "."); strcat (name2, p2->value.v_cstr); p2 = _ksba_asn_find_node (node, name2); if (!p2) { fprintf (stderr,"object id reference `%s' not found\n", name2); return gpg_error (GPG_ERR_IDENTIFIER_NOT_FOUND); } else if ( p2->type != TYPE_OBJECT_ID || !p2->flags.assignment ) { fprintf (stderr,"`%s' is not an object id\n", name2); return gpg_error (GPG_ERR_IDENTIFIER_NOT_FOUND); } /* fprintf (stdout,"found objid reference for `%s' (", name2); */ /* print_node (p2, stdout); */ /* fputs (")\n", stdout); */ } } } } return 0; } /* Get the next node until root is reached in which case NULL is returned */ AsnNode _ksba_asn_walk_tree (AsnNode root, AsnNode node) { if (!node) ; else if (node->down) node = node->down; else { if (node == root) node = NULL; else if (node->right) node = node->right; else { for (;;) { node = find_up (node); if (node == root) { node = NULL; break; } if (node->right) { node = node->right; break; } } } } return node; } AsnNode _ksba_asn_walk_tree_up_right (AsnNode root, AsnNode node) { if (node) { if (node == root) node = NULL; else { for (;;) { node = find_up (node); if (node == root) { node = NULL; break; } if (node->right) { node = node->right; break; } } } } return node; } /* walk over the tree and change the value type of all integer types from string to long. */ int _ksba_asn_change_integer_value (AsnNode node) { AsnNode p; if (node == NULL) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); for (p = node; p; p = _ksba_asn_walk_tree (node, p)) { if (p->type == TYPE_INTEGER && p->flags.assignment) { if (p->valuetype == VALTYPE_CSTR) { long val = strtol (p->value.v_cstr, NULL, 10); _ksba_asn_set_value (p, VALTYPE_LONG, &val, sizeof(val)); } } } return 0; } /* Expand all object ID constants */ int _ksba_asn_expand_object_id (AsnNode node) { AsnNode p, p2, p3, p4, p5; char name_root[129], name2[129*2+1] = ""; /* Fixme: Make a cleaner implementation */ if (!node) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); if (!node->name) return gpg_error (GPG_ERR_INV_VALUE); if (strlen(node->name) >= DIM(name_root)-1) return gpg_error (GPG_ERR_GENERAL); strcpy (name_root, node->name); restart: for (p = node; p; p = _ksba_asn_walk_tree (node, p)) { if (p->type == TYPE_OBJECT_ID && p->flags.assignment) { p2 = p->down; if (p2 && p2->type == TYPE_CONSTANT) { if (p2->valuetype == VALTYPE_CSTR && !isdigit (p2->value.v_cstr[0])) { if (strlen(p2->value.v_cstr)+1+strlen(name2) >= DIM(name2)-1) return gpg_error (GPG_ERR_GENERAL); strcpy (name2, name_root); strcat (name2, "."); strcat (name2, p2->value.v_cstr); p3 = _ksba_asn_find_node (node, name2); if (!p3 || p3->type != TYPE_OBJECT_ID || !p3->flags.assignment) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); set_down (p, p2->right); _ksba_asn_remove_node (p2); p2 = p; p4 = p3->down; while (p4) { if (p4->type == TYPE_CONSTANT) { p5 = add_node (TYPE_CONSTANT); _ksba_asn_set_name (p5, p4->name); _ksba_asn_set_value (p5, VALTYPE_CSTR, p4->value.v_cstr, 0); if (p2 == p) { set_right (p5, p->down); set_down (p, p5); } else { set_right (p5, p2->right); set_right (p2, p5); } p2 = p5; } p4 = p4->right; } goto restart; /* the most simple way to get it right ;-) */ } } } } return 0; } /* Walk the parse tree and set the default tag where appropriate. The node must be of type DEFINITIONS */ void _ksba_asn_set_default_tag (AsnNode node) { AsnNode p; return_if_fail (node && node->type == TYPE_DEFINITIONS); for (p = node; p; p = _ksba_asn_walk_tree (node, p)) { if ( p->type == TYPE_TAG && !p->flags.explicit && !p->flags.implicit) { if (node->flags.explicit) p->flags.explicit = 1; else p->flags.implicit = 1; } } /* now mark the nodes which are implicit */ for (p = node; p; p = _ksba_asn_walk_tree (node, p)) { if ( p->type == TYPE_TAG && p->flags.implicit && p->down) { if (p->down->type == TYPE_CHOICE) ; /* a CHOICE is per se implicit */ else if (p->down->type != TYPE_TAG) p->down->flags.is_implicit = 1; } } } /* Walk the tree and set the is_set and not_used flags for all nodes below a node of type SET. */ void _ksba_asn_type_set_config (AsnNode node) { AsnNode p, p2; return_if_fail (node && node->type == TYPE_DEFINITIONS); for (p = node; p; p = _ksba_asn_walk_tree (node, p)) { if (p->type == TYPE_SET) { for (p2 = p->down; p2; p2 = p2->right) { if (p2->type != TYPE_TAG) { p2->flags.in_set = 1; p2->flags.not_used = 1; } } } else if (p->type == TYPE_CHOICE) { for (p2 = p->down; p2; p2 = p2->right) { p2->flags.in_choice = 1; } } else if (p->type == TYPE_SEQUENCE_OF || p->type == TYPE_SET_OF) { for (p2 = p->down; p2; p2 = p2->right) p2->flags.in_array = 1; } else if (p->type == TYPE_ANY) { /* Help the DER encoder to track ANY tags */ p->flags.is_any = 1; } } } /* Create a copy the tree at SRC_ROOT. s is a helper which should be set to SRC_ROOT by the caller */ static AsnNode copy_tree (AsnNode src_root, AsnNode s) { AsnNode first=NULL, dprev=NULL, d, down, tmp; AsnNode *link_nextp = NULL; for (; s; s=s->right ) { down = s->down; d = copy_node (s); if (link_nextp) *link_nextp = d; link_nextp = &d->link_next; if (!first) first = d; else { dprev->right = d; d->left = dprev; } dprev = d; if (down) { tmp = copy_tree (src_root, down); if (tmp) { if (link_nextp) *link_nextp = tmp; link_nextp = &tmp->link_next; while (*link_nextp) link_nextp = &(*link_nextp)->link_next; } if (d->down && tmp) { /* Need to merge it with the existing down */ AsnNode x; for (x=d->down; x->right; x = x->right) ; x->right = tmp; tmp->left = x; } else { d->down = tmp; if (d->down) d->down->left = d; } } } return first; } static AsnNode resolve_identifier (AsnNode root, AsnNode node, int nestlevel) { char buf_space[50]; char *buf; AsnNode n; size_t bufsize; if (nestlevel > 20) return NULL; return_null_if_fail (root); return_null_if_fail (node->valuetype == VALTYPE_CSTR); bufsize = strlen (root->name) + strlen (node->value.v_cstr) + 2; if (bufsize <= sizeof buf_space) buf = buf_space; else { buf = xtrymalloc (bufsize); return_null_if_fail (buf); } strcpy (stpcpy (stpcpy (buf, root->name), "."), node->value.v_cstr); n = _ksba_asn_find_node (root, buf); /* We do just a simple indirection. */ if (n && n->type == TYPE_IDENTIFIER) n = resolve_identifier (root, n, nestlevel+1); if (buf != buf_space) xfree (buf); return n; } static AsnNode do_expand_tree (AsnNode src_root, AsnNode s, int depth) { AsnNode first=NULL, dprev=NULL, d, down, tmp; AsnNode *link_nextp = NULL; /* On the very first level we do not follow the right pointer so that we can break out a valid subtree. */ for (; s; s=depth?s->right:NULL ) { if (s->type == TYPE_SIZE) continue; /* this node gets in the way all the time. It should be an attribute to a node */ down = s->down; if (s->type == TYPE_IDENTIFIER) { AsnNode s2, *dp; d = resolve_identifier (src_root, s, 0); if (!d) { fprintf (stderr, "RESOLVING IDENTIFIER FAILED\n"); continue; } down = d->down; d = copy_node (d); if (link_nextp) *link_nextp = d; link_nextp = &d->link_next; if (s->flags.is_optional) d->flags.is_optional = 1; if (s->flags.in_choice) d->flags.in_choice = 1; if (s->flags.in_array) d->flags.in_array = 1; if (s->flags.is_implicit) d->flags.is_implicit = 1; if (s->flags.is_any) d->flags.is_any = 1; /* we don't want the resolved name - change it back */ _ksba_asn_set_name (d, s->name); /* copy the default and tag attributes */ tmp = NULL; dp = &tmp; for (s2=s->down; s2; s2=s2->right) { AsnNode x; x = copy_node (s2); if (link_nextp) *link_nextp = x; link_nextp = &x->link_next; x->left = *dp? *dp : d; *dp = x; dp = &(*dp)->right; if (x->type == TYPE_TAG) d->flags.has_tag =1; else if (x->type == TYPE_DEFAULT) d->flags.has_default =1; } d->down = tmp; } else { d = copy_node (s); if (link_nextp) *link_nextp = d; link_nextp = &d->link_next; } if (!first) first = d; else { dprev->right = d; d->left = dprev; } dprev = d; if (down) { if (depth >= 1000) { fprintf (stderr, "ASN.1 TREE TOO TALL!\n"); tmp = NULL; } else { tmp = do_expand_tree (src_root, down, depth+1); if (tmp) { if (link_nextp) *link_nextp = tmp; link_nextp = &tmp->link_next; while (*link_nextp) link_nextp = &(*link_nextp)->link_next; } } if (d->down && tmp) { /* Need to merge it with the existing down */ AsnNode x; for (x=d->down; x->right; x = x->right) ; x->right = tmp; tmp->left = x; } else { d->down = tmp; if (d->down) d->down->left = d; } } } return first; } /* Expand the syntax tree so that all references are resolved and we are able to store values right in the tree (except for set/sequence of). This expanded tree is also an requirement for doing the DER decoding as the resolving of identifiers leads to a lot of problems. We use more memory of course, but this is negligible because the entire code will be simpler and faster */ AsnNode _ksba_asn_expand_tree (AsnNode parse_tree, const char *name) { AsnNode root; root = name? find_node (parse_tree, name, 1) : parse_tree; return do_expand_tree (parse_tree, root, 0); } /* Insert a copy of the entire tree at NODE as the sibling of itself and return the copy */ AsnNode _ksba_asn_insert_copy (AsnNode node) { AsnNode n; AsnNode *link_nextp; n = copy_tree (node, node); if (!n) return NULL; /* out of core */ return_null_if_fail (n->right == node->right); node->right = n; n->left = node; /* FIXME: Consider tail pointer for faster insertion. */ link_nextp = &node->link_next; while (*link_nextp) link_nextp = &(*link_nextp)->link_next; *link_nextp = n; return n; } /* Locate a type value sequence like SEQUENCE { type OBJECT IDENTIFIER value ANY } below root and return the 'value' node. OIDBUF should contain the DER encoding of an OID value. idx is the number of OIDs to skip; this can be used to enumerate structures with the same OID */ AsnNode _ksba_asn_find_type_value (const unsigned char *image, AsnNode root, int idx, const void *oidbuf, size_t oidlen) { AsnNode n, noid; if (!image || !root) return NULL; for (n = root; n; n = _ksba_asn_walk_tree (root, n) ) { if ( n->type == TYPE_SEQUENCE && n->down && n->down->type == TYPE_OBJECT_ID) { noid = n->down; if (noid->off != -1 && noid->len == oidlen && !memcmp (image + noid->off + noid->nhdr, oidbuf, oidlen) && noid->right) { if ( !idx-- ) return noid->right; } } } return NULL; }