1*17f7a478Sjob /* $OpenBSD: print.c,v 1.52 2024/02/26 10:02:37 job Exp $ */
2714f4e3fSclaudio /*
3714f4e3fSclaudio * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
4714f4e3fSclaudio * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5714f4e3fSclaudio *
6714f4e3fSclaudio * Permission to use, copy, modify, and distribute this software for any
7714f4e3fSclaudio * purpose with or without fee is hereby granted, provided that the above
8714f4e3fSclaudio * copyright notice and this permission notice appear in all copies.
9714f4e3fSclaudio *
10714f4e3fSclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11714f4e3fSclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12714f4e3fSclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13714f4e3fSclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14714f4e3fSclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15714f4e3fSclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16714f4e3fSclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17714f4e3fSclaudio */
18714f4e3fSclaudio
19714f4e3fSclaudio #include <sys/types.h>
20714f4e3fSclaudio #include <sys/socket.h>
21714f4e3fSclaudio #include <arpa/inet.h>
22714f4e3fSclaudio
23714f4e3fSclaudio #include <err.h>
24714f4e3fSclaudio #include <stdio.h>
25714f4e3fSclaudio #include <string.h>
26714f4e3fSclaudio #include <time.h>
27714f4e3fSclaudio
28e911df76Sjob #include <openssl/evp.h>
29e911df76Sjob
30714f4e3fSclaudio #include "extern.h"
317aabcda0Sclaudio #include "json.h"
32714f4e3fSclaudio
33714f4e3fSclaudio static const char *
pretty_key_id(const char * hex)34254fd372Sclaudio pretty_key_id(const char *hex)
35714f4e3fSclaudio {
36714f4e3fSclaudio static char buf[128]; /* bigger than SHA_DIGEST_LENGTH * 3 */
37714f4e3fSclaudio size_t i;
38714f4e3fSclaudio
39714f4e3fSclaudio for (i = 0; i < sizeof(buf) && *hex != '\0'; i++) {
40254fd372Sclaudio if (i % 3 == 2)
41714f4e3fSclaudio buf[i] = ':';
42714f4e3fSclaudio else
43714f4e3fSclaudio buf[i] = *hex++;
44714f4e3fSclaudio }
45714f4e3fSclaudio if (i == sizeof(buf))
46714f4e3fSclaudio memcpy(buf + sizeof(buf) - 4, "...", 4);
47254fd372Sclaudio else
48254fd372Sclaudio buf[i] = '\0';
49714f4e3fSclaudio return buf;
50714f4e3fSclaudio }
51714f4e3fSclaudio
52220c707cSclaudio char *
nid2str(int nid)5378de3577Stb nid2str(int nid)
5478de3577Stb {
5578de3577Stb static char buf[128];
5678de3577Stb const char *name;
5778de3577Stb
5878de3577Stb if ((name = OBJ_nid2ln(nid)) == NULL)
5978de3577Stb name = OBJ_nid2sn(nid);
6078de3577Stb if (name == NULL)
6178de3577Stb name = "unknown";
6278de3577Stb
6378de3577Stb snprintf(buf, sizeof(buf), "nid %d (%s)", nid, name);
6478de3577Stb
6578de3577Stb return buf;
6678de3577Stb }
6778de3577Stb
6878de3577Stb char *
time2str(time_t t)69220c707cSclaudio time2str(time_t t)
70220c707cSclaudio {
71220c707cSclaudio static char buf[64];
72220c707cSclaudio struct tm tm;
73220c707cSclaudio
74220c707cSclaudio if (gmtime_r(&t, &tm) == NULL)
75220c707cSclaudio return "could not convert time";
76220c707cSclaudio
77cc6f004eSjob strftime(buf, sizeof(buf), "%a %d %b %Y %T %z", &tm);
78cc6f004eSjob
79220c707cSclaudio return buf;
80220c707cSclaudio }
81220c707cSclaudio
82714f4e3fSclaudio void
tal_print(const struct tal * p)83714f4e3fSclaudio tal_print(const struct tal *p)
84714f4e3fSclaudio {
85e911df76Sjob char *ski;
8623c6f3a2Stb const unsigned char *der;
87ddc87cffSjob X509_PUBKEY *pubkey;
88714f4e3fSclaudio size_t i;
89714f4e3fSclaudio
9023c6f3a2Stb der = p->pkey;
9123c6f3a2Stb if ((pubkey = d2i_X509_PUBKEY(NULL, &der, p->pkeysz)) == NULL)
92ddc87cffSjob errx(1, "d2i_X509_PUBKEY failed");
93e911df76Sjob
9423c6f3a2Stb if ((ski = x509_pubkey_get_ski(pubkey, p->descr)) == NULL)
9523c6f3a2Stb errx(1, "x509_pubkey_get_ski failed");
96e911df76Sjob
97530399e8Sjob if (outformats & FORMAT_JSON) {
987aabcda0Sclaudio json_do_string("type", "tal");
997aabcda0Sclaudio json_do_string("name", p->descr);
1007aabcda0Sclaudio json_do_string("ski", pretty_key_id(ski));
1017aabcda0Sclaudio json_do_array("trust_anchor_locations");
1027aabcda0Sclaudio for (i = 0; i < p->urisz; i++)
1037aabcda0Sclaudio json_do_string("tal", p->uri[i]);
1047aabcda0Sclaudio json_do_end();
105530399e8Sjob } else {
106530399e8Sjob printf("Trust anchor name: %s\n", p->descr);
107530399e8Sjob printf("Subject key identifier: %s\n", pretty_key_id(ski));
1084486d057Sjob printf("Trust anchor locations: ");
1094486d057Sjob for (i = 0; i < p->urisz; i++) {
1104486d057Sjob if (i > 0)
1114486d057Sjob printf("%26s", "");
1124486d057Sjob printf("%s\n", p->uri[i]);
1134486d057Sjob }
114530399e8Sjob }
115e911df76Sjob
116ddc87cffSjob X509_PUBKEY_free(pubkey);
117e911df76Sjob free(ski);
118714f4e3fSclaudio }
119714f4e3fSclaudio
120714f4e3fSclaudio void
x509_print(const X509 * x)121530399e8Sjob x509_print(const X509 *x)
122530399e8Sjob {
123530399e8Sjob const ASN1_INTEGER *xserial;
124e0b87278Sjob const X509_NAME *xissuer;
125e0b87278Sjob char *issuer = NULL;
126530399e8Sjob char *serial = NULL;
127530399e8Sjob
128e0b87278Sjob if ((xissuer = X509_get_issuer_name(x)) == NULL) {
129e0b87278Sjob warnx("X509_get_issuer_name failed");
130530399e8Sjob goto out;
131530399e8Sjob }
132530399e8Sjob
133e0b87278Sjob if ((issuer = X509_NAME_oneline(xissuer, NULL, 0)) == NULL) {
134e0b87278Sjob warnx("X509_NAME_oneline failed");
135530399e8Sjob goto out;
136530399e8Sjob }
137530399e8Sjob
138e0b87278Sjob if ((xserial = X509_get0_serialNumber(x)) == NULL) {
139e0b87278Sjob warnx("X509_get0_serialNumber failed");
140e0b87278Sjob goto out;
141e0b87278Sjob }
142e0b87278Sjob
143e0b87278Sjob if ((serial = x509_convert_seqnum(__func__, xserial)) == NULL)
144e0b87278Sjob goto out;
145e0b87278Sjob
146530399e8Sjob if (outformats & FORMAT_JSON) {
1477aabcda0Sclaudio json_do_string("cert_issuer", issuer);
1487aabcda0Sclaudio json_do_string("cert_serial", serial);
149530399e8Sjob } else {
150e0b87278Sjob printf("Certificate issuer: %s\n", issuer);
151530399e8Sjob printf("Certificate serial: %s\n", serial);
152530399e8Sjob }
153530399e8Sjob
154530399e8Sjob out:
155e0b87278Sjob free(issuer);
156530399e8Sjob free(serial);
157530399e8Sjob }
158530399e8Sjob
1597aabcda0Sclaudio static void
as_resources_print(struct cert_as * as,size_t asz)1607aabcda0Sclaudio as_resources_print(struct cert_as *as, size_t asz)
1617aabcda0Sclaudio {
1627aabcda0Sclaudio size_t i;
1637aabcda0Sclaudio
1647aabcda0Sclaudio for (i = 0; i < asz; i++) {
1657aabcda0Sclaudio if (outformats & FORMAT_JSON)
166a09a3191Sclaudio json_do_object("resource", 1);
1677aabcda0Sclaudio switch (as[i].type) {
1687aabcda0Sclaudio case CERT_AS_ID:
1697aabcda0Sclaudio if (outformats & FORMAT_JSON) {
1707aabcda0Sclaudio json_do_uint("asid", as[i].id);
1717aabcda0Sclaudio } else {
1727aabcda0Sclaudio if (i > 0)
1737aabcda0Sclaudio printf("%26s", "");
1747aabcda0Sclaudio printf("AS: %u", as[i].id);
1757aabcda0Sclaudio }
1767aabcda0Sclaudio break;
1777aabcda0Sclaudio case CERT_AS_INHERIT:
1787aabcda0Sclaudio if (outformats & FORMAT_JSON) {
1797aabcda0Sclaudio json_do_bool("asid_inherit", 1);
1807aabcda0Sclaudio } else {
1817aabcda0Sclaudio if (i > 0)
1827aabcda0Sclaudio printf("%26s", "");
1837aabcda0Sclaudio printf("AS: inherit");
1847aabcda0Sclaudio }
1857aabcda0Sclaudio break;
1867aabcda0Sclaudio case CERT_AS_RANGE:
1877aabcda0Sclaudio if (outformats & FORMAT_JSON) {
188a09a3191Sclaudio json_do_object("asrange", 1);
1897aabcda0Sclaudio json_do_uint("min", as[i].range.min);
1907aabcda0Sclaudio json_do_uint("max", as[i].range.max);
1917aabcda0Sclaudio json_do_end();
1927aabcda0Sclaudio } else {
1937aabcda0Sclaudio if (i > 0)
1947aabcda0Sclaudio printf("%26s", "");
1957aabcda0Sclaudio printf("AS: %u -- %u", as[i].range.min,
1967aabcda0Sclaudio as[i].range.max);
1977aabcda0Sclaudio }
1987aabcda0Sclaudio break;
1997aabcda0Sclaudio }
2007aabcda0Sclaudio if (outformats & FORMAT_JSON)
2017aabcda0Sclaudio json_do_end();
2027aabcda0Sclaudio else
2037aabcda0Sclaudio printf("\n");
2047aabcda0Sclaudio }
2057aabcda0Sclaudio }
2067aabcda0Sclaudio
2077aabcda0Sclaudio static void
ip_resources_print(struct cert_ip * ips,size_t ipsz,size_t asz)2087aabcda0Sclaudio ip_resources_print(struct cert_ip *ips, size_t ipsz, size_t asz)
2097aabcda0Sclaudio {
2107aabcda0Sclaudio char buf1[64], buf2[64];
2117aabcda0Sclaudio size_t i;
2127aabcda0Sclaudio int sockt;
2137aabcda0Sclaudio
2147aabcda0Sclaudio
2157aabcda0Sclaudio for (i = 0; i < ipsz; i++) {
2167aabcda0Sclaudio if (outformats & FORMAT_JSON)
217a09a3191Sclaudio json_do_object("resource", 1);
2187aabcda0Sclaudio switch (ips[i].type) {
2197aabcda0Sclaudio case CERT_IP_INHERIT:
2207aabcda0Sclaudio if (outformats & FORMAT_JSON) {
2217aabcda0Sclaudio json_do_bool("ip_inherit", 1);
2227aabcda0Sclaudio } else {
2237aabcda0Sclaudio if (i > 0 || asz > 0)
2247aabcda0Sclaudio printf("%26s", "");
2257aabcda0Sclaudio printf("IP: inherit");
2267aabcda0Sclaudio }
2277aabcda0Sclaudio break;
2287aabcda0Sclaudio case CERT_IP_ADDR:
2297aabcda0Sclaudio ip_addr_print(&ips[i].ip, ips[i].afi, buf1,
2307aabcda0Sclaudio sizeof(buf1));
2317aabcda0Sclaudio if (outformats & FORMAT_JSON) {
2327aabcda0Sclaudio json_do_string("ip_prefix", buf1);
2337aabcda0Sclaudio } else {
2347aabcda0Sclaudio if (i > 0 || asz > 0)
2357aabcda0Sclaudio printf("%26s", "");
2367aabcda0Sclaudio printf("IP: %s", buf1);
2377aabcda0Sclaudio }
2387aabcda0Sclaudio break;
2397aabcda0Sclaudio case CERT_IP_RANGE:
2407aabcda0Sclaudio sockt = (ips[i].afi == AFI_IPV4) ?
2417aabcda0Sclaudio AF_INET : AF_INET6;
2427aabcda0Sclaudio inet_ntop(sockt, ips[i].min, buf1, sizeof(buf1));
2437aabcda0Sclaudio inet_ntop(sockt, ips[i].max, buf2, sizeof(buf2));
2447aabcda0Sclaudio if (outformats & FORMAT_JSON) {
245a09a3191Sclaudio json_do_object("ip_range", 1);
2467aabcda0Sclaudio json_do_string("min", buf1);
2477aabcda0Sclaudio json_do_string("max", buf2);
2487aabcda0Sclaudio json_do_end();
2497aabcda0Sclaudio } else {
2507aabcda0Sclaudio if (i > 0 || asz > 0)
2517aabcda0Sclaudio printf("%26s", "");
2527aabcda0Sclaudio printf("IP: %s -- %s", buf1, buf2);
2537aabcda0Sclaudio }
2547aabcda0Sclaudio break;
2557aabcda0Sclaudio }
2567aabcda0Sclaudio if (outformats & FORMAT_JSON)
2577aabcda0Sclaudio json_do_end();
2587aabcda0Sclaudio else
2597aabcda0Sclaudio printf("\n");
2607aabcda0Sclaudio }
2617aabcda0Sclaudio }
2627aabcda0Sclaudio
263530399e8Sjob void
cert_print(const struct cert * p)264714f4e3fSclaudio cert_print(const struct cert *p)
265714f4e3fSclaudio {
266530399e8Sjob if (outformats & FORMAT_JSON) {
267530399e8Sjob if (p->pubkey != NULL)
2687aabcda0Sclaudio json_do_string("type", "router_key");
269530399e8Sjob else
2707aabcda0Sclaudio json_do_string("type", "ca_cert");
2717aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
272530399e8Sjob if (p->aki != NULL)
2737aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
274530399e8Sjob x509_print(p->x509);
275530399e8Sjob if (p->aia != NULL)
2767aabcda0Sclaudio json_do_string("aia", p->aia);
277530399e8Sjob if (p->mft != NULL)
2787aabcda0Sclaudio json_do_string("manifest", p->mft);
279530399e8Sjob if (p->repo != NULL)
2807aabcda0Sclaudio json_do_string("carepository", p->repo);
281530399e8Sjob if (p->notify != NULL)
2827aabcda0Sclaudio json_do_string("notify_url", p->notify);
283530399e8Sjob if (p->pubkey != NULL)
2847aabcda0Sclaudio json_do_string("router_key", p->pubkey);
2857aabcda0Sclaudio json_do_int("valid_since", p->notbefore);
2867aabcda0Sclaudio json_do_int("valid_until", p->notafter);
287894936b4Sjob if (p->expires)
2887aabcda0Sclaudio json_do_int("expires", p->expires);
2897aabcda0Sclaudio json_do_array("subordinate_resources");
290530399e8Sjob } else {
291714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
292714f4e3fSclaudio if (p->aki != NULL)
29367642cb5Stb printf("Authority key identifier: %s\n",
29467642cb5Stb pretty_key_id(p->aki));
295530399e8Sjob x509_print(p->x509);
296714f4e3fSclaudio if (p->aia != NULL)
297714f4e3fSclaudio printf("Authority info access: %s\n", p->aia);
298714f4e3fSclaudio if (p->mft != NULL)
299714f4e3fSclaudio printf("Manifest: %s\n", p->mft);
300714f4e3fSclaudio if (p->repo != NULL)
301714f4e3fSclaudio printf("caRepository: %s\n", p->repo);
302714f4e3fSclaudio if (p->notify != NULL)
303714f4e3fSclaudio printf("Notify URL: %s\n", p->notify);
30414d83341Sjob if (p->pubkey != NULL) {
3056530cf17Sjob printf("BGPsec ECDSA public key: %s\n",
30667642cb5Stb p->pubkey);
3074dbb22b8Sjob printf("Router key not before: %s\n",
308f5999ddfSjob time2str(p->notbefore));
3094dbb22b8Sjob printf("Router key not after: %s\n",
3109f544822Sjob time2str(p->notafter));
311f5999ddfSjob } else {
3124dbb22b8Sjob printf("Certificate not before: %s\n",
313f5999ddfSjob time2str(p->notbefore));
3144dbb22b8Sjob printf("Certificate not after: %s\n",
3159f544822Sjob time2str(p->notafter));
316f5999ddfSjob }
3174486d057Sjob printf("Subordinate resources: ");
318530399e8Sjob }
319714f4e3fSclaudio
3207aabcda0Sclaudio as_resources_print(p->as, p->asz);
3217aabcda0Sclaudio ip_resources_print(p->ips, p->ipsz, p->asz);
322530399e8Sjob
323530399e8Sjob if (outformats & FORMAT_JSON)
3247aabcda0Sclaudio json_do_end();
325714f4e3fSclaudio }
326714f4e3fSclaudio
327714f4e3fSclaudio void
crl_print(const struct crl * p)328220c707cSclaudio crl_print(const struct crl *p)
329220c707cSclaudio {
330220c707cSclaudio STACK_OF(X509_REVOKED) *revlist;
331220c707cSclaudio X509_REVOKED *rev;
332e0b87278Sjob X509_NAME *xissuer;
333220c707cSclaudio int i;
334e0b87278Sjob char *issuer, *serial;
335220c707cSclaudio time_t t;
336220c707cSclaudio
337530399e8Sjob if (outformats & FORMAT_JSON) {
3387aabcda0Sclaudio json_do_string("type", "crl");
3397aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
340530399e8Sjob } else
341220c707cSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
3427cdd491fSclaudio
343e0b87278Sjob xissuer = X509_CRL_get_issuer(p->x509_crl);
344e0b87278Sjob issuer = X509_NAME_oneline(xissuer, NULL, 0);
3450a722356Stb if (issuer != NULL && p->number != NULL) {
346e0b87278Sjob if (outformats & FORMAT_JSON) {
3477aabcda0Sclaudio json_do_string("crl_issuer", issuer);
3480a722356Stb json_do_string("crl_serial", p->number);
349e0b87278Sjob } else {
350e0b87278Sjob printf("CRL issuer: %s\n", issuer);
3510a722356Stb printf("CRL serial number: %s\n", p->number);
352530399e8Sjob }
353e0b87278Sjob }
354e0b87278Sjob free(issuer);
3557cdd491fSclaudio
356530399e8Sjob if (outformats & FORMAT_JSON) {
357c527cc7aSjob json_do_int("valid_since", p->thisupdate);
3587aabcda0Sclaudio json_do_int("valid_until", p->nextupdate);
3597aabcda0Sclaudio json_do_array("revoked_certs");
360530399e8Sjob } else {
361c527cc7aSjob printf("CRL this update: %s\n",
362c527cc7aSjob time2str(p->thisupdate));
3634dbb22b8Sjob printf("CRL next update: %s\n",
3649f544822Sjob time2str(p->nextupdate));
365530399e8Sjob printf("Revoked Certificates:\n");
366530399e8Sjob }
367220c707cSclaudio
368220c707cSclaudio revlist = X509_CRL_get_REVOKED(p->x509_crl);
369220c707cSclaudio for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) {
370220c707cSclaudio rev = sk_X509_REVOKED_value(revlist, i);
3717cdd491fSclaudio serial = x509_convert_seqnum(__func__,
3727cdd491fSclaudio X509_REVOKED_get0_serialNumber(rev));
373220c707cSclaudio x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t);
374530399e8Sjob if (serial != NULL) {
375530399e8Sjob if (outformats & FORMAT_JSON) {
376a09a3191Sclaudio json_do_object("cert", 1);
3777aabcda0Sclaudio json_do_string("serial", serial);
3787aabcda0Sclaudio json_do_string("date", time2str(t));
3797aabcda0Sclaudio json_do_end();
380530399e8Sjob } else
3814486d057Sjob printf("%25s Serial: %8s Revocation Date: %s"
3824486d057Sjob "\n", "", serial, time2str(t));
383530399e8Sjob }
3847cdd491fSclaudio free(serial);
385220c707cSclaudio }
386530399e8Sjob
387530399e8Sjob if (outformats & FORMAT_JSON)
3887aabcda0Sclaudio json_do_end();
389530399e8Sjob else if (i == 0)
390220c707cSclaudio printf("No Revoked Certificates\n");
391220c707cSclaudio }
392220c707cSclaudio
393220c707cSclaudio void
mft_print(const X509 * x,const struct mft * p)394530399e8Sjob mft_print(const X509 *x, const struct mft *p)
395714f4e3fSclaudio {
396714f4e3fSclaudio size_t i;
397714f4e3fSclaudio char *hash;
398714f4e3fSclaudio
399530399e8Sjob if (outformats & FORMAT_JSON) {
4007aabcda0Sclaudio json_do_string("type", "manifest");
4017aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
402530399e8Sjob x509_print(x);
4037aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
4047aabcda0Sclaudio json_do_string("aia", p->aia);
4057aabcda0Sclaudio json_do_string("sia", p->sia);
4067aabcda0Sclaudio json_do_string("manifest_number", p->seqnum);
4071bb1e509Sjob if (p->signtime != 0)
4087aabcda0Sclaudio json_do_int("signing_time", p->signtime);
4097aabcda0Sclaudio json_do_int("valid_since", p->thisupdate);
4107aabcda0Sclaudio json_do_int("valid_until", p->nextupdate);
411894936b4Sjob if (p->expires)
4127aabcda0Sclaudio json_do_int("expires", p->expires);
413530399e8Sjob } else {
414714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
415714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
416530399e8Sjob x509_print(x);
417714f4e3fSclaudio printf("Authority info access: %s\n", p->aia);
4182cf0e122Sjob printf("Subject info access: %s\n", p->sia);
419489e308aSjob printf("Manifest number: %s\n", p->seqnum);
4201bb1e509Sjob if (p->signtime != 0)
4211bb1e509Sjob printf("Signing time: %s\n",
4221bb1e509Sjob time2str(p->signtime));
4234dbb22b8Sjob printf("Manifest this update: %s\n", time2str(p->thisupdate));
4244dbb22b8Sjob printf("Manifest next update: %s\n", time2str(p->nextupdate));
4254486d057Sjob printf("Files and hashes: ");
426530399e8Sjob }
427530399e8Sjob
4287aabcda0Sclaudio if (outformats & FORMAT_JSON)
4297aabcda0Sclaudio json_do_array("filesandhashes");
430714f4e3fSclaudio for (i = 0; i < p->filesz; i++) {
431714f4e3fSclaudio if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
432714f4e3fSclaudio &hash) == -1)
433714f4e3fSclaudio errx(1, "base64_encode failure");
434530399e8Sjob
435530399e8Sjob if (outformats & FORMAT_JSON) {
436a09a3191Sclaudio json_do_object("filehash", 1);
4377aabcda0Sclaudio json_do_string("filename", p->files[i].file);
4387aabcda0Sclaudio json_do_string("hash", hash);
4397aabcda0Sclaudio json_do_end();
440530399e8Sjob } else {
4414486d057Sjob if (i > 0)
4424486d057Sjob printf("%26s", "");
4434486d057Sjob printf("%zu: %s (hash: %s)\n", i + 1, p->files[i].file,
4444486d057Sjob hash);
445530399e8Sjob }
446530399e8Sjob
447714f4e3fSclaudio free(hash);
448714f4e3fSclaudio }
449530399e8Sjob if (outformats & FORMAT_JSON)
4507aabcda0Sclaudio json_do_end();
451714f4e3fSclaudio }
452714f4e3fSclaudio
453714f4e3fSclaudio void
roa_print(const X509 * x,const struct roa * p)454530399e8Sjob roa_print(const X509 *x, const struct roa *p)
455714f4e3fSclaudio {
456714f4e3fSclaudio char buf[128];
457714f4e3fSclaudio size_t i;
458714f4e3fSclaudio
459530399e8Sjob if (outformats & FORMAT_JSON) {
4607aabcda0Sclaudio json_do_string("type", "roa");
4617aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
462530399e8Sjob x509_print(x);
4637aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
4647aabcda0Sclaudio json_do_string("aia", p->aia);
4657aabcda0Sclaudio json_do_string("sia", p->sia);
4661bb1e509Sjob if (p->signtime != 0)
4677aabcda0Sclaudio json_do_int("signing_time", p->signtime);
4687aabcda0Sclaudio json_do_int("valid_since", p->notbefore);
4697aabcda0Sclaudio json_do_int("valid_until", p->notafter);
4709c1f5d6bSjob if (p->expires)
4717aabcda0Sclaudio json_do_int("expires", p->expires);
472530399e8Sjob } else {
473714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
474530399e8Sjob x509_print(x);
475714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
476714f4e3fSclaudio printf("Authority info access: %s\n", p->aia);
4772cf0e122Sjob printf("Subject info access: %s\n", p->sia);
4781bb1e509Sjob if (p->signtime != 0)
4791bb1e509Sjob printf("Signing time: %s\n",
4801bb1e509Sjob time2str(p->signtime));
4814dbb22b8Sjob printf("ROA not before: %s\n",
482f5999ddfSjob time2str(p->notbefore));
4834dbb22b8Sjob printf("ROA not after: %s\n", time2str(p->notafter));
484714f4e3fSclaudio printf("asID: %u\n", p->asid);
4854486d057Sjob printf("IP address blocks: ");
486530399e8Sjob }
487530399e8Sjob
4887aabcda0Sclaudio if (outformats & FORMAT_JSON)
4897aabcda0Sclaudio json_do_array("vrps");
490714f4e3fSclaudio for (i = 0; i < p->ipsz; i++) {
491714f4e3fSclaudio ip_addr_print(&p->ips[i].addr,
492714f4e3fSclaudio p->ips[i].afi, buf, sizeof(buf));
493530399e8Sjob
494530399e8Sjob if (outformats & FORMAT_JSON) {
495a09a3191Sclaudio json_do_object("vrp", 1);
4967aabcda0Sclaudio json_do_string("prefix", buf);
4977aabcda0Sclaudio json_do_uint("asid", p->asid);
4987aabcda0Sclaudio json_do_uint("maxlen", p->ips[i].maxlength);
4997aabcda0Sclaudio json_do_end();
5004486d057Sjob } else {
5014486d057Sjob if (i > 0)
5024486d057Sjob printf("%26s", "");
5034486d057Sjob printf("%s maxlen: %hhu\n", buf, p->ips[i].maxlength);
5044486d057Sjob }
505714f4e3fSclaudio }
506530399e8Sjob if (outformats & FORMAT_JSON)
5077aabcda0Sclaudio json_do_end();
508714f4e3fSclaudio }
509714f4e3fSclaudio
510714f4e3fSclaudio void
spl_print(const X509 * x,const struct spl * s)511d4be4cdeSjob spl_print(const X509 *x, const struct spl *s)
512d4be4cdeSjob {
513d4be4cdeSjob char buf[128];
514d4be4cdeSjob size_t i;
515d4be4cdeSjob
516d4be4cdeSjob if (outformats & FORMAT_JSON) {
517d4be4cdeSjob json_do_string("type", "spl");
518d4be4cdeSjob json_do_string("ski", pretty_key_id(s->ski));
519d4be4cdeSjob x509_print(x);
520d4be4cdeSjob json_do_string("aki", pretty_key_id(s->aki));
521d4be4cdeSjob json_do_string("aia", s->aia);
522d4be4cdeSjob json_do_string("sia", s->sia);
523d4be4cdeSjob if (s->signtime != 0)
524d4be4cdeSjob json_do_int("signing_time", s->signtime);
525d4be4cdeSjob json_do_int("valid_since", s->notbefore);
526d4be4cdeSjob json_do_int("valid_until", s->notafter);
527d4be4cdeSjob if (s->expires)
528d4be4cdeSjob json_do_int("expires", s->expires);
529d4be4cdeSjob json_do_int("asid", s->asid);
530d4be4cdeSjob } else {
531d4be4cdeSjob printf("Subject key identifier: %s\n", pretty_key_id(s->ski));
532d4be4cdeSjob x509_print(x);
533d4be4cdeSjob printf("Authority key identifier: %s\n", pretty_key_id(s->aki));
534d4be4cdeSjob printf("Authority info access: %s\n", s->aia);
535d4be4cdeSjob printf("Subject info access: %s\n", s->sia);
536d4be4cdeSjob if (s->signtime != 0)
537d4be4cdeSjob printf("Signing time: %s\n",
538d4be4cdeSjob time2str(s->signtime));
539d4be4cdeSjob printf("SPL not before: %s\n",
540d4be4cdeSjob time2str(s->notbefore));
541d4be4cdeSjob printf("SPL not after: %s\n", time2str(s->notafter));
542d4be4cdeSjob printf("asID: %u\n", s->asid);
543d4be4cdeSjob printf("Originated IP Prefixes: ");
544d4be4cdeSjob }
545d4be4cdeSjob
546d4be4cdeSjob if (outformats & FORMAT_JSON)
547d4be4cdeSjob json_do_array("prefixes");
548d4be4cdeSjob for (i = 0; i < s->pfxsz; i++) {
549d4be4cdeSjob ip_addr_print(&s->pfxs[i].prefix, s->pfxs[i].afi, buf,
550d4be4cdeSjob sizeof(buf));
551d4be4cdeSjob
552d4be4cdeSjob if (outformats & FORMAT_JSON) {
553d4be4cdeSjob json_do_string("prefix", buf);
554d4be4cdeSjob } else {
555d4be4cdeSjob if (i > 0)
556d4be4cdeSjob printf("%26s", "");
557d4be4cdeSjob printf("%s\n", buf);
558d4be4cdeSjob }
559d4be4cdeSjob }
560d4be4cdeSjob if (outformats & FORMAT_JSON)
561d4be4cdeSjob json_do_end();
562d4be4cdeSjob }
563d4be4cdeSjob
564d4be4cdeSjob void
gbr_print(const X509 * x,const struct gbr * p)565530399e8Sjob gbr_print(const X509 *x, const struct gbr *p)
566714f4e3fSclaudio {
567530399e8Sjob if (outformats & FORMAT_JSON) {
5687aabcda0Sclaudio json_do_string("type", "gbr");
5697aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
570530399e8Sjob x509_print(x);
5717aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
5727aabcda0Sclaudio json_do_string("aia", p->aia);
5737aabcda0Sclaudio json_do_string("sia", p->sia);
5741bb1e509Sjob if (p->signtime != 0)
5757aabcda0Sclaudio json_do_int("signing_time", p->signtime);
5767aabcda0Sclaudio json_do_int("valid_since", p->notbefore);
5777aabcda0Sclaudio json_do_int("valid_until", p->notafter);
578894936b4Sjob if (p->expires)
5797aabcda0Sclaudio json_do_int("expires", p->expires);
5807aabcda0Sclaudio json_do_string("vcard", p->vcard);
581530399e8Sjob } else {
582714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
583530399e8Sjob x509_print(x);
584714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
585714f4e3fSclaudio printf("Authority info access: %s\n", p->aia);
5862cf0e122Sjob printf("Subject info access: %s\n", p->sia);
5871bb1e509Sjob if (p->signtime != 0)
5881bb1e509Sjob printf("Signing time: %s\n",
5891bb1e509Sjob time2str(p->signtime));
5904dbb22b8Sjob printf("GBR not before: %s\n",
591f5999ddfSjob time2str(p->notbefore));
5924dbb22b8Sjob printf("GBR not after: %s\n", time2str(p->notafter));
593714f4e3fSclaudio printf("vcard:\n%s", p->vcard);
594714f4e3fSclaudio }
595530399e8Sjob }
59604834fbdSjob
59704834fbdSjob void
rsc_print(const X509 * x,const struct rsc * p)59804834fbdSjob rsc_print(const X509 *x, const struct rsc *p)
59904834fbdSjob {
60004834fbdSjob char *hash;
6017aabcda0Sclaudio size_t i;
60204834fbdSjob
60304834fbdSjob if (outformats & FORMAT_JSON) {
6047aabcda0Sclaudio json_do_string("type", "rsc");
6057aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
60604834fbdSjob x509_print(x);
6077aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
6087aabcda0Sclaudio json_do_string("aia", p->aia);
6091bb1e509Sjob if (p->signtime != 0)
6107aabcda0Sclaudio json_do_int("signing_time", p->signtime);
6117aabcda0Sclaudio json_do_int("valid_since", p->notbefore);
6127aabcda0Sclaudio json_do_int("valid_until", p->notafter);
613894936b4Sjob if (p->expires)
6147aabcda0Sclaudio json_do_int("expires", p->expires);
6157aabcda0Sclaudio json_do_array("signed_with_resources");
61604834fbdSjob } else {
61704834fbdSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
61804834fbdSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
61904834fbdSjob x509_print(x);
62004834fbdSjob printf("Authority info access: %s\n", p->aia);
6211bb1e509Sjob if (p->signtime != 0)
6221bb1e509Sjob printf("Signing time: %s\n",
6231bb1e509Sjob time2str(p->signtime));
6244dbb22b8Sjob printf("RSC not before: %s\n",
625f5999ddfSjob time2str(p->notbefore));
6264dbb22b8Sjob printf("RSC not after: %s\n", time2str(p->notafter));
6274486d057Sjob printf("Signed with resources: ");
62804834fbdSjob }
62904834fbdSjob
6307aabcda0Sclaudio as_resources_print(p->as, p->asz);
6317aabcda0Sclaudio ip_resources_print(p->ips, p->ipsz, p->asz);
63204834fbdSjob
63304834fbdSjob if (outformats & FORMAT_JSON) {
6347aabcda0Sclaudio json_do_end();
6357aabcda0Sclaudio json_do_array("filenamesandhashes");
63604834fbdSjob } else
6374486d057Sjob printf("Filenames and hashes: ");
63804834fbdSjob
63904834fbdSjob for (i = 0; i < p->filesz; i++) {
64004834fbdSjob if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash),
64104834fbdSjob &hash) == -1)
64204834fbdSjob errx(1, "base64_encode failure");
64304834fbdSjob
64404834fbdSjob if (outformats & FORMAT_JSON) {
645a09a3191Sclaudio json_do_object("filehash", 1);
6467aabcda0Sclaudio if (p->files[i].filename)
6477aabcda0Sclaudio json_do_string("filename",
6487aabcda0Sclaudio p->files[i].filename);
6497aabcda0Sclaudio json_do_string("hash_digest", hash);
6507aabcda0Sclaudio json_do_end();
65104834fbdSjob } else {
6524486d057Sjob if (i > 0)
6534486d057Sjob printf("%26s", "");
6544486d057Sjob printf("%zu: %s (hash: %s)\n", i + 1,
6554486d057Sjob p->files[i].filename ? p->files[i].filename
6564486d057Sjob : "no filename", hash);
65704834fbdSjob }
65804834fbdSjob
65904834fbdSjob free(hash);
66004834fbdSjob }
66104834fbdSjob
66204834fbdSjob if (outformats & FORMAT_JSON)
6637aabcda0Sclaudio json_do_end();
66404834fbdSjob }
665a29ddfd5Sjob
666a29ddfd5Sjob void
aspa_print(const X509 * x,const struct aspa * p)667a29ddfd5Sjob aspa_print(const X509 *x, const struct aspa *p)
668a29ddfd5Sjob {
6694b5fc138Sjob size_t i;
6704b5fc138Sjob
671a29ddfd5Sjob if (outformats & FORMAT_JSON) {
6727aabcda0Sclaudio json_do_string("type", "aspa");
6737aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
674a29ddfd5Sjob x509_print(x);
6757aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
6767aabcda0Sclaudio json_do_string("aia", p->aia);
6777aabcda0Sclaudio json_do_string("sia", p->sia);
6781bb1e509Sjob if (p->signtime != 0)
6797aabcda0Sclaudio json_do_int("signing_time", p->signtime);
6807aabcda0Sclaudio json_do_int("valid_since", p->notbefore);
6817aabcda0Sclaudio json_do_int("valid_until", p->notafter);
6829c1f5d6bSjob if (p->expires)
6837aabcda0Sclaudio json_do_int("expires", p->expires);
6847aabcda0Sclaudio json_do_uint("customer_asid", p->custasid);
6854a0be80eSjob json_do_array("providers");
686a29ddfd5Sjob } else {
687a29ddfd5Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
688a29ddfd5Sjob x509_print(x);
689a29ddfd5Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
690a29ddfd5Sjob printf("Authority info access: %s\n", p->aia);
6912cf0e122Sjob printf("Subject info access: %s\n", p->sia);
6921bb1e509Sjob if (p->signtime != 0)
6931bb1e509Sjob printf("Signing time: %s\n",
6941bb1e509Sjob time2str(p->signtime));
6954dbb22b8Sjob printf("ASPA not before: %s\n",
696f5999ddfSjob time2str(p->notbefore));
6974dbb22b8Sjob printf("ASPA not after: %s\n", time2str(p->notafter));
698ba54bc08Sjob printf("Customer ASID: %u\n", p->custasid);
6994a0be80eSjob printf("Providers: ");
700a29ddfd5Sjob }
7017aabcda0Sclaudio
7024b5fc138Sjob for (i = 0; i < p->providersz; i++) {
703b179db0eSjob if (outformats & FORMAT_JSON)
704b179db0eSjob json_do_uint("asid", p->providers[i]);
705b179db0eSjob else {
706b179db0eSjob if (i > 0)
7074b5fc138Sjob printf("%26s", "");
708b179db0eSjob printf("AS: %u\n", p->providers[i]);
709b179db0eSjob }
7104b5fc138Sjob }
7117aabcda0Sclaudio
7127aabcda0Sclaudio if (outformats & FORMAT_JSON)
7137aabcda0Sclaudio json_do_end();
714a29ddfd5Sjob }
715ee2a33daSjob
716ee2a33daSjob static void
takey_print(char * name,const struct takey * t)717ee2a33daSjob takey_print(char *name, const struct takey *t)
718ee2a33daSjob {
719ee2a33daSjob char *spki = NULL;
720ee2a33daSjob size_t i, j = 0;
721ee2a33daSjob
722ee2a33daSjob if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0)
723ee2a33daSjob errx(1, "base64_encode failed in %s", __func__);
724ee2a33daSjob
725ee2a33daSjob if (outformats & FORMAT_JSON) {
726a09a3191Sclaudio json_do_object("takey", 0);
7277aabcda0Sclaudio json_do_string("name", name);
7287aabcda0Sclaudio json_do_array("comments");
7297aabcda0Sclaudio for (i = 0; i < t->commentsz; i++)
7307aabcda0Sclaudio json_do_string("comment", t->comments[i]);
7317aabcda0Sclaudio json_do_end();
7327aabcda0Sclaudio json_do_array("uris");
7337aabcda0Sclaudio for (i = 0; i < t->urisz; i++)
7347aabcda0Sclaudio json_do_string("uri", t->uris[i]);
7357aabcda0Sclaudio json_do_end();
7367aabcda0Sclaudio json_do_string("spki", spki);
737*17f7a478Sjob json_do_end();
738ee2a33daSjob } else {
739ee2a33daSjob printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name);
740ee2a33daSjob
7417aabcda0Sclaudio for (i = 0; i < t->commentsz; i++)
742ee2a33daSjob printf("\t# %s\n", t->comments[i]);
743eb3e3a76Sjob if (t->commentsz > 0)
7447aabcda0Sclaudio printf("\n");
7457aabcda0Sclaudio for (i = 0; i < t->urisz; i++)
746eb3e3a76Sjob printf("\t%s\n", t->uris[i]);
747eb3e3a76Sjob printf("\n\t");
748ee2a33daSjob for (i = 0; i < strlen(spki); i++) {
749ee2a33daSjob printf("%c", spki[i]);
7507aabcda0Sclaudio if ((++j % 64) == 0)
751ee2a33daSjob printf("\n\t");
752ee2a33daSjob }
753ee2a33daSjob printf("\n\n");
754ee2a33daSjob }
755ee2a33daSjob
756ee2a33daSjob free(spki);
757ee2a33daSjob }
758ee2a33daSjob
759ee2a33daSjob void
tak_print(const X509 * x,const struct tak * p)760ee2a33daSjob tak_print(const X509 *x, const struct tak *p)
761ee2a33daSjob {
762ee2a33daSjob if (outformats & FORMAT_JSON) {
7637aabcda0Sclaudio json_do_string("type", "tak");
7647aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
765ee2a33daSjob x509_print(x);
7667aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
7677aabcda0Sclaudio json_do_string("aia", p->aia);
7687aabcda0Sclaudio json_do_string("sia", p->sia);
7691bb1e509Sjob if (p->signtime != 0)
7707aabcda0Sclaudio json_do_int("signing_time", p->signtime);
7717aabcda0Sclaudio json_do_int("valid_since", p->notbefore);
7727aabcda0Sclaudio json_do_int("valid_until", p->notafter);
773894936b4Sjob if (p->expires)
7747aabcda0Sclaudio json_do_int("expires", p->expires);
7757aabcda0Sclaudio json_do_array("takeys");
776ee2a33daSjob } else {
777ee2a33daSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
778ee2a33daSjob x509_print(x);
779ee2a33daSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
780ee2a33daSjob printf("Authority info access: %s\n", p->aia);
7812cf0e122Sjob printf("Subject info access: %s\n", p->sia);
7821bb1e509Sjob if (p->signtime != 0)
7831bb1e509Sjob printf("Signing time: %s\n",
7841bb1e509Sjob time2str(p->signtime));
7854dbb22b8Sjob printf("TAK not before: %s\n",
786f5999ddfSjob time2str(p->notbefore));
7874dbb22b8Sjob printf("TAK not after: %s\n", time2str(p->notafter));
788ee2a33daSjob }
789ee2a33daSjob
790ee2a33daSjob takey_print("current", p->current);
7917aabcda0Sclaudio if (p->predecessor != NULL)
792ee2a33daSjob takey_print("predecessor", p->predecessor);
7937aabcda0Sclaudio if (p->successor != NULL)
794ee2a33daSjob takey_print("successor", p->successor);
795ee2a33daSjob
796ee2a33daSjob if (outformats & FORMAT_JSON)
7977aabcda0Sclaudio json_do_end();
798ee2a33daSjob }
799ef3f6f56Sjob
800ef3f6f56Sjob void
geofeed_print(const X509 * x,const struct geofeed * p)801ef3f6f56Sjob geofeed_print(const X509 *x, const struct geofeed *p)
802ef3f6f56Sjob {
803ef3f6f56Sjob char buf[128];
804ef3f6f56Sjob size_t i;
805ef3f6f56Sjob
806ef3f6f56Sjob if (outformats & FORMAT_JSON) {
8077aabcda0Sclaudio json_do_string("type", "geofeed");
8087aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski));
809ef3f6f56Sjob x509_print(x);
8107aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki));
8117aabcda0Sclaudio json_do_string("aia", p->aia);
8121bb1e509Sjob if (p->signtime != 0)
8137aabcda0Sclaudio json_do_int("signing_time", p->signtime);
8147aabcda0Sclaudio json_do_int("valid_since", p->notbefore);
8157aabcda0Sclaudio json_do_int("valid_until", p->notafter);
8167aabcda0Sclaudio if (p->expires)
8177aabcda0Sclaudio json_do_int("expires", p->expires);
8187aabcda0Sclaudio json_do_array("records");
819ef3f6f56Sjob } else {
820ef3f6f56Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
821ef3f6f56Sjob x509_print(x);
822ef3f6f56Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
823ef3f6f56Sjob printf("Authority info access: %s\n", p->aia);
8241bb1e509Sjob if (p->signtime != 0)
8251bb1e509Sjob printf("Signing time: %s\n",
8261bb1e509Sjob time2str(p->signtime));
8274dbb22b8Sjob printf("Geofeed not before: %s\n",
828f5999ddfSjob time2str(p->notbefore));
8294dbb22b8Sjob printf("Geofeed not after: %s\n", time2str(p->notafter));
830bd19f13dSjob printf("Geofeed CSV records: ");
831ef3f6f56Sjob }
832ef3f6f56Sjob
833ef3f6f56Sjob for (i = 0; i < p->geoipsz; i++) {
834ef3f6f56Sjob if (p->geoips[i].ip->type != CERT_IP_ADDR)
835ef3f6f56Sjob continue;
836ef3f6f56Sjob
837ef3f6f56Sjob ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf,
838ef3f6f56Sjob sizeof(buf));
8397aabcda0Sclaudio if (outformats & FORMAT_JSON) {
840a09a3191Sclaudio json_do_object("geoip", 1);
8417aabcda0Sclaudio json_do_string("prefix", buf);
8427aabcda0Sclaudio json_do_string("location", p->geoips[i].loc);
8437aabcda0Sclaudio json_do_end();
8447aabcda0Sclaudio } else {
8454486d057Sjob if (i > 0)
8464486d057Sjob printf("%26s", "");
8477aabcda0Sclaudio printf("IP: %s (%s)\n", buf, p->geoips[i].loc);
8484486d057Sjob }
849ef3f6f56Sjob }
850ef3f6f56Sjob
851ef3f6f56Sjob if (outformats & FORMAT_JSON)
8527aabcda0Sclaudio json_do_end();
853ef3f6f56Sjob }
854