Commit message: None
CVE: CVE-2016-4008
CWE: CWE-399
Vulnerability description: The _asn1_extract_der_octet function in lib/decoding.c in GNU Libtasn1 before 4.8, when used without the ASN1_DECODE_FLAG_STRICT_DER flag, allows remote attackers to cause a denial of service (infinite recursion) via a crafted certificate.

Project: savannah
File: decoding.c

_asn1_get_indefinite_length_string (const unsigned char *der,
				    int der_len, int *len)
{
  int len2, len3, counter, indefinite;
  int result;
  unsigned long tag;
  unsigned char class;

  counter = indefinite = 0;

  while (1)
    {
      if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0))
	{
	  counter += 2;
	  DECR_LEN(der_len, 2);

	  indefinite--;
	  if (indefinite <= 0)
	    break;
	  else
	    continue;
	}

      if (asn1_get_tag_der
	  (der + counter, der_len, &class, &len2,
	   &tag) != ASN1_SUCCESS)
	return ASN1_DER_ERROR;

      DECR_LEN(der_len, len2);
      counter += len2;

      len2 = asn1_get_length_der (der + counter, der_len, &len3);
      if (len2 < -1)
	return ASN1_DER_ERROR;

      if (len2 == -1)
	{
	  indefinite++;
	  counter += 1;
          DECR_LEN(der_len, 1);
	}
      else
	{
	  counter += len2 + len3;
          DECR_LEN(der_len, len2+len3);
	}
    }

  *len = counter;
  return ASN1_SUCCESS;

cleanup:
  return result;
}

Project: savannah
File: decoding.c

_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len,
		    char *str, int str_size, unsigned flags)
{
  int len_len, str_len;
  unsigned i;
  unsigned sign_count = 0;
  unsigned dot_count = 0;
  const unsigned char *p;

  if (der_len <= 0 || str == NULL)
    return ASN1_DER_ERROR;

  str_len = asn1_get_length_der (der, der_len, &len_len);
  if (str_len <= 0 || str_size < str_len)
    return ASN1_DER_ERROR;

  /* perform some sanity checks on the data */
  if (str_len < 8)
    {
      warn();
      return ASN1_DER_ERROR;
    }

  if (flags & ASN1_DECODE_FLAG_STRICT_DER)
    {
      p = &der[len_len];
      for (i=0;i<(unsigned)(str_len-1);i++)
         {
           if (isdigit(p[i]) == 0)
             {
               if (type == ASN1_ETYPE_GENERALIZED_TIME)
                 {
                   /* tolerate lax encodings */
                   if (p[i] == '.' && dot_count == 0)
                     {
                       dot_count++;
                       continue;
                     }

               /* This is not really valid DER, but there are
                * structures using that */
                   if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) &&
                       (p[i] == '+' || p[i] == '-') && sign_count == 0)
                     {
                       sign_count++;
                       continue;
                     }
                 }

               warn();
               return ASN1_DER_ERROR;
             }
         }

      if (sign_count == 0 && p[str_len-1] != 'Z')
        {
          warn();
          return ASN1_DER_ERROR;
        }
    }
  memcpy (str, der + len_len, str_len);
  str[str_len] = 0;
  *ret_len = str_len + len_len;

  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

static int append(uint8_t **dst, unsigned *dst_size, const unsigned char *src, unsigned src_size)
{
  *dst = _asn1_realloc(*dst, *dst_size+src_size);
  if (*dst == NULL)
    return ASN1_MEM_ERROR;
  memcpy(*dst + *dst_size, src, src_size);
  *dst_size += src_size;
  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
			unsigned int _der_len, unsigned char **str,
			unsigned int *str_len, unsigned int *ber_len)
{
  int tag_len, len_len;
  const unsigned char *p;
  int der_len = _der_len;
  uint8_t *total = NULL;
  unsigned total_size = 0;
  unsigned char class;
  unsigned long tag;
  unsigned char *out = NULL;
  unsigned out_len;
  long ret;

  if (ber_len) *ber_len = 0;

  if (der == NULL || der_len == 0)
    {
      warn();
      return ASN1_VALUE_NOT_VALID;
    }

  if (ETYPE_OK (etype) == 0)
    {
      warn();
      return ASN1_VALUE_NOT_VALID;
    }

  /* doesn't handle constructed classes */
  if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL)
    {
      warn();
      return ASN1_VALUE_NOT_VALID;
    }

  p = der;
  ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag);
  if (ret != ASN1_SUCCESS)
    {
      warn();
      return ret;
    }

  if (ber_len) *ber_len += tag_len;

  if (tag != ETYPE_TAG (etype))
    {
      warn();
      return ASN1_DER_ERROR;
    }

  p += tag_len;
  der_len -= tag_len;
  if (der_len <= 0)
    return ASN1_DER_ERROR;

  if (class == ASN1_CLASS_STRUCTURED && ETYPE_IS_STRING(etype))
    {

      len_len = 1;
      if (p[0] != 0x80)
        {
          warn();
          return ASN1_DER_ERROR;
        }

      p += len_len;
      der_len -= len_len;
      if (der_len <= 0)
        return ASN1_DER_ERROR;

      if (ber_len) *ber_len += len_len;

      /* decode the available octet strings */
      do
        {
          unsigned tmp_len;

          ret = asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len);
          if (ret != ASN1_SUCCESS)
            {
              free(total);
              return ret;
            }
          p += tmp_len;
          der_len -= tmp_len;
          if (ber_len) *ber_len += tmp_len;

          if (der_len < 2) /* we need the EOC */
            {
              free(total);
              return ASN1_DER_ERROR;
            }

	  if (out_len > 0)
	    {
              ret = append(&total, &total_size, out, out_len);
              free(out);
              if (ret != ASN1_SUCCESS)
                {
                  free(total);
                  return ret;
                }
	    }

	  if (p[0] == 0 && p[1] == 0) /* EOC */
	    {
              if (ber_len) *ber_len += 2;
  	      break;
  	    }
        }
      while(1);
    }
  else if (class == ETYPE_CLASS(etype))
    {
      if (ber_len)
        {
          ret = asn1_get_length_der (p, der_len, &len_len);
          if (ret < 0)
            {
              warn();
              return ASN1_DER_ERROR;
            }
          *ber_len += ret + len_len;
        }

      /* non-string values are decoded as DER */
      ret = asn1_decode_simple_der(etype, der, _der_len, (const unsigned char**)&out, &out_len);
      if (ret != ASN1_SUCCESS)
        return ret;

      ret = append(&total, &total_size, out, out_len);
      if (ret != ASN1_SUCCESS)
        return ret;
    }
  else
    return ASN1_DER_ERROR;

  *str = total;
  *str_len = total_size;

  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

asn1_der_decoding (asn1_node * element, const void *ider, int ider_len,
		   char *errorDescription)
{
  return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription);
}

Project: savannah
File: decoding.c

asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len,
		    unsigned int flags, char *errorDescription)
{
  asn1_node node, p, p2, p3;
  char temp[128];
  int counter, len2, len3, len4, move, ris, tlen;
  struct node_tail_cache_st tcache = {NULL, NULL};
  unsigned char class;
  unsigned long tag;
  int tag_len;
  int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len;
  int inner_tag_len;
  const unsigned char *der = ider;

  node = *element;

  if (errorDescription != NULL)
    errorDescription[0] = 0;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  if (node->type & CONST_OPTION)
    {
      result = ASN1_GENERIC_ERROR;
      warn();
      goto cleanup;
    }

  counter = 0;
  move = DOWN;
  p = node;
  while (1)
    {
      tag_len = 0;
      inner_tag_len = 0;
      ris = ASN1_SUCCESS;
      if (move != UP)
	{
	  if (p->type & CONST_SET)
	    {
	      p2 = _asn1_find_up (p);
	      len2 = p2->tmp_ival;
	      if (len2 == -1)
		{
		  if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])
		    {
		      p = p2;
		      move = UP;
		      counter += 2;
		      DECR_LEN(ider_len, 2);
		      continue;
		    }
		}
	      else if (counter == len2)
		{
		  p = p2;
		  move = UP;
		  continue;
		}
	      else if (counter > len2)
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}
	      p2 = p2->down;
	      while (p2)
		{
		  if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
		    {
		      ris =
			  extract_tag_der_recursive (p2, der + counter,
						     ider_len, &len2, NULL, flags);
		      if (ris == ASN1_SUCCESS)
			{
			  p2->type &= ~CONST_NOT_USED;
			  p = p2;
			  break;
			}
		    }
		  p2 = p2->right;
		}
	      if (p2 == NULL)
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}
	    }

	  /* the position in the DER structure this starts */
	  p->start = counter;
	  p->end = total_len - 1;

	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
	    {
	      p2 = _asn1_find_up (p);
	      len2 = p2->tmp_ival;
	      if (counter == len2)
		{
		  if (p->right)
		    {
		      p2 = p->right;
		      move = RIGHT;
		    }
		  else
		    move = UP;

		  if (p->type & CONST_OPTION)
		    asn1_delete_structure (&p);

		  p = p2;
		  continue;
		}
	    }

	  if (type_field (p->type) == ASN1_ETYPE_CHOICE)
	    {
	      while (p->down)
		{
		  ris =
		      extract_tag_der_recursive (p->down, der + counter,
					         ider_len, &len2, NULL, flags);

		  if (ris == ASN1_SUCCESS)
		    {
		      delete_unneeded_choice_fields(p->down);
		      break;
		    }
		  else if (ris == ASN1_ERROR_TYPE_ANY)
		    {
		      result = ASN1_ERROR_TYPE_ANY;
                      warn();
		      goto cleanup;
		    }
		  else
		    {
		      p2 = p->down;
		      asn1_delete_structure (&p2);
		    }
		}

	      if (p->down == NULL)
		{
		  if (!(p->type & CONST_OPTION))
		    {
		      result = ASN1_DER_ERROR;
                      warn();
		      goto cleanup;
		    }
		}
	      else if (type_field (p->type) != ASN1_ETYPE_CHOICE)
		p = p->down;

	      p->start = counter;
	    }

	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
	    {
	      p2 = _asn1_find_up (p);
	      len2 = p2->tmp_ival;

	      if ((len2 != -1) && (counter > len2))
		ris = ASN1_TAG_ERROR;
	    }

	  if (ris == ASN1_SUCCESS)
	    ris =
	      extract_tag_der_recursive (p, der + counter, ider_len, 
	                                 &tag_len, &inner_tag_len, flags);

	  if (ris != ASN1_SUCCESS)
	    {
	      if (p->type & CONST_OPTION)
		{
		  p->type |= CONST_NOT_USED;
		  move = RIGHT;
		}
	      else if (p->type & CONST_DEFAULT)
		{
		  _asn1_set_value (p, NULL, 0);
		  move = RIGHT;
		}
	      else
		{
		  if (errorDescription != NULL)
		    _asn1_error_description_tag_error (p, errorDescription);

		  result = ASN1_TAG_ERROR;
                  warn();
		  goto cleanup;
		}
	    }
	  else
	    {
	      DECR_LEN(ider_len, tag_len);
	      counter += tag_len;
	    }
	}

      if (ris == ASN1_SUCCESS)
	{
	  switch (type_field (p->type))
	    {
	    case ASN1_ETYPE_NULL:
	      DECR_LEN(ider_len, 1);
	      if (der[counter])
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}
	      counter++;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_BOOLEAN:
	      DECR_LEN(ider_len, 2);

	      if (der[counter++] != 1)
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}
	      if (der[counter++] == 0)
		_asn1_set_value (p, "F", 1);
	      else
		_asn1_set_value (p, "T", 1);
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_INTEGER:
	    case ASN1_ETYPE_ENUMERATED:
	      len2 =
		asn1_get_length_der (der + counter, ider_len, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}

	      DECR_LEN(ider_len, len3+len2);

	      _asn1_set_value (p, der + counter, len3 + len2);
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_OBJECT_ID:
	      result =
		asn1_get_object_id_der (der + counter, ider_len, &len2,
					temp, sizeof (temp));
	      if (result != ASN1_SUCCESS)
	        {
                  warn();
		  goto cleanup;
		}

	      DECR_LEN(ider_len, len2);

	      tlen = strlen (temp);
	      if (tlen > 0)
		_asn1_set_value (p, temp, tlen + 1);

	      counter += len2;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_GENERALIZED_TIME:
	    case ASN1_ETYPE_UTC_TIME:
	      result =
		_asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp,
				    sizeof (temp) - 1, flags);
	      if (result != ASN1_SUCCESS)
	        {
                  warn();
  		  goto cleanup;
  		}

	      DECR_LEN(ider_len, len2);

	      tlen = strlen (temp);
	      if (tlen > 0)
		_asn1_set_value (p, temp, tlen);

	      counter += len2;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_OCTET_STRING:
	      if (counter < inner_tag_len)
	        {
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
	        }

	      result = get_octet_string (p, der + counter, ider_len, 
	      				 der + counter - inner_tag_len, inner_tag_len,
	      				 &len3, flags);
	      if (result != ASN1_SUCCESS)
	        {
                  warn();
		  goto cleanup;
		}

	      DECR_LEN(ider_len, len3);
	      counter += len3;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_GENERALSTRING:
	    case ASN1_ETYPE_NUMERIC_STRING:
	    case ASN1_ETYPE_IA5_STRING:
	    case ASN1_ETYPE_TELETEX_STRING:
	    case ASN1_ETYPE_PRINTABLE_STRING:
	    case ASN1_ETYPE_UNIVERSAL_STRING:
	    case ASN1_ETYPE_BMP_STRING:
	    case ASN1_ETYPE_UTF8_STRING:
	    case ASN1_ETYPE_VISIBLE_STRING:
	    case ASN1_ETYPE_BIT_STRING:
	      len2 =
		asn1_get_length_der (der + counter, ider_len, &len3);
	      if (len2 < 0)
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}

	      DECR_LEN(ider_len, len3+len2);

	      _asn1_set_value (p, der + counter, len3 + len2);
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_SEQUENCE:
	    case ASN1_ETYPE_SET:
	      if (move == UP)
		{
		  len2 = p->tmp_ival;
		  p->tmp_ival = 0;
		  if (len2 == -1)
		    {		/* indefinite length method */
		      DECR_LEN(ider_len, 2);
		      if ((der[counter]) || der[counter + 1])
		        {
		          result = ASN1_DER_ERROR;
                          warn();
		          goto cleanup;
			}
		      counter += 2;
		    }
		  else
		    {		/* definite length method */
		      if (len2 != counter)
			{
			  result = ASN1_DER_ERROR;
                          warn();
			  goto cleanup;
			}
		    }
		  move = RIGHT;
		}
	      else
		{		/* move==DOWN || move==RIGHT */
		  len3 =
		    asn1_get_length_der (der + counter, ider_len, &len2);
                  if (IS_ERR(len3, flags))
		    {
		      result = ASN1_DER_ERROR;
                      warn();
		      goto cleanup;
		    }

	          DECR_LEN(ider_len, len2);
		  counter += len2;

		  if (len3 > 0)
		    {
		      p->tmp_ival = counter + len3;
		      move = DOWN;
		    }
		  else if (len3 == 0)
		    {
		      p2 = p->down;
		      while (p2)
			{
			  if (type_field (p2->type) != ASN1_ETYPE_TAG)
			    {
			      p3 = p2->right;
			      asn1_delete_structure (&p2);
			      p2 = p3;
			    }
			  else
			    p2 = p2->right;
			}
		      move = RIGHT;
		    }
		  else
		    {		/* indefinite length method */
		      p->tmp_ival = -1;
		      move = DOWN;
		    }
		}
	      break;
	    case ASN1_ETYPE_SEQUENCE_OF:
	    case ASN1_ETYPE_SET_OF:
	      if (move == UP)
		{
		  len2 = p->tmp_ival;
		  if (len2 == -1)
		    {		/* indefinite length method */
		      if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1]))
			{
			  _asn1_append_sequence_set (p, &tcache);
			  p = tcache.tail;
			  move = RIGHT;
			  continue;
			}

		      p->tmp_ival = 0;
		      tcache.tail = NULL; /* finished decoding this structure */
		      tcache.head = NULL;
		      DECR_LEN(ider_len, 2);
		      counter += 2;
		    }
		  else
		    {		/* definite length method */
		      if (len2 > counter)
			{
			  _asn1_append_sequence_set (p, &tcache);
			  p = tcache.tail;
			  move = RIGHT;
			  continue;
			}

		      p->tmp_ival = 0;
		      tcache.tail = NULL; /* finished decoding this structure */
		      tcache.head = NULL;

		      if (len2 != counter)
			{
			  result = ASN1_DER_ERROR;
                          warn();
			  goto cleanup;
			}
		    }
		}
	      else
		{		/* move==DOWN || move==RIGHT */
		  len3 =
		    asn1_get_length_der (der + counter, ider_len, &len2);
                  if (IS_ERR(len3, flags))
		    {
		      result = ASN1_DER_ERROR;
                      warn();
		      goto cleanup;
		    }

		  DECR_LEN(ider_len, len2);
		  counter += len2;
		  if (len3)
		    {
		      if (len3 > 0)
			{	/* definite length method */
		          p->tmp_ival = counter + len3;
			}
		      else
			{	/* indefinite length method */
		          p->tmp_ival = -1;
			}
		      p2 = p->down;
		      while ((type_field (p2->type) == ASN1_ETYPE_TAG)
			     || (type_field (p2->type) == ASN1_ETYPE_SIZE))
			p2 = p2->right;
		      if (p2->right == NULL)
			_asn1_append_sequence_set (p, &tcache);
		      p = p2;
		    }
		}
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_ANY:
	      /* Check indefinite lenth method in an EXPLICIT TAG */
              
	      if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) && 
	          tag_len == 2 && (der[counter - 1] == 0x80))
		indefinite = 1;
	      else
	        indefinite = 0;

	      if (asn1_get_tag_der
		  (der + counter, ider_len, &class, &len2,
		   &tag) != ASN1_SUCCESS)
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}

	      DECR_LEN(ider_len, len2);

	      len4 =
		asn1_get_length_der (der + counter + len2,
				     ider_len, &len3);
              if (IS_ERR(len4, flags))
		{
		  result = ASN1_DER_ERROR;
                  warn();
		  goto cleanup;
		}
	      if (len4 != -1) /* definite */
		{
		  len2 += len4;

	          DECR_LEN(ider_len, len4+len3);
		  _asn1_set_value_lv (p, der + counter, len2 + len3);
		  counter += len2 + len3;
		}
	      else /* == -1 */
		{		/* indefinite length */
		  ider_len += len2; /* undo DECR_LEN */

		  if (counter == 0)
		    {
		      result = ASN1_DER_ERROR;
                      warn();
		      goto cleanup;
		    }

		  result =
		    _asn1_get_indefinite_length_string (der + counter, ider_len, &len2);
		  if (result != ASN1_SUCCESS)
		    {
                      warn();
  		      goto cleanup;
  		    }

	          DECR_LEN(ider_len, len2);
		  _asn1_set_value_lv (p, der + counter, len2);
		  counter += len2;

		}

	        /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
	           an indefinite length method. */
	        if (indefinite)
		  {
	            DECR_LEN(ider_len, 2);
		    if (!der[counter] && !der[counter + 1])
		      {
		        counter += 2;
		      }
		    else
		      {
		        result = ASN1_DER_ERROR;
                        warn();
		        goto cleanup;
		      }
		  }

	      move = RIGHT;
	      break;
	    default:
	      move = (move == UP) ? RIGHT : DOWN;
	      break;
	    }
	}

      if (p)
        {
      	  p->end = counter - 1;
        }

      if (p == node && move != DOWN)
	break;

      if (move == DOWN)
	{
	  if (p->down)
	    p = p->down;
	  else
	    move = RIGHT;
	}
      if ((move == RIGHT) && !(p->type & CONST_SET))
	{
	  if (p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  _asn1_delete_not_used (*element);

  if ((ider_len < 0) ||
      (!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0)))
    {
      warn();
      result = ASN1_DER_ERROR;
      goto cleanup;
    }

  *max_ider_len = total_len - ider_len;

  return ASN1_SUCCESS;

cleanup:
  asn1_delete_structure (element);
  return result;
}

Project: savannah
File: decoding.c

asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len,
			    const char *name_element, int *start, int *end)
{
  asn1_node node, node_to_find;
  int result = ASN1_DER_ERROR;

  node = element;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  node_to_find = asn1_find_node (node, name_element);

  if (node_to_find == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  *start = node_to_find->start;
  *end = node_to_find->end;

  if (*start == 0 && *end == 0)
    {
      if (ider == NULL || ider_len == 0)
        return ASN1_GENERIC_ERROR;

      /* it seems asn1_der_decoding() wasn't called before. Do it now */
      result = asn1_der_decoding (&node, ider, ider_len, NULL);
      if (result != ASN1_SUCCESS)
        {
          warn();
          return result;
        }

      node_to_find = asn1_find_node (node, name_element);
      if (node_to_find == NULL)
        return ASN1_ELEMENT_NOT_FOUND;

      *start = node_to_find->start;
      *end = node_to_find->end;
    }

  if (*end < *start)
    return ASN1_GENERIC_ERROR;

  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

asn1_expand_any_defined_by (asn1_node definitions, asn1_node * element)
{
  char name[2 * ASN1_MAX_NAME_SIZE + 1],
    value[ASN1_MAX_NAME_SIZE];
  int retCode = ASN1_SUCCESS, result;
  int len, len2, len3;
  asn1_node p, p2, p3, aux = NULL;
  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
  const char *definitionsName;

  if ((definitions == NULL) || (*element == NULL))
    return ASN1_ELEMENT_NOT_FOUND;

  definitionsName = definitions->name;

  p = *element;
  while (p)
    {

      switch (type_field (p->type))
	{
	case ASN1_ETYPE_ANY:
	  if ((p->type & CONST_DEFINED_BY) && (p->value))
	    {
	      /* search the "DEF_BY" element */
	      p2 = p->down;
	      while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT))
		p2 = p2->right;

	      if (!p2)
		{
		  retCode = ASN1_ERROR_TYPE_ANY;
		  break;
		}

	      p3 = _asn1_find_up (p);

	      if (!p3)
		{
		  retCode = ASN1_ERROR_TYPE_ANY;
		  break;
		}

	      p3 = p3->down;
	      while (p3)
		{
		  if (!(strcmp (p3->name, p2->name)))
		    break;
		  p3 = p3->right;
		}

	      if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ||
		  (p3->value == NULL))
		{

		  p3 = _asn1_find_up (p);
		  p3 = _asn1_find_up (p3);

		  if (!p3)
		    {
		      retCode = ASN1_ERROR_TYPE_ANY;
		      break;
		    }

		  p3 = p3->down;

		  while (p3)
		    {
		      if (!(strcmp (p3->name, p2->name)))
			break;
		      p3 = p3->right;
		    }

		  if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
		      || (p3->value == NULL))
		    {
		      retCode = ASN1_ERROR_TYPE_ANY;
		      break;
		    }
		}

	      /* search the OBJECT_ID into definitions */
	      p2 = definitions->down;
	      while (p2)
		{
		  if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) &&
		      (p2->type & CONST_ASSIGN))
		    {
		      snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);

		      len = ASN1_MAX_NAME_SIZE;
		      result =
			asn1_read_value (definitions, name, value, &len);

		      if ((result == ASN1_SUCCESS)
			  && (!_asn1_strcmp (p3->value, value)))
			{
			  p2 = p2->right;	/* pointer to the structure to
						   use for expansion */
			  while ((p2) && (p2->type & CONST_ASSIGN))
			    p2 = p2->right;

			  if (p2)
			    {
			      snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);

			      result =
				asn1_create_element (definitions, name, &aux);
			      if (result == ASN1_SUCCESS)
				{
				  _asn1_cpy_name (aux, p);
				  len2 =
				    asn1_get_length_der (p->value,
							 p->value_len, &len3);
				  if (len2 < 0)
				    return ASN1_DER_ERROR;

				  result =
				    asn1_der_decoding (&aux, p->value + len3,
						       len2,
						       errorDescription);
				  if (result == ASN1_SUCCESS)
				    {

				      _asn1_set_right (aux, p->right);
				      _asn1_set_right (p, aux);

				      result = asn1_delete_structure (&p);
				      if (result == ASN1_SUCCESS)
					{
					  p = aux;
					  aux = NULL;
					  break;
					}
				      else
					{	/* error with asn1_delete_structure */
					  asn1_delete_structure (&aux);
					  retCode = result;
					  break;
					}
				    }
				  else
				    {	/* error with asn1_der_decoding */
				      retCode = result;
				      break;
				    }
				}
			      else
				{	/* error with asn1_create_element */
				  retCode = result;
				  break;
				}
			    }
			  else
			    {	/* error with the pointer to the structure to exapand */
			      retCode = ASN1_ERROR_TYPE_ANY;
			      break;
			    }
			}
		    }
		  p2 = p2->right;
		}		/* end while */

	      if (!p2)
		{
		  retCode = ASN1_ERROR_TYPE_ANY;
		  break;
		}

	    }
	  break;
	default:
	  break;
	}


      if (p->down)
	{
	  p = p->down;
	}
      else if (p == *element)
	{
	  p = NULL;
	  break;
	}
      else if (p->right)
	p = p->right;
      else
	{
	  while (1)
	    {
	      p = _asn1_find_up (p);
	      if (p == *element)
		{
		  p = NULL;
		  break;
		}
	      if (p->right)
		{
		  p = p->right;
		  break;
		}
	    }
	}
    }

  return retCode;
}

Project: savannah
File: decoding.c

asn1_get_bit_der (const unsigned char *der, int der_len,
		  int *ret_len, unsigned char *str, int str_size,
		  int *bit_len)
{
  int len_len = 0, len_byte;

  if (der_len <= 0)
    return ASN1_GENERIC_ERROR;

  len_byte = asn1_get_length_der (der, der_len, &len_len) - 1;
  if (len_byte < 0)
    return ASN1_DER_ERROR;

  *ret_len = len_byte + len_len + 1;
  *bit_len = len_byte * 8 - der[len_len];

  if (*bit_len < 0)
    return ASN1_DER_ERROR;

  if (str_size >= len_byte)
    {
      if (len_byte > 0 && str)
        memcpy (str, der + len_len + 1, len_byte);
    }
  else
    {
      return ASN1_MEM_ERROR;
    }

  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len)
{
  int ret;
  long err;

  ret = asn1_get_length_der (ber, ber_len, len);
  if (ret == -1)
    {				/* indefinite length method */
      err = _asn1_get_indefinite_length_string (ber + 1, ber_len, &ret);
      if (err != ASN1_SUCCESS)
	return -3;
    }

  return ret;
}

Project: savannah
File: decoding.c

asn1_get_length_der (const unsigned char *der, int der_len, int *len)
{
  unsigned int ans;
  int k, punt, sum;

  *len = 0;
  if (der_len <= 0)
    return 0;

  if (!(der[0] & 128))
    {
      /* short form */
      *len = 1;
      ans = der[0];
    }
  else
    {
      /* Long form */
      k = der[0] & 0x7F;
      punt = 1;
      if (k)
	{			/* definite length method */
	  ans = 0;
	  while (punt <= k && punt < der_len)
	    {
	      if (INT_MULTIPLY_OVERFLOW (ans, 256))
		return -2;
	      ans *= 256;

	      if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt])))
		return -2;
	      ans += der[punt];
	      punt++;
	    }
	}
      else
	{			/* indefinite length method */
	  *len = punt;
	  return -1;
	}

      *len = punt;
    }

  sum = ans;
  if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len)))
    return -2;
  sum += *len;

  if (sum > der_len)
    return -4;

  return ans;
}

Project: savannah
File: decoding.c

asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len,
			char *str, int str_size)
{
  int len_len, len, k;
  int leading;
  char temp[LTOSTR_MAX_SIZE];
  unsigned long val, val1;

  *ret_len = 0;
  if (str && str_size > 0)
    str[0] = 0;			/* no oid */

  if (str == NULL || der_len <= 0)
    return ASN1_GENERIC_ERROR;

  len = asn1_get_length_der (der, der_len, &len_len);

  if (len <= 0 || len + len_len > der_len)
    return ASN1_DER_ERROR;

  val1 = der[len_len] / 40;
  val = der[len_len] - val1 * 40;

  _asn1_str_cpy (str, str_size, _asn1_ltostr (val1, temp));
  _asn1_str_cat (str, str_size, ".");
  _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp));

  val = 0;
  leading = 1;
  for (k = 1; k < len; k++)
    {
      /* X.690 mandates that the leading byte must never be 0x80
       */
      if (leading != 0 && der[len_len + k] == 0x80)
	return ASN1_DER_ERROR;
      leading = 0;

      /* check for wrap around */
      if (INT_LEFT_SHIFT_OVERFLOW (val, 7))
	return ASN1_DER_ERROR;

      val = val << 7;
      val |= der[len_len + k] & 0x7F;

      if (!(der[len_len + k] & 0x80))
	{
	  _asn1_str_cat (str, str_size, ".");
	  _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp));
	  val = 0;
	  leading = 1;
	}
    }

  if (INT_ADD_OVERFLOW (len, len_len))
    return ASN1_DER_ERROR;

  *ret_len = len + len_len;

  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

asn1_get_octet_der (const unsigned char *der, int der_len,
		    int *ret_len, unsigned char *str, int str_size,
		    int *str_len)
{
  int len_len = 0;

  if (der_len <= 0)
    return ASN1_GENERIC_ERROR;

  *str_len = asn1_get_length_der (der, der_len, &len_len);

  if (*str_len < 0)
    return ASN1_DER_ERROR;

  *ret_len = *str_len + len_len;
  if (str_size >= *str_len)
    {
      if (*str_len > 0 && str != NULL)
        memcpy (str, der + len_len, *str_len);
    }
  else
    {
      return ASN1_MEM_ERROR;
    }

  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

asn1_get_tag_der (const unsigned char *der, int der_len,
		  unsigned char *cls, int *len, unsigned long *tag)
{
  unsigned int ris;
  int punt;

  if (der == NULL || der_len < 2 || len == NULL)
    return ASN1_DER_ERROR;

  *cls = der[0] & 0xE0;
  if ((der[0] & 0x1F) != 0x1F)
    {
      /* short form */
      *len = 1;
      ris = der[0] & 0x1F;
    }
  else
    {
      /* Long form */
      punt = 1;
      ris = 0;
      while (punt < der_len && der[punt] & 128)
	{

	  if (INT_MULTIPLY_OVERFLOW (ris, 128))
	    return ASN1_DER_ERROR;
	  ris *= 128;

	  if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
	    return ASN1_DER_ERROR;
	  ris += (der[punt] & 0x7F);
	  punt++;
	}

      if (punt >= der_len)
	return ASN1_DER_ERROR;

      if (INT_MULTIPLY_OVERFLOW (ris, 128))
	return ASN1_DER_ERROR;
      ris *= 128;

      if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
	return ASN1_DER_ERROR;
      ris += (der[punt] & 0x7F);
      punt++;

      *len = punt;
    }

  if (tag)
    *tag = ris;
  return ASN1_SUCCESS;
}

Project: savannah
File: decoding.c

extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len,
		       int *ret_len, int *inner_len, unsigned flags)
{
asn1_node p;
int ris = ASN1_DER_ERROR;

  if (type_field (node->type) == ASN1_ETYPE_CHOICE)
    {
      p = node->down;
      while (p)
        {
          ris = _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, flags);
          if (ris == ASN1_SUCCESS)
            break;
          p = p->right;
	}

      *ret_len = 0;
      return ris;
    }
  else
    return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, flags);
}

Project: savannah
File: decoding.c

get_octet_string (asn1_node node, const unsigned char *der, int der_len,
                        const unsigned char *tag, unsigned tag_len,
                        int *len, unsigned flags)
{
  int len2, len3, counter, tot_len, indefinite;
  int result;
  int orig_der_len = der_len;

  counter = 0;

  if (tag[0] & ASN1_CLASS_STRUCTURED)
    {
      tot_len = 0;

      indefinite = asn1_get_length_der (der, der_len, &len3);
      if (IS_ERR(indefinite, flags))
	{
	  warn();
	  return ASN1_DER_ERROR;
	}

      counter += len3;
      DECR_LEN(der_len, len3);

      if (indefinite >= 0)
	indefinite += len3;

      while (1)
	{
	  if (indefinite == -1)
	    {
	      if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0))
		{
		  counter += 2;
		  DECR_LEN(der_len, 2);
		  break;
		}
	    }
	  else if (counter >= indefinite)
	    break;

          DECR_LEN(der_len, 1);
	  if (der[counter] != ASN1_TAG_OCTET_STRING)
	    {
	      warn();
	      return ASN1_DER_ERROR;
	    }

	  counter++;

	  len2 = asn1_get_length_der (der + counter, der_len, &len3);
	  if (len2 <= 0)
	    {
	      warn();
	      return ASN1_DER_ERROR;
	    }

          DECR_LEN(der_len, len3 + len2);
	  counter += len3 + len2;

	  tot_len += len2;
	}

      /* copy */
      if (node)
	{
	  unsigned char temp[ASN1_MAX_LENGTH_SIZE];
	  int ret;

	  len2 = sizeof (temp);

	  asn1_length_der (tot_len, temp, &len2);
	  _asn1_set_value (node, temp, len2);

	  ret = _asn1_extract_der_octet (node, der, orig_der_len, flags);
	  if (ret != ASN1_SUCCESS)
	    {
	      warn();
	      return ret;
	    }

	}
    }
  else
    {				/* NOT STRUCTURED */
      len2 = asn1_get_length_der (der, der_len, &len3);
      if (len2 < 0)
        {
          warn();
	  return ASN1_DER_ERROR;
	}

      DECR_LEN(der_len, len3+len2);
      counter = len3 + len2;
      if (node)
	_asn1_set_value (node, der, counter);
    }

  *len = counter;
  return ASN1_SUCCESS;

cleanup:
  return result;
}