1*7aabcda0Sclaudio /* $OpenBSD: print.c,v 1.39 2023/05/30 12:02:22 claudio 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" 31*7aabcda0Sclaudio #include "json.h" 32714f4e3fSclaudio 33714f4e3fSclaudio static const char * 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 * 53220c707cSclaudio time2str(time_t t) 54220c707cSclaudio { 55220c707cSclaudio static char buf[64]; 56220c707cSclaudio struct tm tm; 57220c707cSclaudio 58220c707cSclaudio if (gmtime_r(&t, &tm) == NULL) 59220c707cSclaudio return "could not convert time"; 60220c707cSclaudio 61cc6f004eSjob strftime(buf, sizeof(buf), "%a %d %b %Y %T %z", &tm); 62cc6f004eSjob 63220c707cSclaudio return buf; 64220c707cSclaudio } 65220c707cSclaudio 66714f4e3fSclaudio void 67714f4e3fSclaudio tal_print(const struct tal *p) 68714f4e3fSclaudio { 69e911df76Sjob char *ski; 70e911df76Sjob EVP_PKEY *pk; 71e911df76Sjob RSA *r; 72e911df76Sjob const unsigned char *der; 73e911df76Sjob unsigned char *rder = NULL; 74e911df76Sjob unsigned char md[SHA_DIGEST_LENGTH]; 75e911df76Sjob int rder_len; 76714f4e3fSclaudio size_t i; 77714f4e3fSclaudio 78e911df76Sjob der = p->pkey; 79e911df76Sjob pk = d2i_PUBKEY(NULL, &der, p->pkeysz); 80e911df76Sjob if (pk == NULL) 81e911df76Sjob errx(1, "d2i_PUBKEY failed in %s", __func__); 82e911df76Sjob 83e911df76Sjob r = EVP_PKEY_get0_RSA(pk); 84e911df76Sjob if (r == NULL) 85e911df76Sjob errx(1, "EVP_PKEY_get0_RSA failed in %s", __func__); 86e911df76Sjob if ((rder_len = i2d_RSAPublicKey(r, &rder)) <= 0) 87e911df76Sjob errx(1, "i2d_RSAPublicKey failed in %s", __func__); 88e911df76Sjob 89e911df76Sjob if (!EVP_Digest(rder, rder_len, md, NULL, EVP_sha1(), NULL)) 90e911df76Sjob errx(1, "EVP_Digest failed in %s", __func__); 91e911df76Sjob 92e911df76Sjob ski = hex_encode(md, SHA_DIGEST_LENGTH); 93e911df76Sjob 94530399e8Sjob if (outformats & FORMAT_JSON) { 95*7aabcda0Sclaudio json_do_string("type", "tal"); 96*7aabcda0Sclaudio json_do_string("name", p->descr); 97*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(ski)); 98*7aabcda0Sclaudio json_do_array("trust_anchor_locations"); 99*7aabcda0Sclaudio for (i = 0; i < p->urisz; i++) 100*7aabcda0Sclaudio json_do_string("tal", p->uri[i]); 101*7aabcda0Sclaudio json_do_end(); 102530399e8Sjob } else { 103530399e8Sjob printf("Trust anchor name: %s\n", p->descr); 104530399e8Sjob printf("Subject key identifier: %s\n", pretty_key_id(ski)); 1054486d057Sjob printf("Trust anchor locations: "); 1064486d057Sjob for (i = 0; i < p->urisz; i++) { 1074486d057Sjob if (i > 0) 1084486d057Sjob printf("%26s", ""); 1094486d057Sjob printf("%s\n", p->uri[i]); 1104486d057Sjob } 111530399e8Sjob } 112e911df76Sjob 113e911df76Sjob EVP_PKEY_free(pk); 114e911df76Sjob free(rder); 115e911df76Sjob free(ski); 116714f4e3fSclaudio } 117714f4e3fSclaudio 118714f4e3fSclaudio void 119530399e8Sjob x509_print(const X509 *x) 120530399e8Sjob { 121530399e8Sjob const ASN1_INTEGER *xserial; 122e0b87278Sjob const X509_NAME *xissuer; 123e0b87278Sjob char *issuer = NULL; 124530399e8Sjob char *serial = NULL; 125530399e8Sjob 126e0b87278Sjob if ((xissuer = X509_get_issuer_name(x)) == NULL) { 127e0b87278Sjob warnx("X509_get_issuer_name failed"); 128530399e8Sjob goto out; 129530399e8Sjob } 130530399e8Sjob 131e0b87278Sjob if ((issuer = X509_NAME_oneline(xissuer, NULL, 0)) == NULL) { 132e0b87278Sjob warnx("X509_NAME_oneline failed"); 133530399e8Sjob goto out; 134530399e8Sjob } 135530399e8Sjob 136e0b87278Sjob if ((xserial = X509_get0_serialNumber(x)) == NULL) { 137e0b87278Sjob warnx("X509_get0_serialNumber failed"); 138e0b87278Sjob goto out; 139e0b87278Sjob } 140e0b87278Sjob 141e0b87278Sjob if ((serial = x509_convert_seqnum(__func__, xserial)) == NULL) 142e0b87278Sjob goto out; 143e0b87278Sjob 144530399e8Sjob if (outformats & FORMAT_JSON) { 145*7aabcda0Sclaudio json_do_string("cert_issuer", issuer); 146*7aabcda0Sclaudio json_do_string("cert_serial", serial); 147530399e8Sjob } else { 148e0b87278Sjob printf("Certificate issuer: %s\n", issuer); 149530399e8Sjob printf("Certificate serial: %s\n", serial); 150530399e8Sjob } 151530399e8Sjob 152530399e8Sjob out: 153e0b87278Sjob free(issuer); 154530399e8Sjob free(serial); 155530399e8Sjob } 156530399e8Sjob 157*7aabcda0Sclaudio static void 158*7aabcda0Sclaudio as_resources_print(struct cert_as *as, size_t asz) 159*7aabcda0Sclaudio { 160*7aabcda0Sclaudio size_t i; 161*7aabcda0Sclaudio 162*7aabcda0Sclaudio for (i = 0; i < asz; i++) { 163*7aabcda0Sclaudio if (outformats & FORMAT_JSON) 164*7aabcda0Sclaudio json_do_object("resource"); 165*7aabcda0Sclaudio switch (as[i].type) { 166*7aabcda0Sclaudio case CERT_AS_ID: 167*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 168*7aabcda0Sclaudio json_do_uint("asid", as[i].id); 169*7aabcda0Sclaudio } else { 170*7aabcda0Sclaudio if (i > 0) 171*7aabcda0Sclaudio printf("%26s", ""); 172*7aabcda0Sclaudio printf("AS: %u", as[i].id); 173*7aabcda0Sclaudio } 174*7aabcda0Sclaudio break; 175*7aabcda0Sclaudio case CERT_AS_INHERIT: 176*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 177*7aabcda0Sclaudio json_do_bool("asid_inherit", 1); 178*7aabcda0Sclaudio } else { 179*7aabcda0Sclaudio if (i > 0) 180*7aabcda0Sclaudio printf("%26s", ""); 181*7aabcda0Sclaudio printf("AS: inherit"); 182*7aabcda0Sclaudio } 183*7aabcda0Sclaudio break; 184*7aabcda0Sclaudio case CERT_AS_RANGE: 185*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 186*7aabcda0Sclaudio json_do_object("asrange"); 187*7aabcda0Sclaudio json_do_uint("min", as[i].range.min); 188*7aabcda0Sclaudio json_do_uint("max", as[i].range.max); 189*7aabcda0Sclaudio json_do_end(); 190*7aabcda0Sclaudio } else { 191*7aabcda0Sclaudio if (i > 0) 192*7aabcda0Sclaudio printf("%26s", ""); 193*7aabcda0Sclaudio printf("AS: %u -- %u", as[i].range.min, 194*7aabcda0Sclaudio as[i].range.max); 195*7aabcda0Sclaudio } 196*7aabcda0Sclaudio break; 197*7aabcda0Sclaudio } 198*7aabcda0Sclaudio if (outformats & FORMAT_JSON) 199*7aabcda0Sclaudio json_do_end(); 200*7aabcda0Sclaudio else 201*7aabcda0Sclaudio printf("\n"); 202*7aabcda0Sclaudio } 203*7aabcda0Sclaudio } 204*7aabcda0Sclaudio 205*7aabcda0Sclaudio static void 206*7aabcda0Sclaudio ip_resources_print(struct cert_ip *ips, size_t ipsz, size_t asz) 207*7aabcda0Sclaudio { 208*7aabcda0Sclaudio char buf1[64], buf2[64]; 209*7aabcda0Sclaudio size_t i; 210*7aabcda0Sclaudio int sockt; 211*7aabcda0Sclaudio 212*7aabcda0Sclaudio 213*7aabcda0Sclaudio for (i = 0; i < ipsz; i++) { 214*7aabcda0Sclaudio if (outformats & FORMAT_JSON) 215*7aabcda0Sclaudio json_do_object("resource"); 216*7aabcda0Sclaudio switch (ips[i].type) { 217*7aabcda0Sclaudio case CERT_IP_INHERIT: 218*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 219*7aabcda0Sclaudio json_do_bool("ip_inherit", 1); 220*7aabcda0Sclaudio } else { 221*7aabcda0Sclaudio if (i > 0 || asz > 0) 222*7aabcda0Sclaudio printf("%26s", ""); 223*7aabcda0Sclaudio printf("IP: inherit"); 224*7aabcda0Sclaudio } 225*7aabcda0Sclaudio break; 226*7aabcda0Sclaudio case CERT_IP_ADDR: 227*7aabcda0Sclaudio ip_addr_print(&ips[i].ip, ips[i].afi, buf1, 228*7aabcda0Sclaudio sizeof(buf1)); 229*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 230*7aabcda0Sclaudio json_do_string("ip_prefix", buf1); 231*7aabcda0Sclaudio } else { 232*7aabcda0Sclaudio if (i > 0 || asz > 0) 233*7aabcda0Sclaudio printf("%26s", ""); 234*7aabcda0Sclaudio printf("IP: %s", buf1); 235*7aabcda0Sclaudio } 236*7aabcda0Sclaudio break; 237*7aabcda0Sclaudio case CERT_IP_RANGE: 238*7aabcda0Sclaudio sockt = (ips[i].afi == AFI_IPV4) ? 239*7aabcda0Sclaudio AF_INET : AF_INET6; 240*7aabcda0Sclaudio inet_ntop(sockt, ips[i].min, buf1, sizeof(buf1)); 241*7aabcda0Sclaudio inet_ntop(sockt, ips[i].max, buf2, sizeof(buf2)); 242*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 243*7aabcda0Sclaudio json_do_object("ip_range"); 244*7aabcda0Sclaudio json_do_string("min", buf1); 245*7aabcda0Sclaudio json_do_string("max", buf2); 246*7aabcda0Sclaudio json_do_end(); 247*7aabcda0Sclaudio } else { 248*7aabcda0Sclaudio if (i > 0 || asz > 0) 249*7aabcda0Sclaudio printf("%26s", ""); 250*7aabcda0Sclaudio printf("IP: %s -- %s", buf1, buf2); 251*7aabcda0Sclaudio } 252*7aabcda0Sclaudio break; 253*7aabcda0Sclaudio } 254*7aabcda0Sclaudio if (outformats & FORMAT_JSON) 255*7aabcda0Sclaudio json_do_end(); 256*7aabcda0Sclaudio else 257*7aabcda0Sclaudio printf("\n"); 258*7aabcda0Sclaudio } 259*7aabcda0Sclaudio } 260*7aabcda0Sclaudio 261530399e8Sjob void 262714f4e3fSclaudio cert_print(const struct cert *p) 263714f4e3fSclaudio { 264530399e8Sjob if (outformats & FORMAT_JSON) { 265530399e8Sjob if (p->pubkey != NULL) 266*7aabcda0Sclaudio json_do_string("type", "router_key"); 267530399e8Sjob else 268*7aabcda0Sclaudio json_do_string("type", "ca_cert"); 269*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 270530399e8Sjob if (p->aki != NULL) 271*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 272530399e8Sjob x509_print(p->x509); 273530399e8Sjob if (p->aia != NULL) 274*7aabcda0Sclaudio json_do_string("aia", p->aia); 275530399e8Sjob if (p->mft != NULL) 276*7aabcda0Sclaudio json_do_string("manifest", p->mft); 277530399e8Sjob if (p->repo != NULL) 278*7aabcda0Sclaudio json_do_string("carepository", p->repo); 279530399e8Sjob if (p->notify != NULL) 280*7aabcda0Sclaudio json_do_string("notify_url", p->notify); 281530399e8Sjob if (p->pubkey != NULL) 282*7aabcda0Sclaudio json_do_string("router_key", p->pubkey); 283*7aabcda0Sclaudio json_do_int("valid_since", p->notbefore); 284*7aabcda0Sclaudio json_do_int("valid_until", p->notafter); 285894936b4Sjob if (p->expires) 286*7aabcda0Sclaudio json_do_int("expires", p->expires); 287*7aabcda0Sclaudio json_do_array("subordinate_resources"); 288530399e8Sjob } else { 289714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 290714f4e3fSclaudio if (p->aki != NULL) 29167642cb5Stb printf("Authority key identifier: %s\n", 29267642cb5Stb pretty_key_id(p->aki)); 293530399e8Sjob x509_print(p->x509); 294714f4e3fSclaudio if (p->aia != NULL) 295714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 296714f4e3fSclaudio if (p->mft != NULL) 297714f4e3fSclaudio printf("Manifest: %s\n", p->mft); 298714f4e3fSclaudio if (p->repo != NULL) 299714f4e3fSclaudio printf("caRepository: %s\n", p->repo); 300714f4e3fSclaudio if (p->notify != NULL) 301714f4e3fSclaudio printf("Notify URL: %s\n", p->notify); 30214d83341Sjob if (p->pubkey != NULL) { 3036530cf17Sjob printf("BGPsec ECDSA public key: %s\n", 30467642cb5Stb p->pubkey); 3054dbb22b8Sjob printf("Router key not before: %s\n", 306f5999ddfSjob time2str(p->notbefore)); 3074dbb22b8Sjob printf("Router key not after: %s\n", 3089f544822Sjob time2str(p->notafter)); 309f5999ddfSjob } else { 3104dbb22b8Sjob printf("Certificate not before: %s\n", 311f5999ddfSjob time2str(p->notbefore)); 3124dbb22b8Sjob printf("Certificate not after: %s\n", 3139f544822Sjob time2str(p->notafter)); 314f5999ddfSjob } 3154486d057Sjob printf("Subordinate resources: "); 316530399e8Sjob } 317714f4e3fSclaudio 318*7aabcda0Sclaudio as_resources_print(p->as, p->asz); 319*7aabcda0Sclaudio ip_resources_print(p->ips, p->ipsz, p->asz); 320530399e8Sjob 321530399e8Sjob if (outformats & FORMAT_JSON) 322*7aabcda0Sclaudio json_do_end(); 323714f4e3fSclaudio } 324714f4e3fSclaudio 325714f4e3fSclaudio void 326220c707cSclaudio crl_print(const struct crl *p) 327220c707cSclaudio { 328220c707cSclaudio STACK_OF(X509_REVOKED) *revlist; 329220c707cSclaudio X509_REVOKED *rev; 3307cdd491fSclaudio ASN1_INTEGER *crlnum; 331e0b87278Sjob X509_NAME *xissuer; 332220c707cSclaudio int i; 333e0b87278Sjob char *issuer, *serial; 334220c707cSclaudio time_t t; 335220c707cSclaudio 336530399e8Sjob if (outformats & FORMAT_JSON) { 337*7aabcda0Sclaudio json_do_string("type", "crl"); 338*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 339530399e8Sjob } else 340220c707cSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 3417cdd491fSclaudio 342e0b87278Sjob xissuer = X509_CRL_get_issuer(p->x509_crl); 343e0b87278Sjob issuer = X509_NAME_oneline(xissuer, NULL, 0); 3447cdd491fSclaudio crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL); 3457cdd491fSclaudio serial = x509_convert_seqnum(__func__, crlnum); 346e0b87278Sjob if (issuer != NULL && serial != NULL) { 347e0b87278Sjob if (outformats & FORMAT_JSON) { 348*7aabcda0Sclaudio json_do_string("crl_issuer", issuer); 349*7aabcda0Sclaudio json_do_string("crl_serial", serial); 350e0b87278Sjob } else { 351e0b87278Sjob printf("CRL issuer: %s\n", issuer); 352e0b87278Sjob printf("CRL serial number: %s\n", serial); 353530399e8Sjob } 354e0b87278Sjob } 355e0b87278Sjob free(issuer); 3567cdd491fSclaudio free(serial); 3577cdd491fSclaudio ASN1_INTEGER_free(crlnum); 3587cdd491fSclaudio 359530399e8Sjob if (outformats & FORMAT_JSON) { 360*7aabcda0Sclaudio json_do_int("valid_since", p->lastupdate); 361*7aabcda0Sclaudio json_do_int("valid_until", p->nextupdate); 362*7aabcda0Sclaudio json_do_array("revoked_certs"); 363530399e8Sjob } else { 3644dbb22b8Sjob printf("CRL last update: %s\n", 3659f544822Sjob time2str(p->lastupdate)); 3664dbb22b8Sjob printf("CRL next update: %s\n", 3679f544822Sjob time2str(p->nextupdate)); 368530399e8Sjob printf("Revoked Certificates:\n"); 369530399e8Sjob } 370220c707cSclaudio 371220c707cSclaudio revlist = X509_CRL_get_REVOKED(p->x509_crl); 372220c707cSclaudio for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) { 373220c707cSclaudio rev = sk_X509_REVOKED_value(revlist, i); 3747cdd491fSclaudio serial = x509_convert_seqnum(__func__, 3757cdd491fSclaudio X509_REVOKED_get0_serialNumber(rev)); 376220c707cSclaudio x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t); 377530399e8Sjob if (serial != NULL) { 378530399e8Sjob if (outformats & FORMAT_JSON) { 379*7aabcda0Sclaudio json_do_object("cert"); 380*7aabcda0Sclaudio json_do_string("serial", serial); 381*7aabcda0Sclaudio json_do_string("date", time2str(t)); 382*7aabcda0Sclaudio json_do_end(); 383530399e8Sjob } else 3844486d057Sjob printf("%25s Serial: %8s Revocation Date: %s" 3854486d057Sjob "\n", "", serial, time2str(t)); 386530399e8Sjob } 3877cdd491fSclaudio free(serial); 388220c707cSclaudio } 389530399e8Sjob 390530399e8Sjob if (outformats & FORMAT_JSON) 391*7aabcda0Sclaudio json_do_end(); 392530399e8Sjob else if (i == 0) 393220c707cSclaudio printf("No Revoked Certificates\n"); 394220c707cSclaudio } 395220c707cSclaudio 396220c707cSclaudio void 397530399e8Sjob mft_print(const X509 *x, const struct mft *p) 398714f4e3fSclaudio { 399714f4e3fSclaudio size_t i; 400714f4e3fSclaudio char *hash; 401714f4e3fSclaudio 402530399e8Sjob if (outformats & FORMAT_JSON) { 403*7aabcda0Sclaudio json_do_string("type", "manifest"); 404*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 405530399e8Sjob x509_print(x); 406*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 407*7aabcda0Sclaudio json_do_string("aia", p->aia); 408*7aabcda0Sclaudio json_do_string("sia", p->sia); 409*7aabcda0Sclaudio json_do_string("manifest_number", p->seqnum); 4101bb1e509Sjob if (p->signtime != 0) 411*7aabcda0Sclaudio json_do_int("signing_time", p->signtime); 412*7aabcda0Sclaudio json_do_int("valid_since", p->thisupdate); 413*7aabcda0Sclaudio json_do_int("valid_until", p->nextupdate); 414894936b4Sjob if (p->expires) 415*7aabcda0Sclaudio json_do_int("expires", p->expires); 416530399e8Sjob } else { 417714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 418714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 419530399e8Sjob x509_print(x); 420714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 4212cf0e122Sjob printf("Subject info access: %s\n", p->sia); 422489e308aSjob printf("Manifest number: %s\n", p->seqnum); 4231bb1e509Sjob if (p->signtime != 0) 4241bb1e509Sjob printf("Signing time: %s\n", 4251bb1e509Sjob time2str(p->signtime)); 4264dbb22b8Sjob printf("Manifest this update: %s\n", time2str(p->thisupdate)); 4274dbb22b8Sjob printf("Manifest next update: %s\n", time2str(p->nextupdate)); 4284486d057Sjob printf("Files and hashes: "); 429530399e8Sjob } 430530399e8Sjob 431*7aabcda0Sclaudio if (outformats & FORMAT_JSON) 432*7aabcda0Sclaudio json_do_array("filesandhashes"); 433714f4e3fSclaudio for (i = 0; i < p->filesz; i++) { 434714f4e3fSclaudio if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 435714f4e3fSclaudio &hash) == -1) 436714f4e3fSclaudio errx(1, "base64_encode failure"); 437530399e8Sjob 438530399e8Sjob if (outformats & FORMAT_JSON) { 439*7aabcda0Sclaudio json_do_object("filehash"); 440*7aabcda0Sclaudio json_do_string("filename", p->files[i].file); 441*7aabcda0Sclaudio json_do_string("hash", hash); 442*7aabcda0Sclaudio json_do_end(); 443530399e8Sjob } else { 4444486d057Sjob if (i > 0) 4454486d057Sjob printf("%26s", ""); 4464486d057Sjob printf("%zu: %s (hash: %s)\n", i + 1, p->files[i].file, 4474486d057Sjob hash); 448530399e8Sjob } 449530399e8Sjob 450714f4e3fSclaudio free(hash); 451714f4e3fSclaudio } 452530399e8Sjob if (outformats & FORMAT_JSON) 453*7aabcda0Sclaudio json_do_end(); 454714f4e3fSclaudio } 455714f4e3fSclaudio 456714f4e3fSclaudio void 457530399e8Sjob roa_print(const X509 *x, const struct roa *p) 458714f4e3fSclaudio { 459714f4e3fSclaudio char buf[128]; 460714f4e3fSclaudio size_t i; 461714f4e3fSclaudio 462530399e8Sjob if (outformats & FORMAT_JSON) { 463*7aabcda0Sclaudio json_do_string("type", "roa"); 464*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 465530399e8Sjob x509_print(x); 466*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 467*7aabcda0Sclaudio json_do_string("aia", p->aia); 468*7aabcda0Sclaudio json_do_string("sia", p->sia); 4691bb1e509Sjob if (p->signtime != 0) 470*7aabcda0Sclaudio json_do_int("signing_time", p->signtime); 471*7aabcda0Sclaudio json_do_int("valid_since", p->notbefore); 472*7aabcda0Sclaudio json_do_int("valid_until", p->notafter); 4739c1f5d6bSjob if (p->expires) 474*7aabcda0Sclaudio json_do_int("expires", p->expires); 475530399e8Sjob } else { 476714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 477530399e8Sjob x509_print(x); 478714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 479714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 4802cf0e122Sjob printf("Subject info access: %s\n", p->sia); 4811bb1e509Sjob if (p->signtime != 0) 4821bb1e509Sjob printf("Signing time: %s\n", 4831bb1e509Sjob time2str(p->signtime)); 4844dbb22b8Sjob printf("ROA not before: %s\n", 485f5999ddfSjob time2str(p->notbefore)); 4864dbb22b8Sjob printf("ROA not after: %s\n", time2str(p->notafter)); 487714f4e3fSclaudio printf("asID: %u\n", p->asid); 4884486d057Sjob printf("IP address blocks: "); 489530399e8Sjob } 490530399e8Sjob 491*7aabcda0Sclaudio if (outformats & FORMAT_JSON) 492*7aabcda0Sclaudio json_do_array("vrps"); 493714f4e3fSclaudio for (i = 0; i < p->ipsz; i++) { 494714f4e3fSclaudio ip_addr_print(&p->ips[i].addr, 495714f4e3fSclaudio p->ips[i].afi, buf, sizeof(buf)); 496530399e8Sjob 497530399e8Sjob if (outformats & FORMAT_JSON) { 498*7aabcda0Sclaudio json_do_object("vrp"); 499*7aabcda0Sclaudio json_do_string("prefix", buf); 500*7aabcda0Sclaudio json_do_uint("asid", p->asid); 501*7aabcda0Sclaudio json_do_uint("maxlen", p->ips[i].maxlength); 502*7aabcda0Sclaudio json_do_end(); 5034486d057Sjob } else { 5044486d057Sjob if (i > 0) 5054486d057Sjob printf("%26s", ""); 5064486d057Sjob printf("%s maxlen: %hhu\n", buf, p->ips[i].maxlength); 5074486d057Sjob } 508714f4e3fSclaudio } 509530399e8Sjob if (outformats & FORMAT_JSON) 510*7aabcda0Sclaudio json_do_end(); 511714f4e3fSclaudio } 512714f4e3fSclaudio 513714f4e3fSclaudio void 514530399e8Sjob gbr_print(const X509 *x, const struct gbr *p) 515714f4e3fSclaudio { 516530399e8Sjob if (outformats & FORMAT_JSON) { 517*7aabcda0Sclaudio json_do_string("type", "gbr"); 518*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 519530399e8Sjob x509_print(x); 520*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 521*7aabcda0Sclaudio json_do_string("aia", p->aia); 522*7aabcda0Sclaudio json_do_string("sia", p->sia); 5231bb1e509Sjob if (p->signtime != 0) 524*7aabcda0Sclaudio json_do_int("signing_time", p->signtime); 525*7aabcda0Sclaudio json_do_int("valid_since", p->notbefore); 526*7aabcda0Sclaudio json_do_int("valid_until", p->notafter); 527894936b4Sjob if (p->expires) 528*7aabcda0Sclaudio json_do_int("expires", p->expires); 529*7aabcda0Sclaudio json_do_string("vcard", p->vcard); 530530399e8Sjob } else { 531714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 532530399e8Sjob x509_print(x); 533714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 534714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 5352cf0e122Sjob printf("Subject info access: %s\n", p->sia); 5361bb1e509Sjob if (p->signtime != 0) 5371bb1e509Sjob printf("Signing time: %s\n", 5381bb1e509Sjob time2str(p->signtime)); 5394dbb22b8Sjob printf("GBR not before: %s\n", 540f5999ddfSjob time2str(p->notbefore)); 5414dbb22b8Sjob printf("GBR not after: %s\n", time2str(p->notafter)); 542714f4e3fSclaudio printf("vcard:\n%s", p->vcard); 543714f4e3fSclaudio } 544530399e8Sjob } 54504834fbdSjob 54604834fbdSjob void 54704834fbdSjob rsc_print(const X509 *x, const struct rsc *p) 54804834fbdSjob { 54904834fbdSjob char *hash; 550*7aabcda0Sclaudio size_t i; 55104834fbdSjob 55204834fbdSjob if (outformats & FORMAT_JSON) { 553*7aabcda0Sclaudio json_do_string("type", "rsc"); 554*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 55504834fbdSjob x509_print(x); 556*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 557*7aabcda0Sclaudio json_do_string("aia", p->aia); 5581bb1e509Sjob if (p->signtime != 0) 559*7aabcda0Sclaudio json_do_int("signing_time", p->signtime); 560*7aabcda0Sclaudio json_do_int("valid_since", p->notbefore); 561*7aabcda0Sclaudio json_do_int("valid_until", p->notafter); 562894936b4Sjob if (p->expires) 563*7aabcda0Sclaudio json_do_int("expires", p->expires); 564*7aabcda0Sclaudio json_do_array("signed_with_resources"); 56504834fbdSjob } else { 56604834fbdSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 56704834fbdSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 56804834fbdSjob x509_print(x); 56904834fbdSjob printf("Authority info access: %s\n", p->aia); 5701bb1e509Sjob if (p->signtime != 0) 5711bb1e509Sjob printf("Signing time: %s\n", 5721bb1e509Sjob time2str(p->signtime)); 5734dbb22b8Sjob printf("RSC not before: %s\n", 574f5999ddfSjob time2str(p->notbefore)); 5754dbb22b8Sjob printf("RSC not after: %s\n", time2str(p->notafter)); 5764486d057Sjob printf("Signed with resources: "); 57704834fbdSjob } 57804834fbdSjob 579*7aabcda0Sclaudio as_resources_print(p->as, p->asz); 580*7aabcda0Sclaudio ip_resources_print(p->ips, p->ipsz, p->asz); 58104834fbdSjob 58204834fbdSjob if (outformats & FORMAT_JSON) { 583*7aabcda0Sclaudio json_do_end(); 584*7aabcda0Sclaudio json_do_array("filenamesandhashes"); 58504834fbdSjob } else 5864486d057Sjob printf("Filenames and hashes: "); 58704834fbdSjob 58804834fbdSjob for (i = 0; i < p->filesz; i++) { 58904834fbdSjob if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 59004834fbdSjob &hash) == -1) 59104834fbdSjob errx(1, "base64_encode failure"); 59204834fbdSjob 59304834fbdSjob if (outformats & FORMAT_JSON) { 594*7aabcda0Sclaudio json_do_object("filehash"); 595*7aabcda0Sclaudio if (p->files[i].filename) 596*7aabcda0Sclaudio json_do_string("filename", 597*7aabcda0Sclaudio p->files[i].filename); 598*7aabcda0Sclaudio json_do_string("hash_digest", hash); 599*7aabcda0Sclaudio json_do_end(); 60004834fbdSjob } else { 6014486d057Sjob if (i > 0) 6024486d057Sjob printf("%26s", ""); 6034486d057Sjob printf("%zu: %s (hash: %s)\n", i + 1, 6044486d057Sjob p->files[i].filename ? p->files[i].filename 6054486d057Sjob : "no filename", hash); 60604834fbdSjob } 60704834fbdSjob 60804834fbdSjob free(hash); 60904834fbdSjob } 61004834fbdSjob 61104834fbdSjob if (outformats & FORMAT_JSON) 612*7aabcda0Sclaudio json_do_end(); 61304834fbdSjob } 614a29ddfd5Sjob 615ba54bc08Sjob static void 616*7aabcda0Sclaudio aspa_provider(uint32_t as, enum afi afi) 617*7aabcda0Sclaudio { 618*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 619*7aabcda0Sclaudio json_do_object("aspa"); 620*7aabcda0Sclaudio json_do_uint("asid", as); 621*7aabcda0Sclaudio if (afi == AFI_IPV4) 622*7aabcda0Sclaudio json_do_string("afi_limit", "ipv4"); 623*7aabcda0Sclaudio if (afi == AFI_IPV6) 624*7aabcda0Sclaudio json_do_string("afi_limit", "ipv6"); 625*7aabcda0Sclaudio json_do_end(); 626*7aabcda0Sclaudio } else { 627*7aabcda0Sclaudio printf("AS: %u", as); 628*7aabcda0Sclaudio if (afi == AFI_IPV4) 629*7aabcda0Sclaudio printf(" (IPv4 only)"); 630*7aabcda0Sclaudio if (afi == AFI_IPV6) 631*7aabcda0Sclaudio printf(" (IPv6 only)"); 632*7aabcda0Sclaudio printf("\n"); 633*7aabcda0Sclaudio } 634*7aabcda0Sclaudio } 635*7aabcda0Sclaudio 636*7aabcda0Sclaudio static void 637*7aabcda0Sclaudio aspa_providers(const struct aspa *a) 638ba54bc08Sjob { 639ba54bc08Sjob size_t i; 640*7aabcda0Sclaudio int hasv4 = 0, hasv6 = 0; 641ba54bc08Sjob 642ba54bc08Sjob for (i = 0; i < a->providersz; i++) { 643*7aabcda0Sclaudio if ((outformats & FORMAT_JSON) == 0 && i > 0) 644*7aabcda0Sclaudio printf("%26s", ""); 645*7aabcda0Sclaudio aspa_provider(a->providers[i].as, a->providers[i].afi); 646*7aabcda0Sclaudio 647ba54bc08Sjob switch (a->providers[i].afi) { 648ba54bc08Sjob case AFI_IPV4: 649*7aabcda0Sclaudio hasv4 = 1; 650ba54bc08Sjob break; 651ba54bc08Sjob case AFI_IPV6: 652*7aabcda0Sclaudio hasv6 = 1; 653ba54bc08Sjob break; 654ba54bc08Sjob default: 655*7aabcda0Sclaudio hasv4 = hasv6 = 1; 656*7aabcda0Sclaudio break; 657ba54bc08Sjob } 658ba54bc08Sjob } 659ba54bc08Sjob 660*7aabcda0Sclaudio if (!hasv4) 661*7aabcda0Sclaudio aspa_provider(0, AFI_IPV4); 662*7aabcda0Sclaudio if (!hasv6) 663*7aabcda0Sclaudio aspa_provider(0, AFI_IPV6); 664ba54bc08Sjob } 665ba54bc08Sjob 666a29ddfd5Sjob void 667a29ddfd5Sjob aspa_print(const X509 *x, const struct aspa *p) 668a29ddfd5Sjob { 669a29ddfd5Sjob if (outformats & FORMAT_JSON) { 670*7aabcda0Sclaudio json_do_string("type", "aspa"); 671*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 672a29ddfd5Sjob x509_print(x); 673*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 674*7aabcda0Sclaudio json_do_string("aia", p->aia); 675*7aabcda0Sclaudio json_do_string("sia", p->sia); 6761bb1e509Sjob if (p->signtime != 0) 677*7aabcda0Sclaudio json_do_int("signing_time", p->signtime); 678*7aabcda0Sclaudio json_do_int("valid_since", p->notbefore); 679*7aabcda0Sclaudio json_do_int("valid_until", p->notafter); 6809c1f5d6bSjob if (p->expires) 681*7aabcda0Sclaudio json_do_int("expires", p->expires); 682*7aabcda0Sclaudio json_do_uint("customer_asid", p->custasid); 683*7aabcda0Sclaudio json_do_array("provider_set"); 684a29ddfd5Sjob } else { 685a29ddfd5Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 686a29ddfd5Sjob x509_print(x); 687a29ddfd5Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 688a29ddfd5Sjob printf("Authority info access: %s\n", p->aia); 6892cf0e122Sjob printf("Subject info access: %s\n", p->sia); 6901bb1e509Sjob if (p->signtime != 0) 6911bb1e509Sjob printf("Signing time: %s\n", 6921bb1e509Sjob time2str(p->signtime)); 6934dbb22b8Sjob printf("ASPA not before: %s\n", 694f5999ddfSjob time2str(p->notbefore)); 6954dbb22b8Sjob printf("ASPA not after: %s\n", time2str(p->notafter)); 696ba54bc08Sjob printf("Customer ASID: %u\n", p->custasid); 697ba54bc08Sjob printf("Provider set: "); 698a29ddfd5Sjob } 699*7aabcda0Sclaudio 700*7aabcda0Sclaudio aspa_providers(p); 701*7aabcda0Sclaudio 702*7aabcda0Sclaudio if (outformats & FORMAT_JSON) 703*7aabcda0Sclaudio json_do_end(); 704a29ddfd5Sjob } 705ee2a33daSjob 706ee2a33daSjob static void 707ee2a33daSjob takey_print(char *name, const struct takey *t) 708ee2a33daSjob { 709ee2a33daSjob char *spki = NULL; 710ee2a33daSjob size_t i, j = 0; 711ee2a33daSjob 712ee2a33daSjob if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0) 713ee2a33daSjob errx(1, "base64_encode failed in %s", __func__); 714ee2a33daSjob 715ee2a33daSjob if (outformats & FORMAT_JSON) { 716*7aabcda0Sclaudio json_do_object("takey"); 717*7aabcda0Sclaudio json_do_string("name", name); 718*7aabcda0Sclaudio json_do_array("comments"); 719*7aabcda0Sclaudio for (i = 0; i < t->commentsz; i++) 720*7aabcda0Sclaudio json_do_string("comment", t->comments[i]); 721*7aabcda0Sclaudio json_do_end(); 722*7aabcda0Sclaudio json_do_array("uris"); 723*7aabcda0Sclaudio for (i = 0; i < t->urisz; i++) 724*7aabcda0Sclaudio json_do_string("uri", t->uris[i]); 725*7aabcda0Sclaudio json_do_end(); 726*7aabcda0Sclaudio json_do_string("spki", spki); 727ee2a33daSjob } else { 728ee2a33daSjob printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name); 729ee2a33daSjob 730*7aabcda0Sclaudio for (i = 0; i < t->commentsz; i++) 731ee2a33daSjob printf("\t# %s\n", t->comments[i]); 732*7aabcda0Sclaudio printf("\n"); 733*7aabcda0Sclaudio for (i = 0; i < t->urisz; i++) 734*7aabcda0Sclaudio printf("\t%s\n\t", t->uris[i]); 735ee2a33daSjob for (i = 0; i < strlen(spki); i++) { 736ee2a33daSjob printf("%c", spki[i]); 737*7aabcda0Sclaudio if ((++j % 64) == 0) 738ee2a33daSjob printf("\n\t"); 739ee2a33daSjob } 740ee2a33daSjob printf("\n\n"); 741ee2a33daSjob } 742ee2a33daSjob 743ee2a33daSjob free(spki); 744ee2a33daSjob } 745ee2a33daSjob 746ee2a33daSjob void 747ee2a33daSjob tak_print(const X509 *x, const struct tak *p) 748ee2a33daSjob { 749ee2a33daSjob if (outformats & FORMAT_JSON) { 750*7aabcda0Sclaudio json_do_string("type", "tak"); 751*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 752ee2a33daSjob x509_print(x); 753*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 754*7aabcda0Sclaudio json_do_string("aia", p->aia); 755*7aabcda0Sclaudio json_do_string("sia", p->sia); 7561bb1e509Sjob if (p->signtime != 0) 757*7aabcda0Sclaudio json_do_int("signing_time", p->signtime); 758*7aabcda0Sclaudio json_do_int("valid_since", p->notbefore); 759*7aabcda0Sclaudio json_do_int("valid_until", p->notafter); 760894936b4Sjob if (p->expires) 761*7aabcda0Sclaudio json_do_int("expires", p->expires); 762*7aabcda0Sclaudio json_do_array("takeys"); 763ee2a33daSjob } else { 764ee2a33daSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 765ee2a33daSjob x509_print(x); 766ee2a33daSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 767ee2a33daSjob printf("Authority info access: %s\n", p->aia); 7682cf0e122Sjob printf("Subject info access: %s\n", p->sia); 7691bb1e509Sjob if (p->signtime != 0) 7701bb1e509Sjob printf("Signing time: %s\n", 7711bb1e509Sjob time2str(p->signtime)); 7724dbb22b8Sjob printf("TAK not before: %s\n", 773f5999ddfSjob time2str(p->notbefore)); 7744dbb22b8Sjob printf("TAK not after: %s\n", time2str(p->notafter)); 775ee2a33daSjob } 776ee2a33daSjob 777ee2a33daSjob takey_print("current", p->current); 778*7aabcda0Sclaudio if (p->predecessor != NULL) 779ee2a33daSjob takey_print("predecessor", p->predecessor); 780*7aabcda0Sclaudio if (p->successor != NULL) 781ee2a33daSjob takey_print("successor", p->successor); 782ee2a33daSjob 783ee2a33daSjob if (outformats & FORMAT_JSON) 784*7aabcda0Sclaudio json_do_end(); 785ee2a33daSjob } 786ef3f6f56Sjob 787ef3f6f56Sjob void 788ef3f6f56Sjob geofeed_print(const X509 *x, const struct geofeed *p) 789ef3f6f56Sjob { 790ef3f6f56Sjob char buf[128]; 791ef3f6f56Sjob size_t i; 792ef3f6f56Sjob 793ef3f6f56Sjob if (outformats & FORMAT_JSON) { 794*7aabcda0Sclaudio json_do_string("type", "geofeed"); 795*7aabcda0Sclaudio json_do_string("ski", pretty_key_id(p->ski)); 796ef3f6f56Sjob x509_print(x); 797*7aabcda0Sclaudio json_do_string("aki", pretty_key_id(p->aki)); 798*7aabcda0Sclaudio json_do_string("aia", p->aia); 7991bb1e509Sjob if (p->signtime != 0) 800*7aabcda0Sclaudio json_do_int("signing_time", p->signtime); 801*7aabcda0Sclaudio json_do_int("valid_since", p->notbefore); 802*7aabcda0Sclaudio json_do_int("valid_until", p->notafter); 803*7aabcda0Sclaudio if (p->expires) 804*7aabcda0Sclaudio json_do_int("expires", p->expires); 805*7aabcda0Sclaudio json_do_array("records"); 806ef3f6f56Sjob } else { 807ef3f6f56Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 808ef3f6f56Sjob x509_print(x); 809ef3f6f56Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 810ef3f6f56Sjob printf("Authority info access: %s\n", p->aia); 8111bb1e509Sjob if (p->signtime != 0) 8121bb1e509Sjob printf("Signing time: %s\n", 8131bb1e509Sjob time2str(p->signtime)); 8144dbb22b8Sjob printf("Geofeed not before: %s\n", 815f5999ddfSjob time2str(p->notbefore)); 8164dbb22b8Sjob printf("Geofeed not after: %s\n", time2str(p->notafter)); 817bd19f13dSjob printf("Geofeed CSV records: "); 818ef3f6f56Sjob } 819ef3f6f56Sjob 820ef3f6f56Sjob for (i = 0; i < p->geoipsz; i++) { 821ef3f6f56Sjob if (p->geoips[i].ip->type != CERT_IP_ADDR) 822ef3f6f56Sjob continue; 823ef3f6f56Sjob 824ef3f6f56Sjob ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf, 825ef3f6f56Sjob sizeof(buf)); 826*7aabcda0Sclaudio if (outformats & FORMAT_JSON) { 827*7aabcda0Sclaudio json_do_object("geoip"); 828*7aabcda0Sclaudio json_do_string("prefix", buf); 829*7aabcda0Sclaudio json_do_string("location", p->geoips[i].loc); 830*7aabcda0Sclaudio json_do_end(); 831*7aabcda0Sclaudio } else { 8324486d057Sjob if (i > 0) 8334486d057Sjob printf("%26s", ""); 834*7aabcda0Sclaudio printf("IP: %s (%s)\n", buf, p->geoips[i].loc); 8354486d057Sjob } 836ef3f6f56Sjob } 837ef3f6f56Sjob 838ef3f6f56Sjob if (outformats & FORMAT_JSON) 839*7aabcda0Sclaudio json_do_end(); 840ef3f6f56Sjob } 841