x509asn1.c 34 KB

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