quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

x509asn1.c (36098B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "../curl_setup.h"
     26 
     27 #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
     28   defined(USE_MBEDTLS) || defined(USE_RUSTLS)
     29 
     30 #if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \
     31   defined(USE_WOLFSSL) || defined(USE_RUSTLS)
     32 #define WANT_PARSEX509 /* uses Curl_parseX509() */
     33 #endif
     34 
     35 #if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \
     36   defined(USE_RUSTLS)
     37 #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */
     38 #endif
     39 
     40 #include <curl/curl.h>
     41 #include "../urldata.h"
     42 #include "../curl_ctype.h"
     43 #include "hostcheck.h"
     44 #include "vtls.h"
     45 #include "vtls_int.h"
     46 #include "../sendf.h"
     47 #include "../curlx/inet_pton.h"
     48 #include "../curlx/base64.h"
     49 #include "x509asn1.h"
     50 #include "../curlx/dynbuf.h"
     51 
     52 /* The last 3 #include files should be in this order */
     53 #include "../curl_printf.h"
     54 #include "../curl_memory.h"
     55 #include "../memdebug.h"
     56 
     57 /*
     58  * Constants.
     59  */
     60 
     61 /* Largest supported ASN.1 structure. */
     62 #define CURL_ASN1_MAX                   ((size_t) 0x40000)      /* 256K */
     63 
     64 /* ASN.1 classes. */
     65 /* #define CURL_ASN1_UNIVERSAL             0 */
     66 /* #define CURL_ASN1_APPLICATION           1 */
     67 /* #define CURL_ASN1_CONTEXT_SPECIFIC      2 */
     68 /* #define CURL_ASN1_PRIVATE               3 */
     69 
     70 /* ASN.1 types. */
     71 #define CURL_ASN1_BOOLEAN               1
     72 #define CURL_ASN1_INTEGER               2
     73 #define CURL_ASN1_BIT_STRING            3
     74 #define CURL_ASN1_OCTET_STRING          4
     75 #define CURL_ASN1_NULL                  5
     76 #define CURL_ASN1_OBJECT_IDENTIFIER     6
     77 /* #define CURL_ASN1_OBJECT_DESCRIPTOR     7 */
     78 /* #define CURL_ASN1_INSTANCE_OF           8 */
     79 /* #define CURL_ASN1_REAL                  9 */
     80 #define CURL_ASN1_ENUMERATED            10
     81 /* #define CURL_ASN1_EMBEDDED              11 */
     82 #define CURL_ASN1_UTF8_STRING           12
     83 /* #define CURL_ASN1_RELATIVE_OID          13 */
     84 /* #define CURL_ASN1_SEQUENCE              16 */
     85 /* #define CURL_ASN1_SET                   17 */
     86 #define CURL_ASN1_NUMERIC_STRING        18
     87 #define CURL_ASN1_PRINTABLE_STRING      19
     88 #define CURL_ASN1_TELETEX_STRING        20
     89 /* #define CURL_ASN1_VIDEOTEX_STRING       21 */
     90 #define CURL_ASN1_IA5_STRING            22
     91 #define CURL_ASN1_UTC_TIME              23
     92 #define CURL_ASN1_GENERALIZED_TIME      24
     93 /* #define CURL_ASN1_GRAPHIC_STRING        25 */
     94 #define CURL_ASN1_VISIBLE_STRING        26
     95 /* #define CURL_ASN1_GENERAL_STRING        27 */
     96 #define CURL_ASN1_UNIVERSAL_STRING      28
     97 /* #define CURL_ASN1_CHARACTER_STRING      29 */
     98 #define CURL_ASN1_BMP_STRING            30
     99 
    100 
    101 #ifdef WANT_EXTRACT_CERTINFO
    102 /* ASN.1 OID table entry. */
    103 struct Curl_OID {
    104   const char *numoid;  /* Dotted-numeric OID. */
    105   const char *textoid; /* OID name. */
    106 };
    107 
    108 /* ASN.1 OIDs. */
    109 static const struct Curl_OID OIDtable[] = {
    110   { "1.2.840.10040.4.1",        "dsa" },
    111   { "1.2.840.10040.4.3",        "dsa-with-sha1" },
    112   { "1.2.840.10045.2.1",        "ecPublicKey" },
    113   { "1.2.840.10045.3.0.1",      "c2pnb163v1" },
    114   { "1.2.840.10045.4.1",        "ecdsa-with-SHA1" },
    115   { "1.2.840.10045.4.3.1",      "ecdsa-with-SHA224" },
    116   { "1.2.840.10045.4.3.2",      "ecdsa-with-SHA256" },
    117   { "1.2.840.10045.4.3.3",      "ecdsa-with-SHA384" },
    118   { "1.2.840.10045.4.3.4",      "ecdsa-with-SHA512" },
    119   { "1.2.840.10046.2.1",        "dhpublicnumber" },
    120   { "1.2.840.113549.1.1.1",     "rsaEncryption" },
    121   { "1.2.840.113549.1.1.2",     "md2WithRSAEncryption" },
    122   { "1.2.840.113549.1.1.4",     "md5WithRSAEncryption" },
    123   { "1.2.840.113549.1.1.5",     "sha1WithRSAEncryption" },
    124   { "1.2.840.113549.1.1.10",    "RSASSA-PSS" },
    125   { "1.2.840.113549.1.1.14",    "sha224WithRSAEncryption" },
    126   { "1.2.840.113549.1.1.11",    "sha256WithRSAEncryption" },
    127   { "1.2.840.113549.1.1.12",    "sha384WithRSAEncryption" },
    128   { "1.2.840.113549.1.1.13",    "sha512WithRSAEncryption" },
    129   { "1.2.840.113549.2.2",       "md2" },
    130   { "1.2.840.113549.2.5",       "md5" },
    131   { "1.3.14.3.2.26",            "sha1" },
    132   { "2.5.4.3",                  "CN" },
    133   { "2.5.4.4",                  "SN" },
    134   { "2.5.4.5",                  "serialNumber" },
    135   { "2.5.4.6",                  "C" },
    136   { "2.5.4.7",                  "L" },
    137   { "2.5.4.8",                  "ST" },
    138   { "2.5.4.9",                  "streetAddress" },
    139   { "2.5.4.10",                 "O" },
    140   { "2.5.4.11",                 "OU" },
    141   { "2.5.4.12",                 "title" },
    142   { "2.5.4.13",                 "description" },
    143   { "2.5.4.17",                 "postalCode" },
    144   { "2.5.4.41",                 "name" },
    145   { "2.5.4.42",                 "givenName" },
    146   { "2.5.4.43",                 "initials" },
    147   { "2.5.4.44",                 "generationQualifier" },
    148   { "2.5.4.45",                 "X500UniqueIdentifier" },
    149   { "2.5.4.46",                 "dnQualifier" },
    150   { "2.5.4.65",                 "pseudonym" },
    151   { "1.2.840.113549.1.9.1",     "emailAddress" },
    152   { "2.5.4.72",                 "role" },
    153   { "2.5.29.17",                "subjectAltName" },
    154   { "2.5.29.18",                "issuerAltName" },
    155   { "2.5.29.19",                "basicConstraints" },
    156   { "2.16.840.1.101.3.4.2.4",   "sha224" },
    157   { "2.16.840.1.101.3.4.2.1",   "sha256" },
    158   { "2.16.840.1.101.3.4.2.2",   "sha384" },
    159   { "2.16.840.1.101.3.4.2.3",   "sha512" },
    160   { "1.2.840.113549.1.9.2",     "unstructuredName" },
    161   { (const char *) NULL,        (const char *) NULL }
    162 };
    163 
    164 #endif /* WANT_EXTRACT_CERTINFO */
    165 
    166 /*
    167  * Lightweight ASN.1 parser.
    168  * In particular, it does not check for syntactic/lexical errors.
    169  * It is intended to support certificate information gathering for SSL backends
    170  * that offer a mean to get certificates as a whole, but do not supply
    171  * entry points to get particular certificate sub-fields.
    172  * Please note there is no pretension here to rewrite a full SSL library.
    173  */
    174 
    175 static const char *getASN1Element(struct Curl_asn1Element *elem,
    176                                   const char *beg, const char *end)
    177   WARN_UNUSED_RESULT;
    178 
    179 #define CURL_ASN1_MAX_RECURSIONS    16
    180 
    181 static const char *getASN1Element_(struct Curl_asn1Element *elem,
    182                                    const char *beg, const char *end,
    183                                    size_t lvl)
    184 {
    185   unsigned char b;
    186   size_t len;
    187   struct Curl_asn1Element lelem;
    188 
    189   /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg'
    190      ending at `end'.
    191      Returns a pointer in source string after the parsed element, or NULL
    192      if an error occurs. */
    193   if(!beg || !end || beg >= end || !*beg ||
    194      ((size_t)(end - beg) > CURL_ASN1_MAX) ||
    195      lvl >=  CURL_ASN1_MAX_RECURSIONS)
    196     return NULL;
    197 
    198   /* Process header byte. */
    199   elem->header = beg;
    200   b = (unsigned char) *beg++;
    201   elem->constructed = (b & 0x20) != 0;
    202   elem->class = (b >> 6) & 3;
    203   b &= 0x1F;
    204   if(b == 0x1F)
    205     return NULL; /* Long tag values not supported here. */
    206   elem->tag = b;
    207 
    208   /* Process length. */
    209   if(beg >= end)
    210     return NULL;
    211   b = (unsigned char) *beg++;
    212   if(!(b & 0x80))
    213     len = b;
    214   else if(!(b &= 0x7F)) {
    215     /* Unspecified length. Since we have all the data, we can determine the
    216        effective length by skipping element until an end element is found. */
    217     if(!elem->constructed)
    218       return NULL;
    219     elem->beg = beg;
    220     while(beg < end && *beg) {
    221       beg = getASN1Element_(&lelem, beg, end, lvl + 1);
    222       if(!beg)
    223         return NULL;
    224     }
    225     if(beg >= end)
    226       return NULL;
    227     elem->end = beg;
    228     return beg + 1;
    229   }
    230   else if((unsigned)b > (size_t)(end - beg))
    231     return NULL; /* Does not fit in source. */
    232   else {
    233     /* Get long length. */
    234     len = 0;
    235     do {
    236       if(len & 0xFF000000L)
    237         return NULL;  /* Lengths > 32 bits are not supported. */
    238       len = (len << 8) | (unsigned char) *beg++;
    239     } while(--b);
    240   }
    241   if(len > (size_t)(end - beg))
    242     return NULL;  /* Element data does not fit in source. */
    243   elem->beg = beg;
    244   elem->end = beg + len;
    245   return elem->end;
    246 }
    247 
    248 static const char *getASN1Element(struct Curl_asn1Element *elem,
    249                                   const char *beg, const char *end)
    250 {
    251   return getASN1Element_(elem, beg, end, 0);
    252 }
    253 
    254 #ifdef WANT_EXTRACT_CERTINFO
    255 
    256 /*
    257  * Search the null-terminated OID or OID identifier in local table.
    258  * Return the table entry pointer or NULL if not found.
    259  */
    260 static const struct Curl_OID *searchOID(const char *oid)
    261 {
    262   const struct Curl_OID *op;
    263   for(op = OIDtable; op->numoid; op++)
    264     if(!strcmp(op->numoid, oid) || curl_strequal(op->textoid, oid))
    265       return op;
    266 
    267   return NULL;
    268 }
    269 
    270 #ifdef UNITTESTS
    271 /* used by unit1657.c */
    272 CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
    273                                   const char *beg, const char *end)
    274 {
    275   if(getASN1Element(elem, beg, end))
    276     return CURLE_OK;
    277   return CURLE_BAD_FUNCTION_ARGUMENT;
    278 }
    279 #endif
    280 
    281 /*
    282  * Convert an ASN.1 Boolean value into its string representation.
    283  *
    284  * Return error code.
    285  */
    286 
    287 static CURLcode bool2str(struct dynbuf *store,
    288                          const char *beg, const char *end)
    289 {
    290   if(end - beg != 1)
    291     return CURLE_BAD_FUNCTION_ARGUMENT;
    292   return curlx_dyn_add(store, *beg ? "TRUE": "FALSE");
    293 }
    294 
    295 /*
    296  * Convert an ASN.1 octet string to a printable string.
    297  *
    298  * Return error code.
    299  */
    300 static CURLcode octet2str(struct dynbuf *store,
    301                           const char *beg, const char *end)
    302 {
    303   CURLcode result = CURLE_OK;
    304 
    305   while(!result && beg < end)
    306     result = curlx_dyn_addf(store, "%02x:", (unsigned char) *beg++);
    307 
    308   return result;
    309 }
    310 
    311 static CURLcode bit2str(struct dynbuf *store,
    312                         const char *beg, const char *end)
    313 {
    314   /* Convert an ASN.1 bit string to a printable string. */
    315 
    316   if(++beg > end)
    317     return CURLE_BAD_FUNCTION_ARGUMENT;
    318   return octet2str(store, beg, end);
    319 }
    320 
    321 /*
    322  * Convert an ASN.1 integer value into its string representation.
    323  *
    324  * Returns error.
    325  */
    326 static CURLcode int2str(struct dynbuf *store,
    327                         const char *beg, const char *end)
    328 {
    329   unsigned int val = 0;
    330   size_t n = end - beg;
    331 
    332   if(!n)
    333     return CURLE_BAD_FUNCTION_ARGUMENT;
    334 
    335   if(n > 4)
    336     return octet2str(store, beg, end);
    337 
    338   /* Represent integers <= 32-bit as a single value. */
    339   if(*beg & 0x80)
    340     val = ~val;
    341 
    342   do
    343     val = (val << 8) | *(const unsigned char *) beg++;
    344   while(beg < end);
    345   return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
    346 }
    347 
    348 /*
    349  * Convert from an ASN.1 typed string to UTF8.
    350  *
    351  * The result is stored in a dynbuf that is inited by the user of this
    352  * function.
    353  *
    354  * Returns error.
    355  */
    356 static CURLcode
    357 utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
    358 {
    359   size_t inlength = end - from;
    360   int size = 1;
    361   CURLcode result = CURLE_OK;
    362 
    363   switch(type) {
    364   case CURL_ASN1_BMP_STRING:
    365     size = 2;
    366     break;
    367   case CURL_ASN1_UNIVERSAL_STRING:
    368     size = 4;
    369     break;
    370   case CURL_ASN1_NUMERIC_STRING:
    371   case CURL_ASN1_PRINTABLE_STRING:
    372   case CURL_ASN1_TELETEX_STRING:
    373   case CURL_ASN1_IA5_STRING:
    374   case CURL_ASN1_VISIBLE_STRING:
    375   case CURL_ASN1_UTF8_STRING:
    376     break;
    377   default:
    378     return CURLE_BAD_FUNCTION_ARGUMENT;  /* Conversion not supported. */
    379   }
    380 
    381   if(inlength % size)
    382     /* Length inconsistent with character size. */
    383     return CURLE_BAD_FUNCTION_ARGUMENT;
    384 
    385   if(type == CURL_ASN1_UTF8_STRING) {
    386     /* Just copy. */
    387     if(inlength)
    388       result = curlx_dyn_addn(to, from, inlength);
    389   }
    390   else {
    391     while(!result && (from < end)) {
    392       char buf[4]; /* decode buffer */
    393       size_t charsize = 1;
    394       unsigned int wc = 0;
    395 
    396       switch(size) {
    397       case 4:
    398         wc = (wc << 8) | *(const unsigned char *) from++;
    399         wc = (wc << 8) | *(const unsigned char *) from++;
    400         FALLTHROUGH();
    401       case 2:
    402         wc = (wc << 8) | *(const unsigned char *) from++;
    403         FALLTHROUGH();
    404       default: /* case 1: */
    405         wc = (wc << 8) | *(const unsigned char *) from++;
    406       }
    407       if(wc >= 0x00000080) {
    408         if(wc >= 0x00000800) {
    409           if(wc >= 0x00010000) {
    410             if(wc >= 0x00200000) {
    411               /* Invalid char. size for target encoding. */
    412               return CURLE_WEIRD_SERVER_REPLY;
    413             }
    414             buf[3] = (char) (0x80 | (wc & 0x3F));
    415             wc = (wc >> 6) | 0x00010000;
    416             charsize++;
    417           }
    418           buf[2] = (char) (0x80 | (wc & 0x3F));
    419           wc = (wc >> 6) | 0x00000800;
    420           charsize++;
    421         }
    422         buf[1] = (char) (0x80 | (wc & 0x3F));
    423         wc = (wc >> 6) | 0x000000C0;
    424         charsize++;
    425       }
    426       buf[0] = (char) wc;
    427       result = curlx_dyn_addn(to, buf, charsize);
    428     }
    429   }
    430   return result;
    431 }
    432 
    433 /*
    434  * Convert an ASN.1 OID into its dotted string representation.
    435  *
    436  * Return error code.
    437  */
    438 static CURLcode encodeOID(struct dynbuf *store,
    439                           const char *beg, const char *end)
    440 {
    441   unsigned int x;
    442   unsigned int y;
    443   CURLcode result = CURLE_OK;
    444 
    445   /* Process the first two numbers. */
    446   y = *(const unsigned char *) beg++;
    447   x = y / 40;
    448   y -= x * 40;
    449 
    450   result = curlx_dyn_addf(store, "%u.%u", x, y);
    451   if(result)
    452     return result;
    453 
    454   /* Process the trailing numbers. */
    455   while(beg < end) {
    456     x = 0;
    457     do {
    458       if(x & 0xFF000000)
    459         return 0;
    460       y = *(const unsigned char *) beg++;
    461       x = (x << 7) | (y & 0x7F);
    462     } while(y & 0x80);
    463     result = curlx_dyn_addf(store, ".%u", x);
    464   }
    465   return result;
    466 }
    467 
    468 /*
    469  * Convert an ASN.1 OID into its dotted or symbolic string representation.
    470  *
    471  * Return error code.
    472  */
    473 
    474 static CURLcode OID2str(struct dynbuf *store,
    475                         const char *beg, const char *end, bool symbolic)
    476 {
    477   CURLcode result = CURLE_OK;
    478   if(beg < end) {
    479     if(symbolic) {
    480       struct dynbuf buf;
    481       curlx_dyn_init(&buf, CURL_X509_STR_MAX);
    482       result = encodeOID(&buf, beg, end);
    483 
    484       if(!result) {
    485         const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf));
    486         if(op)
    487           result = curlx_dyn_add(store, op->textoid);
    488         else
    489           result = curlx_dyn_add(store, curlx_dyn_ptr(&buf));
    490         curlx_dyn_free(&buf);
    491       }
    492     }
    493     else
    494       result = encodeOID(store, beg, end);
    495   }
    496   return result;
    497 }
    498 
    499 static CURLcode GTime2str(struct dynbuf *store,
    500                           const char *beg, const char *end)
    501 {
    502   const char *tzp;
    503   const char *fracp;
    504   char sec1, sec2;
    505   size_t fracl;
    506   size_t tzl;
    507   const char *sep = "";
    508 
    509   /* Convert an ASN.1 Generalized time to a printable string.
    510      Return the dynamically allocated string, or NULL if an error occurs. */
    511 
    512   for(fracp = beg; fracp < end && ISDIGIT(*fracp); fracp++)
    513     ;
    514 
    515   /* Get seconds digits. */
    516   sec1 = '0';
    517   switch(fracp - beg - 12) {
    518   case 0:
    519     sec2 = '0';
    520     break;
    521   case 2:
    522     sec1 = fracp[-2];
    523     FALLTHROUGH();
    524   case 1:
    525     sec2 = fracp[-1];
    526     break;
    527   default:
    528     return CURLE_BAD_FUNCTION_ARGUMENT;
    529   }
    530 
    531   /* timezone follows optional fractional seconds. */
    532   tzp = fracp;
    533   fracl = 0; /* no fractional seconds detected so far */
    534   if(fracp < end && (*fracp == '.' || *fracp == ',')) {
    535     /* Have fractional seconds, e.g. "[.,]\d+". How many? */
    536     fracp++; /* should be a digit char or BAD ARGUMENT */
    537     tzp = fracp;
    538     while(tzp < end && ISDIGIT(*tzp))
    539       tzp++;
    540     if(tzp == fracp) /* never looped, no digit after [.,] */
    541       return CURLE_BAD_FUNCTION_ARGUMENT;
    542     fracl = tzp - fracp; /* number of fractional sec digits */
    543     DEBUGASSERT(fracl > 0);
    544     /* Strip trailing zeroes in fractional seconds.
    545      * May reduce fracl to 0 if only '0's are present. */
    546     while(fracl && fracp[fracl - 1] == '0')
    547       fracl--;
    548   }
    549 
    550   /* Process timezone. */
    551   if(tzp >= end) {
    552     tzp = "";
    553     tzl = 0;
    554   }
    555   else if(*tzp == 'Z') {
    556     sep = " ";
    557     tzp = "GMT";
    558     tzl = 3;
    559   }
    560   else if((*tzp == '+') || (*tzp == '-')) {
    561     sep = " UTC";
    562     tzl = end - tzp;
    563   }
    564   else {
    565     sep = " ";
    566     tzl = end - tzp;
    567   }
    568 
    569   return curlx_dyn_addf(store,
    570                         "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
    571                         beg, beg + 4, beg + 6,
    572                         beg + 8, beg + 10, sec1, sec2,
    573                         fracl ? ".": "", (int)fracl, fracp,
    574                         sep, (int)tzl, tzp);
    575 }
    576 
    577 #ifdef UNITTESTS
    578 /* used by unit1656.c */
    579 CURLcode Curl_x509_GTime2str(struct dynbuf *store,
    580                              const char *beg, const char *end)
    581 {
    582   return GTime2str(store, beg, end);
    583 }
    584 #endif
    585 
    586 /*
    587  * Convert an ASN.1 UTC time to a printable string.
    588  *
    589  * Return error code.
    590  */
    591 static CURLcode UTime2str(struct dynbuf *store,
    592                           const char *beg, const char *end)
    593 {
    594   const char *tzp;
    595   size_t tzl;
    596   const char *sec;
    597 
    598   for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++)
    599     ;
    600   /* Get the seconds. */
    601   sec = beg + 10;
    602   switch(tzp - sec) {
    603   case 0:
    604     sec = "00";
    605     FALLTHROUGH();
    606   case 2:
    607     break;
    608   default:
    609     return CURLE_BAD_FUNCTION_ARGUMENT;
    610   }
    611 
    612   /* Process timezone. */
    613   if(tzp >= end)
    614     return CURLE_BAD_FUNCTION_ARGUMENT;
    615   if(*tzp == 'Z') {
    616     tzp = "GMT";
    617     end = tzp + 3;
    618   }
    619   else
    620     tzp++;
    621 
    622   tzl = end - tzp;
    623   return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
    624                         20 - (*beg >= '5'), beg, beg + 2, beg + 4,
    625                         beg + 6, beg + 8, sec,
    626                         (int)tzl, tzp);
    627 }
    628 
    629 /*
    630  * Convert an ASN.1 element to a printable string.
    631  *
    632  * Return error
    633  */
    634 static CURLcode ASN1tostr(struct dynbuf *store,
    635                           struct Curl_asn1Element *elem, int type)
    636 {
    637   CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
    638   if(elem->constructed)
    639     return result; /* No conversion of structured elements. */
    640 
    641   if(!type)
    642     type = elem->tag;   /* Type not forced: use element tag as type. */
    643 
    644   switch(type) {
    645   case CURL_ASN1_BOOLEAN:
    646     result = bool2str(store, elem->beg, elem->end);
    647     break;
    648   case CURL_ASN1_INTEGER:
    649   case CURL_ASN1_ENUMERATED:
    650     result = int2str(store, elem->beg, elem->end);
    651     break;
    652   case CURL_ASN1_BIT_STRING:
    653     result = bit2str(store, elem->beg, elem->end);
    654     break;
    655   case CURL_ASN1_OCTET_STRING:
    656     result = octet2str(store, elem->beg, elem->end);
    657     break;
    658   case CURL_ASN1_NULL:
    659     result = curlx_dyn_addn(store, "", 1);
    660     break;
    661   case CURL_ASN1_OBJECT_IDENTIFIER:
    662     result = OID2str(store, elem->beg, elem->end, TRUE);
    663     break;
    664   case CURL_ASN1_UTC_TIME:
    665     result = UTime2str(store, elem->beg, elem->end);
    666     break;
    667   case CURL_ASN1_GENERALIZED_TIME:
    668     result = GTime2str(store, elem->beg, elem->end);
    669     break;
    670   case CURL_ASN1_UTF8_STRING:
    671   case CURL_ASN1_NUMERIC_STRING:
    672   case CURL_ASN1_PRINTABLE_STRING:
    673   case CURL_ASN1_TELETEX_STRING:
    674   case CURL_ASN1_IA5_STRING:
    675   case CURL_ASN1_VISIBLE_STRING:
    676   case CURL_ASN1_UNIVERSAL_STRING:
    677   case CURL_ASN1_BMP_STRING:
    678     result = utf8asn1str(store, type, elem->beg, elem->end);
    679     break;
    680   }
    681 
    682   return result;
    683 }
    684 
    685 /*
    686  * ASCII encode distinguished name at `dn' into the store dynbuf.
    687  *
    688  * Returns error.
    689  */
    690 static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
    691 {
    692   struct Curl_asn1Element rdn;
    693   struct Curl_asn1Element atv;
    694   struct Curl_asn1Element oid;
    695   struct Curl_asn1Element value;
    696   const char *p1;
    697   const char *p2;
    698   const char *p3;
    699   const char *str;
    700   CURLcode result = CURLE_OK;
    701   bool added = FALSE;
    702   struct dynbuf temp;
    703   curlx_dyn_init(&temp, CURL_X509_STR_MAX);
    704 
    705   for(p1 = dn->beg; p1 < dn->end;) {
    706     p1 = getASN1Element(&rdn, p1, dn->end);
    707     if(!p1) {
    708       result = CURLE_BAD_FUNCTION_ARGUMENT;
    709       goto error;
    710     }
    711     for(p2 = rdn.beg; p2 < rdn.end;) {
    712       p2 = getASN1Element(&atv, p2, rdn.end);
    713       if(!p2) {
    714         result = CURLE_BAD_FUNCTION_ARGUMENT;
    715         goto error;
    716       }
    717       p3 = getASN1Element(&oid, atv.beg, atv.end);
    718       if(!p3) {
    719         result = CURLE_BAD_FUNCTION_ARGUMENT;
    720         goto error;
    721       }
    722       if(!getASN1Element(&value, p3, atv.end)) {
    723         result = CURLE_BAD_FUNCTION_ARGUMENT;
    724         goto error;
    725       }
    726       curlx_dyn_reset(&temp);
    727       result = ASN1tostr(&temp, &oid, 0);
    728       if(result)
    729         goto error;
    730 
    731       str = curlx_dyn_ptr(&temp);
    732 
    733       if(!str) {
    734         result = CURLE_BAD_FUNCTION_ARGUMENT;
    735         goto error;
    736       }
    737 
    738       /* Encode delimiter.
    739          If attribute has a short uppercase name, delimiter is ", ". */
    740       for(p3 = str; ISUPPER(*p3); p3++)
    741         ;
    742       if(added) {
    743         if(p3 - str > 2)
    744           result = curlx_dyn_addn(store, "/", 1);
    745         else
    746           result = curlx_dyn_addn(store, ", ", 2);
    747         if(result)
    748           goto error;
    749       }
    750 
    751       /* Encode attribute name. */
    752       result = curlx_dyn_add(store, str);
    753       if(result)
    754         goto error;
    755 
    756       /* Generate equal sign. */
    757       result = curlx_dyn_addn(store, "=", 1);
    758       if(result)
    759         goto error;
    760 
    761       /* Generate value. */
    762       result = ASN1tostr(store, &value, 0);
    763       if(result)
    764         goto error;
    765       curlx_dyn_reset(&temp);
    766       added = TRUE; /* use separator for next */
    767     }
    768   }
    769 error:
    770   curlx_dyn_free(&temp);
    771 
    772   return result;
    773 }
    774 
    775 #endif /* WANT_EXTRACT_CERTINFO */
    776 
    777 #ifdef WANT_PARSEX509
    778 /*
    779  * ASN.1 parse an X509 certificate into structure subfields.
    780  * Syntax is assumed to have already been checked by the SSL backend.
    781  * See RFC 5280.
    782  */
    783 int Curl_parseX509(struct Curl_X509certificate *cert,
    784                    const char *beg, const char *end)
    785 {
    786   struct Curl_asn1Element elem;
    787   struct Curl_asn1Element tbsCertificate;
    788   const char *ccp;
    789   static const char defaultVersion = 0;  /* v1. */
    790 
    791   cert->certificate.header = NULL;
    792   cert->certificate.beg = beg;
    793   cert->certificate.end = end;
    794 
    795   /* Get the sequence content. */
    796   if(!getASN1Element(&elem, beg, end))
    797     return -1;  /* Invalid bounds/size. */
    798   beg = elem.beg;
    799   end = elem.end;
    800 
    801   /* Get tbsCertificate. */
    802   beg = getASN1Element(&tbsCertificate, beg, end);
    803   if(!beg)
    804     return -1;
    805   /* Skip the signatureAlgorithm. */
    806   beg = getASN1Element(&cert->signatureAlgorithm, beg, end);
    807   if(!beg)
    808     return -1;
    809   /* Get the signatureValue. */
    810   if(!getASN1Element(&cert->signature, beg, end))
    811     return -1;
    812 
    813   /* Parse TBSCertificate. */
    814   beg = tbsCertificate.beg;
    815   end = tbsCertificate.end;
    816   /* Get optional version, get serialNumber. */
    817   cert->version.header = NULL;
    818   cert->version.beg = &defaultVersion;
    819   cert->version.end = &defaultVersion + sizeof(defaultVersion);
    820   beg = getASN1Element(&elem, beg, end);
    821   if(!beg)
    822     return -1;
    823   if(elem.tag == 0) {
    824     if(!getASN1Element(&cert->version, elem.beg, elem.end))
    825       return -1;
    826     beg = getASN1Element(&elem, beg, end);
    827     if(!beg)
    828       return -1;
    829   }
    830   cert->serialNumber = elem;
    831   /* Get signature algorithm. */
    832   beg = getASN1Element(&cert->signatureAlgorithm, beg, end);
    833   /* Get issuer. */
    834   beg = getASN1Element(&cert->issuer, beg, end);
    835   if(!beg)
    836     return -1;
    837   /* Get notBefore and notAfter. */
    838   beg = getASN1Element(&elem, beg, end);
    839   if(!beg)
    840     return -1;
    841   ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end);
    842   if(!ccp)
    843     return -1;
    844   if(!getASN1Element(&cert->notAfter, ccp, elem.end))
    845     return -1;
    846   /* Get subject. */
    847   beg = getASN1Element(&cert->subject, beg, end);
    848   if(!beg)
    849     return -1;
    850   /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */
    851   beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end);
    852   if(!beg)
    853     return -1;
    854   ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm,
    855                        cert->subjectPublicKeyInfo.beg,
    856                        cert->subjectPublicKeyInfo.end);
    857   if(!ccp)
    858     return -1;
    859   if(!getASN1Element(&cert->subjectPublicKey, ccp,
    860                      cert->subjectPublicKeyInfo.end))
    861     return -1;
    862   /* Get optional issuerUniqueID, subjectUniqueID and extensions. */
    863   cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0;
    864   cert->extensions.tag = elem.tag = 0;
    865   cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL;
    866   cert->issuerUniqueID.beg = cert->issuerUniqueID.end = "";
    867   cert->subjectUniqueID.beg = cert->subjectUniqueID.end = "";
    868   cert->extensions.header = NULL;
    869   cert->extensions.beg = cert->extensions.end = "";
    870   if(beg < end) {
    871     beg = getASN1Element(&elem, beg, end);
    872     if(!beg)
    873       return -1;
    874   }
    875   if(elem.tag == 1) {
    876     cert->issuerUniqueID = elem;
    877     if(beg < end) {
    878       beg = getASN1Element(&elem, beg, end);
    879       if(!beg)
    880         return -1;
    881     }
    882   }
    883   if(elem.tag == 2) {
    884     cert->subjectUniqueID = elem;
    885     if(beg < end) {
    886       beg = getASN1Element(&elem, beg, end);
    887       if(!beg)
    888         return -1;
    889     }
    890   }
    891   if(elem.tag == 3)
    892     if(!getASN1Element(&cert->extensions, elem.beg, elem.end))
    893       return -1;
    894   return 0;
    895 }
    896 
    897 #endif /* WANT_PARSEX509 */
    898 
    899 #ifdef WANT_EXTRACT_CERTINFO
    900 
    901 static CURLcode dumpAlgo(struct dynbuf *store,
    902                          struct Curl_asn1Element *param,
    903                          const char *beg, const char *end)
    904 {
    905   struct Curl_asn1Element oid;
    906 
    907   /* Get algorithm parameters and return algorithm name. */
    908 
    909   beg = getASN1Element(&oid, beg, end);
    910   if(!beg)
    911     return CURLE_BAD_FUNCTION_ARGUMENT;
    912   param->header = NULL;
    913   param->tag = 0;
    914   param->beg = param->end = end;
    915   if(beg < end) {
    916     const char *p = getASN1Element(param, beg, end);
    917     if(!p)
    918       return CURLE_BAD_FUNCTION_ARGUMENT;
    919   }
    920   return OID2str(store, oid.beg, oid.end, TRUE);
    921 }
    922 
    923 /*
    924  * This is a convenience function for push_certinfo_len that takes a zero
    925  * terminated value.
    926  */
    927 static CURLcode ssl_push_certinfo(struct Curl_easy *data,
    928                                   int certnum,
    929                                   const char *label,
    930                                   const char *value)
    931 {
    932   size_t valuelen = strlen(value);
    933 
    934   return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
    935 }
    936 
    937 /*
    938  * This is a convenience function for push_certinfo_len that takes a
    939  * dynbuf value.
    940  *
    941  * It also does the verbose output if !certnum.
    942  */
    943 static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data,
    944                                       int certnum,
    945                                       const char *label,
    946                                       struct dynbuf *ptr)
    947 {
    948   size_t valuelen = curlx_dyn_len(ptr);
    949   char *value = curlx_dyn_ptr(ptr);
    950 
    951   CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label,
    952                                                value, valuelen);
    953 
    954   if(!certnum && !result)
    955     infof(data, "   %s: %s", label, value);
    956 
    957   return result;
    958 }
    959 
    960 static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
    961                                 const char *label,
    962                                 struct Curl_asn1Element *elem)
    963 {
    964   CURLcode result;
    965   struct dynbuf out;
    966 
    967   curlx_dyn_init(&out, CURL_X509_STR_MAX);
    968 
    969   /* Generate a certificate information record for the public key. */
    970 
    971   result = ASN1tostr(&out, elem, 0);
    972   if(!result) {
    973     if(data->set.ssl.certinfo)
    974       result = ssl_push_certinfo_dyn(data, certnum, label, &out);
    975     curlx_dyn_free(&out);
    976   }
    977   return result;
    978 }
    979 
    980 /* return 0 on success, 1 on error */
    981 static int do_pubkey(struct Curl_easy *data, int certnum,
    982                      const char *algo, struct Curl_asn1Element *param,
    983                      struct Curl_asn1Element *pubkey)
    984 {
    985   struct Curl_asn1Element elem;
    986   struct Curl_asn1Element pk;
    987   const char *p;
    988 
    989   /* Generate all information records for the public key. */
    990 
    991   if(curl_strequal(algo, "ecPublicKey")) {
    992     /*
    993      * ECC public key is all the data, a value of type BIT STRING mapped to
    994      * OCTET STRING and should not be parsed as an ASN.1 value.
    995      */
    996     const size_t len = ((pubkey->end - pubkey->beg - 2) * 4);
    997     if(!certnum)
    998       infof(data, "   ECC Public Key (%zu bits)", len);
    999     if(data->set.ssl.certinfo) {
   1000       char q[sizeof(len) * 8 / 3 + 1];
   1001       (void)msnprintf(q, sizeof(q), "%zu", len);
   1002       if(ssl_push_certinfo(data, certnum, "ECC Public Key", q))
   1003         return 1;
   1004     }
   1005     return do_pubkey_field(data, certnum, "ecPublicKey", pubkey) == CURLE_OK
   1006       ? 0 : 1;
   1007   }
   1008 
   1009   /* Get the public key (single element). */
   1010   if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end))
   1011     return 1;
   1012 
   1013   if(curl_strequal(algo, "rsaEncryption")) {
   1014     const char *q;
   1015     size_t len;
   1016 
   1017     p = getASN1Element(&elem, pk.beg, pk.end);
   1018     if(!p)
   1019       return 1;
   1020 
   1021     /* Compute key length. */
   1022     for(q = elem.beg; !*q && q < elem.end; q++)
   1023       ;
   1024     len = ((elem.end - q) * 8);
   1025     if(len) {
   1026       unsigned int i;
   1027       for(i = *(const unsigned char *) q; !(i & 0x80); i <<= 1)
   1028         len--;
   1029     }
   1030     if(len > 32)
   1031       elem.beg = q;     /* Strip leading zero bytes. */
   1032     if(!certnum)
   1033       infof(data, "   RSA Public Key (%zu bits)", len);
   1034     if(data->set.ssl.certinfo) {
   1035       char r[sizeof(len) * 8 / 3 + 1];
   1036       msnprintf(r, sizeof(r), "%zu", len);
   1037       if(ssl_push_certinfo(data, certnum, "RSA Public Key", r))
   1038         return 1;
   1039     }
   1040     /* Generate coefficients. */
   1041     if(do_pubkey_field(data, certnum, "rsa(n)", &elem))
   1042       return 1;
   1043     if(!getASN1Element(&elem, p, pk.end))
   1044       return 1;
   1045     if(do_pubkey_field(data, certnum, "rsa(e)", &elem))
   1046       return 1;
   1047   }
   1048   else if(curl_strequal(algo, "dsa")) {
   1049     p = getASN1Element(&elem, param->beg, param->end);
   1050     if(p) {
   1051       if(do_pubkey_field(data, certnum, "dsa(p)", &elem))
   1052         return 1;
   1053       p = getASN1Element(&elem, p, param->end);
   1054       if(p) {
   1055         if(do_pubkey_field(data, certnum, "dsa(q)", &elem))
   1056           return 1;
   1057         if(getASN1Element(&elem, p, param->end)) {
   1058           if(do_pubkey_field(data, certnum, "dsa(g)", &elem))
   1059             return 1;
   1060           if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk))
   1061             return 1;
   1062         }
   1063       }
   1064     }
   1065   }
   1066   else if(curl_strequal(algo, "dhpublicnumber")) {
   1067     p = getASN1Element(&elem, param->beg, param->end);
   1068     if(p) {
   1069       if(do_pubkey_field(data, certnum, "dh(p)", &elem))
   1070         return 1;
   1071       if(getASN1Element(&elem, param->beg, param->end)) {
   1072         if(do_pubkey_field(data, certnum, "dh(g)", &elem))
   1073           return 1;
   1074         if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk))
   1075           return 1;
   1076       }
   1077     }
   1078   }
   1079   return 0;
   1080 }
   1081 
   1082 /*
   1083  * Convert an ASN.1 distinguished name into a printable string.
   1084  * Return error.
   1085  */
   1086 static CURLcode DNtostr(struct dynbuf *store,
   1087                         struct Curl_asn1Element *dn)
   1088 {
   1089   return encodeDN(store, dn);
   1090 }
   1091 
   1092 CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   1093                                int certnum,
   1094                                const char *beg,
   1095                                const char *end)
   1096 {
   1097   struct Curl_X509certificate cert;
   1098   struct Curl_asn1Element param;
   1099   char *certptr;
   1100   size_t clen;
   1101   struct dynbuf out;
   1102   CURLcode result = CURLE_OK;
   1103   unsigned int version;
   1104   const char *ptr;
   1105   int rc;
   1106 
   1107   if(!data->set.ssl.certinfo)
   1108     if(certnum)
   1109       return CURLE_OK;
   1110 
   1111   curlx_dyn_init(&out, CURL_X509_STR_MAX);
   1112   /* Prepare the certificate information for curl_easy_getinfo(). */
   1113 
   1114   /* Extract the certificate ASN.1 elements. */
   1115   if(Curl_parseX509(&cert, beg, end))
   1116     return CURLE_PEER_FAILED_VERIFICATION;
   1117 
   1118   /* Subject. */
   1119   result = DNtostr(&out, &cert.subject);
   1120   if(result)
   1121     goto done;
   1122   if(data->set.ssl.certinfo) {
   1123     result = ssl_push_certinfo_dyn(data, certnum, "Subject", &out);
   1124     if(result)
   1125       goto done;
   1126   }
   1127   curlx_dyn_reset(&out);
   1128 
   1129   /* Issuer. */
   1130   result = DNtostr(&out, &cert.issuer);
   1131   if(result)
   1132     goto done;
   1133   if(data->set.ssl.certinfo) {
   1134     result = ssl_push_certinfo_dyn(data, certnum, "Issuer", &out);
   1135     if(result)
   1136       goto done;
   1137   }
   1138   curlx_dyn_reset(&out);
   1139 
   1140   /* Version (always fits in less than 32 bits). */
   1141   version = 0;
   1142   for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
   1143     version = (version << 8) | *(const unsigned char *) ptr;
   1144   if(data->set.ssl.certinfo) {
   1145     result = curlx_dyn_addf(&out, "%x", version);
   1146     if(result)
   1147       goto done;
   1148     result = ssl_push_certinfo_dyn(data, certnum, "Version", &out);
   1149     if(result)
   1150       goto done;
   1151     curlx_dyn_reset(&out);
   1152   }
   1153 
   1154   /* Serial number. */
   1155   result = ASN1tostr(&out, &cert.serialNumber, 0);
   1156   if(result)
   1157     goto done;
   1158   if(data->set.ssl.certinfo) {
   1159     result = ssl_push_certinfo_dyn(data, certnum, "Serial Number", &out);
   1160     if(result)
   1161       goto done;
   1162   }
   1163   curlx_dyn_reset(&out);
   1164 
   1165   /* Signature algorithm .*/
   1166   result = dumpAlgo(&out, &param, cert.signatureAlgorithm.beg,
   1167                     cert.signatureAlgorithm.end);
   1168   if(result)
   1169     goto done;
   1170   if(data->set.ssl.certinfo) {
   1171     result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm",
   1172                                    &out);
   1173     if(result)
   1174       goto done;
   1175   }
   1176   curlx_dyn_reset(&out);
   1177 
   1178   /* Start Date. */
   1179   result = ASN1tostr(&out, &cert.notBefore, 0);
   1180   if(result)
   1181     goto done;
   1182   if(data->set.ssl.certinfo) {
   1183     result = ssl_push_certinfo_dyn(data, certnum, "Start Date", &out);
   1184     if(result)
   1185       goto done;
   1186   }
   1187   curlx_dyn_reset(&out);
   1188 
   1189   /* Expire Date. */
   1190   result = ASN1tostr(&out, &cert.notAfter, 0);
   1191   if(result)
   1192     goto done;
   1193   if(data->set.ssl.certinfo) {
   1194     result = ssl_push_certinfo_dyn(data, certnum, "Expire Date", &out);
   1195     if(result)
   1196       goto done;
   1197   }
   1198   curlx_dyn_reset(&out);
   1199 
   1200   /* Public Key Algorithm. */
   1201   result = dumpAlgo(&out, &param, cert.subjectPublicKeyAlgorithm.beg,
   1202                     cert.subjectPublicKeyAlgorithm.end);
   1203   if(result)
   1204     goto done;
   1205   if(data->set.ssl.certinfo) {
   1206     result = ssl_push_certinfo_dyn(data, certnum, "Public Key Algorithm",
   1207                                    &out);
   1208     if(result)
   1209       goto done;
   1210   }
   1211 
   1212   rc = do_pubkey(data, certnum, curlx_dyn_ptr(&out),
   1213                  &param, &cert.subjectPublicKey);
   1214   if(rc) {
   1215     result = CURLE_OUT_OF_MEMORY; /* the most likely error */
   1216     goto done;
   1217   }
   1218   curlx_dyn_reset(&out);
   1219 
   1220   /* Signature. */
   1221   result = ASN1tostr(&out, &cert.signature, 0);
   1222   if(result)
   1223     goto done;
   1224   if(data->set.ssl.certinfo) {
   1225     result = ssl_push_certinfo_dyn(data, certnum, "Signature", &out);
   1226     if(result)
   1227       goto done;
   1228   }
   1229   curlx_dyn_reset(&out);
   1230 
   1231   /* Generate PEM certificate. */
   1232   result = curlx_base64_encode(cert.certificate.beg,
   1233                                cert.certificate.end - cert.certificate.beg,
   1234                                &certptr, &clen);
   1235   if(result)
   1236     goto done;
   1237 
   1238   /* Generate the final output certificate string. Format is:
   1239      -----BEGIN CERTIFICATE-----\n
   1240      <max 64 base64 characters>\n
   1241      .
   1242      .
   1243      .
   1244      -----END CERTIFICATE-----\n
   1245    */
   1246 
   1247   curlx_dyn_reset(&out);
   1248 
   1249   /* Build the certificate string. */
   1250   result = curlx_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
   1251   if(!result) {
   1252     size_t j = 0;
   1253 
   1254     while(!result && (j < clen)) {
   1255       size_t chunksize = (clen - j) > 64 ? 64 : (clen - j);
   1256       result = curlx_dyn_addn(&out, &certptr[j], chunksize);
   1257       if(!result)
   1258         result = curlx_dyn_addn(&out, "\n", 1);
   1259       j += chunksize;
   1260     }
   1261     if(!result)
   1262       result = curlx_dyn_add(&out, "-----END CERTIFICATE-----\n");
   1263   }
   1264   free(certptr);
   1265   if(!result)
   1266     if(data->set.ssl.certinfo)
   1267       result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out);
   1268 
   1269 done:
   1270   if(result)
   1271     failf(data, "Failed extracting certificate chain");
   1272   curlx_dyn_free(&out);
   1273   return result;
   1274 }
   1275 
   1276 #endif /* WANT_EXTRACT_CERTINFO */
   1277 
   1278 #endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_MBEDTLS or
   1279           USE_RUSTLS */