/* * Copyright (C) 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011 * Free Software Foundation, Inc. * * This file is part of LIBTASN1. * * The LIBTASN1 library is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include "parser_aux.h" #include "gstr.h" #include "structure.h" #include "element.h" char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not found */ /***********************************************/ /* Type: list_type */ /* Description: type used in the list during */ /* the structure creation. */ /***********************************************/ typedef struct list_struct { ASN1_TYPE node; struct list_struct *next; } list_type; /* Pointer to the first element of the list */ list_type *firstElement = NULL; /******************************************************/ /* Function : _asn1_add_node */ /* Description: creates a new NODE_ASN element and */ /* puts it in the list pointed by firstElement. */ /* Parameters: */ /* type: type of the new element (see TYPE_ */ /* and CONST_ constants). */ /* Return: pointer to the new element. */ /******************************************************/ ASN1_TYPE _asn1_add_node (unsigned int type) { list_type *listElement; ASN1_TYPE punt; punt = (ASN1_TYPE) _asn1_calloc (1, sizeof (struct node_asn_struct)); if (punt == NULL) return NULL; listElement = (list_type *) _asn1_malloc (sizeof (list_type)); if (listElement == NULL) { _asn1_free (punt); return NULL; } listElement->node = punt; listElement->next = firstElement; firstElement = listElement; punt->type = type; return punt; } /** * asn1_find_node: * @pointer: NODE_ASN element pointer. * @name: null terminated string with the element's name to find. * * Searches for an element called @name starting from @pointer. The * name is composed by differents identifiers separated by dots. When * *@pointer has a name, the first identifier must be the name of * *@pointer, otherwise it must be the name of one child of *@pointer. * * Returns: the search result, or %NULL if not found. **/ ASN1_TYPE asn1_find_node (ASN1_TYPE pointer, const char *name) { ASN1_TYPE p; char *n_end, n[ASN1_MAX_NAME_SIZE + 1]; const char *n_start; if (pointer == NULL) return NULL; if (name == NULL) return NULL; p = pointer; n_start = name; if (p->name != NULL) { /* has *pointer got a name ? */ n_end = strchr (n_start, '.'); /* search the first dot */ if (n_end) { memcpy (n, n_start, n_end - n_start); n[n_end - n_start] = 0; n_start = n_end; n_start++; } else { _asn1_str_cpy (n, sizeof (n), n_start); n_start = NULL; } while (p) { if ((p->name) && (!strcmp (p->name, n))) break; else p = p->right; } /* while */ if (p == NULL) return NULL; } else { /* *pointer doesn't have a name */ if (n_start[0] == 0) return p; } while (n_start) { /* Has the end of NAME been reached? */ n_end = strchr (n_start, '.'); /* search the next dot */ if (n_end) { memcpy (n, n_start, n_end - n_start); n[n_end - n_start] = 0; n_start = n_end; n_start++; } else { _asn1_str_cpy (n, sizeof (n), n_start); n_start = NULL; } if (p->down == NULL) return NULL; p = p->down; /* The identifier "?LAST" indicates the last element in the right chain. */ if (!strcmp (n, "?LAST")) { if (p == NULL) return NULL; while (p->right) p = p->right; } else { /* no "?LAST" */ while (p) { if ((p->name) && (!strcmp (p->name, n))) break; else p = p->right; } if (p == NULL) return NULL; } } /* while */ return p; } /******************************************************************/ /* Function : _asn1_set_value */ /* Description: sets the field VALUE in a NODE_ASN element. The */ /* previous value (if exist) will be lost */ /* Parameters: */ /* node: element pointer. */ /* value: pointer to the value that you want to set. */ /* len: character number of value. */ /* Return: pointer to the NODE_ASN element. */ /******************************************************************/ ASN1_TYPE _asn1_set_value (ASN1_TYPE node, const void *value, unsigned int len) { if (node == NULL) return node; if (node->value) { if (node->value != node->small_value) _asn1_free (node->value); node->value = NULL; node->value_len = 0; } if (!len) return node; if (len < sizeof (node->small_value)) { node->value = node->small_value; } else { node->value = _asn1_malloc (len); if (node->value == NULL) return NULL; } node->value_len = len; memcpy (node->value, value, len); return node; } /******************************************************************/ /* Function : _asn1_set_value_octet */ /* Description: sets the field VALUE in a NODE_ASN element. The */ /* previous value (if exist) will be lost. The value */ /* given is stored as an octet string. */ /* Parameters: */ /* node: element pointer. */ /* value: pointer to the value that you want to set. */ /* len: character number of value. */ /* Return: pointer to the NODE_ASN element. */ /******************************************************************/ ASN1_TYPE _asn1_set_value_octet (ASN1_TYPE node, const void *value, unsigned int len) { int len2; void *temp; if (node == NULL) return node; asn1_length_der (len, NULL, &len2); temp = (unsigned char *) _asn1_malloc (len + len2); if (temp == NULL) return NULL; asn1_octet_der (value, len, temp, &len2); return _asn1_set_value_m (node, temp, len2); } /* the same as _asn1_set_value except that it sets an already malloc'ed * value. */ ASN1_TYPE _asn1_set_value_m (ASN1_TYPE node, void *value, unsigned int len) { if (node == NULL) return node; if (node->value) { if (node->value != node->small_value) _asn1_free (node->value); node->value = NULL; node->value_len = 0; } if (!len) return node; node->value = value; node->value_len = len; return node; } /******************************************************************/ /* Function : _asn1_append_value */ /* Description: appends to the field VALUE in a NODE_ASN element. */ /* */ /* Parameters: */ /* node: element pointer. */ /* value: pointer to the value that you want to be appended. */ /* len: character number of value. */ /* Return: pointer to the NODE_ASN element. */ /******************************************************************/ ASN1_TYPE _asn1_append_value (ASN1_TYPE node, const void *value, unsigned int len) { if (node == NULL) return node; if (node->value != NULL && node->value != node->small_value) { /* value is allocated */ int prev_len = node->value_len; node->value_len += len; node->value = _asn1_realloc (node->value, node->value_len); if (node->value == NULL) { node->value_len = 0; return NULL; } memcpy (&node->value[prev_len], value, len); return node; } else if (node->value == node->small_value) { /* value is in node */ int prev_len = node->value_len; node->value_len += len; node->value = _asn1_malloc (node->value_len); if (node->value == NULL) { node->value_len = 0; return NULL; } memcpy (node->value, node->small_value, prev_len); memcpy (&node->value[prev_len], value, len); return node; } else /* node->value == NULL */ return _asn1_set_value (node, value, len); } /******************************************************************/ /* Function : _asn1_set_name */ /* Description: sets the field NAME in a NODE_ASN element. The */ /* previous value (if exist) will be lost */ /* Parameters: */ /* node: element pointer. */ /* name: a null terminated string with the name that you want */ /* to set. */ /* Return: pointer to the NODE_ASN element. */ /******************************************************************/ ASN1_TYPE _asn1_set_name (ASN1_TYPE node, const char *name) { if (node == NULL) return node; if (node->name) { _asn1_free (node->name); node->name = NULL; } if (name == NULL) return node; if (strlen (name)) { node->name = (char *) _asn1_strdup (name); if (node->name == NULL) return NULL; } else node->name = NULL; return node; } /******************************************************************/ /* Function : _asn1_set_right */ /* Description: sets the field RIGHT in a NODE_ASN element. */ /* Parameters: */ /* node: element pointer. */ /* right: pointer to a NODE_ASN element that you want be pointed*/ /* by NODE. */ /* Return: pointer to *NODE. */ /******************************************************************/ ASN1_TYPE _asn1_set_right (ASN1_TYPE node, ASN1_TYPE right) { if (node == NULL) return node; node->right = right; if (right) right->left = node; return node; } /******************************************************************/ /* Function : _asn1_get_right */ /* Description: returns the element pointed by the RIGHT field of */ /* a NODE_ASN element. */ /* Parameters: */ /* node: NODE_ASN element pointer. */ /* Return: field RIGHT of NODE. */ /******************************************************************/ ASN1_TYPE _asn1_get_right (ASN1_TYPE node) { if (node == NULL) return NULL; return node->right; } /******************************************************************/ /* Function : _asn1_get_last_right */ /* Description: return the last element along the right chain. */ /* Parameters: */ /* node: starting element pointer. */ /* Return: pointer to the last element along the right chain. */ /******************************************************************/ ASN1_TYPE _asn1_get_last_right (ASN1_TYPE node) { ASN1_TYPE p; if (node == NULL) return NULL; p = node; while (p->right) p = p->right; return p; } /******************************************************************/ /* Function : _asn1_set_down */ /* Description: sets the field DOWN in a NODE_ASN element. */ /* Parameters: */ /* node: element pointer. */ /* down: pointer to a NODE_ASN element that you want be pointed */ /* by NODE. */ /* Return: pointer to *NODE. */ /******************************************************************/ ASN1_TYPE _asn1_set_down (ASN1_TYPE node, ASN1_TYPE down) { if (node == NULL) return node; node->down = down; if (down) down->left = node; return node; } /******************************************************************/ /* Function : _asn1_get_down */ /* Description: returns the element pointed by the DOWN field of */ /* a NODE_ASN element. */ /* Parameters: */ /* node: NODE_ASN element pointer. */ /* Return: field DOWN of NODE. */ /******************************************************************/ ASN1_TYPE _asn1_get_down (ASN1_TYPE node) { if (node == NULL) return NULL; return node->down; } /******************************************************************/ /* Function : _asn1_get_name */ /* Description: returns the name of a NODE_ASN element. */ /* Parameters: */ /* node: NODE_ASN element pointer. */ /* Return: a null terminated string. */ /******************************************************************/ char * _asn1_get_name (ASN1_TYPE node) { if (node == NULL) return NULL; return node->name; } /******************************************************************/ /* Function : _asn1_mod_type */ /* Description: change the field TYPE of an NODE_ASN element. */ /* The new value is the old one | (bitwise or) the */ /* paramener VALUE. */ /* Parameters: */ /* node: NODE_ASN element pointer. */ /* value: the integer value that must be or-ed with the current */ /* value of field TYPE. */ /* Return: NODE pointer. */ /******************************************************************/ ASN1_TYPE _asn1_mod_type (ASN1_TYPE node, unsigned int value) { if (node == NULL) return node; node->type |= value; return node; } /******************************************************************/ /* Function : _asn1_remove_node */ /* Description: gets free the memory allocated for an NODE_ASN */ /* element (not the elements pointed by it). */ /* Parameters: */ /* node: NODE_ASN element pointer. */ /******************************************************************/ void _asn1_remove_node (ASN1_TYPE node) { if (node == NULL) return; if (node->name != NULL) _asn1_free (node->name); if (node->value != NULL && node->value != node->small_value) _asn1_free (node->value); _asn1_free (node); } /******************************************************************/ /* Function : _asn1_find_up */ /* Description: return the father of the NODE_ASN element. */ /* Parameters: */ /* node: NODE_ASN element pointer. */ /* Return: Null if not found. */ /******************************************************************/ ASN1_TYPE _asn1_find_up (ASN1_TYPE node) { ASN1_TYPE p; if (node == NULL) return NULL; p = node; while ((p->left != NULL) && (p->left->right == p)) p = p->left; return p->left; } /******************************************************************/ /* Function : _asn1_delete_list */ /* Description: deletes the list elements (not the elements */ /* pointed by them). */ /******************************************************************/ void _asn1_delete_list (void) { list_type *listElement; while (firstElement) { listElement = firstElement; firstElement = firstElement->next; _asn1_free (listElement); } } /******************************************************************/ /* Function : _asn1_delete_list_and nodes */ /* Description: deletes the list elements and the elements */ /* pointed by them. */ /******************************************************************/ void _asn1_delete_list_and_nodes (void) { list_type *listElement; while (firstElement) { listElement = firstElement; firstElement = firstElement->next; _asn1_remove_node (listElement->node); _asn1_free (listElement); } } char * _asn1_ltostr (long v, char *str) { long d, r; char temp[20]; int count, k, start; if (v < 0) { str[0] = '-'; start = 1; v = -v; } else start = 0; count = 0; do { d = v / 10; r = v - d * 10; temp[start + count] = '0' + (char) r; count++; v = d; } while (v); for (k = 0; k < count; k++) str[k + start] = temp[start + count - k - 1]; str[count + start] = 0; return str; } /******************************************************************/ /* Function : _asn1_change_integer_value */ /* Description: converts into DER coding the value assign to an */ /* INTEGER constant. */ /* Parameters: */ /* node: root of an ASN1element. */ /* Return: */ /* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ /* otherwise ASN1_SUCCESS */ /******************************************************************/ asn1_retCode _asn1_change_integer_value (ASN1_TYPE node) { ASN1_TYPE p; unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1]; int len; if (node == NULL) return ASN1_ELEMENT_NOT_FOUND; p = node; while (p) { if ((type_field (p->type) == TYPE_INTEGER) && (p->type & CONST_ASSIGN)) { if (p->value) { _asn1_convert_integer (p->value, val, sizeof (val), &len); asn1_octet_der (val, len, val2, &len); _asn1_set_value (p, val2, len); } } if (p->down) { p = p->down; } else { if (p == node) p = NULL; else if (p->right) p = p->right; else { while (1) { p = _asn1_find_up (p); if (p == node) { p = NULL; break; } if (p->right) { p = p->right; break; } } } } } return ASN1_SUCCESS; } /******************************************************************/ /* Function : _asn1_expand_object_id */ /* Description: expand the IDs of an OBJECT IDENTIFIER constant. */ /* Parameters: */ /* node: root of an ASN1 element. */ /* Return: */ /* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ /* otherwise ASN1_SUCCESS */ /******************************************************************/ asn1_retCode _asn1_expand_object_id (ASN1_TYPE node) { ASN1_TYPE p, p2, p3, p4, p5; char name_root[ASN1_MAX_NAME_SIZE], name2[2 * ASN1_MAX_NAME_SIZE + 1]; int move, tlen; if (node == NULL) return ASN1_ELEMENT_NOT_FOUND; _asn1_str_cpy (name_root, sizeof (name_root), node->name); p = node; move = DOWN; while (!((p == node) && (move == UP))) { if (move != UP) { if ((type_field (p->type) == TYPE_OBJECT_ID) && (p->type & CONST_ASSIGN)) { p2 = p->down; if (p2 && (type_field (p2->type) == TYPE_CONSTANT)) { if (p2->value && !isdigit (p2->value[0])) { _asn1_str_cpy (name2, sizeof (name2), name_root); _asn1_str_cat (name2, sizeof (name2), "."); _asn1_str_cat (name2, sizeof (name2), p2->value); p3 = asn1_find_node (node, name2); if (!p3 || (type_field (p3->type) != TYPE_OBJECT_ID) || !(p3->type & CONST_ASSIGN)) return ASN1_ELEMENT_NOT_FOUND; _asn1_set_down (p, p2->right); _asn1_remove_node (p2); p2 = p; p4 = p3->down; while (p4) { if (type_field (p4->type) == TYPE_CONSTANT) { p5 = _asn1_add_node_only (TYPE_CONSTANT); _asn1_set_name (p5, p4->name); tlen = strlen (p4->value); if (tlen > 0) _asn1_set_value (p5, p4->value, tlen + 1); if (p2 == p) { _asn1_set_right (p5, p->down); _asn1_set_down (p, p5); } else { _asn1_set_right (p5, p2->right); _asn1_set_right (p2, p5); } p2 = p5; } p4 = p4->right; } move = DOWN; continue; } } } move = DOWN; } else move = RIGHT; if (move == DOWN) { if (p->down) p = p->down; else move = RIGHT; } if (p == node) { move = UP; continue; } if (move == RIGHT) { if (p->right) p = p->right; else move = UP; } if (move == UP) p = _asn1_find_up (p); } /*******************************/ /* expand DEFAULT */ /*******************************/ p = node; move = DOWN; while (!((p == node) && (move == UP))) { if (move != UP) { if ((type_field (p->type) == TYPE_OBJECT_ID) && (p->type & CONST_DEFAULT)) { p2 = p->down; if (p2 && (type_field (p2->type) == TYPE_DEFAULT)) { _asn1_str_cpy (name2, sizeof (name2), name_root); _asn1_str_cat (name2, sizeof (name2), "."); _asn1_str_cat (name2, sizeof (name2), p2->value); p3 = asn1_find_node (node, name2); if (!p3 || (type_field (p3->type) != TYPE_OBJECT_ID) || !(p3->type & CONST_ASSIGN)) return ASN1_ELEMENT_NOT_FOUND; p4 = p3->down; name2[0] = 0; while (p4) { if (type_field (p4->type) == TYPE_CONSTANT) { if (name2[0]) _asn1_str_cat (name2, sizeof (name2), "."); _asn1_str_cat (name2, sizeof (name2), p4->value); } p4 = p4->right; } tlen = strlen (name2); if (tlen > 0) _asn1_set_value (p2, name2, tlen + 1); } } move = DOWN; } else move = RIGHT; if (move == DOWN) { if (p->down) p = p->down; else move = RIGHT; } if (p == node) { move = UP; continue; } if (move == RIGHT) { if (p->right) p = p->right; else move = UP; } if (move == UP) p = _asn1_find_up (p); } return ASN1_SUCCESS; } /******************************************************************/ /* Function : _asn1_type_set_config */ /* Description: sets the CONST_SET and CONST_NOT_USED properties */ /* in the fields of the SET elements. */ /* Parameters: */ /* node: root of an ASN1 element. */ /* Return: */ /* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ /* otherwise ASN1_SUCCESS */ /******************************************************************/ asn1_retCode _asn1_type_set_config (ASN1_TYPE node) { ASN1_TYPE p, p2; int move; if (node == NULL) return ASN1_ELEMENT_NOT_FOUND; p = node; move = DOWN; while (!((p == node) && (move == UP))) { if (move != UP) { if (type_field (p->type) == TYPE_SET) { p2 = p->down; while (p2) { if (type_field (p2->type) != TYPE_TAG) p2->type |= CONST_SET | CONST_NOT_USED; p2 = p2->right; } } move = DOWN; } else move = RIGHT; if (move == DOWN) { if (p->down) p = p->down; else move = RIGHT; } if (p == node) { move = UP; continue; } if (move == RIGHT) { if (p->right) p = p->right; else move = UP; } if (move == UP) p = _asn1_find_up (p); } return ASN1_SUCCESS; } /******************************************************************/ /* Function : _asn1_check_identifier */ /* Description: checks the definitions of all the identifiers */ /* and the first element of an OBJECT_ID (e.g. {pkix 0 4}). */ /* The _asn1_identifierMissing global variable is filled if */ /* necessary. */ /* Parameters: */ /* node: root of an ASN1 element. */ /* Return: */ /* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ /* ASN1_IDENTIFIER_NOT_FOUND if an identifier is not defined, */ /* otherwise ASN1_SUCCESS */ /******************************************************************/ asn1_retCode _asn1_check_identifier (ASN1_TYPE node) { ASN1_TYPE p, p2; char name2[ASN1_MAX_NAME_SIZE * 2 + 2]; if (node == NULL) return ASN1_ELEMENT_NOT_FOUND; p = node; while (p) { if (type_field (p->type) == TYPE_IDENTIFIER) { _asn1_str_cpy (name2, sizeof (name2), node->name); _asn1_str_cat (name2, sizeof (name2), "."); _asn1_str_cat (name2, sizeof (name2), p->value); p2 = asn1_find_node (node, name2); if (p2 == NULL) { strcpy (_asn1_identifierMissing, p->value); return ASN1_IDENTIFIER_NOT_FOUND; } } else if ((type_field (p->type) == TYPE_OBJECT_ID) && (p->type & CONST_DEFAULT)) { p2 = p->down; if (p2 && (type_field (p2->type) == TYPE_DEFAULT)) { _asn1_str_cpy (name2, sizeof (name2), node->name); _asn1_str_cat (name2, sizeof (name2), "."); _asn1_str_cat (name2, sizeof (name2), p2->value); strcpy (_asn1_identifierMissing, p2->value); p2 = asn1_find_node (node, name2); if (!p2 || (type_field (p2->type) != TYPE_OBJECT_ID) || !(p2->type & CONST_ASSIGN)) return ASN1_IDENTIFIER_NOT_FOUND; else _asn1_identifierMissing[0] = 0; } } else if ((type_field (p->type) == TYPE_OBJECT_ID) && (p->type & CONST_ASSIGN)) { p2 = p->down; if (p2 && (type_field (p2->type) == TYPE_CONSTANT)) { if (p2->value && !isdigit (p2->value[0])) { _asn1_str_cpy (name2, sizeof (name2), node->name); _asn1_str_cat (name2, sizeof (name2), "."); _asn1_str_cat (name2, sizeof (name2), p2->value); strcpy (_asn1_identifierMissing, p2->value); p2 = asn1_find_node (node, name2); if (!p2 || (type_field (p2->type) != TYPE_OBJECT_ID) || !(p2->type & CONST_ASSIGN)) return ASN1_IDENTIFIER_NOT_FOUND; else _asn1_identifierMissing[0] = 0; } } } if (p->down) { p = p->down; } else if (p->right) p = p->right; else { while (1) { p = _asn1_find_up (p); if (p == node) { p = NULL; break; } if (p->right) { p = p->right; break; } } } } return ASN1_SUCCESS; } /******************************************************************/ /* Function : _asn1_set_default_tag */ /* Description: sets the default IMPLICIT or EXPLICIT property in */ /* the tagged elements that don't have this declaration. */ /* Parameters: */ /* node: pointer to a DEFINITIONS element. */ /* Return: */ /* ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to */ /* a DEFINITIONS element, */ /* otherwise ASN1_SUCCESS */ /******************************************************************/ asn1_retCode _asn1_set_default_tag (ASN1_TYPE node) { ASN1_TYPE p; if ((node == NULL) || (type_field (node->type) != TYPE_DEFINITIONS)) return ASN1_ELEMENT_NOT_FOUND; p = node; while (p) { if ((type_field (p->type) == TYPE_TAG) && !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT)) { if (node->type & CONST_EXPLICIT) p->type |= CONST_EXPLICIT; else p->type |= CONST_IMPLICIT; } if (p->down) { p = p->down; } else if (p->right) p = p->right; else { while (1) { p = _asn1_find_up (p); if (p == node) { p = NULL; break; } if (p->right) { p = p->right; break; } } } } return ASN1_SUCCESS; }