1*cc6f004eSjob /* $OpenBSD: print.c,v 1.26 2023/01/10 13:26:34 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" 31714f4e3fSclaudio 32714f4e3fSclaudio static const char * 33254fd372Sclaudio pretty_key_id(const char *hex) 34714f4e3fSclaudio { 35714f4e3fSclaudio static char buf[128]; /* bigger than SHA_DIGEST_LENGTH * 3 */ 36714f4e3fSclaudio size_t i; 37714f4e3fSclaudio 38714f4e3fSclaudio for (i = 0; i < sizeof(buf) && *hex != '\0'; i++) { 39254fd372Sclaudio if (i % 3 == 2) 40714f4e3fSclaudio buf[i] = ':'; 41714f4e3fSclaudio else 42714f4e3fSclaudio buf[i] = *hex++; 43714f4e3fSclaudio } 44714f4e3fSclaudio if (i == sizeof(buf)) 45714f4e3fSclaudio memcpy(buf + sizeof(buf) - 4, "...", 4); 46254fd372Sclaudio else 47254fd372Sclaudio buf[i] = '\0'; 48714f4e3fSclaudio return buf; 49714f4e3fSclaudio } 50714f4e3fSclaudio 51220c707cSclaudio char * 52220c707cSclaudio time2str(time_t t) 53220c707cSclaudio { 54220c707cSclaudio static char buf[64]; 55220c707cSclaudio struct tm tm; 56220c707cSclaudio 57220c707cSclaudio if (gmtime_r(&t, &tm) == NULL) 58220c707cSclaudio return "could not convert time"; 59220c707cSclaudio 60*cc6f004eSjob strftime(buf, sizeof(buf), "%a %d %b %Y %T %z", &tm); 61*cc6f004eSjob 62220c707cSclaudio return buf; 63220c707cSclaudio } 64220c707cSclaudio 65714f4e3fSclaudio void 66714f4e3fSclaudio tal_print(const struct tal *p) 67714f4e3fSclaudio { 68e911df76Sjob char *ski; 69e911df76Sjob EVP_PKEY *pk; 70e911df76Sjob RSA *r; 71e911df76Sjob const unsigned char *der; 72e911df76Sjob unsigned char *rder = NULL; 73e911df76Sjob unsigned char md[SHA_DIGEST_LENGTH]; 74e911df76Sjob int rder_len; 75714f4e3fSclaudio size_t i; 76714f4e3fSclaudio 77e911df76Sjob der = p->pkey; 78e911df76Sjob pk = d2i_PUBKEY(NULL, &der, p->pkeysz); 79e911df76Sjob if (pk == NULL) 80e911df76Sjob errx(1, "d2i_PUBKEY failed in %s", __func__); 81e911df76Sjob 82e911df76Sjob r = EVP_PKEY_get0_RSA(pk); 83e911df76Sjob if (r == NULL) 84e911df76Sjob errx(1, "EVP_PKEY_get0_RSA failed in %s", __func__); 85e911df76Sjob if ((rder_len = i2d_RSAPublicKey(r, &rder)) <= 0) 86e911df76Sjob errx(1, "i2d_RSAPublicKey failed in %s", __func__); 87e911df76Sjob 88e911df76Sjob if (!EVP_Digest(rder, rder_len, md, NULL, EVP_sha1(), NULL)) 89e911df76Sjob errx(1, "EVP_Digest failed in %s", __func__); 90e911df76Sjob 91e911df76Sjob ski = hex_encode(md, SHA_DIGEST_LENGTH); 92e911df76Sjob 93530399e8Sjob if (outformats & FORMAT_JSON) { 94530399e8Sjob printf("\t\"type\": \"tal\",\n"); 953b3f075aSjob printf("\t\"name\": \"%s\",\n", p->descr); 963b3f075aSjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(ski)); 97530399e8Sjob printf("\t\"trust_anchor_locations\": ["); 98530399e8Sjob for (i = 0; i < p->urisz; i++) { 99530399e8Sjob printf("\"%s\"", p->uri[i]); 100530399e8Sjob if (i + 1 < p->urisz) 101530399e8Sjob printf(", "); 102530399e8Sjob } 1033b3f075aSjob printf("],\n"); 104530399e8Sjob } else { 105530399e8Sjob printf("Trust anchor name: %s\n", p->descr); 106530399e8Sjob printf("Subject key identifier: %s\n", pretty_key_id(ski)); 107e911df76Sjob printf("Trust anchor locations:\n"); 108714f4e3fSclaudio for (i = 0; i < p->urisz; i++) 109e911df76Sjob printf("%5zu: %s\n", i + 1, p->uri[i]); 110530399e8Sjob } 111e911df76Sjob 112e911df76Sjob EVP_PKEY_free(pk); 113e911df76Sjob free(rder); 114e911df76Sjob free(ski); 115714f4e3fSclaudio } 116714f4e3fSclaudio 117714f4e3fSclaudio void 118530399e8Sjob x509_print(const X509 *x) 119530399e8Sjob { 120530399e8Sjob const ASN1_INTEGER *xserial; 121e0b87278Sjob const X509_NAME *xissuer; 122e0b87278Sjob char *issuer = NULL; 123530399e8Sjob char *serial = NULL; 124530399e8Sjob 125e0b87278Sjob if ((xissuer = X509_get_issuer_name(x)) == NULL) { 126e0b87278Sjob warnx("X509_get_issuer_name failed"); 127530399e8Sjob goto out; 128530399e8Sjob } 129530399e8Sjob 130e0b87278Sjob if ((issuer = X509_NAME_oneline(xissuer, NULL, 0)) == NULL) { 131e0b87278Sjob warnx("X509_NAME_oneline failed"); 132530399e8Sjob goto out; 133530399e8Sjob } 134530399e8Sjob 135e0b87278Sjob if ((xserial = X509_get0_serialNumber(x)) == NULL) { 136e0b87278Sjob warnx("X509_get0_serialNumber failed"); 137e0b87278Sjob goto out; 138e0b87278Sjob } 139e0b87278Sjob 140e0b87278Sjob if ((serial = x509_convert_seqnum(__func__, xserial)) == NULL) 141e0b87278Sjob goto out; 142e0b87278Sjob 143530399e8Sjob if (outformats & FORMAT_JSON) { 144e0b87278Sjob printf("\t\"cert_issuer\": \"%s\",\n", issuer); 145530399e8Sjob printf("\t\"cert_serial\": \"%s\",\n", serial); 146530399e8Sjob } else { 147e0b87278Sjob printf("Certificate issuer: %s\n", issuer); 148530399e8Sjob printf("Certificate serial: %s\n", serial); 149530399e8Sjob } 150530399e8Sjob 151530399e8Sjob out: 152e0b87278Sjob free(issuer); 153530399e8Sjob free(serial); 154530399e8Sjob } 155530399e8Sjob 156530399e8Sjob void 157714f4e3fSclaudio cert_print(const struct cert *p) 158714f4e3fSclaudio { 159530399e8Sjob size_t i, j; 160714f4e3fSclaudio char buf1[64], buf2[64]; 161714f4e3fSclaudio int sockt; 162530399e8Sjob 163530399e8Sjob if (outformats & FORMAT_JSON) { 164530399e8Sjob if (p->pubkey != NULL) 165530399e8Sjob printf("\t\"type\": \"router_key\",\n"); 166530399e8Sjob else 167530399e8Sjob printf("\t\"type\": \"ca_cert\",\n"); 168530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 169530399e8Sjob if (p->aki != NULL) 170530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 171530399e8Sjob x509_print(p->x509); 172530399e8Sjob if (p->aia != NULL) 173530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 174530399e8Sjob if (p->mft != NULL) 175530399e8Sjob printf("\t\"manifest\": \"%s\",\n", p->mft); 176530399e8Sjob if (p->repo != NULL) 177530399e8Sjob printf("\t\"carepository\": \"%s\",\n", p->repo); 178530399e8Sjob if (p->notify != NULL) 179530399e8Sjob printf("\t\"notify_url\": \"%s\",\n", p->notify); 180530399e8Sjob if (p->pubkey != NULL) 181530399e8Sjob printf("\t\"router_key\": \"%s\",\n", p->pubkey); 182530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 183530399e8Sjob printf("\t\"subordinate_resources\": [\n"); 184530399e8Sjob } else { 185714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 186714f4e3fSclaudio if (p->aki != NULL) 18767642cb5Stb printf("Authority key identifier: %s\n", 18867642cb5Stb pretty_key_id(p->aki)); 189530399e8Sjob x509_print(p->x509); 190714f4e3fSclaudio if (p->aia != NULL) 191714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 192714f4e3fSclaudio if (p->mft != NULL) 193714f4e3fSclaudio printf("Manifest: %s\n", p->mft); 194714f4e3fSclaudio if (p->repo != NULL) 195714f4e3fSclaudio printf("caRepository: %s\n", p->repo); 196714f4e3fSclaudio if (p->notify != NULL) 197714f4e3fSclaudio printf("Notify URL: %s\n", p->notify); 19814d83341Sjob if (p->pubkey != NULL) { 1996530cf17Sjob printf("BGPsec ECDSA public key: %s\n", 20067642cb5Stb p->pubkey); 20128439bbcSjob printf("Router key valid until: %s\n", 20228439bbcSjob time2str(p->expires)); 20314d83341Sjob } else 20428439bbcSjob printf("Certificate valid until: %s\n", 20528439bbcSjob time2str(p->expires)); 2068aa0cba1Sjob printf("Subordinate resources:\n"); 207530399e8Sjob } 208714f4e3fSclaudio 209530399e8Sjob for (i = 0; i < p->asz; i++) { 210714f4e3fSclaudio switch (p->as[i].type) { 211714f4e3fSclaudio case CERT_AS_ID: 212530399e8Sjob if (outformats & FORMAT_JSON) 213530399e8Sjob printf("\t\t{ \"asid\": %u }", p->as[i].id); 214530399e8Sjob else 215530399e8Sjob printf("%5zu: AS: %u", i + 1, p->as[i].id); 216714f4e3fSclaudio break; 217714f4e3fSclaudio case CERT_AS_INHERIT: 218530399e8Sjob if (outformats & FORMAT_JSON) 219530399e8Sjob printf("\t\t{ \"asid_inherit\": \"true\" }"); 220530399e8Sjob else 221530399e8Sjob printf("%5zu: AS: inherit", i + 1); 222714f4e3fSclaudio break; 223714f4e3fSclaudio case CERT_AS_RANGE: 224530399e8Sjob if (outformats & FORMAT_JSON) 225530399e8Sjob printf("\t\t{ \"asrange\": { \"min\": %u, " 226530399e8Sjob "\"max\": %u }}", p->as[i].range.min, 227530399e8Sjob p->as[i].range.max); 228530399e8Sjob else 229530399e8Sjob printf("%5zu: AS: %u -- %u", i + 1, 230714f4e3fSclaudio p->as[i].range.min, p->as[i].range.max); 231714f4e3fSclaudio break; 232714f4e3fSclaudio } 233530399e8Sjob if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz) 234530399e8Sjob printf(",\n"); 235530399e8Sjob else 236530399e8Sjob printf("\n"); 237714f4e3fSclaudio } 238714f4e3fSclaudio 239530399e8Sjob for (j = 0; j < p->ipsz; j++) { 240530399e8Sjob switch (p->ips[j].type) { 241530399e8Sjob case CERT_IP_INHERIT: 242530399e8Sjob if (outformats & FORMAT_JSON) 243530399e8Sjob printf("\t\t{ \"ip_inherit\": \"true\" }"); 244530399e8Sjob else 245530399e8Sjob printf("%5zu: IP: inherit", i + j + 1); 246530399e8Sjob break; 247530399e8Sjob case CERT_IP_ADDR: 248530399e8Sjob ip_addr_print(&p->ips[j].ip, 249530399e8Sjob p->ips[j].afi, buf1, sizeof(buf1)); 250530399e8Sjob if (outformats & FORMAT_JSON) 251530399e8Sjob printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1); 252530399e8Sjob else 253530399e8Sjob printf("%5zu: IP: %s", i + j + 1, buf1); 254530399e8Sjob break; 255530399e8Sjob case CERT_IP_RANGE: 256530399e8Sjob sockt = (p->ips[j].afi == AFI_IPV4) ? 257530399e8Sjob AF_INET : AF_INET6; 258530399e8Sjob inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1)); 259530399e8Sjob inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2)); 260530399e8Sjob if (outformats & FORMAT_JSON) 261530399e8Sjob printf("\t\t{ \"ip_range\": { \"min\": \"%s\"" 262530399e8Sjob ", \"max\": \"%s\" }}", buf1, buf2); 263530399e8Sjob else 264530399e8Sjob printf("%5zu: IP: %s -- %s", i + j + 1, buf1, 265530399e8Sjob buf2); 266530399e8Sjob break; 267530399e8Sjob } 268530399e8Sjob if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz) 269530399e8Sjob printf(",\n"); 270530399e8Sjob else 271530399e8Sjob printf("\n"); 272530399e8Sjob } 273530399e8Sjob 274530399e8Sjob if (outformats & FORMAT_JSON) 275530399e8Sjob printf("\t],\n"); 276714f4e3fSclaudio } 277714f4e3fSclaudio 278714f4e3fSclaudio void 279220c707cSclaudio crl_print(const struct crl *p) 280220c707cSclaudio { 281220c707cSclaudio STACK_OF(X509_REVOKED) *revlist; 282220c707cSclaudio X509_REVOKED *rev; 2837cdd491fSclaudio ASN1_INTEGER *crlnum; 284e0b87278Sjob X509_NAME *xissuer; 285220c707cSclaudio int i; 286e0b87278Sjob char *issuer, *serial; 287220c707cSclaudio time_t t; 288220c707cSclaudio 289530399e8Sjob if (outformats & FORMAT_JSON) { 290530399e8Sjob printf("\t\"type\": \"crl\",\n"); 291530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 292530399e8Sjob } else 293220c707cSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 2947cdd491fSclaudio 295e0b87278Sjob xissuer = X509_CRL_get_issuer(p->x509_crl); 296e0b87278Sjob issuer = X509_NAME_oneline(xissuer, NULL, 0); 2977cdd491fSclaudio crlnum = X509_CRL_get_ext_d2i(p->x509_crl, NID_crl_number, NULL, NULL); 2987cdd491fSclaudio serial = x509_convert_seqnum(__func__, crlnum); 299e0b87278Sjob if (issuer != NULL && serial != NULL) { 300e0b87278Sjob if (outformats & FORMAT_JSON) { 301e0b87278Sjob printf("\t\"crl_issuer\": \"%s\",\n", issuer); 302530399e8Sjob printf("\t\"crl_serial\": \"%s\",\n", serial); 303e0b87278Sjob } else { 304e0b87278Sjob printf("CRL issuer: %s\n", issuer); 305e0b87278Sjob printf("CRL serial number: %s\n", serial); 306530399e8Sjob } 307e0b87278Sjob } 308e0b87278Sjob free(issuer); 3097cdd491fSclaudio free(serial); 3107cdd491fSclaudio ASN1_INTEGER_free(crlnum); 3117cdd491fSclaudio 312530399e8Sjob if (outformats & FORMAT_JSON) { 313530399e8Sjob printf("\t\"valid_since\": %lld,\n", (long long)p->issued); 314530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 315530399e8Sjob printf("\t\"revoked_certs\": [\n"); 316530399e8Sjob } else { 317220c707cSclaudio printf("CRL valid since: %s\n", time2str(p->issued)); 318220c707cSclaudio printf("CRL valid until: %s\n", time2str(p->expires)); 319530399e8Sjob printf("Revoked Certificates:\n"); 320530399e8Sjob } 321220c707cSclaudio 322220c707cSclaudio revlist = X509_CRL_get_REVOKED(p->x509_crl); 323220c707cSclaudio for (i = 0; i < sk_X509_REVOKED_num(revlist); i++) { 324220c707cSclaudio rev = sk_X509_REVOKED_value(revlist, i); 3257cdd491fSclaudio serial = x509_convert_seqnum(__func__, 3267cdd491fSclaudio X509_REVOKED_get0_serialNumber(rev)); 327220c707cSclaudio x509_get_time(X509_REVOKED_get0_revocationDate(rev), &t); 328530399e8Sjob if (serial != NULL) { 329530399e8Sjob if (outformats & FORMAT_JSON) { 330530399e8Sjob printf("\t\t{ \"serial\": \"%s\"", serial); 331530399e8Sjob printf(", \"date\": \"%s\" }", time2str(t)); 332530399e8Sjob if (i + 1 < sk_X509_REVOKED_num(revlist)) 333530399e8Sjob printf(","); 334530399e8Sjob printf("\n"); 335530399e8Sjob } else 336530399e8Sjob printf(" Serial: %8s Revocation Date: %s" 337530399e8Sjob "\n", serial, time2str(t)); 338530399e8Sjob } 3397cdd491fSclaudio free(serial); 340220c707cSclaudio } 341530399e8Sjob 342530399e8Sjob if (outformats & FORMAT_JSON) 343530399e8Sjob printf("\t],\n"); 344530399e8Sjob else if (i == 0) 345220c707cSclaudio printf("No Revoked Certificates\n"); 346220c707cSclaudio } 347220c707cSclaudio 348220c707cSclaudio void 349530399e8Sjob mft_print(const X509 *x, const struct mft *p) 350714f4e3fSclaudio { 351714f4e3fSclaudio size_t i; 352714f4e3fSclaudio char *hash; 353714f4e3fSclaudio 354530399e8Sjob if (outformats & FORMAT_JSON) { 355530399e8Sjob printf("\t\"type\": \"manifest\",\n"); 356530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 357530399e8Sjob x509_print(x); 358530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 359530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 3602cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 361530399e8Sjob printf("\t\"manifest_number\": \"%s\",\n", p->seqnum); 362530399e8Sjob printf("\t\"valid_since\": %lld,\n", (long long)p->valid_since); 363530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->valid_until); 364530399e8Sjob } else { 365714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 366714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 367530399e8Sjob x509_print(x); 368714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 3692cf0e122Sjob printf("Subject info access: %s\n", p->sia); 370714f4e3fSclaudio printf("Manifest Number: %s\n", p->seqnum); 371530399e8Sjob printf("Manifest valid since: %s\n", time2str(p->valid_since)); 372530399e8Sjob printf("Manifest valid until: %s\n", time2str(p->valid_until)); 3736530cf17Sjob printf("Files and hashes:\n"); 374530399e8Sjob } 375530399e8Sjob 376714f4e3fSclaudio for (i = 0; i < p->filesz; i++) { 377530399e8Sjob if (i == 0 && outformats & FORMAT_JSON) 378530399e8Sjob printf("\t\"filesandhashes\": [\n"); 379530399e8Sjob 380714f4e3fSclaudio if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 381714f4e3fSclaudio &hash) == -1) 382714f4e3fSclaudio errx(1, "base64_encode failure"); 383530399e8Sjob 384530399e8Sjob if (outformats & FORMAT_JSON) { 385530399e8Sjob printf("\t\t{ \"filename\": \"%s\",", p->files[i].file); 386530399e8Sjob printf(" \"hash\": \"%s\" }", hash); 387530399e8Sjob if (i + 1 < p->filesz) 388530399e8Sjob printf(","); 389530399e8Sjob printf("\n"); 390530399e8Sjob } else { 391714f4e3fSclaudio printf("%5zu: %s\n", i + 1, p->files[i].file); 392714f4e3fSclaudio printf("\thash %s\n", hash); 393530399e8Sjob } 394530399e8Sjob 395714f4e3fSclaudio free(hash); 396714f4e3fSclaudio } 397530399e8Sjob 398530399e8Sjob if (outformats & FORMAT_JSON) 399530399e8Sjob printf("\t],\n"); 400714f4e3fSclaudio } 401714f4e3fSclaudio 402714f4e3fSclaudio void 403530399e8Sjob roa_print(const X509 *x, const struct roa *p) 404714f4e3fSclaudio { 405714f4e3fSclaudio char buf[128]; 406714f4e3fSclaudio size_t i; 407714f4e3fSclaudio 408530399e8Sjob if (outformats & FORMAT_JSON) { 409530399e8Sjob printf("\t\"type\": \"roa\",\n"); 410530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 411530399e8Sjob x509_print(x); 412530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 413530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 4142cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 415530399e8Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 416530399e8Sjob } else { 417714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 418530399e8Sjob x509_print(x); 419714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 420714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 4212cf0e122Sjob printf("Subject info access: %s\n", p->sia); 422220c707cSclaudio printf("ROA valid until: %s\n", time2str(p->expires)); 423714f4e3fSclaudio printf("asID: %u\n", p->asid); 4242cf0e122Sjob printf("IP address blocks:\n"); 425530399e8Sjob } 426530399e8Sjob 427714f4e3fSclaudio for (i = 0; i < p->ipsz; i++) { 428530399e8Sjob if (i == 0 && outformats & FORMAT_JSON) 429530399e8Sjob printf("\t\"vrps\": [\n"); 430530399e8Sjob 431714f4e3fSclaudio ip_addr_print(&p->ips[i].addr, 432714f4e3fSclaudio p->ips[i].afi, buf, sizeof(buf)); 433530399e8Sjob 434530399e8Sjob if (outformats & FORMAT_JSON) { 435530399e8Sjob printf("\t\t{ \"prefix\": \"%s\",", buf); 436530399e8Sjob printf(" \"asid\": %u,", p->asid); 437530399e8Sjob printf(" \"maxlen\": %hhu }", p->ips[i].maxlength); 438530399e8Sjob if (i + 1 < p->ipsz) 439530399e8Sjob printf(","); 440530399e8Sjob printf("\n"); 441530399e8Sjob } else 442530399e8Sjob printf("%5zu: %s maxlen: %hhu\n", i + 1, buf, 443530399e8Sjob p->ips[i].maxlength); 444714f4e3fSclaudio } 445530399e8Sjob 446530399e8Sjob if (outformats & FORMAT_JSON) 447530399e8Sjob printf("\t],\n"); 448714f4e3fSclaudio } 449714f4e3fSclaudio 450714f4e3fSclaudio void 451530399e8Sjob gbr_print(const X509 *x, const struct gbr *p) 452714f4e3fSclaudio { 453530399e8Sjob size_t i; 454530399e8Sjob 455530399e8Sjob if (outformats & FORMAT_JSON) { 456530399e8Sjob printf("\t\"type\": \"gbr\",\n"); 457530399e8Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 458530399e8Sjob x509_print(x); 459530399e8Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 460530399e8Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 4612cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 462530399e8Sjob printf("\t\"vcard\": \""); 463530399e8Sjob for (i = 0; i < strlen(p->vcard); i++) { 464530399e8Sjob if (p->vcard[i] == '"') 465530399e8Sjob printf("\\\""); 466530399e8Sjob if (p->vcard[i] == '\r') 467530399e8Sjob continue; 468530399e8Sjob if (p->vcard[i] == '\n') 469530399e8Sjob printf("\\r\\n"); 470530399e8Sjob else 471530399e8Sjob putchar(p->vcard[i]); 472530399e8Sjob } 473530399e8Sjob printf("\",\n"); 474530399e8Sjob } else { 475714f4e3fSclaudio printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 476530399e8Sjob x509_print(x); 477714f4e3fSclaudio printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 478714f4e3fSclaudio printf("Authority info access: %s\n", p->aia); 4792cf0e122Sjob printf("Subject info access: %s\n", p->sia); 480714f4e3fSclaudio printf("vcard:\n%s", p->vcard); 481714f4e3fSclaudio } 482530399e8Sjob } 48304834fbdSjob 48404834fbdSjob void 48504834fbdSjob rsc_print(const X509 *x, const struct rsc *p) 48604834fbdSjob { 48728439bbcSjob char buf1[64], buf2[64]; 48804834fbdSjob char *hash; 48904834fbdSjob int sockt; 49004834fbdSjob size_t i, j; 49104834fbdSjob 49204834fbdSjob if (outformats & FORMAT_JSON) { 49304834fbdSjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 49404834fbdSjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 49504834fbdSjob x509_print(x); 49604834fbdSjob printf("\t\"aia\": \"%s\",\n", p->aia); 49704834fbdSjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 49804834fbdSjob printf("\t\"signed_with_resources\": [\n"); 49904834fbdSjob } else { 50004834fbdSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 50104834fbdSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 50204834fbdSjob x509_print(x); 50304834fbdSjob printf("Authority info access: %s\n", p->aia); 50428439bbcSjob printf("RSC valid until: %s\n", time2str(p->expires)); 50504834fbdSjob printf("Signed with resources:\n"); 50604834fbdSjob } 50704834fbdSjob 50804834fbdSjob for (i = 0; i < p->asz; i++) { 50904834fbdSjob switch (p->as[i].type) { 51004834fbdSjob case CERT_AS_ID: 51104834fbdSjob if (outformats & FORMAT_JSON) 51204834fbdSjob printf("\t\t{ \"asid\": %u }", p->as[i].id); 51304834fbdSjob else 51404834fbdSjob printf("%5zu: AS: %u", i + 1, p->as[i].id); 51504834fbdSjob break; 51604834fbdSjob case CERT_AS_RANGE: 51704834fbdSjob if (outformats & FORMAT_JSON) 51804834fbdSjob printf("\t\t{ \"asrange\": { \"min\": %u, " 51904834fbdSjob "\"max\": %u }}", p->as[i].range.min, 52004834fbdSjob p->as[i].range.max); 52104834fbdSjob else 52204834fbdSjob printf("%5zu: AS: %u -- %u", i + 1, 52304834fbdSjob p->as[i].range.min, p->as[i].range.max); 52404834fbdSjob break; 52504834fbdSjob case CERT_AS_INHERIT: 52604834fbdSjob /* inheritance isn't possible in RSC */ 52704834fbdSjob break; 52804834fbdSjob } 52904834fbdSjob if (outformats & FORMAT_JSON && i + 1 < p->asz + p->ipsz) 53004834fbdSjob printf(",\n"); 53104834fbdSjob else 53204834fbdSjob printf("\n"); 53304834fbdSjob } 53404834fbdSjob 53504834fbdSjob for (j = 0; j < p->ipsz; j++) { 53604834fbdSjob switch (p->ips[j].type) { 53704834fbdSjob case CERT_IP_ADDR: 53804834fbdSjob ip_addr_print(&p->ips[j].ip, 53904834fbdSjob p->ips[j].afi, buf1, sizeof(buf1)); 54004834fbdSjob if (outformats & FORMAT_JSON) 54104834fbdSjob printf("\t\t{ \"ip_prefix\": \"%s\" }", buf1); 54204834fbdSjob else 54304834fbdSjob printf("%5zu: IP: %s", i + j + 1, buf1); 54404834fbdSjob break; 54504834fbdSjob case CERT_IP_RANGE: 54604834fbdSjob sockt = (p->ips[j].afi == AFI_IPV4) ? 54704834fbdSjob AF_INET : AF_INET6; 54804834fbdSjob inet_ntop(sockt, p->ips[j].min, buf1, sizeof(buf1)); 54904834fbdSjob inet_ntop(sockt, p->ips[j].max, buf2, sizeof(buf2)); 55004834fbdSjob if (outformats & FORMAT_JSON) 55104834fbdSjob printf("\t\t{ \"ip_range\": { \"min\": \"%s\"" 55204834fbdSjob ", \"max\": \"%s\" }}", buf1, buf2); 55304834fbdSjob else 55404834fbdSjob printf("%5zu: IP: %s -- %s", i + j + 1, buf1, 55504834fbdSjob buf2); 55604834fbdSjob break; 55704834fbdSjob case CERT_IP_INHERIT: 55804834fbdSjob /* inheritance isn't possible in RSC */ 55904834fbdSjob break; 56004834fbdSjob } 56104834fbdSjob if (outformats & FORMAT_JSON && i + j + 1 < p->asz + p->ipsz) 56204834fbdSjob printf(",\n"); 56304834fbdSjob else 56404834fbdSjob printf("\n"); 56504834fbdSjob } 56604834fbdSjob 56704834fbdSjob if (outformats & FORMAT_JSON) { 56804834fbdSjob printf("\t],\n"); 56904834fbdSjob printf("\t\"filenamesandhashes\": [\n"); 57004834fbdSjob } else 57104834fbdSjob printf("Filenames and hashes:\n"); 57204834fbdSjob 57304834fbdSjob for (i = 0; i < p->filesz; i++) { 57404834fbdSjob if (base64_encode(p->files[i].hash, sizeof(p->files[i].hash), 57504834fbdSjob &hash) == -1) 57604834fbdSjob errx(1, "base64_encode failure"); 57704834fbdSjob 57804834fbdSjob if (outformats & FORMAT_JSON) { 57904834fbdSjob printf("\t\t{ \"filename\": \"%s\",", 58004834fbdSjob p->files[i].filename ? p->files[i].filename : ""); 58104834fbdSjob printf(" \"hash_digest\": \"%s\" }", hash); 58204834fbdSjob if (i + 1 < p->filesz) 58304834fbdSjob printf(","); 58404834fbdSjob printf("\n"); 58504834fbdSjob } else { 58604834fbdSjob printf("%5zu: %s\n", i + 1, p->files[i].filename 58704834fbdSjob ? p->files[i].filename : "no filename"); 58804834fbdSjob printf("\thash %s\n", hash); 58904834fbdSjob } 59004834fbdSjob 59104834fbdSjob free(hash); 59204834fbdSjob } 59304834fbdSjob 59404834fbdSjob if (outformats & FORMAT_JSON) 59504834fbdSjob printf("\t],\n"); 59604834fbdSjob } 597a29ddfd5Sjob 598a29ddfd5Sjob void 599a29ddfd5Sjob aspa_print(const X509 *x, const struct aspa *p) 600a29ddfd5Sjob { 601a29ddfd5Sjob size_t i; 602a29ddfd5Sjob 603a29ddfd5Sjob if (outformats & FORMAT_JSON) { 604a29ddfd5Sjob printf("\t\"type\": \"aspa\",\n"); 605a29ddfd5Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 606a29ddfd5Sjob x509_print(x); 607a29ddfd5Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 608a29ddfd5Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 6092cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 61028439bbcSjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 611a29ddfd5Sjob printf("\t\"customer_asid\": %u,\n", p->custasid); 612a29ddfd5Sjob printf("\t\"provider_set\": [\n"); 613a29ddfd5Sjob for (i = 0; i < p->providersz; i++) { 614a29ddfd5Sjob printf("\t\t{ \"asid\": %u", p->providers[i].as); 615a29ddfd5Sjob if (p->providers[i].afi == AFI_IPV4) 616a29ddfd5Sjob printf(", \"afi_limit\": \"ipv4\""); 617a29ddfd5Sjob if (p->providers[i].afi == AFI_IPV6) 618a29ddfd5Sjob printf(", \"afi_limit\": \"ipv6\""); 619a29ddfd5Sjob printf(" }"); 620a29ddfd5Sjob if (i + 1 < p->providersz) 621a29ddfd5Sjob printf(","); 622a29ddfd5Sjob printf("\n"); 623a29ddfd5Sjob } 624a29ddfd5Sjob printf("\t],\n"); 625a29ddfd5Sjob } else { 626a29ddfd5Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 627a29ddfd5Sjob x509_print(x); 628a29ddfd5Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 629a29ddfd5Sjob printf("Authority info access: %s\n", p->aia); 6302cf0e122Sjob printf("Subject info access: %s\n", p->sia); 63128439bbcSjob printf("ASPA valid until: %s\n", time2str(p->expires)); 632a29ddfd5Sjob printf("Customer AS: %u\n", p->custasid); 633a29ddfd5Sjob printf("Provider Set:\n"); 634a29ddfd5Sjob for (i = 0; i < p->providersz; i++) { 635a29ddfd5Sjob printf("%5zu: AS: %d", i + 1, p->providers[i].as); 636a29ddfd5Sjob switch (p->providers[i].afi) { 637a29ddfd5Sjob case AFI_IPV4: 638a29ddfd5Sjob printf(" (IPv4 only)"); 639a29ddfd5Sjob break; 640a29ddfd5Sjob case AFI_IPV6: 641a29ddfd5Sjob printf(" (IPv6 only)"); 642a29ddfd5Sjob break; 643a29ddfd5Sjob default: 644a29ddfd5Sjob break; 645a29ddfd5Sjob } 646a29ddfd5Sjob printf("\n"); 647a29ddfd5Sjob } 648a29ddfd5Sjob } 649a29ddfd5Sjob } 650ee2a33daSjob 651ee2a33daSjob static void 652ee2a33daSjob takey_print(char *name, const struct takey *t) 653ee2a33daSjob { 654ee2a33daSjob char *spki = NULL; 655ee2a33daSjob size_t i, j = 0; 656ee2a33daSjob 657ee2a33daSjob if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0) 658ee2a33daSjob errx(1, "base64_encode failed in %s", __func__); 659ee2a33daSjob 660ee2a33daSjob if (outformats & FORMAT_JSON) { 661ee2a33daSjob printf("\t\t{\n\t\t\t\"name\": \"%s\",\n", name); 662ee2a33daSjob printf("\t\t\t\"comments\": ["); 663ee2a33daSjob for (i = 0; i < t->commentsz; i++) { 664ee2a33daSjob printf("\"%s\"", t->comments[i]); 665ee2a33daSjob if (i + 1 < t->commentsz) 666ee2a33daSjob printf(", "); 667ee2a33daSjob } 668ee2a33daSjob printf("],\n"); 669ee2a33daSjob printf("\t\t\t\"uris\": ["); 670ee2a33daSjob for (i = 0; i < t->urisz; i++) { 671ee2a33daSjob printf("\"%s\"", t->uris[i]); 672ee2a33daSjob if (i + 1 < t->urisz) 673ee2a33daSjob printf(", "); 674ee2a33daSjob } 675ee2a33daSjob printf("],\n"); 676ee2a33daSjob printf("\t\t\t\"spki\": \"%s\"\n\t\t}", spki); 677ee2a33daSjob } else { 678ee2a33daSjob printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name); 679ee2a33daSjob 680ee2a33daSjob for (i = 0; i < t->commentsz; i++) { 681ee2a33daSjob printf("\t# %s\n", t->comments[i]); 682ee2a33daSjob } 683ee2a33daSjob 684ee2a33daSjob for (i = 0; i < t->urisz; i++) { 685ee2a33daSjob printf("\t%s\n\n\t", t->uris[i]); 686ee2a33daSjob } 687ee2a33daSjob 688ee2a33daSjob for (i = 0; i < strlen(spki); i++) { 689ee2a33daSjob printf("%c", spki[i]); 690ee2a33daSjob j++; 691ee2a33daSjob if (j == 64) { 692ee2a33daSjob printf("\n\t"); 693ee2a33daSjob j = 0; 694ee2a33daSjob } 695ee2a33daSjob } 696ee2a33daSjob 697ee2a33daSjob printf("\n\n"); 698ee2a33daSjob } 699ee2a33daSjob 700ee2a33daSjob free(spki); 701ee2a33daSjob } 702ee2a33daSjob 703ee2a33daSjob void 704ee2a33daSjob tak_print(const X509 *x, const struct tak *p) 705ee2a33daSjob { 706ee2a33daSjob if (outformats & FORMAT_JSON) { 707ee2a33daSjob printf("\t\"type\": \"tak\",\n"); 708ee2a33daSjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 709ee2a33daSjob x509_print(x); 710ee2a33daSjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 711ee2a33daSjob printf("\t\"aia\": \"%s\",\n", p->aia); 7122cf0e122Sjob printf("\t\"sia\": \"%s\",\n", p->sia); 713ee2a33daSjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 714ee2a33daSjob printf("\t\"takeys\": [\n"); 715ee2a33daSjob } else { 716ee2a33daSjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 717ee2a33daSjob x509_print(x); 718ee2a33daSjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 719ee2a33daSjob printf("Authority info access: %s\n", p->aia); 7202cf0e122Sjob printf("Subject info access: %s\n", p->sia); 72128439bbcSjob printf("TAK valid until: %s\n", time2str(p->expires)); 722ee2a33daSjob } 723ee2a33daSjob 724ee2a33daSjob takey_print("current", p->current); 725ee2a33daSjob 726ee2a33daSjob if (p->predecessor != NULL) { 727ee2a33daSjob if (outformats & FORMAT_JSON) 728ee2a33daSjob printf(",\n"); 729ee2a33daSjob takey_print("predecessor", p->predecessor); 730ee2a33daSjob } 731ee2a33daSjob 732ee2a33daSjob if (p->successor != NULL) { 733ee2a33daSjob if (outformats & FORMAT_JSON) 734ee2a33daSjob printf(",\n"); 735ee2a33daSjob takey_print("successor", p->successor); 736ee2a33daSjob } 737ee2a33daSjob 738ee2a33daSjob if (outformats & FORMAT_JSON) 739ee2a33daSjob printf("\n\t],\n"); 740ee2a33daSjob } 741ef3f6f56Sjob 742ef3f6f56Sjob void 743ef3f6f56Sjob geofeed_print(const X509 *x, const struct geofeed *p) 744ef3f6f56Sjob { 745ef3f6f56Sjob char buf[128]; 746ef3f6f56Sjob size_t i; 747ef3f6f56Sjob 748ef3f6f56Sjob if (outformats & FORMAT_JSON) { 749ef3f6f56Sjob printf("\t\"type\": \"geofeed\",\n"); 750ef3f6f56Sjob printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); 751ef3f6f56Sjob x509_print(x); 752ef3f6f56Sjob printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); 753ef3f6f56Sjob printf("\t\"aia\": \"%s\",\n", p->aia); 754ef3f6f56Sjob printf("\t\"valid_until\": %lld,\n", (long long)p->expires); 755ef3f6f56Sjob printf("\t\"records\": [\n"); 756ef3f6f56Sjob } else { 757ef3f6f56Sjob printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); 758ef3f6f56Sjob x509_print(x); 759ef3f6f56Sjob printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); 760ef3f6f56Sjob printf("Authority info access: %s\n", p->aia); 761ef3f6f56Sjob printf("Geofeed valid until: %s\n", time2str(p->expires)); 762ef3f6f56Sjob printf("Geofeed CSV records:\n"); 763ef3f6f56Sjob } 764ef3f6f56Sjob 765ef3f6f56Sjob for (i = 0; i < p->geoipsz; i++) { 766ef3f6f56Sjob if (p->geoips[i].ip->type != CERT_IP_ADDR) 767ef3f6f56Sjob continue; 768ef3f6f56Sjob 769ef3f6f56Sjob ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf, 770ef3f6f56Sjob sizeof(buf)); 771ef3f6f56Sjob if (outformats & FORMAT_JSON) 772ef3f6f56Sjob printf("\t\t{ \"prefix\": \"%s\", \"location\": \"%s\"" 773ef3f6f56Sjob "}", buf, p->geoips[i].loc); 774ef3f6f56Sjob else 775ef3f6f56Sjob printf("%5zu: IP: %s (%s)", i + 1, buf, 776ef3f6f56Sjob p->geoips[i].loc); 777ef3f6f56Sjob 778ef3f6f56Sjob if (outformats & FORMAT_JSON && i + 1 < p->geoipsz) 779ef3f6f56Sjob printf(",\n"); 780ef3f6f56Sjob else 781ef3f6f56Sjob printf("\n"); 782ef3f6f56Sjob } 783ef3f6f56Sjob 784ef3f6f56Sjob if (outformats & FORMAT_JSON) 785ef3f6f56Sjob printf("\t],\n"); 786ef3f6f56Sjob } 787