x509asn1: make utf8asn1str() use dynbuf instead of malloc + memcpy

Closes #12808
diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c
index e49be0a..8407940 100644
--- a/lib/vtls/x509asn1.c
+++ b/lib/vtls/x509asn1.c
@@ -97,6 +97,11 @@
 #define CURL_ASN1_CHARACTER_STRING      29
 #define CURL_ASN1_BMP_STRING            30
 
+/* Max sixes */
+
+#define MAX_X509_STR  10000
+#define MAX_X509_CERT 100000
+
 #ifdef WANT_EXTRACT_CERTINFO
 /* ASN.1 OID table entry. */
 struct Curl_OID {
@@ -322,21 +327,20 @@
 }
 
 /*
- * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the
- * destination buffer dynamically. The allocation size will normally be too
- * large: this is to avoid buffer overflows.
- * Terminate the string with a nul byte and return the converted
- * string length.
+ * Convert from an ASN.1 typed string to UTF8.
+ *
+ * The result is stored in a dynbuf that is inited by the user of this
+ * function.
+ *
+ * Return negative on error.
  */
 static ssize_t
-utf8asn1str(char **to, int type, const char *from, const char *end)
+utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
 {
   size_t inlength = end - from;
   int size = 1;
-  size_t outlength;
-  char *buf;
+  CURLcode result = CURLE_OK;
 
-  *to = NULL;
   switch(type) {
   case CURL_ASN1_BMP_STRING:
     size = 2;
@@ -357,24 +361,18 @@
 
   if(inlength % size)
     return -1;  /* Length inconsistent with character size. */
-  if(inlength / size > (SIZE_T_MAX - 1) / 4)
-    return -1;  /* Too big. */
-  buf = malloc(4 * (inlength / size) + 1);
-  if(!buf)
-    return -1;  /* Not enough memory. */
 
   if(type == CURL_ASN1_UTF8_STRING) {
     /* Just copy. */
-    outlength = inlength;
-    if(outlength)
-      memcpy(buf, from, outlength);
+    if(inlength)
+      result = Curl_dyn_addn(to, from, inlength);
   }
   else {
-    for(outlength = 0; from < end;) {
-      int charsize;
-      unsigned int wc;
+    while(!result && (from < end)) {
+      char buf[4]; /* decode buffer */
+      int charsize = 1;
+      unsigned int wc = 0;
 
-      wc = 0;
       switch(size) {
       case 4:
         wc = (wc << 8) | *(const unsigned char *) from++;
@@ -386,7 +384,6 @@
       default: /* case 1: */
         wc = (wc << 8) | *(const unsigned char *) from++;
       }
-      charsize = 1;
       if(wc >= 0x00000080) {
         if(wc >= 0x00000800) {
           if(wc >= 0x00010000) {
@@ -394,25 +391,23 @@
               free(buf);
               return -1;        /* Invalid char. size for target encoding. */
             }
-            buf[outlength + 3] = (char) (0x80 | (wc & 0x3F));
+            buf[3] = (char) (0x80 | (wc & 0x3F));
             wc = (wc >> 6) | 0x00010000;
             charsize++;
           }
-          buf[outlength + 2] = (char) (0x80 | (wc & 0x3F));
+          buf[2] = (char) (0x80 | (wc & 0x3F));
           wc = (wc >> 6) | 0x00000800;
           charsize++;
         }
-        buf[outlength + 1] = (char) (0x80 | (wc & 0x3F));
+        buf[1] = (char) (0x80 | (wc & 0x3F));
         wc = (wc >> 6) | 0x000000C0;
         charsize++;
       }
-      buf[outlength] = (char) wc;
-      outlength += charsize;
+      buf[0] = (char) wc;
+      result = Curl_dyn_addn(to, buf, charsize);
     }
   }
-  buf[outlength] = '\0';
-  *to = buf;
-  return outlength;
+  return result ? (ssize_t) -1 : (ssize_t)Curl_dyn_len(to);
 }
 
 /*
@@ -421,10 +416,11 @@
  */
 static const char *string2str(int type, const char *beg, const char *end)
 {
-  char *buf;
+  struct dynbuf buf;
+  Curl_dyn_init(&buf, MAX_X509_STR);
   if(utf8asn1str(&buf, type, beg, end) < 0)
     return NULL;
-  return buf;
+  return Curl_dyn_ptr(&buf);
 }
 
 /*
@@ -1053,8 +1049,6 @@
   return buf;
 }
 
-#define MAX_X509_CERT 100000
-
 CURLcode Curl_extract_certinfo(struct Curl_easy *data,
                                int certnum,
                                const char *beg,