Page MenuHome GnuPG

asn1-func.c
No OneTemporary

asn1-func.c

/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef BUILD_GENTOOLS
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#ifdef BUILD_GENTOOLS
# include "gen-help.h"
#else
# include "util.h"
# include "ksba.h"
#endif
#include "asn1-func.h"
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 */
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;
break;
case VALTYPE_CSTR:
node->value.v_cstr = xstrdup (value);
break;
case VALTYPE_MEM:
node->value.v_mem.len = len;
if (len)
{
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);
node->value.v_long = *(long *)value;
break;
case VALTYPE_ULONG:
return_if_fail (sizeof (unsigned long) == len);
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 */
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;
/* 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 */
}
else if (!strcmp (buf, "?LAST"))
{
if (!p)
return NULL;
while (p->right)
p = p->right;
}
else
{
for (; p ; p = p->right)
{
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;
}

File Metadata

Mime Type
text/x-c
Expires
Mon, Dec 23, 2:01 PM (1 d, 9 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ac/da/ee1ad4ef2cefd56a34ff10454995

Event Timeline