Commit message: asn1_find_node: added safety check on asn1_find_node()

This prevents a stack overflow in asn1_find_node() which
is triggered by too long variable names in the definitions
files. That means that applications have to deliberately
pass a too long 'name' constant to asn1_write_value()
and friends.  Reported by Jakub Jirasek.

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
CVE: CVE-2017-6891
CWE: CWE-119
Vulnerability description: Two errors in the "asn1_find_node()" function (lib/parser_aux.c) within GnuTLS libtasn1 version 4.10 can be exploited to cause a stacked-based buffer overflow by tricking a user into processing a specially crafted assignments file via the e.g. asn1Coding utility.

Project: libtasn1
File: parser_aux.c

_asn1_set_default_tag (asn1_node node)
{
  asn1_node p;

  if ((node == NULL) || (type_field (node->type) != ASN1_ETYPE_DEFINITIONS))
    return ASN1_ELEMENT_NOT_FOUND;

  p = node;
  while (p)
    {
      if ((type_field (p->type) == ASN1_ETYPE_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 && p->right)
		{
		  p = p->right;
		  break;
		}
	    }
	}
    }

  return ASN1_SUCCESS;
}

Project: libtasn1
File: parser_aux.c

_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
{
  uint64_t d, r;
  char temp[LTOSTR_MAX_SIZE];
  int count, k, start;
  uint64_t val;

  if (v < 0)
    {
      str[0] = '-';
      start = 1;
      val = -((uint64_t)v);
    }
  else
    {
      val = v;
      start = 0;
    }

  count = 0;
  do
    {
      d = val / 10;
      r = val - d * 10;
      temp[start + count] = '0' + (char) r;
      count++;
      val = d;
    }
  while (val && ((start+count) < LTOSTR_MAX_SIZE-1));

  for (k = 0; k < count; k++)
    str[k + start] = temp[start + count - k - 1];
  str[count + start] = 0;
  return str;
}

Project: libtasn1
File: parser_aux.c

_asn1_set_right (asn1_node node, asn1_node right)
{
  if (node == NULL)
    return node;
  node->right = right;
  if (right)
    right->left = node;
  return node;
}

Project: libtasn1
File: parser_aux.c

_asn1_check_identifier (asn1_node node)
{
  asn1_node p, p2;
  char name2[ASN1_MAX_NAME_SIZE * 2 + 2];

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  p = node;
  while (p)
    {
      if (p->value && type_field (p->type) == ASN1_ETYPE_IDENTIFIER)
	{
	  _asn1_str_cpy (name2, sizeof (name2), node->name);
	  _asn1_str_cat (name2, sizeof (name2), ".");
	  _asn1_str_cat (name2, sizeof (name2), (char *) p->value);
	  p2 = asn1_find_node (node, name2);
	  if (p2 == NULL)
	    {
	      if (p->value)
		_asn1_strcpy (_asn1_identifierMissing, p->value);
	      else
		_asn1_strcpy (_asn1_identifierMissing, "(null)");
	      return ASN1_IDENTIFIER_NOT_FOUND;
	    }
	}
      else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
	       (p->type & CONST_DEFAULT))
	{
	  p2 = p->down;
	  if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT))
	    {
	      _asn1_str_cpy (name2, sizeof (name2), node->name);
	      _asn1_str_cat (name2, sizeof (name2), ".");
	      _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
	      _asn1_strcpy (_asn1_identifierMissing, p2->value);
	      p2 = asn1_find_node (node, name2);
	      if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) ||
		  !(p2->type & CONST_ASSIGN))
		return ASN1_IDENTIFIER_NOT_FOUND;
	      else
		_asn1_identifierMissing[0] = 0;
	    }
	}
      else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
	       (p->type & CONST_ASSIGN))
	{
	  p2 = p->down;
	  if (p2 && (type_field (p2->type) == ASN1_ETYPE_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), (char *) p2->value);
		  _asn1_strcpy (_asn1_identifierMissing, p2->value);
		  p2 = asn1_find_node (node, name2);
		  if (!p2 || (type_field (p2->type) != ASN1_ETYPE_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 && p->right)
		{
		  p = p->right;
		  break;
		}
	    }
	}
    }

  return ASN1_SUCCESS;
}

Project: libtasn1
File: parser_aux.c

_asn1_type_set_config (asn1_node node)
{
  asn1_node 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) == ASN1_ETYPE_SET)
	    {
	      p2 = p->down;
	      while (p2)
		{
		  if (type_field (p2->type) != ASN1_ETYPE_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 && p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  return ASN1_SUCCESS;
}

Project: libtasn1
File: parser_aux.c

asn1_find_node (asn1_node pointer, const char *name)
{
  asn1_node p;
  char *n_end, n[ASN1_MAX_NAME_SIZE + 1];
  const char *n_start;
  unsigned int nsize;
  unsigned int nhash;

  if (pointer == NULL)
    return NULL;

  if (name == NULL)
    return NULL;

  p = pointer;
  n_start = name;

  if (name[0] == '?' && name[1] == 'C' && p->name[0] == '?')
    { /* ?CURRENT */
      n_start = strchr(n_start, '.');
      if (n_start)
        n_start++;
    }
  else if (p->name[0] != 0)
    {				/* has *pointer got a name ? */
      n_end = strchr (n_start, '.');	/* search the first dot */
      if (n_end)
	{
	  nsize = n_end - n_start;
	  if (nsize >= sizeof(n))
		return NULL;

	  memcpy (n, n_start, nsize);
	  n[nsize] = 0;
	  n_start = n_end;
	  n_start++;

	  nhash = hash_pjw_bare (n, nsize);
	}
      else
	{
	  nsize = _asn1_str_cpy (n, sizeof (n), n_start);
	  nhash = hash_pjw_bare (n, nsize);

	  n_start = NULL;
	}

      while (p)
	{
	  if (nhash == p->name_hash && (!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)
	{
	  nsize = n_end - n_start;
	  if (nsize >= sizeof(n))
		return NULL;

	  memcpy (n, n_start, nsize);
	  n[nsize] = 0;
	  n_start = n_end;
	  n_start++;

	  nhash = hash_pjw_bare (n, nsize);
	}
      else
	{
	  nsize = _asn1_str_cpy (n, sizeof (n), n_start);
	  nhash = hash_pjw_bare (n, nsize);
	  n_start = NULL;
	}

      if (p->down == NULL)
	return NULL;

      p = p->down;
      if (p == NULL)
        return NULL;

      /* The identifier "?LAST" indicates the last element
         in the right chain. */
      if (n[0] == '?' && n[1] == 'L') /* ?LAST */
	{
	  while (p->right)
	    p = p->right;
	}
      else
	{			/* no "?LAST" */
	  while (p)
	    {
	      if (p->name_hash == nhash && !strcmp (p->name, n))
		break;
	      else
		p = p->right;
	    }
	}
      if (p == NULL)
        return NULL;
    }				/* while */

  return p;
}

Project: libtasn1
File: parser_aux.c

_asn1_expand_object_id (asn1_node node)
{
  asn1_node 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) == ASN1_ETYPE_OBJECT_ID)
	      && (p->type & CONST_ASSIGN))
	    {
	      p2 = p->down;
	      if (p2 && (type_field (p2->type) == ASN1_ETYPE_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),
				     (char *) p2->value);
		      p3 = asn1_find_node (node, name2);
		      if (!p3
			  || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
			  || !(p3->type & CONST_ASSIGN))
			return ASN1_ELEMENT_NOT_FOUND;
		      _asn1_set_down (p, p2->right);
		      _asn1_remove_node (p2, 0);
		      p2 = p;
		      p4 = p3->down;
		      while (p4)
			{
			  if (type_field (p4->type) == ASN1_ETYPE_CONSTANT)
			    {
			      p5 =
				_asn1_add_single_node (ASN1_ETYPE_CONSTANT);
			      _asn1_set_name (p5, p4->name);
			      if (p4->value)
			        {
			          tlen = _asn1_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 && 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) == ASN1_ETYPE_OBJECT_ID) &&
	      (p->type & CONST_DEFAULT))
	    {
	      p2 = p->down;
	      if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT))
		{
		  _asn1_str_cpy (name2, sizeof (name2), name_root);
		  _asn1_str_cat (name2, sizeof (name2), ".");
		  _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
		  p3 = asn1_find_node (node, name2);
		  if (!p3 || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
		      || !(p3->type & CONST_ASSIGN))
		    return ASN1_ELEMENT_NOT_FOUND;
		  p4 = p3->down;
		  name2[0] = 0;
		  while (p4)
		    {
		      if (type_field (p4->type) == ASN1_ETYPE_CONSTANT)
			{
			  if (p4->value == NULL)
			    return ASN1_VALUE_NOT_FOUND;

			  if (name2[0])
			    _asn1_str_cat (name2, sizeof (name2), ".");
			  _asn1_str_cat (name2, sizeof (name2),
					 (char *) 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 && p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  return ASN1_SUCCESS;
}

Project: libtasn1
File: parser_aux.c

_asn1_append_value (asn1_node node, const void *value, unsigned int len)
{
  if (node == NULL)
    return node;

  if (node->value == NULL)
    return _asn1_set_value (node, value, len);

  if (len == 0)
    return node;

  if (node->value == node->small_value)
    {
      /* value is in node */
      int prev_len = node->value_len;
      node->value_len += len;
      node->value = malloc (node->value_len);
      if (node->value == NULL)
	{
	  node->value_len = 0;
	  return NULL;
	}

      if (prev_len > 0)
        memcpy (node->value, node->small_value, prev_len);

      memcpy (&node->value[prev_len], value, len);

      return node;
    }
  else /* 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;
    }
}

Project: libtasn1
File: parser_aux.c

_asn1_change_integer_value (asn1_node node)
{
  asn1_node 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) == ASN1_ETYPE_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 && p->right)
		    {
		      p = p->right;
		      break;
		    }
		}
	    }
	}
    }

  return ASN1_SUCCESS;
}