diff --git a/src/asn1-func.c b/src/asn1-func.c index e64c479..05ec897 100755 --- a/src/asn1-func.c +++ b/src/asn1-func.c @@ -1,1316 +1,1316 @@ /* 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 */ 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. 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]; + 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; } diff --git a/src/ber-help.c b/src/ber-help.c index 1b72bf0..81c31ed 100644 --- a/src/ber-help.c +++ b/src/ber-help.c @@ -1,712 +1,713 @@ /* ber-help.c - BER herlper functions * Copyright (C) 2001, 2012 g10 Code GmbH * * This file is part of KSBA. * * 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 . */ #include #include #include #include #include #include "util.h" #include "asn1-func.h" /* need some constants */ #include "convert.h" #include "ber-help.h" /* Fixme: The parser functions should check that primitive types don't have the constructed bit set (which is not allowed). This saves us some work when using these parsers */ static int read_byte (ksba_reader_t reader) { unsigned char buf; size_t nread; int rc; do rc = ksba_reader_read (reader, &buf, 1, &nread); while (!rc && !nread); return rc? -1: buf; } static int premature_eof (struct tag_info *ti) { /* Note: We do an strcmp on this string at other places. */ ti->err_string = "premature EOF"; return gpg_error (GPG_ERR_BAD_BER); } static gpg_error_t eof_or_error (ksba_reader_t reader, struct tag_info *ti, int premature) { gpg_error_t err; err = ksba_reader_error (reader); if (err) { ti->err_string = "read error"; return err; } if (premature) return premature_eof (ti); return gpg_error (GPG_ERR_EOF); } /* Read the tag and the length part from the TLV triplet. */ gpg_error_t _ksba_ber_read_tl (ksba_reader_t reader, struct tag_info *ti) { int c; unsigned long tag; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; ti->err_string = NULL; ti->non_der = 0; /* Get the tag */ c = read_byte (reader); if (c==-1) return eof_or_error (reader, ti, 0); ti->buf[ti->nhdr++] = c; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { /* We silently ignore an overflow in the tag value. It is not worth checking for it. */ tag <<= 7; c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) { ti->ndef = 1; ti->non_der = 1; } else if (c == 0xff) { ti->err_string = "forbidden length value"; return gpg_error (GPG_ERR_BAD_BER); } else { unsigned long len = 0; int count = c & 0x7f; if (count > sizeof (len) || count > sizeof (size_t)) return gpg_error (GPG_ERR_BAD_BER); for (; count; count--) { len <<= 8; c = read_byte (reader); if (c == -1) return eof_or_error (reader, ti, 1); if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; len |= c & 0xff; } ti->length = len; } /* Without this kludge some example certs can't be parsed */ if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; return 0; } /* Parse the buffer at the address BUFFER which of SIZE and return the * tag and the length part from the TLV triplet. Update BUFFER and * SIZE on success. Note that this function will never return * GPG_ERR_INV_OBJ so that this error code can be used by the parse_foo * functions below to return an error for unexpected tags and the * caller is able to backoff in that case. */ gpg_error_t _ksba_ber_parse_tl (unsigned char const **buffer, size_t *size, struct tag_info *ti) { int c; unsigned long tag; const unsigned char *buf = *buffer; size_t length = *size; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; ti->err_string = NULL; ti->non_der = 0; /* Get the tag */ if (!length) return premature_eof (ti); c = *buf++; length--; ti->buf[ti->nhdr++] = c; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { /* We silently ignore an overflow in the tag value. It is not worth checking for it. */ tag <<= 7; if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) { ti->ndef = 1; ti->non_der = 1; } else if (c == 0xff) { ti->err_string = "forbidden length value"; return gpg_error (GPG_ERR_BAD_BER); } else { unsigned long len = 0; int count = c & 0x7f; if (count > sizeof (len) || count > sizeof (size_t)) return gpg_error (GPG_ERR_BAD_BER); for (; count; count--) { len <<= 8; if (!length) return premature_eof (ti); c = *buf++; length--; if (ti->nhdr >= DIM (ti->buf)) { ti->err_string = "tag+length header too large"; return gpg_error (GPG_ERR_BAD_BER); } ti->buf[ti->nhdr++] = c; len |= c & 0xff; } /* Sanity check for the length: This is done so that we can take * the value for malloc plus some additional bytes without * risking an overflow. */ if (len > (1 << 30)) return gpg_error (GPG_ERR_BAD_BER); ti->length = len; } /* Without this kludge some example certs can't be parsed */ if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; *buffer = buf; *size = length; return 0; } /* Write TAG of CLASS to WRITER. constructed is a flag telling whether the value is a constructed one. length gives the length of the value, if it is 0 undefinite length is assumed. length is ignored for the NULL tag. */ gpg_error_t _ksba_ber_write_tl (ksba_writer_t writer, unsigned long tag, enum tag_class class, int constructed, unsigned long length) { unsigned char buf[50]; int buflen = 0; if (tag < 0x1f) { *buf = (class << 6) | tag; if (constructed) *buf |= 0x20; buflen++; } else { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } if (!tag && !class) buf[buflen++] = 0; /* end tag */ else if (tag == TYPE_NULL && !class) buf[buflen++] = 0; /* NULL tag */ else if (!length) buf[buflen++] = 0x80; /* indefinite length */ else if (length < 128) buf[buflen++] = length; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objects - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); buf[buflen++] = (0x80 | i); if (i > 3) buf[buflen++] = length >> 24; if (i > 2) buf[buflen++] = length >> 16; if (i > 1) buf[buflen++] = length >> 8; buf[buflen++] = length; } return ksba_writer_write (writer, buf, buflen); } /* Encode TAG of CLASS in BUFFER. CONSTRUCTED is a flag telling whether the value is a constructed one. LENGTH gives the length of the value, if it is 0 undefinite length is assumed. LENGTH is ignored for the NULL tag. It is assumed that the provide buffer is large enough for storing the result - this is usually achieved by using _ksba_ber_count_tl() in advance. Returns 0 in case of an error or the length of the encoding.*/ size_t _ksba_ber_encode_tl (unsigned char *buffer, unsigned long tag, enum tag_class class, int constructed, unsigned long length) { unsigned char *buf = buffer; if (tag < 0x1f) { *buf = (class << 6) | tag; if (constructed) *buf |= 0x20; buf++; } else { return 0; /*Not implemented*/ } if (!tag && !class) *buf++ = 0; /* end tag */ else if (tag == TYPE_NULL && !class) *buf++ = 0; /* NULL tag */ else if (!length) *buf++ = 0x80; /* indefinite length */ else if (length < 128) *buf++ = length; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objetcs - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); *buf++ = (0x80 | i); if (i > 3) *buf++ = length >> 24; if (i > 2) *buf++ = length >> 16; if (i > 1) *buf++ = length >> 8; *buf++ = length; } return buf - buffer; } /* Calculate the length of the TL needed to encode a TAG of CLASS. CONSTRUCTED is a flag telling whether the value is a constructed one. LENGTH gives the length of the value; if it is 0 an indefinite length is assumed. LENGTH is ignored for the NULL tag. */ size_t _ksba_ber_count_tl (unsigned long tag, enum tag_class class, int constructed, unsigned long length) { int buflen = 0; (void)constructed; /* Not used, but passed for uniformity of such calls. */ + /* coverity[identical_branches] */ if (tag < 0x1f) { buflen++; } else { buflen++; /* assume one and let the actual write function bail out */ } if (!tag && !class) buflen++; /* end tag */ else if (tag == TYPE_NULL && !class) buflen++; /* NULL tag */ else if (!length) buflen++; /* indefinite length */ else if (length < 128) buflen++; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objetcs - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); buflen++; if (i > 3) buflen++; if (i > 2) buflen++; if (i > 1) buflen++; buflen++; } return buflen; } gpg_error_t _ksba_parse_sequence (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_SEQUENCE && ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } /* Note that this function returns GPG_ERR_FALSE if the TLV is valid * but the tag does not match. The caller may thus check for this * error code and compare against other tag values. */ gpg_error_t _ksba_parse_context_tag (unsigned char const **buf, size_t *len, struct tag_info *ti, int tag) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_CONTEXT && ti->is_constructed)) err = gpg_error (GPG_ERR_INV_OBJ); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (ti->tag != tag) err = gpg_error (GPG_ERR_FALSE); return err; } gpg_error_t _ksba_parse_enumerated (unsigned char const **buf, size_t *len, struct tag_info *ti, size_t maxlen) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_ENUMERATED && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (maxlen && ti->length > maxlen) err = gpg_error (GPG_ERR_TOO_LARGE); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } gpg_error_t _ksba_parse_integer (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_INTEGER && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } gpg_error_t _ksba_parse_octet_string (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err= _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_OCTET_STRING && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } /* Note that R_BOOL will only be set if a value has been given. Thus the caller should set it to the default value prior to calling this function. Obviously no call to parse_skip is required after calling this function. */ gpg_error_t _ksba_parse_optional_boolean (unsigned char const **buf, size_t *len, int *r_bool) { gpg_error_t err; struct tag_info ti; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if (!ti.length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti.length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN && !ti.is_constructed) { if (ti.length != 1) err = gpg_error (GPG_ERR_BAD_BER); *r_bool = !!**buf; parse_skip (buf, len, &ti); } else { /* Undo the read. */ *buf -= ti.nhdr; *len += ti.nhdr; } return err; } /* Parse an optional Null tag. Ir R_SEEN is not NULL it is set to * true if a NULL tag was encountered. */ gpg_error_t _ksba_parse_optional_null (unsigned char const **buf, size_t *len, int *r_seen) { gpg_error_t err; struct tag_info ti; if (r_seen) *r_seen = 0; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if (ti.length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_NULL && !ti.is_constructed) { if (ti.length) err = gpg_error (GPG_ERR_BAD_BER); if (r_seen) *r_seen = 1; parse_skip (buf, len, &ti); } else { /* Undo the read. */ *buf -= ti.nhdr; *len += ti.nhdr; } return err; } gpg_error_t _ksba_parse_object_id_into_str (unsigned char const **buf, size_t *len, char **oid) { struct tag_info ti; gpg_error_t err; *oid = NULL; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti.length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti.length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (!(*oid = ksba_oid_to_str (*buf, ti.length))) err = gpg_error_from_syserror (); else { *buf += ti.length; *len -= ti.length; } return err; } gpg_error_t _ksba_parse_asntime_into_isotime (unsigned char const **buf, size_t *len, ksba_isotime_t isotime) { struct tag_info ti; gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if ( !(ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (ti.length > *len) err = gpg_error (GPG_ERR_INV_BER); else if (!(err = _ksba_asntime_to_iso (*buf, ti.length, ti.tag == TYPE_UTC_TIME, isotime))) parse_skip (buf, len, &ti); return err; } diff --git a/src/time.c b/src/time.c index d793476..39aad79 100644 --- a/src/time.c +++ b/src/time.c @@ -1,166 +1,166 @@ /* time.c - UTCTime and GeneralizedTime helper * Copyright (C) 2001, 2003, 2005, 2012 g10 Code GmbH * * This file is part of KSBA. * * 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 . */ #include #include #include #include #include #include #include "util.h" #include "convert.h" /* Converts an UTCTime or GeneralizedTime to ISO format. Sets the returns string to empty on error and returns the error code. The function figures automagically the right format. fixme: Currently we only zupport Zulu time and no timezone which is sufficient for DER encoding. It IS_UTCTIME is true, the function assumes that the time is in UTCTime and thus allows to parse UTCTimes without seconds (which is legal ASN.1; however Gutmann claims that the rules changed in 1996 to always require seconds; OTOH, Dubuisson's reference book from 2001 doesn't say so). */ gpg_error_t _ksba_asntime_to_iso (const char *buffer, size_t length, int is_utctime, ksba_isotime_t timebuf) { const char *s; size_t n; int year; *timebuf = 0; for (s=buffer, n=0; n < length && digitp (s); n++, s++) ; if (is_utctime) { if ((n != 10 && n != 12) || *s != 'Z') return gpg_error (GPG_ERR_INV_TIME); } else if ((n != 12 && n != 14) || *s != 'Z') return gpg_error (GPG_ERR_INV_TIME); s = buffer; if (n==12 || n == 10 ) /* UTCTime with or without seconds. */ { year = atoi_2 (s); timebuf[0] = year < 50? '2': '1'; timebuf[1] = year < 50? '0': '9'; memcpy (timebuf+2, s, 6); s += 6; } else { memcpy (timebuf, s, 8); s += 8; } timebuf[8] = 'T'; if (n == 10) /* UTCTime w/0 seconds. */ { memcpy (timebuf+9, s, 4); timebuf[13] = timebuf[14] = '0'; } else { memcpy (timebuf+9, s, 6); } timebuf[15] = 0; return 0; } /* Return 0 if ATIME has the proper format (e.g. "19660205T131415"). */ gpg_error_t _ksba_assert_time_format (const ksba_isotime_t atime) { int i; const char *s; if (!*atime) return gpg_error (GPG_ERR_NO_VALUE); for (s=atime, i=0; i < 8; i++, s++) if (!digitp (s)) return gpg_error (GPG_ERR_BUG); if (*s != 'T') return gpg_error (GPG_ERR_BUG); for (s++, i=9; i < 15; i++, s++) if (!digitp (s)) return gpg_error (GPG_ERR_BUG); if (*s) return gpg_error (GPG_ERR_BUG); return 0; } /* Copy ISO time S to D. This is a function so that we can detect faulty time formats. */ void _ksba_copy_time (ksba_isotime_t d, const ksba_isotime_t s) { if (!*s) memset (d, 0, 16); else if ( _ksba_assert_time_format (s) ) { fprintf (stderr, "BUG: invalid isotime buffer\n"); abort (); } else strcpy (d, s); } /* Compare the time strings A and B. Return 0 if they show the very same time, return 1 if A is newer than B and -1 if A is older than B. */ int _ksba_cmp_time (const ksba_isotime_t a, const ksba_isotime_t b) { return strcmp (a, b); } /* Fill the TIMEBUF with the current time (UTC of course). */ void _ksba_current_time (ksba_isotime_t timebuf) { time_t epoch = time (NULL); struct tm *tp; #ifdef HAVE_GMTIME_R struct tm tmbuf; tp = gmtime_r ( &epoch, &tmbuf); #else tp = gmtime ( &epoch ); #endif - sprintf (timebuf,"%04d%02d%02dT%02d%02d%02d", - 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday, - tp->tm_hour, tp->tm_min, tp->tm_sec); + snprintf (timebuf, sizeof (ksba_isotime_t), "%04d%02d%02dT%02d%02d%02d", + 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); } diff --git a/tests/t-oid.c b/tests/t-oid.c index 0fe5944..04156b6 100644 --- a/tests/t-oid.c +++ b/tests/t-oid.c @@ -1,209 +1,210 @@ /* t-oid.c - Test utility for the OID functions * Copyright (C) 2009 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "../src/ksba.h" #define PGM "t-oid" #define BADOID "1.3.6.1.4.1.11591.2.12242973" static void * read_into_buffer (FILE *fp, size_t *r_length) { char *buffer; size_t buflen; size_t nread, bufsize = 0; *r_length = 0; #define NCHUNK 8192 #ifdef HAVE_W32_SYSTEM setmode (fileno(fp), O_BINARY); #endif buffer = NULL; buflen = 0; do { bufsize += NCHUNK; buffer = realloc (buffer, bufsize); if (!buffer) { perror ("realloc failed"); exit (1); } nread = fread (buffer + buflen, 1, NCHUNK, fp); if (nread < NCHUNK && ferror (fp)) { perror ("fread failed"); exit (1); } buflen += nread; } while (nread == NCHUNK); #undef NCHUNK *r_length = buflen; return buffer; } static void test_oid_to_str (void) { struct { unsigned int binlen; unsigned char *bin; char *str; } tests[] = { { 7, "\x02\x82\x06\x01\x0A\x0C\x00", "0.2.262.1.10.12.0" }, { 7, "\x02\x82\x06\x01\x0A\x0C\x01", "0.2.262.1.10.12.1" }, { 7, "\x2A\x86\x48\xCE\x38\x04\x01", "1.2.840.10040.4.1" }, { 7, "\x2A\x86\x48\xCE\x38\x04\x03", "1.2.840.10040.4.3" }, { 10, "\x2B\x06\x01\x04\x01\xDA\x47\x02\x01\x01", "1.3.6.1.4.1.11591.2.1.1" }, { 3, "\x55\x1D\x0E", "2.5.29.14" }, { 9, "\x80\x02\x70\x50\x25\x46\xfd\x0c\xc0", BADOID }, { 1, "\x80", BADOID }, { 2, "\x81\x00", "2.48" }, { 2, "\x81\x01", "2.49" }, { 2, "\x81\x7f", "2.175" }, { 2, "\x81\x80", /* legal encoding? */ "2.48" }, { 2, "\x81\x81\x01", /* legal encoding? */ "2.49" }, { 0, "", "" }, { 0, NULL, NULL } }; int tidx; char *str; for (tidx=0; tests[tidx].bin; tidx++) { str = ksba_oid_to_str (tests[tidx].bin, tests[tidx].binlen); if (!str) { perror ("ksba_oid_to_str failed"); exit (1); } if (strcmp (tests[tidx].str, str)) { fprintf (stderr, "ksba_oid_to_str test %d failed\n", tidx); fprintf (stderr, " got=%s\n", str); fprintf (stderr, " want=%s\n", tests[tidx].str); exit (1); } ksba_free (str); } } int main (int argc, char **argv) { gpg_error_t err; if (argc) { argc--; argv++; } if (!argc) { test_oid_to_str (); } else if (!strcmp (*argv, "--from-str")) { unsigned char *buffer; size_t n, buflen; for (argv++,argc-- ; argc; argc--, argv++) { err = ksba_oid_from_str (*argv, &buffer, &buflen); if (err) { fprintf (stderr, "can't convert `%s': %s\n", *argv, gpg_strerror (err)); return 1; } printf ("%s ->", *argv); for (n=0; n < buflen; n++) printf (" %02X", buffer[n]); putchar ('\n'); free (buffer); + buffer = NULL; } } else if (!strcmp (*argv, "--to-str")) { char *buffer; size_t buflen; char *result; argv++;argc--; buffer = read_into_buffer (stdin, &buflen); result = ksba_oid_to_str (buffer, buflen); free (buffer); printf ("%s\n", result? result:"[malloc failed]"); free (result); } else { fputs ("usage: "PGM" [--from-str|--to-str]\n", stderr); return 1; } return 0; }